import { action, computed, makeObservable, observable, reaction } from 'mobx'; import { CustomField, ExtendedFieldsDataMap, Translate } from '@wix/bex-core'; import type { OnCustomFieldChange } from '../../components/CustomFieldsWidget/OnCustomFieldChange'; import { BaseInputRef } from '../../fields'; import { CustomFieldsWidgetState } from './CustomFieldsWidgetState'; export interface CustomFieldsFormStateParams { namespace: string; customFieldsWidgetState: CustomFieldsWidgetState; translate: Translate; } export class CustomFieldsFormState { readonly _namespace; readonly _customFieldsWidgetState; _fields; _extendedFieldsDataMap; _initialValues; _translate: Translate; _inputRefs: { [fieldId: string]: BaseInputRef }; constructor(params: CustomFieldsFormStateParams) { const { namespace, customFieldsWidgetState, translate } = params; this._namespace = namespace; this._customFieldsWidgetState = customFieldsWidgetState; const { fields, extendedFieldsDataMap } = customFieldsWidgetState.getNamespaceFormSchemaAndData(namespace); this._fields = fields ?? []; this._extendedFieldsDataMap = observable.map( extendedFieldsDataMap, ); this._initialValues = { ...extendedFieldsDataMap }; this._inputRefs = {}; this._translate = translate; makeObservable(this, { setValue: action, setNewFields: action, values: computed, fields: computed, isDirty: computed, _fields: observable.ref, writeableFields: computed, _isUserFieldsNamespace: computed, _inputRefs: observable.ref, appName: computed, }); } _getFullyQualifiedFieldId(originalId: string) { return `extendedFields.namespaces.${this._namespace}.${originalId}`; } get fields() { return this._fields; } get writeableFields() { const { _isUserFieldsNamespace, _fields } = this; if (_isUserFieldsNamespace) { return _fields; } return _fields.filter((field) => field.permissions?.write?.includes('users'), ); } get _isUserFieldsNamespace() { const { _customFieldsWidgetState, _namespace } = this; const { _userFieldsNamespace } = _customFieldsWidgetState; return _namespace === _userFieldsNamespace; } init() { const { _customFieldsWidgetState } = this; const disposers = [ reaction( () => _customFieldsWidgetState.getNamespaceFormSchemaAndData( this._namespace, ), ({ fields }) => { if (!fields) { return; } this.setNewFields(fields); }, { fireImmediately: true, }, ), ]; return () => { disposers.forEach((d) => d?.()); }; } setValue({ id, value, onChange, }: { id: string; value: T; onChange?: OnCustomFieldChange; }) { this._extendedFieldsDataMap.set(id, value); onChange?.({ id: `extendedFields.namespaces.${this._namespace}.${id}`, value, allFields: Object.keys(this.values).reduce((acc, fieldId) => { acc[fieldId] = { id: this._getFullyQualifiedFieldId(fieldId), value: this.values[fieldId], }; return acc; }, {} as Record), namespace: this._namespace, }); } getInitialValue(id: string) { return this._initialValues[id]; } setInputRef(id: string, input: BaseInputRef) { this._inputRefs[id] = input; } setNewFields(fields: CustomField[]) { this._fields = fields; this._adjustExtendedFields(); } _adjustExtendedFields() { const existingFields = this._fields.map((field) => field.id); this._extendedFieldsDataMap.forEach((_, key) => { if (!existingFields.includes(key)) { this._extendedFieldsDataMap.delete(key); } }); } validate(): { isValid: boolean; extendedFields?: ExtendedFieldsDataMap } { const invalid = Object.keys(this._inputRefs).find((fieldId) => { const ref = this._inputRefs[fieldId]; return ref.invalid; }); if (invalid) { this._inputRefs[invalid].focus?.(); return { isValid: false }; } return { isValid: true, extendedFields: this.values }; } get isDirty() { return !!Array.from(this._extendedFieldsDataMap.entries()).find( ([key, value]) => { // Files field is not considered dirty since it cannot be changed const customField = this._fields.find((field) => field.id === key); if (customField?.type === 'files') { return false; } if (this._initialValues.hasOwnProperty(key)) { return this._initialValues[key] !== value; } else { // includes both any string value and boolean types (all existing field types values) return !!value; } }, ); } get appName() { const { _customFieldsWidgetState, _namespace } = this; const { apps, dataExtension } = _customFieldsWidgetState; const { dataExtensionService: { userFieldsNamespace }, } = dataExtension; if (_namespace === userFieldsNamespace) { return this._translate('cairo.customFields.entityPage.sectionTitle'); } const appSchema = dataExtension.appSchemas?.find( (schema) => schema.namespace === _namespace, ); return apps.appNames[appSchema?.appDefId || '']; } get values() { return Object.fromEntries(this._extendedFieldsDataMap); } }