From 99ca0136ed2ee5aa1cfd8eddb153a66c208e81dc Mon Sep 17 00:00:00 2001 From: Pete Matsyburka Date: Fri, 15 May 2026 20:07:29 +0300 Subject: [PATCH] add percent format --- app/javascript/submission_form/area.vue | 4 +++ app/javascript/template_builder/area.vue | 4 +++ .../template_builder/dynamic_variable.vue | 27 ++++++++++++------- .../template_builder/field_context_menu.vue | 2 +- .../template_builder/field_settings.vue | 27 ++++++++++++------- lib/number_utils.rb | 4 +++ 6 files changed, 47 insertions(+), 21 deletions(-) diff --git a/app/javascript/submission_form/area.vue b/app/javascript/submission_form/area.vue index 69a453ac..9cfa44a1 100644 --- a/app/javascript/submission_form/area.vue +++ b/app/javascript/submission_form/area.vue @@ -643,6 +643,10 @@ export default { return new Intl.NumberFormat('de-DE').format(number) } else if (format === 'space') { return new Intl.NumberFormat('fr-FR').format(number) + } else if (format === 'percent') { + return `${number}%` + } else if (format === 'percent_space') { + return `${String(number).replace('.', ',')} %` } else { return number } diff --git a/app/javascript/template_builder/area.vue b/app/javascript/template_builder/area.vue index 3e7e20a6..06a3eb9b 100644 --- a/app/javascript/template_builder/area.vue +++ b/app/javascript/template_builder/area.vue @@ -620,6 +620,10 @@ export default { return new Intl.NumberFormat('de-DE').format(number) } else if (format === 'space') { return new Intl.NumberFormat('fr-FR').format(number) + } else if (format === 'percent') { + return `${number}%` + } else if (format === 'percent_space') { + return `${String(number).replace('.', ',')} %` } else { return number } diff --git a/app/javascript/template_builder/dynamic_variable.vue b/app/javascript/template_builder/dynamic_variable.vue index d0ad3ac2..9d9f2498 100644 --- a/app/javascript/template_builder/dynamic_variable.vue +++ b/app/javascript/template_builder/dynamic_variable.vue @@ -248,7 +248,7 @@ export default { FieldType, IconSettings }, - inject: ['t', 'save', 'backgroundColor', 'dateFormats'], + inject: ['t', 'save', 'backgroundColor', 'dateFormats', 'locale'], provide () { return { fieldTypes: ['text', 'number', 'date', 'checkbox', 'radio', 'select'] @@ -308,15 +308,18 @@ export default { return { text: 'string', number: 'number', date: 'date', checkbox: 'boolean', radio: 'string', select: 'string' } }, numberFormats () { - return [ - 'none', - 'usd', - 'eur', - 'gbp', - 'comma', - 'dot', - 'space' - ] + const formats = ['none', 'usd', 'eur', 'gbp', 'comma', 'dot', 'space'] + const spaceLocales = ['fr-FR', 'es-ES', 'pt-PT', 'de-DE', 'it-IT', 'nl-NL'] + + formats.push(spaceLocales.includes(this.locale) ? 'percent_space' : 'percent') + + const selectedFormat = this.schema.format + + if (selectedFormat && !formats.includes(selectedFormat)) { + formats.push(selectedFormat) + } + + return formats }, availableDateFormats () { const formats = this.dateFormats.length @@ -397,6 +400,10 @@ export default { return new Intl.NumberFormat('de-DE').format(number) } else if (format === 'space') { return new Intl.NumberFormat('fr-FR').format(number) + } else if (format === 'percent') { + return `${number}%` + } else if (format === 'percent_space') { + return `${String(number).replace('.', ',')} %` } else { return number } diff --git a/app/javascript/template_builder/field_context_menu.vue b/app/javascript/template_builder/field_context_menu.vue index 388ee80e..40566957 100644 --- a/app/javascript/template_builder/field_context_menu.vue +++ b/app/javascript/template_builder/field_context_menu.vue @@ -514,7 +514,7 @@ export default { ContextSubmenu, ContextModal }, - inject: ['t', 'getFieldTypeIndex', 'template', 'withCustomFields', 'currencies', 'dateFormats'], + inject: ['t', 'getFieldTypeIndex', 'template', 'withCustomFields', 'currencies', 'dateFormats', 'locale'], props: { contextMenu: { type: Object, diff --git a/app/javascript/template_builder/field_settings.vue b/app/javascript/template_builder/field_settings.vue index 4b5a71f0..48bfed16 100644 --- a/app/javascript/template_builder/field_settings.vue +++ b/app/javascript/template_builder/field_settings.vue @@ -610,7 +610,7 @@ export default { IconTypography, IconX }, - inject: ['template', 't', 'dateFormats'], + inject: ['template', 't', 'dateFormats', 'locale'], props: { field: { type: Object, @@ -691,15 +691,18 @@ export default { } }, numberFormats () { - return [ - 'none', - 'usd', - 'eur', - 'gbp', - 'comma', - 'dot', - 'space' - ] + const formats = ['none', 'usd', 'eur', 'gbp', 'comma', 'dot', 'space'] + const spaceLocales = ['fr-FR', 'es-ES', 'pt-PT', 'de-DE', 'it-IT', 'nl-NL'] + + formats.push(spaceLocales.includes(this.locale) ? 'percent_space' : 'percent') + + const selectedFormat = this.field.preferences?.format + + if (selectedFormat && !formats.includes(selectedFormat)) { + formats.push(selectedFormat) + } + + return formats }, availableDateFormats () { const formats = this.dateFormats.length @@ -824,6 +827,10 @@ export default { return new Intl.NumberFormat('de-DE').format(number) } else if (format === 'space') { return new Intl.NumberFormat('fr-FR').format(number) + } else if (format === 'percent') { + return `${number}%` + } else if (format === 'percent_space') { + return `${String(number).replace('.', ',')} %` } else { return number } diff --git a/lib/number_utils.rb b/lib/number_utils.rb index 4cfa3879..946ea472 100644 --- a/lib/number_utils.rb +++ b/lib/number_utils.rb @@ -25,6 +25,10 @@ module NumberUtils ApplicationController.helpers.number_to_currency(number, locale:, precision: 2, unit: CURRENCY_SYMBOLS[format]) elsif locale ApplicationController.helpers.number_with_delimiter(number, locale:) + elsif format == 'percent' + "#{number}%" + elsif format == 'percent_space' + "#{number.to_s.tr('.', ',')} %" else number end