Skip to content

Commit

Permalink
add ability to set date field format
Browse files Browse the repository at this point in the history
  • Loading branch information
omohokcoj committed Dec 17, 2023
1 parent 990e402 commit e558f4d
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 6 deletions.
38 changes: 37 additions & 1 deletion app/javascript/submission_form/area.vue
Original file line number Diff line number Diff line change
Expand Up @@ -273,9 +273,15 @@ export default {
return null
}
},
locale () {
return Intl.DateTimeFormat().resolvedOptions()?.locale
},
formattedDate () {
if (this.field.type === 'date' && this.modelValue) {
return new Intl.DateTimeFormat([], { year: 'numeric', month: 'long', day: 'numeric', timeZone: 'UTC' }).format(new Date(this.modelValue))
return this.formatDate(
new Date(this.modelValue),
this.field.preferences?.format || (this.locale.endsWith('-US') ? 'MM/DD/YYYY' : 'DD/MM/YYYY')
)
} else {
return ''
}
Expand Down Expand Up @@ -315,6 +321,36 @@ export default {
}
},
methods: {
formatDate (date, format) {
const monthFormats = {
M: 'numeric',
MM: '2-digit',
MMM: 'short',
MMMM: 'long'
}
const dayFormats = {
D: 'numeric',
DD: '2-digit'
}
const yearFormats = {
YYYY: 'numeric',
YY: '2-digit'
}
const parts = new Intl.DateTimeFormat([], {
day: dayFormats[format.match(/D+/)],
month: monthFormats[format.match(/M+/)],
year: yearFormats[format.match(/Y+/)],
timeZone: 'UTC'
}).formatToParts(date)
return format
.replace(/D+/, parts.find((p) => p.type === 'day').value)
.replace(/M+/, parts.find((p) => p.type === 'month').value)
.replace(/Y+/, parts.find((p) => p.type === 'year').value)
},
updateMultipleSelectValue (value) {
if (this.modelValue?.includes(value)) {
const newValue = [...this.modelValue]
Expand Down
12 changes: 12 additions & 0 deletions app/javascript/template_builder/builder.vue
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,12 @@ export default {
field.options = [{ value: '', uuid: v4() }]
}
if (type === 'date') {
field.preferences = {
format: Intl.DateTimeFormat().resolvedOptions().locale.endsWith('-US') ? 'MM/DD/YYYY' : 'DD/MM/YYYY'
}
}
this.drawField = field
this.drawOption = null
},
Expand Down Expand Up @@ -650,6 +656,12 @@ export default {
field.options = [{ value: '', uuid: v4() }]
}
if (field.type === 'date') {
field.preferences = {
format: Intl.DateTimeFormat().resolvedOptions().locale.endsWith('-US') ? 'MM/DD/YYYY' : 'DD/MM/YYYY'
}
}
const fieldArea = {
x: (area.x - 6) / area.maskW,
y: area.y / area.maskH,
Expand Down
77 changes: 77 additions & 0 deletions app/javascript/template_builder/field.vue
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,33 @@
Default value
</label>
</div>
<div
v-if="field.type === 'date'"
class="py-1.5 px-1 relative"
@click.stop
>
<select
v-model="field.preferences.format"
placeholder="Format"
class="select select-bordered select-xs font-normal w-full max-w-xs !h-7 !outline-0"
@change="save"
>
<option
v-for="format in dateFormats"
:key="format"
:value="format"
>
{{ formatDate(new Date(), format) }}
</option>
</select>
<label
:style="{ backgroundColor: backgroundColor }"
class="absolute -top-1 left-2.5 px-1 h-4"
style="font-size: 8px"
>
Format
</label>
</div>
<li
v-if="field.type != 'phone'"
@click.stop
Expand Down Expand Up @@ -307,6 +334,19 @@ export default {
},
computed: {
fieldNames: FieldType.computed.fieldNames,
dateFormats () {
return [
'MM/DD/YYYY',
'DD/MM/YYYY',
'YYYY-MM-DD',
'DD-MM-YYYY',
'DD.MM.YYYY',
'MMM D, YYYY',
'MMMM D, YYYY',
'D MMM YYYY',
'D MMMM YYYY'
]
},
defaultName () {
if (this.field.type === 'payment' && this.field.preferences?.price) {
const { price, currency } = this.field.preferences || {}
Expand All @@ -329,7 +369,44 @@ export default {
return this.field.areas || []
}
},
created () {
this.field.preferences ||= {}
if (this.field.type === 'date') {
this.field.preferences.format ||=
(Intl.DateTimeFormat().resolvedOptions().locale.endsWith('-US') ? 'MM/DD/YYYY' : 'DD/MM/YYYY')
}
},
methods: {
formatDate (date, format) {
const monthFormats = {
M: 'numeric',
MM: '2-digit',
MMM: 'short',
MMMM: 'long'
}
const dayFormats = {
D: 'numeric',
DD: '2-digit'
}
const yearFormats = {
YYYY: 'numeric',
YY: '2-digit'
}
const parts = new Intl.DateTimeFormat([], {
day: dayFormats[format.match(/D+/)],
month: monthFormats[format.match(/M+/)],
year: yearFormats[format.match(/Y+/)]
}).formatToParts(date)
return format
.replace(/D+/, parts.find((p) => p.type === 'day').value)
.replace(/M+/, parts.find((p) => p.type === 'month').value)
.replace(/Y+/, parts.find((p) => p.type === 'year').value)
},
copyToAllPages (field) {
const areaString = JSON.stringify(field.areas[0])
Expand Down
6 changes: 6 additions & 0 deletions app/javascript/template_builder/fields.vue
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,12 @@ export default {
field.options = [{ value: '', uuid: v4() }]
}
if (type === 'date') {
field.preferences = {
format: Intl.DateTimeFormat().resolvedOptions().locale.endsWith('-US') ? 'MM/DD/YYYY' : 'DD/MM/YYYY'
}
}
this.fields.push(field)
if (['signature', 'initials', 'cells'].includes(type)) {
Expand Down
2 changes: 1 addition & 1 deletion app/views/submissions/_value.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
<% elsif field['type'] == 'date' %>
<autosize-field></autosize-field>
<div class="flex items-center px-0.5">
<%= l(Date.parse(value), format: :long, locale: local_assigns[:locale]) %>
<%= TimeUtils.format_date_string(value, field.dig('preferences', 'format'), local_assigns[:locale]) %>
</div>
<% else %>
<autosize-field></autosize-field>
Expand Down
2 changes: 1 addition & 1 deletion app/views/submissions/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@
<% elsif field['type'] == 'checkbox' %>
<%= svg_icon('check', class: 'w-6 h-6') %>
<% elsif field['type'] == 'date' %>
<%= l(Date.parse(value), locale: @submission.template.account.locale, format: :long) %>
<%= TimeUtils.format_date_string(value, field.dig('preferences', 'format'), @submission.template.account.locale) %>
<% else %>
<%= Array.wrap(value).join(', ') %>
<% end %>
Expand Down
5 changes: 4 additions & 1 deletion lib/submissions/generate_audit_trail.rb
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,10 @@ def call(submission)
elsif field['type'] == 'checkbox'
composer.formatted_text_box([{ text: value.to_s.titleize }], padding: [0, 0, 10, 0])
else
value = I18n.l(Date.parse(value), format: :long, locale: account.locale) if field['type'] == 'date'
if field['type'] == 'date'
value = TimeUtils.format_date_string(value, field.dig('preferences', 'format'), account.locale)
end

value = value.join(', ') if value.is_a?(Array)

composer.formatted_text_box([{ text: value.to_s.presence || 'n/a' }], padding: [0, 0, 10, 0])
Expand Down
6 changes: 4 additions & 2 deletions lib/submissions/generate_result_attachments.rb
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,9 @@ def call(submitter)
height - (area['y'] * height))
end
else
value = I18n.l(Date.parse(value), format: :default, locale: account.locale) if field['type'] == 'date'
if field['type'] == 'date'
value = TimeUtils.format_date_string(value, field.dig('preferences', 'format'), account.locale)
end

text = HexaPDF::Layout::TextFragment.create(Array.wrap(value).join(', '), font: pdf.fonts.add(FONT_NAME),
font_size:)
Expand Down Expand Up @@ -268,7 +270,7 @@ def build_pdfs_index(submitter)
Submissions::EnsureResultGenerated.call(latest_submitter) if latest_submitter

documents = latest_submitter&.documents&.preload(:blob).to_a.presence
documents ||= submitter.submission.template.schema_documents.preload(:blob)
documents ||= submitter.submission.template_schema_documents.preload(:blob)

documents.to_h do |attachment|
pdf =
Expand Down
36 changes: 36 additions & 0 deletions lib/time_utils.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
# frozen_string_literal: true

module TimeUtils
MONTH_FORMATS = {
'M' => '%-m',
'MM' => '%m',
'MMM' => '%b',
'MMMM' => '%B'
}.freeze

DAY_FORMATS = {
'D' => '%-d',
'DD' => '%d'
}.freeze

YEAR_FORMATS = {
'YYYY' => '%Y',
'YY' => '%y'
}.freeze

DEFAULT_DATE_FORMAT_US = 'MM/DD/YYYY'
DEFAULT_DATE_FORMAT = 'DD/MM/YYYY'

module_function

def timezone_abbr(timezone, time = Time.current)
Expand All @@ -10,4 +30,20 @@ def timezone_abbr(timezone, time = Time.current)

tz_info.abbreviation(time)
end

def format_date_string(string, format, locale)
date = Date.parse(string)

format ||= locale.to_s.ends_with?('US') ? DEFAULT_DATE_FORMAT_US : DEFAULT_DATE_FORMAT

i18n_format = format.sub(/D+/, DAY_FORMATS[format[/D+/]])
.sub(/M+/, MONTH_FORMATS[format[/M+/]])
.sub(/Y+/, YEAR_FORMATS[format[/Y+/]])

I18n.l(date, format: i18n_format, locale:)
rescue Date::Error => e
Rollbar.error(e) if defined?(Rollbar)

string
end
end

0 comments on commit e558f4d

Please sign in to comment.