From 139ba183de9cac9905a72a0e6ae0b8825b9fa4d8 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Fri, 13 Mar 2026 11:18:47 +0100 Subject: [PATCH] fix(custom-fields) : include orphan values with embedded definitions in edit pages After JSON-to-tables migration, custom field definitions not linked to a ModelType were invisible on edit pages because buildCustomFieldInputs only mapped over structure definitions. Now also includes values whose embedded customField definition has no matching structure entry. Co-Authored-By: Claude Opus 4.6 --- app/shared/utils/customFieldFormUtils.ts | 41 ++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/app/shared/utils/customFieldFormUtils.ts b/app/shared/utils/customFieldFormUtils.ts index ea962ea..c9e2483 100644 --- a/app/shared/utils/customFieldFormUtils.ts +++ b/app/shared/utils/customFieldFormUtils.ts @@ -225,7 +225,10 @@ export const buildCustomFieldInputs = ( if (fieldName) mapByName.set(fieldName, entry) }) - return definitions + const matchedIds = new Set() + const matchedNames = new Set() + + const result = definitions .map((definition) => { const definitionId = definition.customFieldId || definition.id || null const matched = (definitionId ? mapById.get(definitionId) : null) || mapByName.get(definition.name) @@ -239,6 +242,11 @@ export const buildCustomFieldInputs = ( } } + const matchedFieldId = matched.customField?.id || matched.customFieldId || null + if (matchedFieldId) matchedIds.add(matchedFieldId) + const matchedFieldName = matched.customField?.name || matched.name || null + if (matchedFieldName) matchedNames.add(matchedFieldName) + const resolvedValue = extractStoredCustomFieldValue(matched) return { ...definition, @@ -253,7 +261,36 @@ export const buildCustomFieldInputs = ( ), } }) - .sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0)) + + // Include values with embedded definitions that didn't match any structure definition + valueList.forEach((entry, index) => { + if (!entry || typeof entry !== 'object') return + const cf = entry.customField + if (!cf || typeof cf !== 'object') return + const fieldId = cf.id || entry.customFieldId || null + const fieldName = cf.name || entry.name || null + if (fieldId && matchedIds.has(fieldId)) return + if (fieldName && matchedNames.has(fieldName)) return + + const name = resolveFieldName(cf) + if (!name) return + + const type = resolveFieldType(cf) + const resolvedValue = extractStoredCustomFieldValue(entry) + result.push({ + id: fieldId, + name, + type, + required: resolveRequiredFlag(cf), + options: resolveOptions(cf), + value: formatDefaultValue(type, resolvedValue), + customFieldId: fieldId, + customFieldValueId: entry.id ?? null, + orderIndex: typeof cf.orderIndex === 'number' ? cf.orderIndex : definitions.length + index, + }) + }) + + return result.sort((a, b) => (a.orderIndex ?? 0) - (b.orderIndex ?? 0)) } // ---------------------------------------------------------------------------