create-nextjs-cms 0.9.20 → 0.9.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -3
- package/templates/default/components/GalleryPhoto.tsx +109 -93
- package/templates/default/components/PhotoGallery.tsx +47 -35
- package/templates/default/components/form/Form.tsx +385 -370
- package/templates/default/components/form/FormInputs.tsx +138 -136
- package/templates/default/components/form/helpers/_section-hot-reload.js +1 -1
- package/templates/default/components/form/inputs/DateFormInput.tsx +11 -10
- package/templates/default/components/form/inputs/DocumentFormInput.tsx +270 -222
- package/templates/default/components/form/inputs/PhotoFormInput.tsx +7 -9
- package/templates/default/components/form/inputs/SelectFormInput.tsx +16 -16
- package/templates/default/components/form/inputs/VideoFormInput.tsx +270 -118
- package/templates/default/dynamic-schemas/schema.ts +202 -31
- package/templates/default/package.json +1 -1
|
@@ -1,370 +1,385 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import ContainerBox from '@/components/ContainerBox'
|
|
4
|
-
import {
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
9
|
-
import ProgressBar from '@/components/ProgressBar'
|
|
10
|
-
import
|
|
11
|
-
import
|
|
12
|
-
import
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
} from 'nextjs-cms/
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
richTextFieldSchema,
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
} from '
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
import
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
| RouterOutputs['hasItemsSections']['
|
|
78
|
-
| RouterOutputs['
|
|
79
|
-
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
const
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
)
|
|
163
|
-
break
|
|
164
|
-
|
|
165
|
-
case 'checkbox':
|
|
166
|
-
schema = schema.extend({
|
|
167
|
-
[input.name]: checkboxFieldSchema(input as CheckboxFieldClientConfig, language),
|
|
168
|
-
})
|
|
169
|
-
break
|
|
170
|
-
|
|
171
|
-
case 'text':
|
|
172
|
-
schema = schema.extend({
|
|
173
|
-
[input.name]: textFieldSchema(input as TextFieldClientConfig, language),
|
|
174
|
-
})
|
|
175
|
-
break
|
|
176
|
-
|
|
177
|
-
case 'textarea':
|
|
178
|
-
schema = schema.extend({
|
|
179
|
-
[input.name]: textareaFieldSchema(input as TextAreaFieldClientConfig, language),
|
|
180
|
-
})
|
|
181
|
-
break
|
|
182
|
-
|
|
183
|
-
case 'rich_text':
|
|
184
|
-
schema = schema.extend({
|
|
185
|
-
[input.name]: richTextFieldSchema(input as RichTextFieldClientConfig, language),
|
|
186
|
-
})
|
|
187
|
-
break
|
|
188
|
-
|
|
189
|
-
case 'photo':
|
|
190
|
-
if (formType === 'edit' && !!input.value) break
|
|
191
|
-
schema = schema.extend({
|
|
192
|
-
[input.name]: photoFieldSchema(input as PhotoFieldClientConfig, language),
|
|
193
|
-
})
|
|
194
|
-
break
|
|
195
|
-
|
|
196
|
-
case 'document':
|
|
197
|
-
if (formType === 'edit' && !!input.value) break
|
|
198
|
-
schema = schema.extend({
|
|
199
|
-
[input.name]: documentFieldSchema(input as DocumentFieldClientConfig, language),
|
|
200
|
-
})
|
|
201
|
-
break
|
|
202
|
-
|
|
203
|
-
case 'video':
|
|
204
|
-
if (formType === 'edit' && !!input.value) break
|
|
205
|
-
schema = schema.extend({
|
|
206
|
-
[input.name]: videoFieldSchema(input as VideoFieldClientConfig, language),
|
|
207
|
-
})
|
|
208
|
-
break
|
|
209
|
-
|
|
210
|
-
case 'number':
|
|
211
|
-
schema = schema.extend({
|
|
212
|
-
[input.name]: numberFieldSchema(input as NumberFieldClientConfig, language),
|
|
213
|
-
})
|
|
214
|
-
break
|
|
215
|
-
case 'color':
|
|
216
|
-
schema = schema.extend({
|
|
217
|
-
[input.name]: colorFieldSchema(input as ColorFieldClientConfig, language),
|
|
218
|
-
})
|
|
219
|
-
break
|
|
220
|
-
|
|
221
|
-
case 'map':
|
|
222
|
-
schema = schema.extend({
|
|
223
|
-
[input.name]: mapFieldSchema(input as MapFieldClientConfig, language),
|
|
224
|
-
})
|
|
225
|
-
break
|
|
226
|
-
|
|
227
|
-
case 'password':
|
|
228
|
-
schema = schema.extend({
|
|
229
|
-
[input.name]: passwordFieldSchema(input as PasswordFieldClientConfig, language),
|
|
230
|
-
})
|
|
231
|
-
break
|
|
232
|
-
|
|
233
|
-
case 'slug':
|
|
234
|
-
schema = schema.extend({
|
|
235
|
-
[input.name]: slugFieldSchema(input as SlugFieldClientConfig, language),
|
|
236
|
-
})
|
|
237
|
-
break
|
|
238
|
-
}
|
|
239
|
-
})
|
|
240
|
-
})
|
|
241
|
-
|
|
242
|
-
const methods = useForm({
|
|
243
|
-
resolver: zodResolver(schema),
|
|
244
|
-
})
|
|
245
|
-
|
|
246
|
-
const { dirtyFields } = methods.formState
|
|
247
|
-
const hasDirtyFields = Object.keys(dirtyFields).length > 0
|
|
248
|
-
useEffect(() => {
|
|
249
|
-
onDirtyChange?.(hasDirtyFields)
|
|
250
|
-
}, [hasDirtyFields, onDirtyChange])
|
|
251
|
-
|
|
252
|
-
return (
|
|
253
|
-
<LocalizationProvider value={contentLocale ?? null}>
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
1
|
+
import type { RouterOutputs } from 'nextjs-cms/api'
|
|
2
|
+
import React, { RefObject, useCallback, useEffect } from 'react'
|
|
3
|
+
import ContainerBox from '@/components/ContainerBox'
|
|
4
|
+
import Dropzone, { DropzoneHandles } from '@/components/Dropzone'
|
|
5
|
+
import { LocalizationProvider } from '@/components/form/ContentLocaleContext'
|
|
6
|
+
import FormInputs from '@/components/form/FormInputs'
|
|
7
|
+
import { configLastUpdated } from '@/components/form/helpers/util'
|
|
8
|
+
import NewVariantComponent, { VariantHandles } from '@/components/NewVariantComponent'
|
|
9
|
+
import ProgressBar from '@/components/ProgressBar'
|
|
10
|
+
import { zodResolver } from '@hookform/resolvers/zod'
|
|
11
|
+
import classNames from 'classnames'
|
|
12
|
+
import { useSession } from 'nextjs-cms/auth/react'
|
|
13
|
+
import {
|
|
14
|
+
CheckboxFieldClientConfig,
|
|
15
|
+
ColorFieldClientConfig,
|
|
16
|
+
DateFieldClientConfig,
|
|
17
|
+
DateRangeFieldClientConfig,
|
|
18
|
+
DocumentFieldClientConfig,
|
|
19
|
+
MapFieldClientConfig,
|
|
20
|
+
NumberFieldClientConfig,
|
|
21
|
+
PasswordFieldClientConfig,
|
|
22
|
+
PhotoFieldClientConfig,
|
|
23
|
+
RichTextFieldClientConfig,
|
|
24
|
+
SelectFieldClientConfig,
|
|
25
|
+
SelectMultipleFieldClientConfig,
|
|
26
|
+
SlugFieldClientConfig,
|
|
27
|
+
TextAreaFieldClientConfig,
|
|
28
|
+
TextFieldClientConfig,
|
|
29
|
+
VideoFieldClientConfig,
|
|
30
|
+
} from 'nextjs-cms/core/fields'
|
|
31
|
+
import { ConditionalField, FieldType } from 'nextjs-cms/core/types'
|
|
32
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
33
|
+
import {
|
|
34
|
+
checkboxFieldSchema,
|
|
35
|
+
colorFieldSchema,
|
|
36
|
+
dateFieldSchema,
|
|
37
|
+
dateRangeFieldSchema,
|
|
38
|
+
documentFieldSchema,
|
|
39
|
+
mapFieldSchema,
|
|
40
|
+
numberFieldSchema,
|
|
41
|
+
passwordFieldSchema,
|
|
42
|
+
photoFieldSchema,
|
|
43
|
+
richTextFieldSchema,
|
|
44
|
+
selectFieldSchema,
|
|
45
|
+
selectMultipleFieldSchema,
|
|
46
|
+
slugFieldSchema,
|
|
47
|
+
textareaFieldSchema,
|
|
48
|
+
textFieldSchema,
|
|
49
|
+
videoFieldSchema,
|
|
50
|
+
} from 'nextjs-cms/validators'
|
|
51
|
+
import { FormProvider, useForm } from 'react-hook-form'
|
|
52
|
+
import * as z from 'zod'
|
|
53
|
+
|
|
54
|
+
import PhotoGallery from '../PhotoGallery'
|
|
55
|
+
|
|
56
|
+
export const revalidate = 1
|
|
57
|
+
|
|
58
|
+
export default function Form({
|
|
59
|
+
formType,
|
|
60
|
+
data,
|
|
61
|
+
dropzoneRef,
|
|
62
|
+
variantRef,
|
|
63
|
+
handleSubmit,
|
|
64
|
+
isSubmitting,
|
|
65
|
+
response,
|
|
66
|
+
progress,
|
|
67
|
+
progressVariant,
|
|
68
|
+
buttonType = 'big',
|
|
69
|
+
submitSuccessCount = 0,
|
|
70
|
+
contentLocale,
|
|
71
|
+
defaultLocale,
|
|
72
|
+
onDirtyChange,
|
|
73
|
+
}: {
|
|
74
|
+
formType?: 'new' | 'edit'
|
|
75
|
+
data:
|
|
76
|
+
| RouterOutputs['hasItemsSections']['newItem']
|
|
77
|
+
| RouterOutputs['hasItemsSections']['editItem']
|
|
78
|
+
| RouterOutputs['simpleSections']['create']
|
|
79
|
+
| {
|
|
80
|
+
section: {
|
|
81
|
+
name: string
|
|
82
|
+
gallery?: { localized?: boolean } | null
|
|
83
|
+
title?: { section?: string; singular?: string; plural?: string }
|
|
84
|
+
configFile?: string
|
|
85
|
+
}
|
|
86
|
+
inputGroups:
|
|
87
|
+
| {
|
|
88
|
+
groupId: number | undefined
|
|
89
|
+
groupTitle: string
|
|
90
|
+
groupOrder: number
|
|
91
|
+
inputs: {
|
|
92
|
+
type: FieldType
|
|
93
|
+
name: string
|
|
94
|
+
label: string
|
|
95
|
+
required: boolean
|
|
96
|
+
conditionalFields: ConditionalField[]
|
|
97
|
+
placeholder?: string
|
|
98
|
+
readonly: boolean
|
|
99
|
+
value: any
|
|
100
|
+
}[]
|
|
101
|
+
}[]
|
|
102
|
+
| undefined
|
|
103
|
+
}
|
|
104
|
+
dropzoneRef?: RefObject<DropzoneHandles | null>
|
|
105
|
+
variantRef?: RefObject<VariantHandles[]>
|
|
106
|
+
handleSubmit: any
|
|
107
|
+
isSubmitting: boolean
|
|
108
|
+
response?: any
|
|
109
|
+
progress?: number
|
|
110
|
+
progressVariant?: 'determinate' | 'query'
|
|
111
|
+
buttonType?: 'big' | 'small'
|
|
112
|
+
submitSuccessCount?: number
|
|
113
|
+
contentLocale?: { code: string; label: string; rtl?: boolean }
|
|
114
|
+
defaultLocale?: { code: string; label: string; rtl?: boolean }
|
|
115
|
+
onDirtyChange?: (isDirty: boolean) => void
|
|
116
|
+
}) {
|
|
117
|
+
const t = useI18n()
|
|
118
|
+
const session = useSession()
|
|
119
|
+
const language = session?.data?.user?.language
|
|
120
|
+
|
|
121
|
+
// When editing a non-default locale, only show fields marked as localized
|
|
122
|
+
const isTranslationMode = !!(contentLocale && defaultLocale && contentLocale.code !== defaultLocale.code)
|
|
123
|
+
const sectionGallery = data.section?.gallery as { localized?: boolean } | null | undefined
|
|
124
|
+
const showGallery = !!sectionGallery && (!isTranslationMode || sectionGallery.localized === true)
|
|
125
|
+
|
|
126
|
+
const filterInputsForLocale = <T,>(inputs: T[]): T[] => {
|
|
127
|
+
if (!isTranslationMode) return inputs
|
|
128
|
+
return inputs.filter((input) => (input as any).localized)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const hasNoLocalizedContent =
|
|
132
|
+
isTranslationMode &&
|
|
133
|
+
(data.inputGroups?.every((g) => filterInputsForLocale(g.inputs as any[]).length === 0) ?? true) &&
|
|
134
|
+
sectionGallery?.localized !== true
|
|
135
|
+
|
|
136
|
+
let schema = z.object({})
|
|
137
|
+
/**
|
|
138
|
+
* Construct the schema for the form
|
|
139
|
+
*/
|
|
140
|
+
data.inputGroups?.forEach((inputGroup) => {
|
|
141
|
+
filterInputsForLocale(inputGroup.inputs as any[]).forEach((input: any) => {
|
|
142
|
+
if (input.readonly) return
|
|
143
|
+
switch (input.type) {
|
|
144
|
+
case 'select_multiple':
|
|
145
|
+
schema = schema.extend({
|
|
146
|
+
[input.name]: selectMultipleFieldSchema(input as SelectMultipleFieldClientConfig, language),
|
|
147
|
+
})
|
|
148
|
+
break
|
|
149
|
+
case 'select':
|
|
150
|
+
schema = schema.extend({
|
|
151
|
+
[input.name]: selectFieldSchema(input as SelectFieldClientConfig, language),
|
|
152
|
+
})
|
|
153
|
+
break
|
|
154
|
+
|
|
155
|
+
case 'date':
|
|
156
|
+
schema = schema.extend({
|
|
157
|
+
[input.name]: dateFieldSchema(input as DateFieldClientConfig, language),
|
|
158
|
+
})
|
|
159
|
+
break
|
|
160
|
+
|
|
161
|
+
case 'date_range':
|
|
162
|
+
schema = schema.extend(dateRangeFieldSchema(input as DateRangeFieldClientConfig, language))
|
|
163
|
+
break
|
|
164
|
+
|
|
165
|
+
case 'checkbox':
|
|
166
|
+
schema = schema.extend({
|
|
167
|
+
[input.name]: checkboxFieldSchema(input as CheckboxFieldClientConfig, language),
|
|
168
|
+
})
|
|
169
|
+
break
|
|
170
|
+
|
|
171
|
+
case 'text':
|
|
172
|
+
schema = schema.extend({
|
|
173
|
+
[input.name]: textFieldSchema(input as TextFieldClientConfig, language),
|
|
174
|
+
})
|
|
175
|
+
break
|
|
176
|
+
|
|
177
|
+
case 'textarea':
|
|
178
|
+
schema = schema.extend({
|
|
179
|
+
[input.name]: textareaFieldSchema(input as TextAreaFieldClientConfig, language),
|
|
180
|
+
})
|
|
181
|
+
break
|
|
182
|
+
|
|
183
|
+
case 'rich_text':
|
|
184
|
+
schema = schema.extend({
|
|
185
|
+
[input.name]: richTextFieldSchema(input as RichTextFieldClientConfig, language),
|
|
186
|
+
})
|
|
187
|
+
break
|
|
188
|
+
|
|
189
|
+
case 'photo':
|
|
190
|
+
if (formType === 'edit' && !!input.value) break
|
|
191
|
+
schema = schema.extend({
|
|
192
|
+
[input.name]: photoFieldSchema(input as PhotoFieldClientConfig, language),
|
|
193
|
+
})
|
|
194
|
+
break
|
|
195
|
+
|
|
196
|
+
case 'document':
|
|
197
|
+
if (formType === 'edit' && !!input.value) break
|
|
198
|
+
schema = schema.extend({
|
|
199
|
+
[input.name]: documentFieldSchema(input as DocumentFieldClientConfig, language),
|
|
200
|
+
})
|
|
201
|
+
break
|
|
202
|
+
|
|
203
|
+
case 'video':
|
|
204
|
+
if (formType === 'edit' && !!input.value) break
|
|
205
|
+
schema = schema.extend({
|
|
206
|
+
[input.name]: videoFieldSchema(input as VideoFieldClientConfig, language),
|
|
207
|
+
})
|
|
208
|
+
break
|
|
209
|
+
|
|
210
|
+
case 'number':
|
|
211
|
+
schema = schema.extend({
|
|
212
|
+
[input.name]: numberFieldSchema(input as NumberFieldClientConfig, language),
|
|
213
|
+
})
|
|
214
|
+
break
|
|
215
|
+
case 'color':
|
|
216
|
+
schema = schema.extend({
|
|
217
|
+
[input.name]: colorFieldSchema(input as ColorFieldClientConfig, language),
|
|
218
|
+
})
|
|
219
|
+
break
|
|
220
|
+
|
|
221
|
+
case 'map':
|
|
222
|
+
schema = schema.extend({
|
|
223
|
+
[input.name]: mapFieldSchema(input as MapFieldClientConfig, language),
|
|
224
|
+
})
|
|
225
|
+
break
|
|
226
|
+
|
|
227
|
+
case 'password':
|
|
228
|
+
schema = schema.extend({
|
|
229
|
+
[input.name]: passwordFieldSchema(input as PasswordFieldClientConfig, language),
|
|
230
|
+
})
|
|
231
|
+
break
|
|
232
|
+
|
|
233
|
+
case 'slug':
|
|
234
|
+
schema = schema.extend({
|
|
235
|
+
[input.name]: slugFieldSchema(input as SlugFieldClientConfig, language),
|
|
236
|
+
})
|
|
237
|
+
break
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
const methods = useForm({
|
|
243
|
+
resolver: zodResolver(schema),
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
const { dirtyFields } = methods.formState
|
|
247
|
+
const hasDirtyFields = Object.keys(dirtyFields).length > 0
|
|
248
|
+
useEffect(() => {
|
|
249
|
+
onDirtyChange?.(hasDirtyFields)
|
|
250
|
+
}, [hasDirtyFields, onDirtyChange])
|
|
251
|
+
|
|
252
|
+
return (
|
|
253
|
+
<LocalizationProvider value={contentLocale ?? null}>
|
|
254
|
+
<FormProvider {...methods}>
|
|
255
|
+
<form
|
|
256
|
+
method='post'
|
|
257
|
+
onSubmit={(e) => {
|
|
258
|
+
e.preventDefault()
|
|
259
|
+
// e.stopPropagation()
|
|
260
|
+
methods.handleSubmit((data) => {
|
|
261
|
+
handleSubmit(new FormData(e.target as HTMLFormElement))
|
|
262
|
+
})(e)
|
|
263
|
+
}}
|
|
264
|
+
encType='multipart/form-data'
|
|
265
|
+
>
|
|
266
|
+
<div className='w-full' data-section-schema-version={configLastUpdated}>
|
|
267
|
+
{/*<ContainerBox title={formType ? t(formType === 'new' ? 'add_new' : 'edit') : undefined}>*/}
|
|
268
|
+
<div className='p-4'>
|
|
269
|
+
<div className=''>
|
|
270
|
+
{data.inputGroups ? (
|
|
271
|
+
<div className='flex flex-col gap-4'>
|
|
272
|
+
{hasNoLocalizedContent ? (
|
|
273
|
+
<div className='rounded border border-amber-300 bg-amber-50 p-4 text-amber-800 dark:border-amber-700 dark:bg-amber-950 dark:text-amber-300'>
|
|
274
|
+
<h3 className='font-semibold'>{t('noLocalizedFields')}</h3>
|
|
275
|
+
<p className='mt-1'>
|
|
276
|
+
{t('noLocalizedFieldsHint', {
|
|
277
|
+
section:
|
|
278
|
+
typeof data.section.title === 'object'
|
|
279
|
+
? (data.section.title?.section ?? '')
|
|
280
|
+
: '',
|
|
281
|
+
file: data.section.configFile,
|
|
282
|
+
})}
|
|
283
|
+
</p>
|
|
284
|
+
</div>
|
|
285
|
+
) : (
|
|
286
|
+
<>
|
|
287
|
+
{data.inputGroups.length > 0
|
|
288
|
+
? data.inputGroups.map((inputGroup, index: number) => {
|
|
289
|
+
const filteredInputs = filterInputsForLocale(
|
|
290
|
+
inputGroup.inputs as any[],
|
|
291
|
+
)
|
|
292
|
+
if (filteredInputs.length === 0) return null
|
|
293
|
+
return (
|
|
294
|
+
<ContainerBox title={inputGroup.groupTitle} key={index}>
|
|
295
|
+
<FormInputs
|
|
296
|
+
inputs={filteredInputs}
|
|
297
|
+
sectionName={data.section.name}
|
|
298
|
+
submitSuccessCount={submitSuccessCount}
|
|
299
|
+
/>
|
|
300
|
+
</ContainerBox>
|
|
301
|
+
)
|
|
302
|
+
})
|
|
303
|
+
: null}
|
|
304
|
+
{showGallery ? (
|
|
305
|
+
<>
|
|
306
|
+
<div className='w-full'>
|
|
307
|
+
<PhotoGallery
|
|
308
|
+
sectionName={data.section.name}
|
|
309
|
+
gallery={'gallery' in data ? data.gallery : []}
|
|
310
|
+
localized={sectionGallery?.localized === true}
|
|
311
|
+
locale={contentLocale?.code}
|
|
312
|
+
/>
|
|
313
|
+
</div>
|
|
314
|
+
<div className='w-full'>
|
|
315
|
+
<Dropzone ref={dropzoneRef} />
|
|
316
|
+
</div>
|
|
317
|
+
</>
|
|
318
|
+
) : null}
|
|
319
|
+
|
|
320
|
+
{/*{data.section.variants && data.section.variants.length > 0 ? (
|
|
321
|
+
<div className='w-full'>
|
|
322
|
+
<div className='flex flex-col gap-4'>
|
|
323
|
+
{data.section.variants.map((variant, index) => {
|
|
324
|
+
// Only one variant is allowed for now
|
|
325
|
+
// I have to find a way to make multiple variantRef in order to handle multiple variants
|
|
326
|
+
if (index > 0) return
|
|
327
|
+
|
|
328
|
+
return (
|
|
329
|
+
<NewVariantComponent
|
|
330
|
+
ref={(el) => (variantRef.current[index] = el)}
|
|
331
|
+
key={index}
|
|
332
|
+
section={section}
|
|
333
|
+
variantInfo={variant.info}
|
|
334
|
+
xsrfToken={xsrfToken}
|
|
335
|
+
/>
|
|
336
|
+
)
|
|
337
|
+
})}
|
|
338
|
+
</div>
|
|
339
|
+
</div>
|
|
340
|
+
) : null}*/}
|
|
341
|
+
|
|
342
|
+
<div className='flex flex-col gap-3 pb-4'>
|
|
343
|
+
<div className=''>
|
|
344
|
+
<button
|
|
345
|
+
className={classNames({
|
|
346
|
+
'w-full': buttonType === 'big',
|
|
347
|
+
'float-end': buttonType === 'small',
|
|
348
|
+
'bg-linear-to-r rounded px-4 py-2 font-bold text-white drop-shadow-sm': true,
|
|
349
|
+
'from-emerald-700 via-green-700 to-green-500 dark:from-blue-800 dark:via-sky-800 dark:to-slate-500':
|
|
350
|
+
!isSubmitting,
|
|
351
|
+
'from-gray-600 via-gray-500 to-gray-400': isSubmitting,
|
|
352
|
+
})}
|
|
353
|
+
type='submit'
|
|
354
|
+
disabled={isSubmitting}
|
|
355
|
+
>
|
|
356
|
+
{isSubmitting
|
|
357
|
+
? t('loading')
|
|
358
|
+
: t(formType === 'new' ? 'create' : 'save')}
|
|
359
|
+
</button>
|
|
360
|
+
{progressVariant && progress ? (
|
|
361
|
+
isSubmitting ? (
|
|
362
|
+
<div className='mt-0.5'>
|
|
363
|
+
<ProgressBar
|
|
364
|
+
variant={progressVariant}
|
|
365
|
+
value={progress}
|
|
366
|
+
/>
|
|
367
|
+
</div>
|
|
368
|
+
) : null
|
|
369
|
+
) : null}
|
|
370
|
+
</div>
|
|
371
|
+
{response ? <div className='w-full'>{response}</div> : null}
|
|
372
|
+
</div>
|
|
373
|
+
</>
|
|
374
|
+
)}
|
|
375
|
+
</div>
|
|
376
|
+
) : null}
|
|
377
|
+
</div>
|
|
378
|
+
</div>
|
|
379
|
+
{/*</ContainerBox>*/}
|
|
380
|
+
</div>
|
|
381
|
+
</form>
|
|
382
|
+
</FormProvider>
|
|
383
|
+
</LocalizationProvider>
|
|
384
|
+
)
|
|
385
|
+
}
|