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.
@@ -1,370 +1,385 @@
1
- export const revalidate = 1
2
-
3
- import ContainerBox from '@/components/ContainerBox'
4
- import { useI18n } from 'nextjs-cms/translations/client'
5
- import FormInputs from '@/components/form/FormInputs'
6
- import Dropzone, { DropzoneHandles } from '@/components/Dropzone'
7
- import NewVariantComponent, { VariantHandles } from '@/components/NewVariantComponent'
8
- import classNames from 'classnames'
9
- import ProgressBar from '@/components/ProgressBar'
10
- import React, { RefObject, useCallback, useEffect } from 'react'
11
- import type { RouterOutputs } from 'nextjs-cms/api'
12
- import * as z from 'zod'
13
- import { zodResolver } from '@hookform/resolvers/zod'
14
- import { useForm, FormProvider } from 'react-hook-form'
15
- import {
16
- CheckboxFieldClientConfig,
17
- ColorFieldClientConfig,
18
- DateFieldClientConfig,
19
- DateRangeFieldClientConfig,
20
- DocumentFieldClientConfig,
21
- MapFieldClientConfig,
22
- NumberFieldClientConfig,
23
- PasswordFieldClientConfig,
24
- PhotoFieldClientConfig,
25
- RichTextFieldClientConfig,
26
- SelectFieldClientConfig,
27
- SelectMultipleFieldClientConfig,
28
- SlugFieldClientConfig,
29
- TextAreaFieldClientConfig,
30
- TextFieldClientConfig,
31
- VideoFieldClientConfig,
32
- } from 'nextjs-cms/core/fields'
33
-
34
- import {
35
- numberFieldSchema,
36
- textFieldSchema,
37
- selectFieldSchema,
38
- selectMultipleFieldSchema,
39
- dateFieldSchema,
40
- dateRangeFieldSchema,
41
- checkboxFieldSchema,
42
- textareaFieldSchema,
43
- richTextFieldSchema,
44
- photoFieldSchema,
45
- documentFieldSchema,
46
- videoFieldSchema,
47
- colorFieldSchema,
48
- mapFieldSchema,
49
- passwordFieldSchema,
50
- slugFieldSchema,
51
- } from 'nextjs-cms/validators'
52
-
53
- import { ConditionalField, FieldType } from 'nextjs-cms/core/types'
54
- import { configLastUpdated } from '@/components/form/helpers/util'
55
- import PhotoGallery from '../PhotoGallery'
56
- import { useSession } from 'nextjs-cms/auth/react'
57
- import { LocalizationProvider } from '@/components/form/ContentLocaleContext'
58
-
59
- export default function Form({
60
- formType,
61
- data,
62
- dropzoneRef,
63
- variantRef,
64
- handleSubmit,
65
- isSubmitting,
66
- response,
67
- progress,
68
- progressVariant,
69
- buttonType = 'big',
70
- submitSuccessCount = 0,
71
- contentLocale,
72
- defaultLocale,
73
- onDirtyChange,
74
- }: {
75
- formType?: 'new' | 'edit'
76
- data:
77
- | RouterOutputs['hasItemsSections']['newItem']
78
- | RouterOutputs['hasItemsSections']['editItem']
79
- | RouterOutputs['simpleSections']['create']
80
- | {
81
- section: {
82
- name: string
83
- gallery?: boolean
84
- title?: { section?: string; singular?: string; plural?: string }
85
- configFile?: string
86
- }
87
- inputGroups:
88
- | {
89
- groupId: number | undefined
90
- groupTitle: string
91
- groupOrder: number
92
- inputs: {
93
- type: FieldType
94
- name: string
95
- label: string
96
- required: boolean
97
- conditionalFields: ConditionalField[]
98
- placeholder?: string
99
- readonly: boolean
100
- value: any
101
- }[]
102
- }[]
103
- | undefined
104
- }
105
- dropzoneRef?: RefObject<DropzoneHandles | null>
106
- variantRef?: RefObject<VariantHandles[]>
107
- handleSubmit: any
108
- isSubmitting: boolean
109
- response?: any
110
- progress?: number
111
- progressVariant?: 'determinate' | 'query'
112
- buttonType?: 'big' | 'small'
113
- submitSuccessCount?: number
114
- contentLocale?: { code: string; label: string; rtl?: boolean }
115
- defaultLocale?: { code: string; label: string; rtl?: boolean }
116
- onDirtyChange?: (isDirty: boolean) => void
117
- }) {
118
- const t = useI18n()
119
- const session = useSession()
120
- const language = session?.data?.user?.language
121
-
122
- // When editing a non-default locale, only show fields marked as localized
123
- const isTranslationMode = !!(contentLocale && defaultLocale && contentLocale.code !== defaultLocale.code)
124
-
125
- const filterInputsForLocale = <T,>(inputs: T[]): T[] => {
126
- if (!isTranslationMode) return inputs
127
- return inputs.filter((input) => (input as any).localized)
128
- }
129
-
130
- const hasNoLocalizedFields =
131
- isTranslationMode &&
132
- (data.inputGroups?.every((g) => filterInputsForLocale(g.inputs as any[]).length === 0) ?? true)
133
-
134
- let schema = z.object({})
135
- /**
136
- * Construct the schema for the form
137
- */
138
- data.inputGroups?.forEach((inputGroup) => {
139
- filterInputsForLocale(inputGroup.inputs as any[]).forEach((input: any) => {
140
- if (input.readonly) return
141
- switch (input.type) {
142
- case 'select_multiple':
143
- schema = schema.extend({
144
- [input.name]: selectMultipleFieldSchema(input as SelectMultipleFieldClientConfig, language),
145
- })
146
- break
147
- case 'select':
148
- schema = schema.extend({
149
- [input.name]: selectFieldSchema(input as SelectFieldClientConfig, language),
150
- })
151
- break
152
-
153
- case 'date':
154
- schema = schema.extend({
155
- [input.name]: dateFieldSchema(input as DateFieldClientConfig, language),
156
- })
157
- break
158
-
159
- case 'date_range':
160
- schema = schema.extend(
161
- dateRangeFieldSchema(input as DateRangeFieldClientConfig, language),
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
- <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
- {hasNoLocalizedFields ? (
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', { section: typeof data.section.title === 'object' ? (data.section.title?.section ?? '') : '', file: data.section.configFile })}
277
- </p>
278
- </div>
279
- ) : (
280
- <>
281
- {data.inputGroups.length > 0
282
- ? data.inputGroups.map((inputGroup, index: number) => {
283
- const filteredInputs = filterInputsForLocale(inputGroup.inputs as any[])
284
- if (filteredInputs.length === 0) return null
285
- return (
286
- <ContainerBox title={inputGroup.groupTitle} key={index}>
287
- <FormInputs
288
- inputs={filteredInputs}
289
- sectionName={data.section.name}
290
- submitSuccessCount={submitSuccessCount}
291
- />
292
- </ContainerBox>
293
- )
294
- })
295
- : null}
296
- {data.section.gallery && !isTranslationMode ? (
297
- <>
298
- <div className='w-full'>
299
- <PhotoGallery sectionName={data.section.name} gallery={'gallery' in data ? data.gallery : []} />
300
- </div>
301
- <div className='w-full'>
302
- <Dropzone ref={dropzoneRef} />
303
- </div>
304
- </>
305
- ) : null}
306
-
307
- {/*{data.section.variants && data.section.variants.length > 0 ? (
308
- <div className='w-full'>
309
- <div className='flex flex-col gap-4'>
310
- {data.section.variants.map((variant, index) => {
311
- // Only one variant is allowed for now
312
- // I have to find a way to make multiple variantRef in order to handle multiple variants
313
- if (index > 0) return
314
-
315
- return (
316
- <NewVariantComponent
317
- ref={(el) => (variantRef.current[index] = el)}
318
- key={index}
319
- section={section}
320
- variantInfo={variant.info}
321
- xsrfToken={xsrfToken}
322
- />
323
- )
324
- })}
325
- </div>
326
- </div>
327
- ) : null}*/}
328
-
329
- <div className='flex flex-col gap-3 pb-4'>
330
- <div className=''>
331
- <button
332
- className={classNames({
333
- 'w-full': buttonType === 'big',
334
- 'float-end': buttonType === 'small',
335
- 'rounded bg-linear-to-r px-4 py-2 font-bold text-white drop-shadow-sm':
336
- true,
337
- 'from-emerald-700 via-green-700 to-green-500 dark:from-blue-800 dark:via-sky-800 dark:to-slate-500':
338
- !isSubmitting,
339
- 'from-gray-600 via-gray-500 to-gray-400': isSubmitting,
340
- })}
341
- type='submit'
342
- disabled={isSubmitting}
343
- >
344
- {isSubmitting
345
- ? t('loading')
346
- : t(formType === 'new' ? 'create' : 'save')}
347
- </button>
348
- {progressVariant && progress ? (
349
- isSubmitting ? (
350
- <div className='mt-0.5'>
351
- <ProgressBar variant={progressVariant} value={progress} />
352
- </div>
353
- ) : null
354
- ) : null}
355
- </div>
356
- {response ? <div className='w-full'>{response}</div> : null}
357
- </div>
358
- </>
359
- )}
360
- </div>
361
- ) : null}
362
- </div>
363
- </div>
364
- {/*</ContainerBox>*/}
365
- </div>
366
- </form>
367
- </FormProvider>
368
- </LocalizationProvider>
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
+ }