create-nextjs-cms 0.5.8

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.
Files changed (187) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +71 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +395 -0
  6. package/dist/lib/utils.d.ts +11 -0
  7. package/dist/lib/utils.d.ts.map +1 -0
  8. package/dist/lib/utils.js +48 -0
  9. package/package.json +44 -0
  10. package/templates/default/.env +24 -0
  11. package/templates/default/.env.development +8 -0
  12. package/templates/default/.eslintrc.json +5 -0
  13. package/templates/default/.prettierignore +7 -0
  14. package/templates/default/.prettierrc.json +19 -0
  15. package/templates/default/CHANGELOG.md +77 -0
  16. package/templates/default/README.md +45 -0
  17. package/templates/default/app/(auth)/auth/login/LoginPage.tsx +175 -0
  18. package/templates/default/app/(auth)/auth/login/page.tsx +12 -0
  19. package/templates/default/app/(rootLayout)/admins/page.tsx +5 -0
  20. package/templates/default/app/(rootLayout)/advanced/page.tsx +5 -0
  21. package/templates/default/app/(rootLayout)/analytics/page.tsx +7 -0
  22. package/templates/default/app/(rootLayout)/browse/[section]/[page]/page.tsx +7 -0
  23. package/templates/default/app/(rootLayout)/categorized/[section]/page.tsx +7 -0
  24. package/templates/default/app/(rootLayout)/dashboard/page.tsx +7 -0
  25. package/templates/default/app/(rootLayout)/edit/[section]/[itemId]/page.tsx +7 -0
  26. package/templates/default/app/(rootLayout)/emails/page.tsx +6 -0
  27. package/templates/default/app/(rootLayout)/layout.tsx +5 -0
  28. package/templates/default/app/(rootLayout)/loading.tsx +10 -0
  29. package/templates/default/app/(rootLayout)/log/page.tsx +7 -0
  30. package/templates/default/app/(rootLayout)/new/[section]/page.tsx +7 -0
  31. package/templates/default/app/(rootLayout)/page.tsx +9 -0
  32. package/templates/default/app/(rootLayout)/section/[section]/page.tsx +7 -0
  33. package/templates/default/app/(rootLayout)/settings/page.tsx +7 -0
  34. package/templates/default/app/_trpc/client.ts +4 -0
  35. package/templates/default/app/api/auth/csrf/route.ts +25 -0
  36. package/templates/default/app/api/auth/refresh/route.ts +10 -0
  37. package/templates/default/app/api/auth/route.ts +23 -0
  38. package/templates/default/app/api/auth/session/route.ts +20 -0
  39. package/templates/default/app/api/editor/photo/route.ts +42 -0
  40. package/templates/default/app/api/photo/route.ts +27 -0
  41. package/templates/default/app/api/placeholder/route.ts +7 -0
  42. package/templates/default/app/api/submit/section/item/[slug]/route.ts +63 -0
  43. package/templates/default/app/api/submit/section/item/route.ts +53 -0
  44. package/templates/default/app/api/submit/section/simple/route.ts +54 -0
  45. package/templates/default/app/api/trpc/[trpc]/route.ts +33 -0
  46. package/templates/default/app/api/video/route.ts +174 -0
  47. package/templates/default/app/dictionaries.ts +14 -0
  48. package/templates/default/app/layout.tsx +28 -0
  49. package/templates/default/app/providers.tsx +151 -0
  50. package/templates/default/cli.ts +4 -0
  51. package/templates/default/components/AdminCard.tsx +163 -0
  52. package/templates/default/components/AdminEditPage.tsx +123 -0
  53. package/templates/default/components/AdminPrivilegeCard.tsx +184 -0
  54. package/templates/default/components/AdminsPage.tsx +43 -0
  55. package/templates/default/components/AdvancedSettingsPage.tsx +167 -0
  56. package/templates/default/components/AnalyticsPage.tsx +127 -0
  57. package/templates/default/components/BarChartBox.tsx +43 -0
  58. package/templates/default/components/BrowsePage.tsx +119 -0
  59. package/templates/default/components/CategorizedSectionPage.tsx +36 -0
  60. package/templates/default/components/CategoryDeleteConfirmPage.tsx +129 -0
  61. package/templates/default/components/CategorySectionSelectInput.tsx +139 -0
  62. package/templates/default/components/ConditionalFields.tsx +49 -0
  63. package/templates/default/components/ContainerBox.tsx +24 -0
  64. package/templates/default/components/DashboardPage.tsx +187 -0
  65. package/templates/default/components/DashboardPageAlt.tsx +43 -0
  66. package/templates/default/components/DefaultNavItems.tsx +3 -0
  67. package/templates/default/components/Dropzone.tsx +153 -0
  68. package/templates/default/components/EmailCard.tsx +137 -0
  69. package/templates/default/components/EmailPasswordForm.tsx +84 -0
  70. package/templates/default/components/EmailQuotaForm.tsx +72 -0
  71. package/templates/default/components/EmailsPage.tsx +48 -0
  72. package/templates/default/components/GalleryPhoto.tsx +93 -0
  73. package/templates/default/components/InfoCard.tsx +94 -0
  74. package/templates/default/components/ItemEditPage.tsx +217 -0
  75. package/templates/default/components/Layout.tsx +70 -0
  76. package/templates/default/components/LoadingSpinners.tsx +67 -0
  77. package/templates/default/components/LogPage.tsx +17 -0
  78. package/templates/default/components/Modal.tsx +99 -0
  79. package/templates/default/components/Navbar.tsx +29 -0
  80. package/templates/default/components/NavbarAlt.tsx +182 -0
  81. package/templates/default/components/NewAdminForm.tsx +172 -0
  82. package/templates/default/components/NewEmailForm.tsx +131 -0
  83. package/templates/default/components/NewPage.tsx +206 -0
  84. package/templates/default/components/NewVariantComponent.tsx +228 -0
  85. package/templates/default/components/PhotoGallery.tsx +35 -0
  86. package/templates/default/components/PieChartBox.tsx +101 -0
  87. package/templates/default/components/ProgressBar.tsx +24 -0
  88. package/templates/default/components/ProtectedDocument.tsx +78 -0
  89. package/templates/default/components/ProtectedImage.tsx +143 -0
  90. package/templates/default/components/ProtectedVideo.tsx +76 -0
  91. package/templates/default/components/SectionItemCard.tsx +143 -0
  92. package/templates/default/components/SectionItemStatusBadge.tsx +16 -0
  93. package/templates/default/components/SectionPage.tsx +124 -0
  94. package/templates/default/components/SelectBox.tsx +99 -0
  95. package/templates/default/components/SelectInputButtons.tsx +124 -0
  96. package/templates/default/components/SettingsPage.tsx +238 -0
  97. package/templates/default/components/Sidebar.tsx +209 -0
  98. package/templates/default/components/SidebarDropdownItem.tsx +74 -0
  99. package/templates/default/components/SidebarItem.tsx +19 -0
  100. package/templates/default/components/TempPage.tsx +12 -0
  101. package/templates/default/components/ThemeProvider.tsx +8 -0
  102. package/templates/default/components/TooltipComponent.tsx +27 -0
  103. package/templates/default/components/VariantCard.tsx +123 -0
  104. package/templates/default/components/VariantEditPage.tsx +229 -0
  105. package/templates/default/components/analytics/BounceRate.tsx +69 -0
  106. package/templates/default/components/analytics/LivePageViews.tsx +54 -0
  107. package/templates/default/components/analytics/LiveUsersCount.tsx +32 -0
  108. package/templates/default/components/analytics/MonthlyPageViews.tsx +41 -0
  109. package/templates/default/components/analytics/TopCountries.tsx +51 -0
  110. package/templates/default/components/analytics/TopDevices.tsx +45 -0
  111. package/templates/default/components/analytics/TopMediums.tsx +57 -0
  112. package/templates/default/components/analytics/TopSources.tsx +44 -0
  113. package/templates/default/components/analytics/TotalPageViews.tsx +40 -0
  114. package/templates/default/components/analytics/TotalSessions.tsx +40 -0
  115. package/templates/default/components/analytics/TotalUniqueUsers.tsx +40 -0
  116. package/templates/default/components/custom/RightHomeRoomVariantCard.tsx +137 -0
  117. package/templates/default/components/dndKit/Draggable.tsx +21 -0
  118. package/templates/default/components/dndKit/Droppable.tsx +20 -0
  119. package/templates/default/components/dndKit/SortableItem.tsx +18 -0
  120. package/templates/default/components/form/DateRangeFormInput.tsx +55 -0
  121. package/templates/default/components/form/Form.tsx +298 -0
  122. package/templates/default/components/form/FormInputElement.tsx +68 -0
  123. package/templates/default/components/form/FormInputs.tsx +108 -0
  124. package/templates/default/components/form/helpers/util.ts +20 -0
  125. package/templates/default/components/form/inputs/CheckboxFormInput.tsx +33 -0
  126. package/templates/default/components/form/inputs/ColorFormInput.tsx +44 -0
  127. package/templates/default/components/form/inputs/DateFormInput.tsx +107 -0
  128. package/templates/default/components/form/inputs/DocumentFormInput.tsx +124 -0
  129. package/templates/default/components/form/inputs/MapFormInput.tsx +139 -0
  130. package/templates/default/components/form/inputs/MultipleSelectFormInput.tsx +150 -0
  131. package/templates/default/components/form/inputs/NumberFormInput.tsx +42 -0
  132. package/templates/default/components/form/inputs/PasswordFormInput.tsx +47 -0
  133. package/templates/default/components/form/inputs/PhotoFormInput.tsx +218 -0
  134. package/templates/default/components/form/inputs/RichTextFormInput.tsx +133 -0
  135. package/templates/default/components/form/inputs/SelectFormInput.tsx +164 -0
  136. package/templates/default/components/form/inputs/TagsFormInput.tsx +63 -0
  137. package/templates/default/components/form/inputs/TextFormInput.tsx +48 -0
  138. package/templates/default/components/form/inputs/TextareaFormInput.tsx +47 -0
  139. package/templates/default/components/form/inputs/VideoFormInput.tsx +117 -0
  140. package/templates/default/components/pagination/Pagination.tsx +36 -0
  141. package/templates/default/components/pagination/PaginationButtons.tsx +145 -0
  142. package/templates/default/components/ui/accordion.tsx +57 -0
  143. package/templates/default/components/ui/alert.tsx +46 -0
  144. package/templates/default/components/ui/badge.tsx +33 -0
  145. package/templates/default/components/ui/button.tsx +57 -0
  146. package/templates/default/components/ui/calendar.tsx +68 -0
  147. package/templates/default/components/ui/card.tsx +76 -0
  148. package/templates/default/components/ui/checkbox.tsx +29 -0
  149. package/templates/default/components/ui/dropdown-menu.tsx +205 -0
  150. package/templates/default/components/ui/input.tsx +25 -0
  151. package/templates/default/components/ui/label.tsx +26 -0
  152. package/templates/default/components/ui/popover.tsx +31 -0
  153. package/templates/default/components/ui/scroll-area.tsx +42 -0
  154. package/templates/default/components/ui/select.tsx +164 -0
  155. package/templates/default/components/ui/sheet.tsx +107 -0
  156. package/templates/default/components/ui/switch.tsx +29 -0
  157. package/templates/default/components/ui/table.tsx +120 -0
  158. package/templates/default/components/ui/tabs.tsx +55 -0
  159. package/templates/default/components/ui/toast.tsx +113 -0
  160. package/templates/default/components/ui/toaster.tsx +35 -0
  161. package/templates/default/components/ui/tooltip.tsx +30 -0
  162. package/templates/default/components/ui/use-toast.ts +188 -0
  163. package/templates/default/components.json +16 -0
  164. package/templates/default/context/ModalProvider.tsx +53 -0
  165. package/templates/default/drizzle.config.ts +4 -0
  166. package/templates/default/dynamic-schemas/schema.ts +373 -0
  167. package/templates/default/env/env.js +130 -0
  168. package/templates/default/envConfig.ts +4 -0
  169. package/templates/default/hooks/useModal.ts +8 -0
  170. package/templates/default/lib/apiHelpers.ts +106 -0
  171. package/templates/default/lz.config.ts +40 -0
  172. package/templates/default/middleware.ts +33 -0
  173. package/templates/default/next.config.ts +46 -0
  174. package/templates/default/package.json +134 -0
  175. package/templates/default/postcss.config.js +6 -0
  176. package/templates/default/postinstall.js +14 -0
  177. package/templates/default/public/blank_avatar.png +0 -0
  178. package/templates/default/public/favicon.ico +0 -0
  179. package/templates/default/public/img/placeholder.svg +1 -0
  180. package/templates/default/public/lazemni_logo.png +0 -0
  181. package/templates/default/public/next.svg +1 -0
  182. package/templates/default/public/vercel.svg +1 -0
  183. package/templates/default/section-tests.ts +92 -0
  184. package/templates/default/styles/globals.css +88 -0
  185. package/templates/default/tailwind.config.js +95 -0
  186. package/templates/default/test.ts +77 -0
  187. package/templates/default/tsconfig.json +44 -0
@@ -0,0 +1,298 @@
1
+ export const revalidate = 1
2
+
3
+
4
+ import ContainerBox from '@/components/ContainerBox'
5
+ import getString from 'nextjs-cms/translations'
6
+ import FormInputs from '@/components/form/FormInputs'
7
+ import Dropzone, { DropzoneHandles } from '@/components/Dropzone'
8
+ import NewVariantComponent, { VariantHandles } from '@/components/NewVariantComponent'
9
+ import classNames from 'classnames'
10
+ import ProgressBar from '@/components/ProgressBar'
11
+ import React, { RefObject, useEffect } from 'react'
12
+ import { RouterOutputs } from 'nextjs-cms/api'
13
+ import * as z from 'zod'
14
+ import { zodResolver } from '@hookform/resolvers/zod'
15
+ import { useForm, FormProvider } from 'react-hook-form'
16
+ import {
17
+ CheckboxFieldClientConfig,
18
+ ColorFieldClientConfig,
19
+ DateFieldClientConfig,
20
+ DocumentFieldClientConfig,
21
+ MapFieldClientConfig,
22
+ NumberFieldClientConfig,
23
+ PasswordFieldClientConfig,
24
+ PhotoFieldClientConfig,
25
+ RichTextFieldClientConfig,
26
+ SelectFieldClientConfig,
27
+ SelectMultipleFieldClientConfig,
28
+ TextAreaFieldClientConfig,
29
+ TextFieldClientConfig,
30
+ VideoFieldClientConfig,
31
+ } from 'nextjs-cms/core/fields'
32
+
33
+ import { numberFieldSchema,
34
+ textFieldSchema,
35
+ selectFieldSchema,
36
+ selectMultipleFieldSchema,
37
+ dateFieldSchema,
38
+ checkboxFieldSchema,
39
+ textareaFieldSchema,
40
+ richTextFieldSchema,
41
+ photoFieldSchema,
42
+ documentFieldSchema,
43
+ videoFieldSchema,
44
+ colorFieldSchema,
45
+ mapFieldSchema,
46
+ passwordFieldSchema
47
+ } from 'nextjs-cms/validators'
48
+
49
+ import { ConditionalField, FieldType } from 'nextjs-cms/core/types'
50
+ import { sectionSchemaLastUpdated } from '@/components/form/helpers/util'
51
+
52
+ export default function Form({
53
+ formType,
54
+ data,
55
+ dropzoneRef,
56
+ variantRef,
57
+ handleSubmit,
58
+ isSubmitting,
59
+ response,
60
+ progress,
61
+ progressVariant,
62
+ buttonType = 'big',
63
+ }: {
64
+ formType?: 'new' | 'edit'
65
+ data:
66
+ | RouterOutputs['hasItemsSections']['newItem']
67
+ | RouterOutputs['hasItemsSections']['editItem']
68
+ | RouterOutputs['simpleSections']['create']
69
+ | {
70
+ section: {
71
+ name: string
72
+ gallery?: boolean
73
+ }
74
+ inputGroups:
75
+ | {
76
+ groupId: number | undefined
77
+ groupTitle: string
78
+ groupOrder: number
79
+ inputs: {
80
+ type: FieldType
81
+ name: string
82
+ label: string
83
+ required: boolean
84
+ conditionalFields: ConditionalField[]
85
+ placeholder?: string
86
+ readonly: boolean
87
+ value: any
88
+ }[]
89
+ }[]
90
+ | undefined
91
+ }
92
+ dropzoneRef?: RefObject<DropzoneHandles | null>
93
+ variantRef?: RefObject<VariantHandles[]>
94
+ handleSubmit: any
95
+ isSubmitting: boolean
96
+ response?: any
97
+ progress?: number
98
+ progressVariant?: 'determinate' | 'query'
99
+ buttonType?: 'big' | 'small'
100
+ }) {
101
+ let schema = z.object({})
102
+ /**
103
+ * Construct the schema for the form
104
+ */
105
+ data.inputGroups?.forEach((inputGroup) => {
106
+ inputGroup.inputs.forEach((input) => {
107
+ if (input.readonly) return
108
+ switch (input.type) {
109
+ case 'select_multiple':
110
+ schema = schema.extend({
111
+ [input.name]: selectMultipleFieldSchema(input as SelectMultipleFieldClientConfig),
112
+ })
113
+ break
114
+ case 'select':
115
+ schema = schema.extend({
116
+ [input.name]: selectFieldSchema(input as SelectFieldClientConfig),
117
+ })
118
+ break
119
+
120
+ case 'date':
121
+ schema = schema.extend({
122
+ [input.name]: dateFieldSchema(input as DateFieldClientConfig),
123
+ })
124
+ break
125
+
126
+ case 'checkbox':
127
+ schema = schema.extend({
128
+ [input.name]: checkboxFieldSchema(input as CheckboxFieldClientConfig),
129
+ })
130
+ break
131
+
132
+ case 'text':
133
+ schema = schema.extend({
134
+ [input.name]: textFieldSchema(input as TextFieldClientConfig),
135
+ })
136
+ break
137
+
138
+ case 'textarea':
139
+ schema = schema.extend({
140
+ [input.name]: textareaFieldSchema(input as TextAreaFieldClientConfig),
141
+ })
142
+ break
143
+
144
+ case 'rich_text':
145
+ schema = schema.extend({
146
+ [input.name]: richTextFieldSchema(input as RichTextFieldClientConfig),
147
+ })
148
+ break
149
+
150
+ case 'photo':
151
+ if (formType === 'edit' && !!input.value) break
152
+ schema = schema.extend({
153
+ [input.name]: photoFieldSchema(input as PhotoFieldClientConfig),
154
+ })
155
+ break
156
+
157
+ case 'document':
158
+ if (formType === 'edit' && !!input.value) break
159
+ schema = schema.extend({
160
+ [input.name]: documentFieldSchema(input as DocumentFieldClientConfig),
161
+ })
162
+ break
163
+
164
+ case 'video':
165
+ if (formType === 'edit' && !!input.value) break
166
+ schema = schema.extend({
167
+ [input.name]: videoFieldSchema(input as VideoFieldClientConfig),
168
+ })
169
+ break
170
+
171
+ case 'number':
172
+ schema = schema.extend({
173
+ [input.name]: numberFieldSchema(input as NumberFieldClientConfig),
174
+ })
175
+ break
176
+ case 'color':
177
+ schema = schema.extend({
178
+ [input.name]: colorFieldSchema(input as ColorFieldClientConfig),
179
+ })
180
+ break
181
+
182
+ case 'map':
183
+ schema = schema.extend({
184
+ [input.name]: mapFieldSchema(input as MapFieldClientConfig),
185
+ })
186
+ break
187
+
188
+ case 'password':
189
+ schema = schema.extend({
190
+ [input.name]: passwordFieldSchema(input as PasswordFieldClientConfig),
191
+ })
192
+ break
193
+ }
194
+ })
195
+ })
196
+
197
+ const methods = useForm({
198
+ resolver: zodResolver(schema),
199
+ })
200
+
201
+ return (
202
+ <FormProvider {...methods}>
203
+ <form
204
+ method='post'
205
+ onSubmit={(e) => {
206
+ e.preventDefault()
207
+ // e.stopPropagation()
208
+ methods.handleSubmit((data) => {
209
+ handleSubmit(new FormData(e.target as HTMLFormElement))
210
+ })(e)
211
+ }}
212
+ encType='multipart/form-data'
213
+ >
214
+ <div className='w-full' data-section-schema-version={sectionSchemaLastUpdated}>
215
+ {/*<ContainerBox title={formType ? getString(formType === 'new' ? 'add_new' : 'edit') : undefined}>*/}
216
+ <div className='p-4'>
217
+ <div className=''>
218
+ {data.inputGroups ? (
219
+ <div className='flex flex-col gap-4'>
220
+ {data.inputGroups.length > 0
221
+ ? data.inputGroups.map((inputGroup, index: number) => {
222
+ return (
223
+ <ContainerBox title={inputGroup.groupTitle} key={index}>
224
+ <FormInputs
225
+ inputs={inputGroup.inputs}
226
+ sectionName={data.section.name}
227
+ />
228
+ </ContainerBox>
229
+ )
230
+ })
231
+ : null}
232
+ {data.section.gallery ? (
233
+ <div className='w-full'>
234
+ <Dropzone ref={dropzoneRef} />
235
+ </div>
236
+ ) : null}
237
+
238
+ {/*{data.section.variants && data.section.variants.length > 0 ? (
239
+ <div className='w-full'>
240
+ <div className='flex flex-col gap-4'>
241
+ {data.section.variants.map((variant, index) => {
242
+ // Only one variant is allowed for now
243
+ // I have to find a way to make multiple variantRef in order to handle multiple variants
244
+ if (index > 0) return
245
+
246
+ return (
247
+ <NewVariantComponent
248
+ ref={(el) => (variantRef.current[index] = el)}
249
+ key={index}
250
+ section={section}
251
+ variantInfo={variant.info}
252
+ xsrfToken={xsrfToken}
253
+ />
254
+ )
255
+ })}
256
+ </div>
257
+ </div>
258
+ ) : null}*/}
259
+
260
+ <div className='flex flex-col gap-3 pb-4'>
261
+ <div className=''>
262
+ <button
263
+ className={classNames({
264
+ 'w-full': buttonType === 'big',
265
+ 'float-end': buttonType === 'small',
266
+ 'rounded bg-gradient-to-r px-4 py-2 font-bold text-white drop-shadow':
267
+ true,
268
+ 'from-emerald-700 via-green-700 to-green-500 dark:from-blue-800 dark:via-sky-800 dark:to-slate-500':
269
+ !isSubmitting,
270
+ 'from-gray-600 via-gray-500 to-gray-400': isSubmitting,
271
+ })}
272
+ type='submit'
273
+ disabled={isSubmitting}
274
+ >
275
+ {isSubmitting
276
+ ? getString('loading')
277
+ : getString(formType === 'new' ? 'create' : 'save')}
278
+ </button>
279
+ {progressVariant && progress ? (
280
+ isSubmitting ? (
281
+ <div className='mt-0.5'>
282
+ <ProgressBar variant={progressVariant} value={progress} />
283
+ </div>
284
+ ) : null
285
+ ) : null}
286
+ </div>
287
+ {response ? <div className='w-full'>{response}</div> : null}
288
+ </div>
289
+ </div>
290
+ ) : null}
291
+ </div>
292
+ </div>
293
+ {/*</ContainerBox>*/}
294
+ </div>
295
+ </form>
296
+ </FormProvider>
297
+ )
298
+ }
@@ -0,0 +1,68 @@
1
+ import React from 'react'
2
+ import classNames from 'classnames'
3
+ import { Badge } from '@/components/ui/badge'
4
+ import { FieldError } from 'react-hook-form'
5
+
6
+ export default function FormInputElement({
7
+ label,
8
+ required = false,
9
+ readonly = false,
10
+ classes = '',
11
+ direction = 'row',
12
+ value,
13
+ children,
14
+ validationError,
15
+ }: {
16
+ label: string
17
+ required: boolean | string
18
+ readonly?: boolean
19
+ classes?: string
20
+ direction?: 'row' | 'col'
21
+ value?: any
22
+ children: React.ReactNode
23
+ validationError: FieldError | undefined
24
+ }) {
25
+ return (
26
+ <div
27
+ className={classNames({
28
+ 'items-center py-2': true,
29
+ 'flex flex-col': direction === 'col',
30
+ 'items-center px-3 md-sidebar:grid md-sidebar:grid-cols-10': direction === 'row',
31
+ classes: classes,
32
+ })}
33
+ >
34
+ <label
35
+ className={classNames({
36
+ 'block pe-3 font-bold text-foreground': true,
37
+ 'w-full text-start': direction === 'col',
38
+ 'col-span-10 pb-1 md-sidebar:col-span-2 md-sidebar:pb-0 md-sidebar:text-end': direction === 'row',
39
+ })}
40
+ >
41
+ {label} {['true', 1, true].includes(required) ? '*' : null}
42
+ {readonly ? (
43
+ <>
44
+ {' '}
45
+ <Badge variant={'primary'}>Readonly</Badge>
46
+ </>
47
+ ) : null}
48
+ </label>
49
+ <div
50
+ className={classNames({
51
+ 'col-span-8': direction === 'row',
52
+ 'w-full': direction === 'col',
53
+ })}
54
+ >
55
+ {readonly ? (
56
+ <div className='w-full rounded bg-input p-3 text-foreground shadow-sm outline-0'>{value}</div>
57
+ ) : (
58
+ <>
59
+ {children}
60
+ {validationError ? (
61
+ <div className='mt-1 text-sm text-red-500'>{validationError.message}</div>
62
+ ) : null}
63
+ </>
64
+ )}
65
+ </div>
66
+ </div>
67
+ )
68
+ }
@@ -0,0 +1,108 @@
1
+ import React from 'react'
2
+ import ColorFormInput from '@/components/form/inputs/ColorFormInput'
3
+ import NumberFormInput from '@/components/form/inputs/NumberFormInput'
4
+ import TextFormInput from '@/components/form/inputs/TextFormInput'
5
+ import DateFormInput from '@/components/form/inputs/DateFormInput'
6
+ import RichTextFormInput from '@/components/form/inputs/RichTextFormInput'
7
+ import SelectFormInput from '@/components/form/inputs/SelectFormInput'
8
+ import MultipleSelectFormInput from '@/components/form/inputs/MultipleSelectFormInput'
9
+ import PhotoFormInput from '@/components/form/inputs/PhotoFormInput'
10
+ import DocumentFormInput from '@/components/form/inputs/DocumentFormInput'
11
+ import TagsFormInput from '@/components/form/inputs/TagsFormInput'
12
+ import CheckboxFormInput from '@/components/form/inputs/CheckboxFormInput'
13
+ import MapFormInput from '@/components/form/inputs/MapFormInput'
14
+ import PasswordFormInput from '@/components/form/inputs/PasswordFormInput'
15
+ import TextareaFormInput from '@/components/form/inputs/TextareaFormInput'
16
+ import VideoFormInput from '@/components/form/inputs/VideoFormInput'
17
+ import type { FieldClientConfig } from 'nextjs-cms/core/fields'
18
+ import {
19
+ CheckboxFieldClientConfig,
20
+ ColorFieldClientConfig,
21
+ DateFieldClientConfig,
22
+ DocumentFieldClientConfig,
23
+ MapFieldClientConfig,
24
+ NumberFieldClientConfig,
25
+ PasswordFieldClientConfig,
26
+ PhotoFieldClientConfig,
27
+ RichTextFieldClientConfig,
28
+ SelectFieldClientConfig,
29
+ SelectMultipleFieldClientConfig,
30
+ TagsFieldClientConfig,
31
+ TextAreaFieldClientConfig,
32
+ TextFieldClientConfig,
33
+ VideoFieldClientConfig,
34
+ } from 'nextjs-cms/core/fields'
35
+
36
+ export default function FormInputs({ inputs, sectionName }: { inputs: FieldClientConfig[]; sectionName: string }) {
37
+ return (
38
+ <>
39
+ {inputs?.length > 0 &&
40
+ inputs?.map((input) => {
41
+ switch (input.type) {
42
+ case 'color':
43
+ return <ColorFormInput input={input as ColorFieldClientConfig} key={input.name} />
44
+ case 'number':
45
+ return <NumberFormInput input={input as NumberFieldClientConfig} key={input.name} />
46
+ case 'text':
47
+ return <TextFormInput input={input as TextFieldClientConfig} key={input.name} />
48
+ case 'date':
49
+ return <DateFormInput input={input as DateFieldClientConfig} key={input.name} />
50
+ case 'rich_text':
51
+ return <RichTextFormInput input={input as RichTextFieldClientConfig} key={input.name} />
52
+ case 'textarea':
53
+ return <TextareaFormInput input={input as TextAreaFieldClientConfig} key={input.name} />
54
+ case 'select':
55
+ return (
56
+ <SelectFormInput
57
+ sectionName={sectionName}
58
+ input={input as SelectFieldClientConfig}
59
+ key={input.name}
60
+ />
61
+ )
62
+ case 'select_multiple':
63
+ return (
64
+ <MultipleSelectFormInput
65
+ input={input as SelectMultipleFieldClientConfig}
66
+ key={input.name}
67
+ />
68
+ )
69
+ case 'photo':
70
+ return (
71
+ <PhotoFormInput
72
+ sectionName={sectionName}
73
+ input={input as PhotoFieldClientConfig}
74
+ key={input.name}
75
+ />
76
+ )
77
+ case 'video':
78
+ return (
79
+ <VideoFormInput
80
+ sectionName={sectionName}
81
+ input={input as VideoFieldClientConfig}
82
+ key={input.name}
83
+ />
84
+ )
85
+ case 'document':
86
+ return (
87
+ <DocumentFormInput
88
+ sectionName={sectionName}
89
+ input={input as DocumentFieldClientConfig}
90
+ key={input.name}
91
+ />
92
+ )
93
+ case 'password':
94
+ return <PasswordFormInput input={input as PasswordFieldClientConfig} key={input.name} />
95
+ case 'tags':
96
+ return <TagsFormInput input={input as TagsFieldClientConfig} key={input.name} />
97
+ case 'map':
98
+ return <MapFormInput input={input as MapFieldClientConfig} key={input.name} />
99
+ case 'checkbox':
100
+ // case 'permission':
101
+ return <CheckboxFormInput input={input as CheckboxFieldClientConfig} key={input.name} />
102
+ default:
103
+ return null
104
+ }
105
+ })}
106
+ </>
107
+ )
108
+ }
@@ -0,0 +1,20 @@
1
+ // Safe import wrapper for sectionSchemaLastUpdated
2
+ // This prevents errors if the auto-generated file is deleted by end-users
3
+ // The file is auto-generated by SectionFactory and can be safely deleted
4
+ // without breaking the application - it will fall back to current timestamp
5
+
6
+ let sectionSchemaLastUpdated: number
7
+
8
+ try {
9
+ // Try to import the auto-generated file from root
10
+ // Using dynamic require to handle missing file gracefully
11
+ // @/ alias resolves to the app root directory
12
+ const sectionInfo = require('@/next-cms-sectioninfo')
13
+ sectionSchemaLastUpdated = sectionInfo?.sectionSchemaLastUpdated ?? Date.now()
14
+ } catch {
15
+ // If file doesn't exist or can't be loaded, use current timestamp as fallback
16
+ // This ensures the app continues to work even if the file is deleted
17
+ sectionSchemaLastUpdated = Date.now()
18
+ }
19
+
20
+ export { sectionSchemaLastUpdated }
@@ -0,0 +1,33 @@
1
+ import React from 'react'
2
+ import FormInputElement from '@/components/form/FormInputElement'
3
+ import { CheckboxFieldClientConfig } from 'nextjs-cms/core/fields'
4
+ import { useController, useFormContext } from 'react-hook-form'
5
+ import { Switch } from '@/components/ui/switch'
6
+
7
+ export default function CheckboxFormInput({ input }: { input: CheckboxFieldClientConfig }) {
8
+ const { control } = useFormContext()
9
+ const {
10
+ field,
11
+ fieldState: { invalid, isTouched, isDirty, error },
12
+ } = useController({
13
+ name: input.name,
14
+ control,
15
+ defaultValue: input.value === 1,
16
+ })
17
+ return (
18
+ <FormInputElement
19
+ validationError={error}
20
+ value={input.value}
21
+ readonly={input.readonly}
22
+ label={input.label}
23
+ required={input.required}
24
+ >
25
+ <Switch
26
+ ref={field.ref}
27
+ onCheckedChange={field.onChange}
28
+ defaultChecked={input.value === 1}
29
+ name={field.name}
30
+ />
31
+ </FormInputElement>
32
+ )
33
+ }
@@ -0,0 +1,44 @@
1
+ import React from 'react'
2
+ import FormInputElement from '@/components/form/FormInputElement'
3
+ import { ColorFieldClientConfig } from 'nextjs-cms/core/fields'
4
+ import { useController, useFormContext } from 'react-hook-form'
5
+
6
+ export default function ColorFormInput({ input }: { input: ColorFieldClientConfig }) {
7
+ const { control } = useFormContext()
8
+ const {
9
+ field,
10
+ fieldState: { invalid, isTouched, isDirty, error },
11
+ } = useController({
12
+ name: input.name,
13
+ control,
14
+ defaultValue: input.value ?? '#000000',
15
+ disabled: input.readonly,
16
+ })
17
+
18
+ const colorSpan = (
19
+ <>
20
+ <span style={{ padding: '2px 40px', backgroundColor: input.value }}></span>
21
+ </>
22
+ )
23
+ return (
24
+ <FormInputElement
25
+ validationError={error}
26
+ value={colorSpan}
27
+ readonly={input.readonly}
28
+ label={input.label}
29
+ required={input.required}
30
+ >
31
+ <input
32
+ type='color'
33
+ readOnly={input.readonly}
34
+ disabled={field.disabled}
35
+ name={field.name}
36
+ onChange={field.onChange}
37
+ onBlur={field.onBlur}
38
+ value={field.value}
39
+ ref={field.ref}
40
+ className='h-10 w-full rounded bg-input p-1 text-foreground shadow-sm outline-0 ring-2 ring-blue-300 focus:ring-blue-500'
41
+ />
42
+ </FormInputElement>
43
+ )
44
+ }
@@ -0,0 +1,107 @@
1
+ import React from 'react'
2
+ import dayjs from 'dayjs'
3
+ import FormInputElement from '@/components/form/FormInputElement'
4
+ import { format } from 'date-fns'
5
+ import { cn } from 'nextjs-cms/utils'
6
+ import { Button } from '@/components/ui/button'
7
+ import { Calendar } from '@/components/ui/calendar'
8
+ import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
9
+ import { CalendarIcon } from '@radix-ui/react-icons'
10
+ import getString from 'nextjs-cms/translations'
11
+ import { DateFieldClientConfig } from 'nextjs-cms/core/fields'
12
+ import { useController, useFormContext } from 'react-hook-form'
13
+
14
+ const seasonEmoji: Record<string, string> = {
15
+ winter: '⛄️',
16
+ spring: '🌸',
17
+ summer: '🌻',
18
+ autumn: '🍂',
19
+ }
20
+
21
+ const getSeason = (month: Date): string => {
22
+ const monthNumber = month.getMonth()
23
+ if (monthNumber >= 0 && monthNumber < 3) return 'winter'
24
+ if (monthNumber >= 3 && monthNumber < 6) return 'spring'
25
+ if (monthNumber >= 6 && monthNumber < 9) return 'summer'
26
+ else return 'autumn'
27
+ }
28
+
29
+ const formatCaption = (month: Date, options?: any) => {
30
+ const season = getSeason(month)
31
+ return seasonEmoji[season] + ' ' + format(month, 'LLLL', { locale: options?.locale })
32
+ }
33
+
34
+ export default function DateFormInput({ input }: { input: DateFieldClientConfig }) {
35
+ const { control } = useFormContext()
36
+ const {
37
+ field,
38
+ fieldState: { invalid, isTouched, isDirty, error },
39
+ } = useController({
40
+ name: input.name,
41
+ control,
42
+ // defaultValue: input.value ?? undefined,
43
+ })
44
+
45
+ const [date, setDate] = React.useState<Date | undefined>(
46
+ dayjs(input.value).isValid() ? dayjs(input.value).toDate() : undefined,
47
+ )
48
+ return (
49
+ <FormInputElement
50
+ validationError={error}
51
+ value={input.value}
52
+ readonly={input.readonly}
53
+ label={input.label}
54
+ required={input.required}
55
+ >
56
+ <input
57
+ type='hidden'
58
+ disabled={field.disabled}
59
+ name={field.name}
60
+ onBlur={field.onBlur}
61
+ value={date ? format(date, 'yyyy-MM-dd') : undefined}
62
+ ref={field.ref}
63
+ className='rounded border p-2'
64
+ />
65
+
66
+ <div className='flex flex-col items-start gap-2'>
67
+ <Popover>
68
+ <PopoverTrigger asChild>
69
+ <Button
70
+ variant={'outline'}
71
+ className={cn(
72
+ 'min-w-[250px] justify-start rounded bg-input px-3 py-6 text-start font-normal text-foreground shadow-sm outline-0 ring-2 ring-gray-300 hover:ring-gray-400 focus:ring-blue-500',
73
+ !date && 'text-muted-foreground',
74
+ )}
75
+ >
76
+ <CalendarIcon className='mr-2 h-4 w-4' />
77
+ {date ? format(date, 'PPP') : <span>Pick a date</span>}
78
+ </Button>
79
+ </PopoverTrigger>
80
+ <PopoverContent className='w-auto p-0'>
81
+ <Calendar
82
+ mode='single'
83
+ selected={date}
84
+ onSelect={(date) => {
85
+ setDate(date)
86
+ field.onChange(date)
87
+ }}
88
+ initialFocus
89
+ formatters={{ formatCaption }}
90
+ />
91
+ </PopoverContent>
92
+ </Popover>
93
+ <Button
94
+ onClick={() => {
95
+ setDate(undefined)
96
+ field.onChange(undefined)
97
+ }}
98
+ variant='default'
99
+ type='button'
100
+ size='sm'
101
+ >
102
+ {getString('cancel')}
103
+ </Button>
104
+ </div>
105
+ </FormInputElement>
106
+ )
107
+ }