From 17db839dacef530a783e984937d96a4f19a08504 Mon Sep 17 00:00:00 2001 From: Davide Silvestri <davide@baserow.io> Date: Mon, 17 Mar 2025 11:08:41 +0100 Subject: [PATCH] Resolve "Fix number formatting to the rollup field to a formula number field" --- changelog/entries/unreleased/bug/3443_.json | 8 ++ web-frontend/modules/core/mixins/form.js | 6 + .../components/field/FieldLookupSubForm.vue | 75 +------------ .../components/field/FieldRollupSubForm.vue | 39 +------ .../formula/FormulaTypeSubForms.vue | 8 ++ .../database/mixins/lookupFieldSubForm.js | 106 ++++++++++++++++++ 6 files changed, 135 insertions(+), 107 deletions(-) create mode 100644 changelog/entries/unreleased/bug/3443_.json create mode 100644 web-frontend/modules/database/mixins/lookupFieldSubForm.js diff --git a/changelog/entries/unreleased/bug/3443_.json b/changelog/entries/unreleased/bug/3443_.json new file mode 100644 index 000000000..089ec0fce --- /dev/null +++ b/changelog/entries/unreleased/bug/3443_.json @@ -0,0 +1,8 @@ +{ + "type": "bug", + "message": "Fix field formatting options in rollup fields targeting formula fields", + "domain": "database", + "issue_number": 3443, + "bullet_points": [], + "created_at": "2025-03-13" +} \ No newline at end of file diff --git a/web-frontend/modules/core/mixins/form.js b/web-frontend/modules/core/mixins/form.js index 1ec7b8841..43bf24c90 100644 --- a/web-frontend/modules/core/mixins/form.js +++ b/web-frontend/modules/core/mixins/form.js @@ -210,6 +210,12 @@ export default { }) ) }, + isDirty() { + for (const [key, value] of Object.entries(this.getDefaultValues())) { + if (this.values[key] !== value) return true + } + return false + }, /** * Resets the form and the child forms to its original state. * diff --git a/web-frontend/modules/database/components/field/FieldLookupSubForm.vue b/web-frontend/modules/database/components/field/FieldLookupSubForm.vue index fc1e0a68c..f656cc686 100644 --- a/web-frontend/modules/database/components/field/FieldLookupSubForm.vue +++ b/web-frontend/modules/database/components/field/FieldLookupSubForm.vue @@ -16,6 +16,7 @@ ></FieldSelectTargetFieldSubForm> <template v-if="selectedTargetField"> <FormulaTypeSubForms + ref="subForm" :default-values="subFormDefaultValues" :formula-type="targetFieldFormulaType" :table="table" @@ -31,6 +32,7 @@ <script> import form from '@baserow/modules/core/mixins/form' import fieldSubForm from '@baserow/modules/database/mixins/fieldSubForm' +import lookupFieldSubForm from '@baserow/modules/database/mixins/lookupFieldSubForm' import FormulaTypeSubForms from '@baserow/modules/database/components/formula/FormulaTypeSubForms' import FieldSelectThroughFieldSubForm from '@baserow/modules/database/components/field/FieldSelectThroughFieldSubForm' import FieldSelectTargetFieldSubForm from '@baserow/modules/database/components/field/FieldSelectTargetFieldSubForm' @@ -42,77 +44,6 @@ export default { FieldSelectTargetFieldSubForm, FormulaTypeSubForms, }, - mixins: [form, fieldSubForm], - data() { - return { - selectedThroughField: null, - selectedTargetField: null, - allowedValues: [], - values: {}, - errorFromServer: null, - subFormDefaultValues: {}, - } - }, - computed: { - targetFieldFormulaType() { - if (this.selectedTargetField) { - return this.getFormulaType(this.selectedTargetField) - } - return 'unknown' - }, - }, - watch: { - defaultValues: { - handler(newDefaultValues) { - this.subFormDefaultValues = { ...newDefaultValues } - }, - immediate: true, - }, - selectedTargetField: { - handler(newTargetField) { - if (!newTargetField) { - return - } - const fieldType = this.$registry.get('field', newTargetField.type) - const formulaType = fieldType.toBaserowFormulaType(newTargetField) - - const formulaTypeChanged = - formulaType && this.getFormulaType(this.defaultValues) !== formulaType - - // New field or different type, use the relevant settings from the target field - const fieldValues = this.defaultValues - if (!fieldValues.id || formulaTypeChanged) { - for (const key in this.selectedTargetField) { - if (key.startsWith(formulaType)) { - this.subFormDefaultValues[key] = this.selectedTargetField[key] - } - } - } - }, - }, - }, - methods: { - getFormulaType(field) { - return field.array_formula_type || field.formula_type || field.type - }, - handleErrorByForm(error) { - if ( - [ - 'ERROR_WITH_FORMULA', - 'ERROR_FIELD_SELF_REFERENCE', - 'ERROR_FIELD_CIRCULAR_REFERENCE', - ].includes(error.handler.code) - ) { - this.errorFromServer = error.handler.detail - return true - } else { - return false - } - }, - reset() { - form.methods.reset.call(this) - this.errorFromServer = null - }, - }, + mixins: [form, fieldSubForm, lookupFieldSubForm], } </script> diff --git a/web-frontend/modules/database/components/field/FieldRollupSubForm.vue b/web-frontend/modules/database/components/field/FieldRollupSubForm.vue index 8d7dca4ca..914947125 100644 --- a/web-frontend/modules/database/components/field/FieldRollupSubForm.vue +++ b/web-frontend/modules/database/components/field/FieldRollupSubForm.vue @@ -39,7 +39,8 @@ </FormGroup> <FormulaTypeSubForms - :default-values="defaultValues" + ref="subForm" + :default-values="subFormDefaultValues" :formula-type="targetFieldFormulaType" :table="table" :view="view" @@ -60,6 +61,7 @@ import { required } from '@vuelidate/validators' import form from '@baserow/modules/core/mixins/form' import fieldSubForm from '@baserow/modules/database/mixins/fieldSubForm' +import lookupFieldSubForm from '@baserow/modules/database/mixins/lookupFieldSubForm' import FormulaTypeSubForms from '@baserow/modules/database/components/formula/FormulaTypeSubForms' import FieldSelectThroughFieldSubForm from '@baserow/modules/database/components/field/FieldSelectThroughFieldSubForm' import FieldSelectTargetFieldSubForm from '@baserow/modules/database/components/field/FieldSelectTargetFieldSubForm' @@ -71,58 +73,25 @@ export default { FieldSelectTargetFieldSubForm, FormulaTypeSubForms, }, - mixins: [form, fieldSubForm], + mixins: [form, fieldSubForm, lookupFieldSubForm], setup() { return { v$: useVuelidate({ $lazy: true }) } }, data() { return { - selectedThroughField: null, - selectedTargetField: null, allowedValues: ['rollup_function'], values: { rollup_function: null, }, - errorFromServer: null, } }, computed: { - targetFieldFormulaType() { - if (this.selectedTargetField) { - return ( - this.selectedTargetField.array_formula_type || - this.selectedTargetField.type - ) - } - return 'unknown' - }, rollupFunctions() { return Object.values(this.$registry.getAll('formula_function')).filter( (f) => f.isRollupCompatible(this.targetFieldFormulaType) ) }, }, - - methods: { - handleErrorByForm(error) { - if ( - [ - 'ERROR_WITH_FORMULA', - 'ERROR_FIELD_SELF_REFERENCE', - 'ERROR_FIELD_CIRCULAR_REFERENCE', - ].includes(error.handler.code) - ) { - this.errorFromServer = error.handler.detail - return true - } else { - return false - } - }, - reset() { - form.methods.reset.call(this) - this.errorFromServer = null - }, - }, validations() { return { values: { diff --git a/web-frontend/modules/database/components/formula/FormulaTypeSubForms.vue b/web-frontend/modules/database/components/formula/FormulaTypeSubForms.vue index 6dec9b97c..cb39b9e5b 100644 --- a/web-frontend/modules/database/components/formula/FormulaTypeSubForms.vue +++ b/web-frontend/modules/database/components/formula/FormulaTypeSubForms.vue @@ -1,6 +1,7 @@ <template> <FieldNumberSubForm v-if="formulaType === 'number'" + ref="subForm" :default-values="defaultValues" :table="table" :view="view" @@ -12,6 +13,7 @@ </FieldNumberSubForm> <FieldDateSubForm v-else-if="['date', 'last_modified', 'created_on'].includes(formulaType)" + ref="subForm" :default-values="defaultValues" :table="table" :view="view" @@ -22,6 +24,7 @@ </FieldDateSubForm> <FieldDurationSubForm v-else-if="formulaType === 'duration'" + ref="subForm" :default-values="defaultValues" :table="table" :view="view" @@ -67,5 +70,10 @@ export default { values: {}, } }, + methods: { + isDirty() { + return this.$refs.subForm?.isDirty() + }, + }, } </script> diff --git a/web-frontend/modules/database/mixins/lookupFieldSubForm.js b/web-frontend/modules/database/mixins/lookupFieldSubForm.js new file mode 100644 index 000000000..3cd23e87f --- /dev/null +++ b/web-frontend/modules/database/mixins/lookupFieldSubForm.js @@ -0,0 +1,106 @@ +import form from '@baserow/modules/core/mixins/form' + +export default { + data() { + return { + selectedThroughField: null, + selectedTargetField: null, + allowedValues: [], + values: {}, + errorFromServer: null, + subFormDefaultValues: {}, + } + }, + computed: { + targetFieldFormulaType() { + if (this.selectedTargetField) { + return this.getFormulaType(this.selectedTargetField) + } + return 'unknown' + }, + }, + watch: { + defaultValues: { + handler(newDefaultValues) { + this.subFormDefaultValues = { ...newDefaultValues } + }, + immediate: true, + }, + selectedTargetField: { + /** + * Updates sub form defaults based on the selected target field. + * For new fields with untouched forms, always suggest target field settings. + * For existing fields, suggest settings only if the formula type changes. + */ + handler(newTargetField, oldTargetField) { + if (!newTargetField) { + return + } + const fieldId = this.defaultValues.id + const newField = !fieldId + const cleanForm = !this.$refs.subForm?.isDirty() + + const newFieldCleanFormAndNewTarget = + newField && cleanForm && newTargetField?.id !== oldTargetField?.id + + const existingFieldButDifferentType = + !newField && !this.matchTargetFieldType(newTargetField) + + const shouldSuggestDefaults = + newFieldCleanFormAndNewTarget || existingFieldButDifferentType + + const fieldType = this.$registry.get('field', newTargetField.type) + const formulaType = fieldType.toBaserowFormulaType(newTargetField) + + // New field or different type, use the relevant settings from the target field + if (shouldSuggestDefaults) { + const defaults = {} + for (const key in this.selectedTargetField) { + if (key.startsWith(formulaType)) { + defaults[key] = this.selectedTargetField[key] + } + } + this.subFormDefaultValues = { + ...this.subFormDefaultValues, + ...defaults, + } + this.$nextTick(() => this.$refs.subForm.reset()) + } + }, + }, + }, + methods: { + /** + * Verify if the final formula type match the target field type. + */ + matchTargetFieldType(targetField) { + const field = this.defaultValues + return ( + field?.type === this.fieldType && + (field?.formula_type === targetField?.type || + field?.array_formula_type === targetField?.type) + ) + }, + getFormulaType(field) { + return field.array_formula_type || field.formula_type || field.type + }, + handleErrorByForm(error) { + if ( + [ + 'ERROR_WITH_FORMULA', + 'ERROR_FIELD_SELF_REFERENCE', + 'ERROR_FIELD_CIRCULAR_REFERENCE', + ].includes(error.handler.code) + ) { + this.errorFromServer = error.handler.detail + return true + } else { + return false + } + }, + reset() { + form.methods.reset.call(this) + this.errorFromServer = null + }, + }, +}