diff --git a/app/controllers/account_configs_controller.rb b/app/controllers/account_configs_controller.rb index 66683e3f..cf128ef9 100644 --- a/app/controllers/account_configs_controller.rb +++ b/app/controllers/account_configs_controller.rb @@ -19,6 +19,7 @@ class AccountConfigsController < ApplicationController AccountConfig::FORCE_SSO_AUTH_KEY, AccountConfig::FLATTEN_RESULT_PDF_KEY, AccountConfig::ENFORCE_SIGNING_ORDER_KEY, + AccountConfig::WITH_FILE_LINKS_KEY, AccountConfig::WITH_SIGNATURE_ID, AccountConfig::COMBINE_PDF_RESULT_KEY, AccountConfig::REQUIRE_SIGNING_REASON_KEY, diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7500acdb..cd8d228c 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -13,6 +13,8 @@ class ApplicationController < ActionController::Base before_action :maybe_redirect_to_setup, unless: :signed_in? before_action :authenticate_user!, unless: :devise_controller? + before_action :set_csp, if: -> { request.get? && !request.headers['HTTP_X_TURBO'] } + helper_method :button_title, :current_account, :form_link_host, @@ -123,4 +125,21 @@ class ApplicationController < ActionController::Base redirect_to request.url.gsub('.co/', '.com/'), allow_other_host: true, status: :moved_permanently end + + def set_csp + request.content_security_policy = current_content_security_policy.tap do |policy| + policy.default_src :self + policy.script_src :self + policy.style_src :self, :unsafe_inline + policy.img_src :self, :https, :http, :blob, :data + policy.font_src :self, :https, :http, :blob, :data + policy.manifest_src :self + policy.media_src :self + policy.frame_src :self + policy.worker_src :self, :blob + policy.connect_src :self + + policy.directives['connect-src'] << 'ws:' if Rails.env.development? + end + end end diff --git a/app/javascript/application.js b/app/javascript/application.js index 7b51186d..a9fb02d0 100644 --- a/app/javascript/application.js +++ b/app/javascript/application.js @@ -23,6 +23,7 @@ import SignatureForm from './elements/signature_form' import SubmitForm from './elements/submit_form' import PromptPassword from './elements/prompt_password' import EmailsTextarea from './elements/emails_textarea' +import ToggleSubmit from './elements/toggle_submit' import ToggleOnSubmit from './elements/toggle_on_submit' import CheckOnClick from './elements/check_on_click' import PasswordInput from './elements/password_input' @@ -34,11 +35,20 @@ import MaskedInput from './elements/masked_input' import SetDateButton from './elements/set_date_button' import IndeterminateCheckbox from './elements/indeterminate_checkbox' import AppTour from './elements/app_tour' +import AppTourStart from './elements/app_tour_start' import DashboardDropzone from './elements/dashboard_dropzone' import RequiredCheckboxGroup from './elements/required_checkbox_group' import PageContainer from './elements/page_container' import EmailEditor from './elements/email_editor' import MountOnClick from './elements/mount_on_click' +import RemoveOnEvent from './elements/remove_on_event' +import ScrollTo from './elements/scroll_to' +import SetValue from './elements/set_value' +import ReviewForm from './elements/review_form' +import ShowOnValue from './elements/show_on_value' +import CustomValidation from './elements/custom_validation' +import ToggleClasses from './elements/toggle_classes' +import AutosizeField from './elements/autosize_field' import * as TurboInstantClick from './lib/turbo_instant_click' @@ -55,6 +65,9 @@ document.addEventListener('keyup', (e) => { }) document.addEventListener('turbo:before-fetch-request', encodeMethodIntoRequestBody) +document.addEventListener('turbo:before-fetch-request', (event) => { + event.detail.fetchOptions.headers['X-Turbo'] = 'true' +}) document.addEventListener('turbo:submit-end', async (event) => { const resp = event.detail?.formSubmission?.result?.fetchResponse?.response @@ -97,6 +110,7 @@ safeRegisterElement('submit-form', SubmitForm) safeRegisterElement('prompt-password', PromptPassword) safeRegisterElement('emails-textarea', EmailsTextarea) safeRegisterElement('toggle-cookies', ToggleCookies) +safeRegisterElement('toggle-submit', ToggleSubmit) safeRegisterElement('toggle-on-submit', ToggleOnSubmit) safeRegisterElement('password-input', PasswordInput) safeRegisterElement('search-input', SearchInput) @@ -107,12 +121,21 @@ safeRegisterElement('masked-input', MaskedInput) safeRegisterElement('set-date-button', SetDateButton) safeRegisterElement('indeterminate-checkbox', IndeterminateCheckbox) safeRegisterElement('app-tour', AppTour) +safeRegisterElement('app-tour-start', AppTourStart) safeRegisterElement('dashboard-dropzone', DashboardDropzone) safeRegisterElement('check-on-click', CheckOnClick) safeRegisterElement('required-checkbox-group', RequiredCheckboxGroup) safeRegisterElement('page-container', PageContainer) safeRegisterElement('email-editor', EmailEditor) safeRegisterElement('mount-on-click', MountOnClick) +safeRegisterElement('remove-on-event', RemoveOnEvent) +safeRegisterElement('scroll-to', ScrollTo) +safeRegisterElement('set-value', SetValue) +safeRegisterElement('review-form', ReviewForm) +safeRegisterElement('show-on-value', ShowOnValue) +safeRegisterElement('custom-validation', CustomValidation) +safeRegisterElement('toggle-classes', ToggleClasses) +safeRegisterElement('autosize-field', AutosizeField) safeRegisterElement('template-builder', class extends HTMLElement { connectedCallback () { diff --git a/app/javascript/application.scss b/app/javascript/application.scss index 1ab92498..47eeb4f6 100644 --- a/app/javascript/application.scss +++ b/app/javascript/application.scss @@ -19,7 +19,7 @@ button .disabled { display: none; } -button[disabled] .disabled { +button[disabled] .disabled, button.btn-disabled .disabled { display: initial; } @@ -27,7 +27,7 @@ button .enabled { display: initial; } -button[disabled] .enabled { +button[disabled] .enabled, button.btn-disabled .enabled { display: none; } diff --git a/app/javascript/elements/app_tour_start.js b/app/javascript/elements/app_tour_start.js new file mode 100644 index 00000000..1b06becb --- /dev/null +++ b/app/javascript/elements/app_tour_start.js @@ -0,0 +1,7 @@ +export default class extends HTMLElement { + connectedCallback () { + this.querySelector('form').addEventListener('submit', () => { + window.app_tour.start() + }) + } +} diff --git a/app/javascript/elements/autosize_field.js b/app/javascript/elements/autosize_field.js new file mode 100644 index 00000000..b6f56763 --- /dev/null +++ b/app/javascript/elements/autosize_field.js @@ -0,0 +1,21 @@ +export default class extends HTMLElement { + connectedCallback () { + const originalFontValue = this.field.style.fontSize + + if (this.field.scrollHeight > this.field.clientHeight) { + this.field.style.fontSize = `calc(${originalFontValue} / 1.5)` + this.field.style.lineHeight = `calc(${this.field.style.fontSize} * 1.3)` + + if (this.field.scrollHeight > this.field.clientHeight) { + this.field.style.fontSize = `calc(${originalFontValue} / 2.0)` + this.field.style.lineHeight = `calc(${this.field.style.fontSize} * 1.3)` + } + } + + this.field.classList.remove('hidden') + } + + get field () { + return this.closest('field-value') + } +} diff --git a/app/javascript/elements/custom_validation.js b/app/javascript/elements/custom_validation.js new file mode 100644 index 00000000..ac738679 --- /dev/null +++ b/app/javascript/elements/custom_validation.js @@ -0,0 +1,14 @@ +export default class extends HTMLElement { + connectedCallback () { + const input = this.querySelector('input') + const invalidMessage = this.dataset.invalidMessage || '' + + input.addEventListener('invalid', () => { + input.setCustomValidity(input.value ? invalidMessage : '') + }) + + input.addEventListener('input', () => { + input.setCustomValidity('') + }) + } +} diff --git a/app/javascript/elements/remove_on_event.js b/app/javascript/elements/remove_on_event.js new file mode 100644 index 00000000..e912e4cd --- /dev/null +++ b/app/javascript/elements/remove_on_event.js @@ -0,0 +1,15 @@ +export default class extends HTMLElement { + connectedCallback () { + const eventType = this.dataset.on || 'click' + const selector = document.getElementById(this.dataset.selectorId) || this + const eventElement = eventType === 'submit' ? this.querySelector('form') : this + + eventElement.addEventListener(eventType, (event) => { + if (eventType === 'click') { + event.preventDefault() + } + + selector.remove() + }) + } +} diff --git a/app/javascript/elements/review_form.js b/app/javascript/elements/review_form.js new file mode 100644 index 00000000..537fe602 --- /dev/null +++ b/app/javascript/elements/review_form.js @@ -0,0 +1,19 @@ +export default class extends HTMLElement { + connectedCallback () { + this.querySelectorAll('input[type="radio"]').forEach(radio => { + radio.addEventListener('change', (event) => { + const rating = parseInt(event.target.value) + + if (rating === 10) { + window.review_comment.value = '' + window.review_comment.classList.add('hidden') + window.review_submit.classList.add('hidden') + event.target.form.submit() + } else { + window.review_comment.classList.remove('hidden') + window.review_submit.classList.remove('hidden') + } + }) + }) + } +} diff --git a/app/javascript/elements/scroll_to.js b/app/javascript/elements/scroll_to.js new file mode 100644 index 00000000..ded724b5 --- /dev/null +++ b/app/javascript/elements/scroll_to.js @@ -0,0 +1,10 @@ +export default class extends HTMLElement { + connectedCallback () { + this.selector = document.getElementById(this.dataset.selectorId) + + this.addEventListener('click', () => { + this.selector.scrollIntoView({ behavior: 'smooth', block: 'start' }) + history.replaceState(null, null, `#${this.dataset.selectorId}`) + }) + } +} diff --git a/app/javascript/elements/search_input.js b/app/javascript/elements/search_input.js index 9b536ab3..585f7122 100644 --- a/app/javascript/elements/search_input.js +++ b/app/javascript/elements/search_input.js @@ -13,6 +13,14 @@ export default class extends HTMLElement { this.input.classList.remove('w-60') } }) + + this.button.addEventListener('click', (event) => { + if (!this.input.value && document.activeElement !== this.input) { + event.preventDefault() + + this.input.focus() + } + }) } get input () { @@ -22,4 +30,8 @@ export default class extends HTMLElement { get title () { return document.querySelector(this.dataset.title) } + + get button () { + return this.querySelector('button') + } } diff --git a/app/javascript/elements/set_value.js b/app/javascript/elements/set_value.js new file mode 100644 index 00000000..49e05c9c --- /dev/null +++ b/app/javascript/elements/set_value.js @@ -0,0 +1,11 @@ +export default class extends HTMLElement { + connectedCallback () { + const input = this.dataset.inputId ? document.getElementById(this.dataset.inputId) : this.querySelector('input') + + this.firstElementChild.addEventListener(this.dataset.on || 'click', () => { + if (this.dataset.emptyOnly !== 'true' || !input.value) { + input.value = this.dataset.value + } + }) + } +} diff --git a/app/javascript/elements/show_on_value.js b/app/javascript/elements/show_on_value.js new file mode 100644 index 00000000..c969d823 --- /dev/null +++ b/app/javascript/elements/show_on_value.js @@ -0,0 +1,17 @@ +export default class extends HTMLElement { + connectedCallback () { + this.addEventListener('change', (event) => { + const targetValue = this.dataset.value + const selectorId = this.dataset.selectorId + const targetElement = document.getElementById(selectorId) + + if (event.target.value === targetValue) { + targetElement.classList.remove('hidden') + } else { + targetElement.classList.add('hidden') + targetElement.value = '' + event.target.form.requestSubmit() + } + }) + } +} diff --git a/app/javascript/elements/submit_form.js b/app/javascript/elements/submit_form.js index 164a069e..e936c0bd 100644 --- a/app/javascript/elements/submit_form.js +++ b/app/javascript/elements/submit_form.js @@ -1,15 +1,27 @@ export default class extends HTMLElement { connectedCallback () { + const form = this.querySelector('form') || (this.querySelector('input, button, select') || this.lastElementChild).form + if (this.dataset.interval) { this.interval = setInterval(() => { - this.querySelector('form').requestSubmit() + form.requestSubmit() }, parseInt(this.dataset.interval)) } else if (this.dataset.on) { - this.lastElementChild.addEventListener(this.dataset.on, () => { - this.lastElementChild.form.requestSubmit() + this.lastElementChild.addEventListener(this.dataset.on, (event) => { + if (this.dataset.disable === 'true') { + form.querySelector('[type="submit"]')?.setAttribute('disabled', true) + } + + if (this.dataset.submitIfValue === 'true') { + if (event.target.value) { + form.requestSubmit() + } + } else { + form.requestSubmit() + } }) } else { - this.querySelector('form').requestSubmit() + form.requestSubmit() } } diff --git a/app/javascript/elements/toggle_classes.js b/app/javascript/elements/toggle_classes.js new file mode 100644 index 00000000..ab96f293 --- /dev/null +++ b/app/javascript/elements/toggle_classes.js @@ -0,0 +1,11 @@ +export default class extends HTMLElement { + connectedCallback () { + const button = this.querySelector('a, button') + + button.addEventListener('click', () => { + this.dataset.classes.split(' ').forEach((cls) => { + button.classList.toggle(cls) + }) + }) + } +} diff --git a/app/javascript/elements/toggle_visible.js b/app/javascript/elements/toggle_visible.js index a796b9c6..7fa968f2 100644 --- a/app/javascript/elements/toggle_visible.js +++ b/app/javascript/elements/toggle_visible.js @@ -4,9 +4,15 @@ export default actionable(class extends HTMLElement { trigger (event) { const elementIds = JSON.parse(this.dataset.elementIds) - elementIds.forEach((elementId) => { - document.getElementById(elementId).classList.toggle('hidden', (event.target.dataset.toggleId || event.target.value) !== elementId) - }) + if (event.target.type === 'checkbox') { + elementIds.forEach((elementId) => { + document.getElementById(elementId)?.classList.toggle('hidden') + }) + } else { + elementIds.forEach((elementId) => { + document.getElementById(elementId).classList.toggle('hidden', (event.target.dataset.toggleId || event.target.value) !== elementId) + }) + } if (this.dataset.focusId) { document.getElementById(this.dataset.focusId)?.focus() diff --git a/app/javascript/form.js b/app/javascript/form.js index ac49174d..b8253f86 100644 --- a/app/javascript/form.js +++ b/app/javascript/form.js @@ -6,6 +6,7 @@ import ToggleSubmit from './elements/toggle_submit' import FetchForm from './elements/fetch_form' import ScrollButtons from './elements/scroll_buttons' import PageContainer from './elements/page_container' +import SubmitForm from './elements/submit_form' const safeRegisterElement = (name, element, options = {}) => !window.customElements.get(name) && window.customElements.define(name, element, options) @@ -14,6 +15,7 @@ safeRegisterElement('toggle-submit', ToggleSubmit) safeRegisterElement('fetch-form', FetchForm) safeRegisterElement('scroll-buttons', ScrollButtons) safeRegisterElement('page-container', PageContainer) +safeRegisterElement('submit-form', SubmitForm) safeRegisterElement('submission-form', class extends HTMLElement { connectedCallback () { this.appElem = document.createElement('div') diff --git a/app/javascript/form.scss b/app/javascript/form.scss index f0d26baf..92fc6f6a 100644 --- a/app/javascript/form.scss +++ b/app/javascript/form.scss @@ -19,7 +19,7 @@ button .disabled { display: none; } -button[disabled] .disabled { +button[disabled] .disabled, button.btn-disabled .disabled { display: initial; } @@ -27,7 +27,7 @@ button .enabled { display: initial; } -button[disabled] .enabled { +button[disabled] .enabled, button.btn-disabled .enabled { display: none; } diff --git a/app/javascript/lib/turbo_instant_click.js b/app/javascript/lib/turbo_instant_click.js index 01789e95..d72d4ce8 100644 --- a/app/javascript/lib/turbo_instant_click.js +++ b/app/javascript/lib/turbo_instant_click.js @@ -56,7 +56,7 @@ function mouseoverListener (event) { const requestOptions = { credentials: 'same-origin', - headers: { Accept: 'text/html, application/xhtml+xml', 'VND.PREFETCH': 'true' }, + headers: { Accept: 'text/html, application/xhtml+xml', 'VND.PREFETCH': 'true', 'X-Turbo': 'true' }, method: 'GET', redirect: 'follow' } diff --git a/app/javascript/submission_form/area.vue b/app/javascript/submission_form/area.vue index 784818b6..2a2fd20e 100644 --- a/app/javascript/submission_form/area.vue +++ b/app/javascript/submission_form/area.vue @@ -219,6 +219,48 @@ > {{ formatNumber(modelValue, field.preferences?.format) }} + + + + + + + + + - - - + + + + diff --git a/app/javascript/submission_form/form.vue b/app/javascript/submission_form/form.vue index d58c04d8..7a825141 100644 --- a/app/javascript/submission_form/form.vue +++ b/app/javascript/submission_form/form.vue @@ -23,7 +23,7 @@ { - acc[f.uuid] = (this.values[f.uuid] || f.default_value) + acc[f.uuid] = isEmpty(this.values[f.uuid]) ? f.default_value : this.values[f.uuid] + + return acc + }, {}) + }, + readonlyFieldValues () { + return this.readonlyFields.reduce((acc, f) => { + acc[f.uuid] = isEmpty(this.values[f.uuid]) ? f.default_value : this.values[f.uuid] return acc }, {}) @@ -972,7 +981,10 @@ export default { return this.currentStepFields[0] }, readonlyConditionalFields () { - return this.fields.filter((f) => f.readonly && f.conditions?.length && this.checkFieldConditions(f) && this.checkFieldDocumentsConditions(f)) + return this.readonlyFields.filter((f) => f.conditions?.length) + }, + readonlyFields () { + return this.fields.filter((f) => f.readonly && this.checkFieldConditions(f) && this.checkFieldDocumentsConditions(f)) }, stepFields () { const verificationFields = [] diff --git a/app/javascript/submission_form/payment_step.vue b/app/javascript/submission_form/payment_step.vue index b345b246..d262d3a6 100644 --- a/app/javascript/submission_form/payment_step.vue +++ b/app/javascript/submission_form/payment_step.vue @@ -92,10 +92,20 @@ export default { type: Object, required: true }, + readonlyValues: { + type: Object, + required: false, + default: () => ({}) + }, values: { type: Object, required: true }, + fields: { + type: Array, + required: false, + default: () => [] + }, submitterSlug: { type: String, required: true @@ -109,6 +119,13 @@ export default { } }, computed: { + fieldsUuidIndex () { + return this.fields.reduce((acc, field) => { + acc[field.uuid] = field + + return acc + }, {}) + }, queryParams () { return new URLSearchParams(window.location.search) }, @@ -116,6 +133,10 @@ export default { return this.queryParams.get('stripe_session_id') }, defaultName () { + if (this.field.preferences?.price_id || this.field.preferences?.payment_link_id) { + return '' + } + const { price, currency } = this.field.preferences || {} const formatter = new Intl.NumberFormat([], { @@ -178,12 +199,23 @@ export default { }, methods: { calculateFormula () { - const transformedFormula = this.field.preferences.formula.replace(/{{(.*?)}}/g, (match, uuid) => { - return this.values[uuid] || 0.0 + const transformedFormula = this.normalizeFormula(this.field.preferences.formula).replace(/{{(.*?)}}/g, (match, uuid) => { + return this.readonlyValues[uuid] || this.values[uuid] || 0.0 }) return this.math.evaluate(transformedFormula.toLowerCase()) }, + normalizeFormula (formula, depth = 0) { + if (depth > 10) return formula + + return formula.replace(/{{(.*?)}}/g, (match, uuid) => { + if (this.fieldsUuidIndex[uuid]) { + return `(${this.normalizeFormula(this.fieldsUuidIndex[uuid].preferences.formula, depth + 1)})` + } else { + return match + } + }) + }, async submit () { if (this.sessionId) { return fetch(this.baseUrl + '/api/stripe_payments/' + this.sessionId, { diff --git a/app/javascript/submission_form/verification_step.vue b/app/javascript/submission_form/verification_step.vue index 39da2419..1f711c5e 100644 --- a/app/javascript/submission_form/verification_step.vue +++ b/app/javascript/submission_form/verification_step.vue @@ -14,7 +14,7 @@
{{ t('complete_all_required_fields_to_proceed_with_identity_verification') }} @@ -100,6 +100,9 @@ export default { } }, computed: { + isRequiredFieldEmpty () { + return this.emptyValueRequiredStep && this.emptyValueRequiredStep[0] !== this.field + }, countryCode () { const browserTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone const browserTz = browserTimeZone.split('/')[1] @@ -131,17 +134,19 @@ export default { } }, async mounted () { - this.isLoading = true + if (!this.isRequiredFieldEmpty) { + this.isLoading = true - if (new URLSearchParams(window.location.search).get('submit') === 'true') { - this.$emit('submit') - } else { - Promise.all([ - import('@eid-easy/eideasy-widget'), - this.start() - ]).finally(() => { - this.isLoading = false - }) + if (new URLSearchParams(window.location.search).get('submit') === 'true') { + this.$emit('submit') + } else { + Promise.all([ + import('@eid-easy/eideasy-widget'), + this.start() + ]).finally(() => { + this.isLoading = false + }) + } } }, methods: { @@ -170,7 +175,7 @@ export default { redirectUrl.searchParams.append('lang', this.locale) this.redirectUrl = redirectUrl.toString() - } else { + } else if (this.$refs.widgetContainer) { const eidEasyWidget = document.createElement('eideasy-widget') for (const key in this.widgetSettings) { diff --git a/app/javascript/template_builder/area.vue b/app/javascript/template_builder/area.vue index c786061a..d88475b3 100644 --- a/app/javascript/template_builder/area.vue +++ b/app/javascript/template_builder/area.vue @@ -10,7 +10,7 @@
+ + + + + + + +
+
@@ -397,6 +439,16 @@ export default { required: false, default: false }, + pageWidth: { + type: Number, + required: false, + default: 0 + }, + pageHeight: { + type: Number, + required: false, + default: 0 + }, defaultSubmitters: { type: Array, required: false, @@ -436,8 +488,33 @@ export default { fieldNames: FieldType.computed.fieldNames, fieldLabels: FieldType.computed.fieldLabels, fieldIcons: FieldType.computed.fieldIcons, + bgClasses () { + if (this.field.type === 'heading') { + return 'bg-gray-50' + } else if (this.field.type === 'strikethrough') { + return 'bg-transparent' + } else { + return this.bgColors[this.submitterIndex % this.bgColors.length] + } + }, + activeBorderClasses () { + if (this.field.type === 'heading') { + return '' + } else if (this.field.type === 'strikethrough') { + return 'border-dashed border-gray-300' + } else { + return this.borderColors[this.submitterIndex % this.borderColors.length] + } + }, isWFullType () { - return ['cells', 'checkbox', 'radio', 'multiple', 'select'].includes(this.field.type) + return ['cells', 'checkbox', 'radio', 'multiple', 'select', 'strikethrough'].includes(this.field.type) + }, + strikethroughWidth () { + if (this.isInlineSize) { + return '0.6cqmin' + } else { + return 'clamp(0px, 0.5vw, 6px)' + } }, fontStyle () { let fontSize = '' @@ -471,8 +548,11 @@ export default { lineHeight () { return 1.3 }, + basePageWidth () { + return 1040.0 + }, fontScale () { - return 1040 / 612.0 + return this.basePageWidth / 612.0 }, isDefaultValuePresent () { return this.field?.default_value || this.field?.default_value === 0 @@ -742,8 +822,13 @@ export default { delete this.field.options } - if (['heading'].includes(this.field.type)) { + if (this.field.type === 'heading') { + this.field.readonly = true + } + + if (this.field.type === 'strikethrough') { this.field.readonly = true + this.field.default_value = true } if (['select', 'multiple', 'radio'].includes(this.field.type)) { diff --git a/app/javascript/template_builder/builder.vue b/app/javascript/template_builder/builder.vue index a9f3709b..c679a37e 100644 --- a/app/javascript/template_builder/builder.vue +++ b/app/javascript/template_builder/builder.vue @@ -402,7 +402,10 @@ :style="{ backgroundColor }" >
-

+

+ {{ t('draw_strikethrough_the_document') }} +

+

{{ t('draw_field_on_the_document') }}

@@ -413,7 +416,7 @@ {{ t('cancel') }} { - if (f !== this.item && (!f.conditions?.length || !f.conditions.find((c) => c.field_uuid === this.item.uuid))) { + if (f !== this.item && !this.excludeTypes.includes(f.type) && (!f.conditions?.length || !f.conditions.find((c) => c.field_uuid === this.item.uuid))) { acc.push(f) } diff --git a/app/javascript/template_builder/field.vue b/app/javascript/template_builder/field.vue index d8b6538e..04bb9e01 100644 --- a/app/javascript/template_builder/field.vue +++ b/app/javascript/template_builder/field.vue @@ -14,7 +14,7 @@
f.type === field.type).indexOf(field) - if (field.type === 'heading') { + if (field.type === 'heading' || field.type === 'strikethrough') { return `${this.fieldNames[field.type]} ${typeIndex + 1}` } else { return `${this.fieldLabels[field.type]} ${typeIndex + 1}` @@ -485,8 +485,13 @@ export default { this.field.options ||= [{ value: '', uuid: v4() }] } - if (['heading'].includes(this.field.type)) { + if (this.field.type === 'heading') { + this.field.readonly = true + } + + if (this.field.type === 'strikethrough') { this.field.readonly = true + this.field.default_value = true } (this.field.areas || []).forEach((area) => { diff --git a/app/javascript/template_builder/field_settings.vue b/app/javascript/template_builder/field_settings.vue index f446bca7..d3e2e842 100644 --- a/app/javascript/template_builder/field_settings.vue +++ b/app/javascript/template_builder/field_settings.vue @@ -380,7 +380,7 @@
  • +
  • - × + ×
    diff --git a/app/views/shared/_github.html.erb b/app/views/shared/_github.html.erb index df415db6..c07cc7cc 100644 --- a/app/views/shared/_github.html.erb +++ b/app/views/shared/_github.html.erb @@ -1,6 +1,6 @@ <%= svg_icon('start', class: 'h-3 w-3') %> - 9k + 10k diff --git a/app/views/shared/_navbar.html.erb b/app/views/shared/_navbar.html.erb index dd124e46..6d66674c 100644 --- a/app/views/shared/_navbar.html.erb +++ b/app/views/shared/_navbar.html.erb @@ -61,7 +61,9 @@ <% if (can?(:manage, EncryptedConfig) && current_user == true_user) || (current_user != true_user && current_account.testing?) %> <%= form_for '', url: testing_account_path, method: current_account.testing? ? :delete : :get, html: { class: 'w-full py-1' } do |f| %>
    <% end %> - + + - diff --git a/app/views/shared/_settings_nav.html.erb b/app/views/shared/_settings_nav.html.erb index 64fc1c9c..1b06a08a 100644 --- a/app/views/shared/_settings_nav.html.erb +++ b/app/views/shared/_settings_nav.html.erb @@ -98,7 +98,9 @@ <%= t('test_mode') %> - <%= f.check_box :testing_toggle, class: 'toggle toggle-sm', checked: current_account.testing?, onchange: 'this.form.requestSubmit()' %> + + <%= f.check_box :testing_toggle, class: 'toggle toggle-sm', checked: current_account.testing? %> + <% end %> diff --git a/app/views/shared/_test_mode_toggle.html.erb b/app/views/shared/_test_mode_toggle.html.erb index c53a713e..f74dd1ce 100644 --- a/app/views/shared/_test_mode_toggle.html.erb +++ b/app/views/shared/_test_mode_toggle.html.erb @@ -4,7 +4,9 @@ <%= t('test_mode') %> - <%= f.check_box :testing_toggle, class: 'toggle', checked: current_account.testing?, onchange: 'this.form.requestSubmit()' %> + + <%= f.check_box :testing_toggle, class: 'toggle', checked: current_account.testing? %> + <% end %> <% end %> diff --git a/app/views/start_form/show.html.erb b/app/views/start_form/show.html.erb index 75c42e28..271b50c1 100644 --- a/app/views/start_form/show.html.erb +++ b/app/views/start_form/show.html.erb @@ -52,7 +52,9 @@ <% if link_form_fields.include?('phone') %>
    <%= f.label :phone, t('phone'), class: 'label' %> - <%= f.telephone_field :phone, value: params[:phone] || @submitter.phone, pattern: '^\+[0-9\s\-]+$', oninvalid: "this.value ? this.setCustomValidity('#{t('use_international_format_1xxx_')}') : ''", oninput: "this.setCustomValidity('')", required: true, class: 'base-input', placeholder: t(multiple_fields ? 'provide_your_phone_in_international_format' : 'provide_your_phone_in_international_format_to_start') %> + + <%= f.telephone_field :phone, value: params[:phone] || @submitter.phone, pattern: '^\+[0-9\s\-]+$', required: true, class: 'base-input w-full', placeholder: t(multiple_fields ? 'provide_your_phone_in_international_format' : 'provide_your_phone_in_international_format_to_start') %> +
    <% end %> diff --git a/app/views/submissions/_detailed_form.html.erb b/app/views/submissions/_detailed_form.html.erb index dd4f9d99..b2c79997 100644 --- a/app/views/submissions/_detailed_form.html.erb +++ b/app/views/submissions/_detailed_form.html.erb @@ -33,11 +33,13 @@ <% has_phone_field = true %> - - "> - <%= tag.input type: 'tel', pattern: '^\+[0-9\s\-]+$', oninvalid: "this.value ? this.setCustomValidity('#{t('use_international_format_1xxx_')}') : ''", oninput: "this.setCustomValidity('')", name: 'submission[1][submitters][][phone]', autocomplete: 'off', class: 'base-input !h-10 mt-1.5 w-full', placeholder: local_assigns[:require_phone_2fa] == true ? t(:phone) : "#{t('phone')} (#{t('optional')})", id: "detailed_phone_#{item['uuid']}", required: local_assigns[:require_phone_2fa] == true %> - - + + + "> + <%= tag.input type: 'tel', pattern: '^\+[0-9\s\-]+$', autocomplete: 'off', class: 'base-input !h-10 mt-1.5 w-full', placeholder: local_assigns[:require_phone_2fa] == true ? t(:phone) : "#{t('phone')} (#{t('optional')})", id: "detailed_phone_#{item['uuid']}", required: local_assigns[:require_phone_2fa] == true %> + + +
    <% end %> <% if prefillable_fields.present? %> @@ -48,11 +50,13 @@ <% if local_assigns[:require_phone_2fa] == true || prefillable_fields.any? { |f| f['type'] == 'phone' } %> <% has_phone_field = true %> - - "> - <%= tag.input type: 'tel', pattern: '^\+[0-9\s\-]+$', oninvalid: "this.value ? this.setCustomValidity('#{t('use_international_format_1xxx_')}') : ''", oninput: "this.setCustomValidity('')", name: 'submission[1][submitters][][phone]', autocomplete: 'off', class: 'base-input !h-10 mt-1.5 w-full', placeholder: t(:phone), id: "detailed_phone_#{item['uuid']}", required: true %> - - + + + "> + <%= tag.input type: 'tel', pattern: '^\+[0-9\s\-]+$', name: 'submission[1][submitters][][phone]', autocomplete: 'off', class: 'base-input !h-10 mt-1.5 w-full', placeholder: t(:phone), id: "detailed_phone_#{item['uuid']}", required: true %> + + + <% end %> <% prefillable_fields.each do |field| %> <% if field['type'] == 'checkbox' %> diff --git a/app/views/submissions/_phone_form.html.erb b/app/views/submissions/_phone_form.html.erb index 649739fc..305f6e87 100644 --- a/app/views/submissions/_phone_form.html.erb +++ b/app/views/submissions/_phone_form.html.erb @@ -19,11 +19,13 @@ <% end %> - - "> - <%= tag.input type: 'tel', pattern: '^\+[0-9\s\-]+$', oninvalid: "this.value ? this.setCustomValidity('#{t('use_international_format_1xxx_')}') : ''", oninput: "this.setCustomValidity('')", name: 'submission[1][submitters][][phone]', autocomplete: 'off', class: 'base-input !h-10 w-full', placeholder: t('phone'), required: index.zero? || template.preferences['require_all_submitters'], id: "phone_phone_#{item['uuid']}" %> - - + + + "> + <%= tag.input type: 'tel', pattern: '^\+[0-9\s\-]+$', name: 'submission[1][submitters][][phone]', autocomplete: 'off', class: 'base-input !h-10 w-full', placeholder: t('phone'), required: index.zero? || template.preferences['require_all_submitters'], id: "phone_phone_#{item['uuid']}" %> + + + <% if submitters.size > 1 %> "> diff --git a/app/views/submissions/_send_email.html.erb b/app/views/submissions/_send_email.html.erb index d7f357c0..f9251baf 100644 --- a/app/views/submissions/_send_email.html.erb +++ b/app/views/submissions/_send_email.html.erb @@ -11,10 +11,12 @@ <% if can_send_emails %> <%= render 'submissions/email_stats' %> <%= content_for(:edit_button) || capture do %> - + + + <% end %> <% end %> diff --git a/app/views/submissions/_value.html.erb b/app/views/submissions/_value.html.erb index 81158cdb..025053ef 100644 --- a/app/views/submissions/_value.html.erb +++ b/app/views/submissions/_value.html.erb @@ -76,6 +76,19 @@
    <%= NumberUtils.format_number(value, field.dig('preferences', 'format')) %>
    + <% elsif field['type'] == 'strikethrough' %> +
    + <% if (((1000.0 / local_assigns[:page_width]) * local_assigns[:page_height]) * area['h']) < 40 %> + + + + <% else %> + + + + + <% end %> +
    <% else %>
    diff --git a/app/views/submissions/show.html.erb b/app/views/submissions/show.html.erb index eeb779e5..6f22607c 100644 --- a/app/views/submissions/show.html.erb +++ b/app/views/submissions/show.html.erb @@ -80,12 +80,12 @@ <% schema.each do |item| %> <% document = @submission.schema_documents.find { |a| item['attachment_uuid'] == a.uuid } %> <% if document.preview_images.first %> - " onclick="[event.preventDefault(), window[event.target.closest('a').href.split('#')[1]].scrollIntoView({ behavior: 'smooth', block: 'start' })]" class="block cursor-pointer"> +
    <%= item['name'].presence || document.filename.base %>
    -
    + <% end %> <% end %>
    @@ -123,7 +123,7 @@
    <% else %> - <%= render 'submissions/value', font_scale:, area:, field:, attachments_index:, value: mask.present? ? Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', ') : value, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter:, with_signature_id:, with_submitter_timezone:, with_signature_id_reason: %> + <%= render 'submissions/value', page_width: width, page_height: height, font_scale:, area:, field:, attachments_index:, value: mask.present? ? Array.wrap(value).map { |e| TextUtils.mask_value(e, mask) }.join(', ') : value, locale: @submission.account.locale, timezone: @submission.account.timezone, submitter:, with_signature_id:, with_submitter_timezone:, with_signature_id_reason: %> <% end %> <% end %> @@ -240,7 +240,7 @@ <% submitter_field_counters[field['type']] += 1 %> <% value = values[field['uuid']].presence || (field['default_value'] != '{{date}}' && field['readonly'] == true && field['default_value'].present? ? Submitters::SubmitValues.template_default_value_for_submitter(field['default_value'], @submission.submitters.find { |e| e.uuid == field['submitter_uuid'] }, with_time: false) : nil) %> <% next if value.blank? %> - <% next if field['type'] == 'heading' %> + <% next if field['type'] == 'heading' || field['type'] == 'strikethrough' %>
    <%= field['name'].presence || "#{t("#{field['type']}_field")} #{submitter_field_counters[field['type']]}" %> @@ -288,16 +288,20 @@ <% end %>
    - + + + -<%= render 'scripts/autosize_field' %> +<% unless request.headers['HTTP_X_TURBO'] %> + <%= render 'scripts/autosize_field' %> +<% end %> diff --git a/app/views/submit_form/show.html.erb b/app/views/submit_form/show.html.erb index 1649b848..04be2c7f 100644 --- a/app/views/submit_form/show.html.erb +++ b/app/views/submit_form/show.html.erb @@ -84,7 +84,7 @@ <% next if field['conditions'].present? && values[field['uuid']].blank? && field['submitter_uuid'] != @submitter.uuid %> <% next if field['conditions'].present? && field['submitter_uuid'] == @submitter.uuid %> <% next if field.dig('preferences', 'formula').present? && field['submitter_uuid'] == @submitter.uuid %> - <%= render 'submissions/value', font_scale:, area:, field:, attachments_index: @attachments_index, value: field.dig('preferences', 'mask').present? ? TextUtils.mask_value(value, field.dig('preferences', 'mask')) : value, locale: @submitter.account.locale, timezone: @submitter.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: @form_configs[:with_signature_id], with_submitter_timezone: @form_configs[:with_submitter_timezone], with_signature_id_reason: @form_configs[:with_signature_id_reason] %> + <%= render 'submissions/value', page_width: width, page_height: height, font_scale:, area:, field:, attachments_index: @attachments_index, value: field.dig('preferences', 'mask').present? ? TextUtils.mask_value(value, field.dig('preferences', 'mask')) : value, locale: @submitter.account.locale, timezone: @submitter.account.timezone, submitter: submitters_index[field['submitter_uuid']], with_signature_id: @form_configs[:with_signature_id], with_submitter_timezone: @form_configs[:with_submitter_timezone], with_signature_id_reason: @form_configs[:with_signature_id_reason] %> <% end %> diff --git a/app/views/submit_form_draw_signature/show.html.erb b/app/views/submit_form_draw_signature/show.html.erb index 1ab495ed..f1ffebf0 100644 --- a/app/views/submit_form_draw_signature/show.html.erb +++ b/app/views/submit_form_draw_signature/show.html.erb @@ -4,7 +4,6 @@ <%= render 'layouts/head_tags' %> <%= csrf_meta_tags %> - <%= csp_meta_tag %> <% if ENV['ROLLBAR_CLIENT_TOKEN'] %> <%= javascript_pack_tag 'rollbar', 'draw', defer: true %> diff --git a/app/views/submitters/edit.html.erb b/app/views/submitters/edit.html.erb index eb309c9e..a4f69f9b 100644 --- a/app/views/submitters/edit.html.erb +++ b/app/views/submitters/edit.html.erb @@ -10,9 +10,11 @@ <%= email_field_tag 'submitter[email]', @submitter.email, autocomplete: 'off', class: 'base-input !h-10 mt-1.5 w-full', placeholder: "#{t('email')} (#{t('optional')})" %> - - <%= telephone_field_tag 'submitter[phone]', @submitter.phone, autocomplete: 'off', pattern: '^\+[0-9\s\-]+$', class: 'base-input !h-10 mt-1.5 w-full', placeholder: "#{t('phone')} (#{t('optional')})", oninvalid: "this.value ? this.setCustomValidity('#{t('use_international_format_1xxx_')}') : ''", oninput: "this.setCustomValidity('')" %> - + + + <%= telephone_field_tag 'submitter[phone]', @submitter.phone, autocomplete: 'off', pattern: '^\+[0-9\s\-]+$', class: 'base-input !h-10 mt-1.5 w-full', placeholder: "#{t('phone')} (#{t('optional')})" %> + + diff --git a/app/views/template_folders/show.html.erb b/app/views/template_folders/show.html.erb index 2f6ac8ea..c51879d8 100644 --- a/app/views/template_folders/show.html.erb +++ b/app/views/template_folders/show.html.erb @@ -34,7 +34,9 @@ <%= render 'shared/search_input' %> <% end %> <% if can?(:create, ::Template) %> - <%= render 'templates/upload_button', folder_name: @template_folder.full_name %> + <%= link_to new_template_path(folder_name: @template_folder.full_name), class: 'white-button !border gap-2', data: { turbo_frame: :modal } do %> <%= svg_icon('plus', class: 'w-6 h-6 stroke-2') %> diff --git a/app/views/templates/_upload_button.html.erb b/app/views/templates/_upload_button.html.erb index 40460070..4b7156aa 100644 --- a/app/views/templates/_upload_button.html.erb +++ b/app/views/templates/_upload_button.html.erb @@ -1,19 +1,22 @@ <%= form_for '', url: templates_upload_path, id: form_id = SecureRandom.uuid, method: :post, class: 'inline', html: { enctype: 'multipart/form-data' } do %> - + <%= svg_icon('upload', class: 'w-6 h-6 stroke-2') %> - + <%= local_assigns[:icon_disabled] || svg_icon('loader', class: 'w-5 h-5 animate-spin') %> - + - + - " multiple> + + " multiple> + <% end %> diff --git a/app/views/templates/new.html.erb b/app/views/templates/new.html.erb index c55ef24a..d3a3febc 100644 --- a/app/views/templates/new.html.erb +++ b/app/views/templates/new.html.erb @@ -12,14 +12,18 @@ <%= f.text_field :name, required: true, placeholder: t('document_name'), class: 'base-input', dir: 'auto' %>
    - + + - - - <%= t('change_folder') %> - + + + + + +
    diff --git a/app/views/templates_code_modal/show.html.erb b/app/views/templates_code_modal/show.html.erb index 96b69cd3..46e9c893 100644 --- a/app/views/templates_code_modal/show.html.erb +++ b/app/views/templates_code_modal/show.html.erb @@ -25,7 +25,9 @@ <%= t('share_template_with_test_mode') %> - <%= f.check_box :value, class: 'toggle', checked: @template.template_sharings.exists?(account_id: current_account.testing_accounts), onchange: 'this.form.requestSubmit()' %> + + <%= f.check_box :value, class: 'toggle', checked: @template.template_sharings.exists?(account_id: current_account.testing_accounts) %> +
    <% end %>
    diff --git a/app/views/templates_dashboard/index.html.erb b/app/views/templates_dashboard/index.html.erb index d8509885..449fa49f 100644 --- a/app/views/templates_dashboard/index.html.erb +++ b/app/views/templates_dashboard/index.html.erb @@ -63,7 +63,7 @@ <% end %> diff --git a/app/views/templates_preferences/show.html.erb b/app/views/templates_preferences/show.html.erb index 0c77ca43..54f8d83f 100644 --- a/app/views/templates_preferences/show.html.erb +++ b/app/views/templates_preferences/show.html.erb @@ -46,9 +46,13 @@
    <% duration_options = Templates::EXPIRATION_DURATIONS.keys.map { |duration| [t(duration), duration] } + [[t('specified_date'), 'specified_date']] %> <%= ff.label :default_expire_at_duration, t('default_expiration'), class: 'label pt-0' %> -
    - <%= ff.select :default_expire_at_duration, duration_options, { include_blank: t('none') }, required: false, class: 'base-select flex-1', dir: 'auto', autocomplete: 'off', onchange: "this.value == 'specified_date' ? window.template_preferences_default_expire_at.classList.remove('hidden') : [window.template_preferences_default_expire_at.classList.add('hidden'), window.template_preferences_default_expire_at.value = '', this.form.requestSubmit()]" %> - <%= ff.datetime_field :default_expire_at, required: false, class: ['base-input flex-1', ff.object.default_expire_at.blank? && 'hidden'].compact_blank.join(' '), dir: 'auto', autocomplete: 'off', onchange: 'this.value && this.form.requestSubmit()' %> +
    + + <%= ff.select :default_expire_at_duration, duration_options, { include_blank: t('none') }, required: false, class: 'base-select flex-1', dir: 'auto', autocomplete: 'off' %> + + + <%= ff.datetime_field :default_expire_at, required: false, class: ['base-input flex-1', ff.object.default_expire_at.blank? && 'hidden'].compact_blank.join(' '), dir: 'auto', autocomplete: 'off' %> +
    <% end %> @@ -168,7 +172,9 @@ <%= 'Send signature request email' %> - <%= ff.check_box :request_email_enabled, { checked: ff.object.request_email_enabled != false, class: 'toggle', onchange: 'this.form.requestSubmit()' }, 'true', 'false' %> + + <%= ff.check_box :request_email_enabled, { checked: ff.object.request_email_enabled != false, class: 'toggle' }, 'true', 'false' %> +
    <% end %>
    @@ -215,19 +221,25 @@ <%= t('attach_documents_to_the_email') %> - <%= ff.check_box :documents_copy_email_attach_documents, { checked: ff.object.documents_copy_email_attach_documents != false, class: 'toggle', onchange: 'this.form.requestSubmit()', disabled: configs['attach_documents'] == false }, 'true', 'false' %> + + <%= ff.check_box :documents_copy_email_attach_documents, { checked: ff.object.documents_copy_email_attach_documents != false, class: 'toggle', disabled: configs['attach_documents'] == false }, 'true', 'false' %> +
    <%= t('attach_audit_log_pdf_to_the_email') %> - <%= ff.check_box :documents_copy_email_attach_audit, { checked: ff.object.documents_copy_email_attach_audit != false, class: 'toggle', onchange: 'this.form.requestSubmit()', disabled: configs['attach_audit_log'] == false }, 'true', 'false' %> + + <%= ff.check_box :documents_copy_email_attach_audit, { checked: ff.object.documents_copy_email_attach_audit != false, class: 'toggle', disabled: configs['attach_audit_log'] == false }, 'true', 'false' %> +
    <%= t('send_emails_automatically_on_completion') %> - <%= ff.check_box :documents_copy_email_enabled, { checked: ff.object.documents_copy_email_enabled != false && configs['enabled'] != false, class: 'toggle', onchange: 'this.form.requestSubmit()', disabled: configs['enabled'] == false }, 'true', 'false' %> + + <%= ff.check_box :documents_copy_email_enabled, { checked: ff.object.documents_copy_email_enabled != false && configs['enabled'] != false, class: 'toggle', disabled: configs['enabled'] == false }, 'true', 'false' %> +
    <% end %>
    @@ -268,19 +280,25 @@ <%= t('attach_documents_to_the_email') %> - <%= ff.check_box :completed_notification_email_attach_documents, { checked: ff.object.completed_notification_email_attach_documents != false, class: 'toggle', onchange: 'this.form.requestSubmit()', disabled: configs['attach_documents'] == false }, 'true', 'false' %> + + <%= ff.check_box :completed_notification_email_attach_documents, { checked: ff.object.completed_notification_email_attach_documents != false, class: 'toggle', disabled: configs['attach_documents'] == false }, 'true', 'false' %> +
    <%= t('attach_audit_log_pdf_to_the_email') %> - <%= ff.check_box :completed_notification_email_attach_audit, { checked: ff.object.completed_notification_email_attach_audit != false, class: 'toggle', onchange: 'this.form.requestSubmit()', disabled: configs['attach_audit_log'] == false }, 'true', 'false' %> + + <%= ff.check_box :completed_notification_email_attach_audit, { checked: ff.object.completed_notification_email_attach_audit != false, class: 'toggle', disabled: configs['attach_audit_log'] == false }, 'true', 'false' %> +
    <%= t('send_emails_automatically_on_completion') %> - <%= ff.check_box :completed_notification_email_enabled, { checked: ff.object.completed_notification_email_enabled != false, class: 'toggle', onchange: 'this.form.requestSubmit()' }, 'true', 'false' %> + + <%= ff.check_box :completed_notification_email_enabled, { checked: ff.object.completed_notification_email_enabled != false, class: 'toggle', disabled: configs['enabled'] == false }, 'true', 'false' %> +
    <% end %>
    @@ -325,7 +343,9 @@
    <%= t('enable_shared_link') %> - <%= f.check_box :shared_link, { class: 'toggle', onchange: 'this.form.requestSubmit()' }, 'true', 'false' %> + + <%= f.check_box :shared_link, { class: 'toggle' }, 'true', 'false' %> +
    <% end %>
    @@ -339,7 +359,9 @@ <%= t('share_template_with_test_mode') %> - <%= f.check_box :value, class: 'toggle', checked: @template.template_sharings.exists?(account_id: current_account.testing_accounts), onchange: 'this.form.requestSubmit()' %> + + <%= f.check_box :value, class: 'toggle', checked: @template.template_sharings.exists?(account_id: current_account.testing_accounts) %> + <% end %>
    diff --git a/app/views/templates_share_link/show.html.erb b/app/views/templates_share_link/show.html.erb index df1ad45e..fed24fe0 100644 --- a/app/views/templates_share_link/show.html.erb +++ b/app/views/templates_share_link/show.html.erb @@ -5,7 +5,9 @@ <%= form_for @template, url: template_share_link_path(@template), method: :post, html: { id: 'shared_link_form', autocomplete: 'off', class: 'mt-3' }, data: { close_on_submit: false } do |f| %>
    @@ -63,7 +65,9 @@ <%= f.fields_for :preferences, Struct.new(:shared_link_2fa).new(@template.preferences['shared_link_2fa'] == true) do |ff| %> <% end %> <% end %> diff --git a/app/views/webhook_events/_drawer_events.html.erb b/app/views/webhook_events/_drawer_events.html.erb index 275259c6..9858be96 100644 --- a/app/views/webhook_events/_drawer_events.html.erb +++ b/app/views/webhook_events/_drawer_events.html.erb @@ -51,7 +51,9 @@ <% unless webhook_event.status == 'pending' %>
    - <%= button_to button_title(title: t('resend'), disabled_with: t('awaiting'), icon: svg_icon('rotate', class: 'w-4 h-4'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), resend_settings_webhook_event_path(webhook_url.id, webhook_event.uuid), form: { id: button_uuid = SecureRandom.uuid }, params: { button_id: button_uuid }, class: 'btn btn-neutral btn-sm text-white', method: :post, onclick: '[this.form.requestSubmit(), this.disabled = true]' %> + + <%= button_to button_title(title: t('resend'), disabled_with: t('awaiting'), icon: svg_icon('rotate', class: 'w-4 h-4'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), resend_settings_webhook_event_path(webhook_url.id, webhook_event.uuid), form: { id: button_uuid = SecureRandom.uuid }, params: { button_id: button_uuid }, class: 'btn btn-neutral btn-sm text-white', method: :post %> +
    <% end %>
    diff --git a/app/views/webhook_events/_event_row.html.erb b/app/views/webhook_events/_event_row.html.erb index a6af3020..2b45f393 100644 --- a/app/views/webhook_events/_event_row.html.erb +++ b/app/views/webhook_events/_event_row.html.erb @@ -21,7 +21,9 @@
    <%= webhook_event.event_type %>
    - <%= button_to button_title(title: t('resend'), disabled_with: t('awaiting'), icon: svg_icon('rotate', class: 'w-4 h-4'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), resend_settings_webhook_event_path(webhook_url.id, webhook_event.uuid), form: { id: button_uuid = SecureRandom.uuid }, params: { button_id: button_uuid }, class: 'btn btn-neutral btn-xs h-2 text-white relative z-[1] hidden md:group-hover:inline-block', data: { turbo_frame: :drawer }, method: :post, onclick: "[this.form.requestSubmit(), this.disabled = true, this.classList.remove('hidden')]" %> + + <%= button_to button_title(title: t('resend'), disabled_with: t('awaiting'), icon: svg_icon('rotate', class: 'w-4 h-4'), icon_disabled: svg_icon('loader', class: 'w-4 h-4 animate-spin')), resend_settings_webhook_event_path(webhook_url.id, webhook_event.uuid), form: { id: button_uuid = SecureRandom.uuid }, params: { button_id: button_uuid }, class: 'btn btn-neutral btn-xs h-2 text-white relative z-[1] hidden md:group-hover:inline-block', data: { turbo_frame: :drawer }, method: :post %> + <%= l(webhook_event.created_at, locale: current_account.locale, format: :short) %>
    diff --git a/app/views/webhook_settings/show.html.erb b/app/views/webhook_settings/show.html.erb index 5084319e..38b08f2c 100644 --- a/app/views/webhook_settings/show.html.erb +++ b/app/views/webhook_settings/show.html.erb @@ -66,7 +66,9 @@ <%= f.fields_for :events do |ff| %>
    diff --git a/config/application.rb b/config/application.rb index 8021f58a..4ba83e90 100644 --- a/config/application.rb +++ b/config/application.rb @@ -31,6 +31,9 @@ module DocuSeal config.exceptions_app = ->(env) { ErrorsController.action(:show).call(env) } + config.content_security_policy_nonce_generator = ->(_) { SecureRandom.base64(16) } + config.content_security_policy_nonce_directives = %w[script-src] + config.action_view.frozen_string_literal = true config.middleware.insert_before ActionDispatch::Static, Rack::Deflater diff --git a/config/environments/development.rb b/config/environments/development.rb index b806a14f..bc9dc2d4 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -66,6 +66,8 @@ Rails.application.configure do # Raise exceptions for disallowed deprecations. config.active_support.disallowed_deprecation = :raise + config.active_storage.service_urls_expire_in = 240.minutes + # Tell Active Support which deprecation messages to disallow. config.active_support.disallowed_deprecation_warnings = [] diff --git a/config/locales/i18n.yml b/config/locales/i18n.yml index eaf10d7f..ac1b8ebd 100644 --- a/config/locales/i18n.yml +++ b/config/locales/i18n.yml @@ -27,6 +27,7 @@ en: &en enabled: Enabled disabled: Disabled party: Party + use_direct_file_attachment_links_in_the_documents: Use direct file attachment links in the documents click_here_to_send_a_reset_password_email_html: ' to send a reset password email.' edit_order: Edit Order expirable_file_download_links: Expirable file download links @@ -592,6 +593,8 @@ en: &en four_days: 4 days eight_days: 8 days fifteen_days: 15 days + twenty_one_days: 21 days + thirty_days: 30 days free: Free unlimited_documents_storage: Unlimited documents storage users_management: Users management @@ -932,6 +935,7 @@ en: &en range_without_total: "%{from}-%{to} events" es: &es + use_direct_file_attachment_links_in_the_documents: Usar enlaces directos de archivos adjuntos en los documentos enabled: Habilitado disabled: Deshabilitado expirable_file_download_links: Enlaces de descarga de archivos con vencimiento @@ -1504,6 +1508,8 @@ es: &es four_days: 4 días eight_days: 8 días fifteen_days: 15 días + twenty_one_days: 21 días + thirty_days: 30 días free: Gratis unlimited_documents_storage: Almacenamiento ilimitado de documentos users_management: Gestión de usuarios @@ -1843,6 +1849,7 @@ es: &es range_without_total: "%{from}-%{to} eventos" it: &it + use_direct_file_attachment_links_in_the_documents: Usa i link diretti per gli allegati nei documenti click_here_to_send_a_reset_password_email_html: ' per inviare una email per reimpostare la password.' enabled: Abilitato disabled: Disabilitato @@ -2414,6 +2421,8 @@ it: &it four_days: 4 giorni eight_days: 8 giorni fifteen_days: 15 giorni + twenty_one_days: 21 giorni + thirty_days: 30 giorni free: Gratuito unlimited_documents_storage: Archiviazione illimitata di documenti users_management: Gestione utenti @@ -2755,6 +2764,7 @@ it: &it range_without_total: "%{from}-%{to} eventi" fr: &fr + use_direct_file_attachment_links_in_the_documents: Utiliser des liens directs pour les pièces jointes dans les documents click_here_to_send_a_reset_password_email_html: ' pour envoyer un e-mail de réinitialisation du mot de passe.' enabled: Activé disabled: Désactivé @@ -3329,6 +3339,8 @@ fr: &fr four_days: 4 jours eight_days: 8 jours fifteen_days: 15 jours + twenty_one_days: 21 jours + thirty_days: 30 jours free: Gratuit unlimited_documents_storage: Stockage illimité de documents users_management: Gestion des utilisateurs @@ -3670,6 +3682,7 @@ fr: &fr range_without_total: "%{from} à %{to} événements" pt: &pt + use_direct_file_attachment_links_in_the_documents: Usar links diretos de anexos de arquivos nos documentos click_here_to_send_a_reset_password_email_html: ' para enviar um e-mail de redefinição de senha.' enabled: Ativado disabled: Desativado @@ -4243,6 +4256,8 @@ pt: &pt four_days: 4 dias eight_days: 8 dias fifteen_days: 15 dias + twenty_one_days: 21 dias + thirty_days: 30 dias free: Gratuito unlimited_documents_storage: Armazenamento ilimitado de documentos users_management: Gerenciamento de usuários @@ -4583,6 +4598,7 @@ pt: &pt range_without_total: "%{from}-%{to} eventos" de: &de + use_direct_file_attachment_links_in_the_documents: Verwenden Sie direkte Dateianhang-Links in den Dokumenten click_here_to_send_a_reset_password_email_html: ', um eine E-Mail zum Zurücksetzen des Passworts zu senden.' enabled: Aktiviert disabled: Deaktiviert @@ -5156,6 +5172,8 @@ de: &de four_days: 4 Tage eight_days: 8 Tage fifteen_days: 15 Tage + twenty_one_days: 21 Tage + thirty_days: 30 Tage free: Kostenlos unlimited_documents_storage: Unbegrenzter Dokumentenspeicher users_management: Benutzerverwaltung diff --git a/lib/account_configs.rb b/lib/account_configs.rb index 111320f8..06fec78f 100644 --- a/lib/account_configs.rb +++ b/lib/account_configs.rb @@ -15,7 +15,9 @@ module AccountConfigs 'six_days' => '6 days', 'seven_days' => '7 days', 'eight_days' => '8 days', - 'fifteen_days' => '15 days' + 'fifteen_days' => '15 days', + 'twenty_one_days' => '21 days', + 'thirty_days' => '30 days' }.freeze module_function diff --git a/lib/submissions/create_from_submitters.rb b/lib/submissions/create_from_submitters.rb index 29aa37e8..0ae3b09b 100644 --- a/lib/submissions/create_from_submitters.rb +++ b/lib/submissions/create_from_submitters.rb @@ -145,7 +145,7 @@ module Submissions submitters_attrs.any? { |e| e[:completed].present? } || !with_template || submission.variables.present? submission.template_fields = template_fields submission.template_schema = submission.template.schema if submission.template_schema.blank? - submission.variables_schema = submission.template.variables_schema if submission.template_id && + submission.variables_schema = submission.template.variables_schema if submission.template && submission.variables_schema.blank? end diff --git a/lib/submissions/generate_audit_trail.rb b/lib/submissions/generate_audit_trail.rb index 6413c68b..b63f3c6f 100644 --- a/lib/submissions/generate_audit_trail.rb +++ b/lib/submissions/generate_audit_trail.rb @@ -329,7 +329,7 @@ module Submissions submitter_field_counters[field['type']] += 1 next if field['submitter_uuid'] != submitter.uuid - next if field['type'] == 'heading' + next if field['type'] == 'heading' || field['type'] == 'strikethrough' next if !with_audit_values && !field['type'].in?(%w[signature initials]) next if skip_grouped_field_uuids[field['uuid']] diff --git a/lib/submissions/generate_result_attachments.rb b/lib/submissions/generate_result_attachments.rb index 4e060770..54eb694f 100644 --- a/lib/submissions/generate_result_attachments.rb +++ b/lib/submissions/generate_result_attachments.rb @@ -206,15 +206,16 @@ module Submissions submission = submitter.submission - return pdfs_index if submission.template_fields.blank? - with_headings = find_last_submitter(submission, submitter:).blank? if with_headings.nil? locale = submitter.metadata.fetch('lang', account.locale) - submission.template_fields.each do |field| - next if field['type'] == 'heading' && !with_headings - next if field['submitter_uuid'] != submitter.uuid && field['type'] != 'heading' + (submission.template_fields || submission.template.fields).each do |field| + next if !with_headings && + (field['type'] == 'heading' || (field['type'] == 'strikethrough' && field['conditions'].blank?)) + + next if field['submitter_uuid'] != submitter.uuid && field['type'] != 'heading' && + (field['type'] != 'strikethrough' || field['conditions'].present?) field.fetch('areas', []).each do |area| pdf = pdfs_index[area['attachment_uuid']] @@ -258,6 +259,7 @@ module Submissions value = submitter.values[field['uuid']] value = field['default_value'] if field['type'] == 'heading' + value = field['default_value'] if field['type'] == 'strikethrough' && value.nil? && field['conditions'].blank? text_align = field.dig('preferences', 'align').to_s.to_sym.presence || (value.to_s.match?(RTL_REGEXP) ? :right : :left) @@ -448,8 +450,7 @@ module Submissions cv.image(PdfIcons.paperclip_io, at: [0, 0], width: box.content_width) end - acc << HexaPDF::Layout::TextFragment.create("#{attachment.filename}\n", font:, - font_size:) + acc << HexaPDF::Layout::TextFragment.create("#{attachment.filename}\n", font:, font_size:) end lines = layouter.fit(items, area['w'] * width, height).lines @@ -567,6 +568,43 @@ module Submissions cell_layouter.fit([text], cell_width, [line_height, area['h'] * height].max) .draw(canvas, x, height - (area['y'] * height)) end + when 'strikethrough' + scale = 1000.0 / width + + line_width = 6.0 / scale + area_height = area['h'] * height + + if area_height * scale < 40.0 + canvas.tap do |c| + c.stroke_color(field.dig('preferences', 'color').presence || 'red') + c.line_width(line_width) + c.line(width * area['x'], + height - (height * area['y']) - (area_height / 2), + (width * area['x']) + (width * area['w']), + height - (height * area['y']) - (area_height / 2)) + c.stroke + end + else + canvas.tap do |c| + c.stroke_color(field.dig('preferences', 'color').presence || 'red') + c.line_width(line_width) + c.line((width * area['x']) + (line_width / 2), + height - (height * area['y']) - (line_width / 2), + (width * area['x']) + (width * area['w']) - (line_width / 2), + height - (height * area['y']) - area_height + (line_width / 2)) + c.stroke + end + + canvas.tap do |c| + c.stroke_color(field.dig('preferences', 'color').presence || 'red') + c.line_width(line_width) + c.line((width * area['x']) + (line_width / 2), + height - (height * area['y']) - area_height + (line_width / 2), + (width * area['x']) + (width * area['w']) - (line_width / 2), + height - (height * area['y']) - (line_width / 2)) + c.stroke + end + end else if field['type'] == 'date' value = TimeUtils.format_date_string(value, field.dig('preferences', 'format'), locale) diff --git a/lib/submitters/submit_values.rb b/lib/submitters/submit_values.rb index 63b1d839..613fca55 100644 --- a/lib/submitters/submit_values.rb +++ b/lib/submitters/submit_values.rb @@ -6,7 +6,7 @@ module Submitters RequiredFieldError = Class.new(StandardError) VARIABLE_REGEXP = /\{\{?(\w+)\}\}?/ - NONEDITABLE_FIELD_TYPES = %w[stamp heading].freeze + NONEDITABLE_FIELD_TYPES = %w[stamp heading strikethrough].freeze STRFTIME_MAP = { 'hour' => '%-k', @@ -189,8 +189,6 @@ module Submitters next if formula.blank? - formula = normalize_formula(formula, submitter.submission) - submission_values ||= if submitter.submission.template_submitters.size > 1 merge_submitters_values(submitter) @@ -198,20 +196,26 @@ module Submitters submitter.values end + formula = normalize_formula(formula, submitter.submission, submission_values:) + acc[field['uuid']] = calculate_formula_value(formula, submission_values.merge(acc.compact_blank)) end computed_values.compact_blank end - def normalize_formula(formula, submission, depth = 0) + def normalize_formula(formula, submission, depth: 0, submission_values: nil) raise ValidationError, 'Formula infinite loop' if depth > 10 formula.gsub(/{{(.*?)}}/) do |match| uuid = Regexp.last_match(1) if (nested_formula = submission.fields_uuid_index.dig(uuid, 'preferences', 'formula').presence) - "(#{normalize_formula(nested_formula, submission, depth + 1)})" + if check_field_conditions(submission_values, submission.fields_uuid_index[uuid], submission.fields_uuid_index) + "(#{normalize_formula(nested_formula, submission, depth: depth + 1, submission_values:)})" + else + '0' + end else match end diff --git a/lib/time_utils.rb b/lib/time_utils.rb index 292b67bb..c1c0d07e 100644 --- a/lib/time_utils.rb +++ b/lib/time_utils.rb @@ -57,9 +57,9 @@ module TimeUtils 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_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 diff --git a/package.json b/package.json index 55e29502..4b664b19 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "shakapacker": "8.0.0", "signature_pad": "^4.1.5", "snarkdown": "^2.0.0", - "tailwindcss": "^3.3.2", + "tailwindcss": "^3.4.17", "terser-webpack-plugin": "5.3.8", "uuid": "^9.0.0", "vue": "^3.3.2", diff --git a/spec/system/template_spec.rb b/spec/system/template_spec.rb index d343283e..83aa0f8d 100644 --- a/spec/system/template_spec.rb +++ b/spec/system/template_spec.rb @@ -78,7 +78,7 @@ RSpec.describe 'Template' do within '#modal' do fill_in 'template[name]', with: 'New Template Name' - click_link 'Change Folder' + find('label', text: 'Change Folder').click fill_in 'folder_name', with: 'New Folder Name' expect do @@ -101,7 +101,7 @@ RSpec.describe 'Template' do within '#modal' do template_folder.reload fill_in 'template[name]', with: 'New Template Name' - click_link 'Change Folder' + find('label', text: 'Change Folder').click end within '.autocomplete' do diff --git a/yarn.lock b/yarn.lock index 08dea370..2faf9648 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1239,6 +1239,18 @@ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + "@jest/schemas@^29.4.3": version "29.4.3" resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.4.3.tgz#39cf1b8469afc40b6f5a2baaa146e332c4151788" @@ -1427,6 +1439,11 @@ "@nodelib/fs.scandir" "2.1.5" fastq "^1.6.0" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== + "@polka/url@^1.0.0-next.20": version "1.0.0-next.21" resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" @@ -1965,6 +1982,11 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== +ansi-regex@^6.0.1: + version "6.2.2" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.2.2.tgz#60216eea464d864597ce2832000738a0589650c1" + integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg== + ansi-styles@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" @@ -1972,13 +1994,18 @@ ansi-styles@^3.2.1: dependencies: color-convert "^1.9.0" -ansi-styles@^4.1.0: +ansi-styles@^4.0.0, ansi-styles@^4.1.0: version "4.3.0" resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: color-convert "^2.0.1" +ansi-styles@^6.1.0: + version "6.2.3" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.3.tgz#c044d5dcc521a076413472597a1acb1f103c4041" + integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg== + any-promise@^1.0.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -2208,7 +2235,14 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" -braces@^3.0.2, braces@~3.0.2: +brace-expansion@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.2.tgz#54fc53237a613d854c7bd37463aad17df87214e7" + integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@^3.0.3, braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== @@ -2322,6 +2356,21 @@ chalk@^4.0, chalk@^4.0.0, chalk@^4.1.0: optionalDependencies: fsevents "~2.3.2" +chokidar@^3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chrome-trace-event@^1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" @@ -2547,6 +2596,15 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + css-declaration-sorter@^6.3.1: version "6.4.0" resolved "https://registry.yarnpkg.com/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz#630618adc21724484b3e9505bce812def44000ad" @@ -2875,6 +2933,11 @@ duplexer@^0.1.2: resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.2.tgz#3abe43aef3835f8ae077d136ddce0f276b0400e6" integrity sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -2885,6 +2948,16 @@ electron-to-chromium@^1.5.4: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.13.tgz#1abf0410c5344b2b829b7247e031f02810d442e6" integrity sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q== +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" @@ -3304,10 +3377,10 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== -fast-glob@^3.2.12: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== +fast-glob@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" @@ -3315,16 +3388,16 @@ fast-glob@^3.2.12: merge2 "^1.3.0" micromatch "^4.0.4" -fast-glob@^3.3.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" - integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== +fast-glob@^3.3.2: + version "3.3.3" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.3.tgz#d06d585ce8dba90a16b0505c543c3ccfb3aeb818" + integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg== dependencies: "@nodelib/fs.stat" "^2.0.2" "@nodelib/fs.walk" "^1.2.3" glob-parent "^5.1.2" merge2 "^1.3.0" - micromatch "^4.0.4" + micromatch "^4.0.8" fast-json-stable-stringify@^2.0.0: version "2.1.0" @@ -3449,6 +3522,14 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +foreground-child@^3.1.0: + version "3.3.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.3.1.tgz#32e8e9ed1b68a3497befb9ac2b6adf92a638576f" + integrity sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw== + dependencies: + cross-spawn "^7.0.6" + signal-exit "^4.0.1" + form-data@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" @@ -3498,6 +3579,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + function.prototype.name@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" @@ -3572,6 +3658,18 @@ glob@7.1.6: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^10.3.10: + version "10.4.5" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.5.tgz#f4d9f0b90ffdbab09c9d77f5f29b4262517b0956" + integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^7.1.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -3683,6 +3781,13 @@ hash-sum@^2.0.0: resolved "https://registry.yarnpkg.com/hash-sum/-/hash-sum-2.0.0.tgz#81d01bb5de8ea4a214ad5d6ead1b523460b0b45a" integrity sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg== +hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + hpack.js@^2.1.6: version "2.1.6" resolved "https://registry.yarnpkg.com/hpack.js/-/hpack.js-2.1.6.tgz#87774c0949e513f42e84575b3c45681fade2a0b2" @@ -3904,6 +4009,13 @@ is-core-module@^2.11.0: dependencies: has "^1.0.3" +is-core-module@^2.16.0: + version "2.16.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.16.1.tgz#2a98801a849f43e2add644fbb6bc6229b19a4ef4" + integrity sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w== + dependencies: + hasown "^2.0.2" + is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" @@ -3921,6 +4033,11 @@ is-extglob@^2.1.1: resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" @@ -4036,6 +4153,15 @@ isobject@^3.0.1: resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== +jackspeak@^3.1.2: + version "3.4.3" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.3.tgz#8833a9d89ab4acde6188942bd1c53b6390ed5a8a" + integrity sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + javascript-natural-sort@^0.7.1: version "0.7.1" resolved "https://registry.yarnpkg.com/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz#f9e2303d4507f6d74355a73664d1440fb5a0ef59" @@ -4082,6 +4208,11 @@ jiti@^1.21.0: resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.0.tgz#7c97f8fe045724e136a397f7340475244156105d" integrity sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q== +jiti@^1.21.6: + version "1.21.7" + resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.21.7.tgz#9dd81043424a3d28458b193d965f0d18a2300ba9" + integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -4177,6 +4308,11 @@ lilconfig@^2.0.5, lilconfig@^2.1.0: resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.1.0.tgz#78e23ac89ebb7e1bfbf25b18043de756548e7f52" integrity sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ== +lilconfig@^3.0.0, lilconfig@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-3.1.3.tgz#a1bcfd6257f9585bf5ae14ceeebb7b559025e4c4" + integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw== + lines-and-columns@^1.1.6: version "1.2.4" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" @@ -4248,6 +4384,11 @@ lodash@^4.17.20, lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== +lru-cache@^10.2.0: + version "10.4.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.4.3.tgz#410fc8a17b70e598013df257c2446b7f3383f119" + integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ== + lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -4346,6 +4487,14 @@ micromatch@^4.0.2, micromatch@^4.0.4, micromatch@^4.0.5: braces "^3.0.2" picomatch "^2.3.1" +micromatch@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.8.tgz#d66fa18f3a47076789320b9b1af32bd86d9fa202" + integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA== + dependencies: + braces "^3.0.3" + picomatch "^2.3.1" + mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -4387,11 +4536,23 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.6: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + mrmime@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" @@ -4429,6 +4590,11 @@ mz@^2.7.0: object-assign "^4.0.1" thenify-all "^1.0.0" +nanoid@^3.3.11: + version "3.3.11" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.11.tgz#4f4f112cefbe303202f2199838128936266d185b" + integrity sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w== + nanoid@^3.3.6: version "3.3.6" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c" @@ -4625,6 +4791,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json-from-dist@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz#4f1471a010827a86f94cfd9b0727e36d267de505" + integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw== + parent-module@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" @@ -4672,6 +4843,14 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -4789,6 +4968,14 @@ postcss-load-config@^4.0.1: lilconfig "^2.0.5" yaml "^2.1.1" +postcss-load-config@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-4.0.2.tgz#7159dcf626118d33e299f485d6afe4aff7c4a3e3" + integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ== + dependencies: + lilconfig "^3.0.0" + yaml "^2.3.4" + postcss-loader@^7.3.0: version "7.3.0" resolved "https://registry.yarnpkg.com/postcss-loader/-/postcss-loader-7.3.0.tgz#05991c1e490d8ff86ef18358d87db3b5b2dcb5f5" @@ -4884,6 +5071,13 @@ postcss-nested@^6.0.1: dependencies: postcss-selector-parser "^6.0.11" +postcss-nested@^6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.2.0.tgz#4c2d22ab5f20b9cb61e2c5c5915950784d068131" + integrity sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ== + dependencies: + postcss-selector-parser "^6.1.1" + postcss-normalize-charset@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/postcss-normalize-charset/-/postcss-normalize-charset-6.0.0.tgz#36cc12457259064969fb96f84df491652a4b0975" @@ -4977,6 +5171,14 @@ postcss-selector-parser@^6.0.11, postcss-selector-parser@^6.0.2, postcss-selecto cssesc "^3.0.0" util-deprecate "^1.0.2" +postcss-selector-parser@^6.1.1, postcss-selector-parser@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz#27ecb41fb0e3b6ba7a1ec84fff347f734c7929de" + integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg== + dependencies: + cssesc "^3.0.0" + util-deprecate "^1.0.2" + postcss-svgo@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/postcss-svgo/-/postcss-svgo-6.0.0.tgz#7b18742d38d4505a0455bbe70d52b49f00eaf69d" @@ -5015,6 +5217,15 @@ postcss@^8.4.14: picocolors "^1.1.1" source-map-js "^1.2.1" +postcss@^8.4.47: + version "8.5.6" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.6.tgz#2825006615a619b4f62a9e7426cc120b349a8f3c" + integrity sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg== + dependencies: + nanoid "^3.3.11" + picocolors "^1.1.1" + source-map-js "^1.2.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -5233,6 +5444,15 @@ resolve@^1.1.7, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.19.0, resolve@^1.20 path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +resolve@^1.22.8: + version "1.22.10" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.10.tgz#b663e83ffb09bbf2386944736baae803029b8b39" + integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w== + dependencies: + is-core-module "^2.16.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + retry@^0.13.1: version "0.13.1" resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" @@ -5473,6 +5693,11 @@ signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== + signature_pad@^4.1.4: version "4.2.0" resolved "https://registry.yarnpkg.com/signature_pad/-/signature_pad-4.2.0.tgz#7513cee8cb8afd6594d871c61cf4d61420601422" @@ -5572,6 +5797,33 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" + string.prototype.trim@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" @@ -5613,13 +5865,27 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.2" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.2.tgz#132875abde678c7ea8d691533f2e7e22bb744dba" + integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA== + dependencies: + ansi-regex "^6.0.1" + strip-bom@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" @@ -5661,6 +5927,19 @@ sucrase@^3.32.0: pirates "^4.0.1" ts-interface-checker "^0.1.9" +sucrase@^3.35.0: + version "3.35.0" + resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.35.0.tgz#57f17a3d7e19b36d8995f06679d121be914ae263" + integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA== + dependencies: + "@jridgewell/gen-mapping" "^0.3.2" + commander "^4.0.0" + glob "^10.3.10" + lines-and-columns "^1.1.6" + mz "^2.7.0" + pirates "^4.0.1" + ts-interface-checker "^0.1.9" + supports-color@^5.3.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -5727,34 +6006,33 @@ tailwindcss@^3.1: resolve "^1.22.2" sucrase "^3.32.0" -tailwindcss@^3.3.2: - version "3.3.2" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.3.2.tgz#2f9e35d715fdf0bbf674d90147a0684d7054a2d3" - integrity sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w== +tailwindcss@^3.4.17: + version "3.4.17" + resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.4.17.tgz#ae8406c0f96696a631c790768ff319d46d5e5a63" + integrity sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og== dependencies: "@alloc/quick-lru" "^5.2.0" arg "^5.0.2" - chokidar "^3.5.3" + chokidar "^3.6.0" didyoumean "^1.2.2" dlv "^1.1.3" - fast-glob "^3.2.12" + fast-glob "^3.3.2" glob-parent "^6.0.2" is-glob "^4.0.3" - jiti "^1.18.2" - lilconfig "^2.1.0" - micromatch "^4.0.5" + jiti "^1.21.6" + lilconfig "^3.1.3" + micromatch "^4.0.8" normalize-path "^3.0.0" object-hash "^3.0.0" - picocolors "^1.0.0" - postcss "^8.4.23" + picocolors "^1.1.1" + postcss "^8.4.47" postcss-import "^15.1.0" postcss-js "^4.0.1" - postcss-load-config "^4.0.1" - postcss-nested "^6.0.1" - postcss-selector-parser "^6.0.11" - postcss-value-parser "^4.2.0" - resolve "^1.22.2" - sucrase "^3.32.0" + postcss-load-config "^4.0.2" + postcss-nested "^6.2.0" + postcss-selector-parser "^6.1.2" + resolve "^1.22.8" + sucrase "^3.35.0" tapable@^2.0, tapable@^2.1.1, tapable@^2.2.0: version "2.2.1" @@ -6258,6 +6536,24 @@ word-wrap@^1.2.3: resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" + wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" @@ -6298,6 +6594,11 @@ yaml@^2.1.1: resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.1.tgz#02fe0975d23cd441242aa7204e09fc28ac2ac33b" integrity sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ== +yaml@^2.3.4: + version "2.8.1" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.1.tgz#1870aa02b631f7e8328b93f8bc574fac5d6c4d79" + integrity sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"