PluginProbe
The Innovative Form Builder – IvyForms / 0.6
The Innovative Form Builder – IvyForms v0.6
1.5.1 1.5 1.4.1 1.4 trunk 0.1.2 0.2 0.2.1 0.3 0.3.1 0.4 0.5 0.6 0.6.1 0.6.1-backup 0.6.1.1 0.7 0.8 0.8.1 0.8.2 0.9 0.9.1 1.0 1.1 1.1.1 All 27 releases
ivyforms / frontend / src / utils / fieldsNormalization.ts

fieldsNormalization.ts in The Innovative Form Builder – IvyForms 0.6, at frontend/src/utils/fieldsNormalization.ts

170 lines 6.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import type { Field, NameSubField, AddressSubField } from '@/types/field'
2
3 function safeParse(json: string | null | undefined): Record<string, unknown> {
4 if (!json) return {}
5 try {
6 return JSON.parse(json) as Record<string, unknown>
7 } catch {
8 return {}
9 }
10 }
11
12 export function normalizeFieldsForBuilder(apiFields: Field[]): Field[] {
13 if (!Array.isArray(apiFields) || apiFields.length === 0) return []
14 const parents: Field[] = []
15 const childrenByParent: Record<number, Field[]> = {}
16
17 for (const f of apiFields) {
18 if (f.parentId && typeof f.parentId === 'number' && f.parentId > 0) {
19 if (!childrenByParent[f.parentId]) childrenByParent[f.parentId] = []
20 childrenByParent[f.parentId].push(f)
21 } else {
22 parents.push(f)
23 }
24 }
25
26 return parents.map((parent) => {
27 if (parent.type === 'name') {
28 const children = childrenByParent[parent.id] ?? []
29 const nameFields: NameSubField[] = children.map((child) => {
30 const s = safeParse((child as Field & { settings?: string }).settings)
31 const label = (child as Partial<Field>).label ?? String(s['label'] ?? '')
32 return {
33 type: 'text',
34 id: String(child.id ?? ''),
35 modelValue: (child as Partial<Field>).defaultValue ?? '',
36 label,
37 required: !!(child as Partial<Field>).required || Boolean(s['required']),
38 optionHide: !!(child as Partial<Field>).hideLabel || Boolean(s['hideLabel']),
39 description: String((child as Partial<Field>).description ?? s['description'] ?? ''),
40 placeholder: String((child as Partial<Field>).placeholder ?? s['placeholder'] ?? ''),
41 }
42 })
43 const nameFieldTypes = nameFields.map((_, idx) => `nameField${idx + 1}`)
44 return {
45 ...parent,
46 nameFields,
47 nameFieldTypes,
48 } as Field
49 }
50 if (parent.type === 'address') {
51 const children = childrenByParent[parent.id] ?? []
52 const defaultAddressOrder = [
53 'streetAddress',
54 'addressLine2',
55 'city',
56 'state',
57 'zip',
58 'country',
59 ]
60 const addressFields: AddressSubField[] = children.map((child, idx) => {
61 const s = safeParse((child as Field & { settings?: string }).settings)
62 const explicitType = (child as Partial<Field>).addressType || String(s['type'] ?? '')
63 const type =
64 explicitType && explicitType.trim() !== ''
65 ? explicitType
66 : (defaultAddressOrder[idx] ?? `addressPart${idx + 1}`)
67 return {
68 type,
69 id: String(child.id ?? ''),
70 label: (child as Partial<Field>).label ?? String(s['label'] ?? ''),
71 value: (child as Partial<Field>).defaultValue ?? String(s['value'] ?? ''),
72 placeholder: (child as Partial<Field>).placeholder ?? String(s['placeholder'] ?? ''),
73 required: !!(child as Partial<Field>).required || Boolean(s['required']),
74 hideLabel: !!(child as Partial<Field>).hideLabel || Boolean(s['hideLabel']),
75 description: String((child as Partial<Field>).description ?? s['description'] ?? ''),
76 requiredMessage: String(
77 (child as Partial<Field>).requiredMessage ?? s['requiredMessage'] ?? '',
78 ),
79 visible: !!(child as Partial<Field>).visible || Boolean(s['visible'] ?? true),
80 }
81 })
82 const addressFieldTypes = addressFields.map((af) => af.type)
83 return {
84 ...parent,
85 addressFields,
86 addressFieldTypes,
87 } as Field
88 }
89 // Regular field
90 return {
91 ...parent,
92 }
93 })
94 }
95
96 export function denormalizeFieldsForApi(fields: Field[]): Field[] {
97 const result: Field[] = []
98 for (const field of fields) {
99 if (field.type === 'name') {
100 if (Array.isArray(field.nameFields)) {
101 const { nameFields, ...parentRest } = field
102 result.push({ ...parentRest, type: 'name' } as Field)
103 nameFields.forEach((sub, idx) => {
104 result.push({
105 id: sub.id ? parseInt(sub.id, 10) || 0 : 0,
106 formId: field.formId,
107 fieldIndex: field.fieldIndex,
108 type: 'text',
109 label: sub.label,
110 defaultValue: sub.modelValue,
111 required: sub.required,
112 placeholder: sub.placeholder,
113 position: field.position,
114 value: sub.modelValue,
115 hideLabel: sub.optionHide,
116 description: sub.description,
117 requiredMessage: '',
118 parentId: field.id,
119 subFieldIndex: idx,
120 } as Field)
121 })
122 continue
123 }
124 }
125 if (field.type === 'address') {
126 if (Array.isArray(field.addressFields)) {
127 const { addressFields, ...parentRest } = field
128 result.push(parentRest as Field)
129 addressFields.forEach((sub, idx) => {
130 const settings = JSON.stringify({
131 hideLabel: sub.hideLabel,
132 description: sub.description,
133 placeholder: sub.placeholder,
134 type: sub.type,
135 requiredMessage: sub.requiredMessage,
136 visible: typeof sub.visible === 'boolean' ? sub.visible : true,
137 })
138 result.push({
139 id: sub.id ? parseInt(sub.id, 10) || 0 : 0,
140 formId: field.formId,
141 fieldIndex: field.fieldIndex,
142 type: 'text',
143 label: sub.label,
144 defaultValue: sub.value,
145 required: sub.required,
146 placeholder: sub.placeholder,
147 position: field.position,
148 value: sub.value,
149 hideLabel: sub.hideLabel,
150 description: sub.description,
151 requiredMessage: sub.requiredMessage,
152 parentId: field.id,
153 addressType: sub.type,
154 settings,
155 subFieldIndex: idx,
156 visible: sub.visible,
157 } as Field)
158 })
159 continue
160 }
161 }
162 // Regular field
163 result.push({
164 ...field,
165 id: typeof field.id === 'string' ? parseInt(field.id, 10) || 0 : field.id || 0,
166 } as Field)
167 }
168 return result
169 }
170