create-nextjs-cms 0.7.0 → 0.7.2
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/LICENSE +21 -21
- package/README.md +71 -71
- package/dist/helpers/utils.js +16 -16
- package/dist/lib/section-creators.js +166 -166
- package/package.json +3 -3
- package/templates/default/.eslintrc.json +5 -5
- package/templates/default/.prettierignore +7 -7
- package/templates/default/.prettierrc.json +27 -27
- package/templates/default/CHANGELOG.md +140 -140
- package/templates/default/_gitignore +57 -57
- package/templates/default/app/(auth)/auth/login/LoginPage.tsx +192 -192
- package/templates/default/app/(auth)/auth/login/page.tsx +11 -11
- package/templates/default/app/(auth)/auth-locale-provider.tsx +34 -34
- package/templates/default/app/(auth)/layout.tsx +81 -81
- package/templates/default/app/(rootLayout)/(plugins)/[...slug]/page.tsx +40 -40
- package/templates/default/app/(rootLayout)/(plugins)/[...slug]/plugin-server-registry.ts +22 -22
- package/templates/default/app/(rootLayout)/admins/page.tsx +10 -10
- package/templates/default/app/(rootLayout)/browse/[section]/[page]/page.tsx +22 -22
- package/templates/default/app/(rootLayout)/categorized/[section]/page.tsx +15 -15
- package/templates/default/app/(rootLayout)/dashboard/page.tsx +63 -63
- package/templates/default/app/(rootLayout)/dashboard-new/page.tsx +7 -7
- package/templates/default/app/(rootLayout)/edit/[section]/[itemId]/page.tsx +17 -17
- package/templates/default/app/(rootLayout)/layout.tsx +81 -81
- package/templates/default/app/(rootLayout)/loading.tsx +10 -10
- package/templates/default/app/(rootLayout)/log/page.tsx +7 -7
- package/templates/default/app/(rootLayout)/new/[section]/page.tsx +15 -15
- package/templates/default/app/(rootLayout)/section/[section]/page.tsx +16 -16
- package/templates/default/app/(rootLayout)/settings/page.tsx +13 -13
- package/templates/default/app/_trpc/client.ts +3 -3
- package/templates/default/app/api/auth/csrf/route.ts +25 -25
- package/templates/default/app/api/auth/refresh/route.ts +10 -10
- package/templates/default/app/api/auth/session/route.ts +20 -20
- package/templates/default/app/api/editor/photo/route.ts +49 -49
- package/templates/default/app/api/photo/route.ts +27 -27
- package/templates/default/app/api/submit/section/item/[slug]/route.ts +66 -66
- package/templates/default/app/api/submit/section/item/route.ts +56 -56
- package/templates/default/app/api/submit/section/simple/route.ts +57 -57
- package/templates/default/app/api/trpc/[trpc]/route.ts +33 -33
- package/templates/default/app/api/video/route.ts +174 -174
- package/templates/default/app/globals.css +219 -219
- package/templates/default/app/providers.tsx +152 -152
- package/templates/default/cms.config.ts +49 -52
- package/templates/default/components/AdminCard.tsx +166 -166
- package/templates/default/components/AdminEditPage.tsx +124 -124
- package/templates/default/components/AdminPrivilegeCard.tsx +185 -185
- package/templates/default/components/AdminsPage.tsx +43 -43
- package/templates/default/components/AnalyticsPage.tsx +128 -128
- package/templates/default/components/BarChartBox.tsx +42 -42
- package/templates/default/components/BrowsePage.tsx +106 -106
- package/templates/default/components/CategorizedSectionPage.tsx +31 -31
- package/templates/default/components/CategoryDeleteConfirmPage.tsx +130 -130
- package/templates/default/components/CategorySectionSelectInput.tsx +140 -140
- package/templates/default/components/ConditionalFields.tsx +49 -49
- package/templates/default/components/ContainerBox.tsx +24 -24
- package/templates/default/components/DashboardNewPage.tsx +253 -253
- package/templates/default/components/DashboardPage.tsx +188 -188
- package/templates/default/components/DashboardPageAlt.tsx +45 -45
- package/templates/default/components/DefaultNavItems.tsx +3 -3
- package/templates/default/components/Dropzone.tsx +154 -154
- package/templates/default/components/EmailCard.tsx +138 -138
- package/templates/default/components/EmailPasswordForm.tsx +85 -85
- package/templates/default/components/EmailQuotaForm.tsx +73 -73
- package/templates/default/components/EmailsPage.tsx +49 -49
- package/templates/default/components/ErrorComponent.tsx +16 -16
- package/templates/default/components/GalleryPhoto.tsx +93 -93
- package/templates/default/components/InfoCard.tsx +93 -93
- package/templates/default/components/ItemEditPage.tsx +214 -214
- package/templates/default/components/Layout.tsx +84 -84
- package/templates/default/components/LoadingSpinners.tsx +67 -67
- package/templates/default/components/LogPage.tsx +107 -107
- package/templates/default/components/Modal.tsx +166 -166
- package/templates/default/components/Navbar.tsx +258 -258
- package/templates/default/components/NewAdminForm.tsx +173 -173
- package/templates/default/components/NewEmailForm.tsx +132 -132
- package/templates/default/components/NewPage.tsx +205 -205
- package/templates/default/components/NewVariantComponent.tsx +229 -229
- package/templates/default/components/PhotoGallery.tsx +35 -35
- package/templates/default/components/PieChartBox.tsx +101 -101
- package/templates/default/components/ProgressBar.tsx +48 -48
- package/templates/default/components/ProtectedDocument.tsx +78 -78
- package/templates/default/components/ProtectedImage.tsx +143 -143
- package/templates/default/components/ProtectedVideo.tsx +76 -76
- package/templates/default/components/SectionItemCard.tsx +144 -144
- package/templates/default/components/SectionItemStatusBadge.tsx +17 -17
- package/templates/default/components/SectionPage.tsx +125 -125
- package/templates/default/components/SelectBox.tsx +98 -98
- package/templates/default/components/SelectInputButtons.tsx +125 -125
- package/templates/default/components/SettingsPage.tsx +232 -232
- package/templates/default/components/Sidebar.tsx +201 -201
- package/templates/default/components/SidebarDropdownItem.tsx +80 -80
- package/templates/default/components/SidebarItem.tsx +20 -20
- package/templates/default/components/ThemeProvider.tsx +8 -8
- package/templates/default/components/TooltipComponent.tsx +27 -27
- package/templates/default/components/VariantCard.tsx +124 -124
- package/templates/default/components/VariantEditPage.tsx +230 -230
- package/templates/default/components/analytics/BounceRate.tsx +70 -70
- package/templates/default/components/analytics/LivePageViews.tsx +55 -55
- package/templates/default/components/analytics/LiveUsersCount.tsx +33 -33
- package/templates/default/components/analytics/MonthlyPageViews.tsx +42 -42
- package/templates/default/components/analytics/TopCountries.tsx +52 -52
- package/templates/default/components/analytics/TopDevices.tsx +46 -46
- package/templates/default/components/analytics/TopMediums.tsx +58 -58
- package/templates/default/components/analytics/TopSources.tsx +45 -45
- package/templates/default/components/analytics/TotalPageViews.tsx +41 -41
- package/templates/default/components/analytics/TotalSessions.tsx +41 -41
- package/templates/default/components/analytics/TotalUniqueUsers.tsx +41 -41
- package/templates/default/components/custom/RightHomeRoomVariantCard.tsx +138 -138
- package/templates/default/components/dndKit/Draggable.tsx +21 -21
- package/templates/default/components/dndKit/Droppable.tsx +20 -20
- package/templates/default/components/dndKit/SortableItem.tsx +18 -18
- package/templates/default/components/form/DateRangeFormInput.tsx +57 -57
- package/templates/default/components/form/Form.tsx +317 -317
- package/templates/default/components/form/FormInputElement.tsx +70 -70
- package/templates/default/components/form/FormInputs.tsx +112 -112
- package/templates/default/components/form/helpers/_section-hot-reload.js +1 -1
- package/templates/default/components/form/helpers/util.ts +17 -17
- package/templates/default/components/form/inputs/CheckboxFormInput.tsx +33 -33
- package/templates/default/components/form/inputs/ColorFormInput.tsx +44 -44
- package/templates/default/components/form/inputs/DateFormInput.tsx +156 -156
- package/templates/default/components/form/inputs/DocumentFormInput.tsx +222 -222
- package/templates/default/components/form/inputs/MapFormInput.tsx +140 -140
- package/templates/default/components/form/inputs/MultipleSelectFormInput.tsx +83 -83
- package/templates/default/components/form/inputs/NumberFormInput.tsx +42 -42
- package/templates/default/components/form/inputs/PasswordFormInput.tsx +47 -47
- package/templates/default/components/form/inputs/PhotoFormInput.tsx +219 -219
- package/templates/default/components/form/inputs/RichTextFormInput.tsx +135 -135
- package/templates/default/components/form/inputs/SelectFormInput.tsx +175 -175
- package/templates/default/components/form/inputs/SlugFormInput.tsx +129 -129
- package/templates/default/components/form/inputs/TagsFormInput.tsx +154 -154
- package/templates/default/components/form/inputs/TextFormInput.tsx +48 -48
- package/templates/default/components/form/inputs/TextareaFormInput.tsx +47 -47
- package/templates/default/components/form/inputs/VideoFormInput.tsx +118 -118
- package/templates/default/components/locale-dropdown.tsx +74 -74
- package/templates/default/components/locale-picker.tsx +85 -85
- package/templates/default/components/login-locale-dropdown.tsx +46 -46
- package/templates/default/components/multi-select.tsx +1144 -1144
- package/templates/default/components/pagination/Pagination.tsx +36 -36
- package/templates/default/components/pagination/PaginationButtons.tsx +147 -147
- package/templates/default/components/theme-toggle.tsx +37 -37
- package/templates/default/components/ui/accordion.tsx +53 -53
- package/templates/default/components/ui/alert-dialog.tsx +157 -157
- package/templates/default/components/ui/alert.tsx +46 -46
- package/templates/default/components/ui/badge.tsx +38 -38
- package/templates/default/components/ui/button.tsx +62 -62
- package/templates/default/components/ui/calendar.tsx +166 -166
- package/templates/default/components/ui/card.tsx +43 -43
- package/templates/default/components/ui/checkbox.tsx +29 -29
- package/templates/default/components/ui/command.tsx +137 -137
- package/templates/default/components/ui/custom-alert-dialog.tsx +113 -113
- package/templates/default/components/ui/custom-dialog.tsx +123 -123
- package/templates/default/components/ui/dialog.tsx +123 -123
- package/templates/default/components/ui/dropdown-menu.tsx +182 -182
- package/templates/default/components/ui/input-group.tsx +54 -54
- package/templates/default/components/ui/input.tsx +22 -22
- package/templates/default/components/ui/label.tsx +19 -19
- package/templates/default/components/ui/popover.tsx +42 -42
- package/templates/default/components/ui/progress.tsx +31 -31
- package/templates/default/components/ui/scroll-area.tsx +42 -42
- package/templates/default/components/ui/select.tsx +165 -165
- package/templates/default/components/ui/separator.tsx +28 -28
- package/templates/default/components/ui/sheet.tsx +103 -103
- package/templates/default/components/ui/switch.tsx +29 -29
- package/templates/default/components/ui/table.tsx +83 -83
- package/templates/default/components/ui/tabs.tsx +55 -55
- package/templates/default/components/ui/toast.tsx +113 -113
- package/templates/default/components/ui/toaster.tsx +35 -35
- package/templates/default/components/ui/tooltip.tsx +30 -30
- package/templates/default/components/ui/use-toast.ts +188 -188
- package/templates/default/components.json +21 -21
- package/templates/default/context/ModalProvider.tsx +53 -53
- package/templates/default/drizzle.config.ts +4 -4
- package/templates/default/dynamic-schemas/schema.ts +10 -0
- package/templates/default/env/env.js +130 -130
- package/templates/default/envConfig.ts +4 -4
- package/templates/default/hooks/useModal.ts +8 -8
- package/templates/default/lib/apiHelpers.ts +92 -92
- package/templates/default/lib/postinstall.js +14 -14
- package/templates/default/lib/utils.ts +6 -6
- package/templates/default/next-env.d.ts +6 -6
- package/templates/default/next.config.ts +23 -23
- package/templates/default/package.json +2 -4
- package/templates/default/postcss.config.mjs +6 -6
- package/templates/default/proxy.ts +32 -32
- package/templates/default/tsconfig.json +48 -48
|
@@ -1,135 +1,135 @@
|
|
|
1
|
-
import React, { useRef, useState } from 'react'
|
|
2
|
-
import { Editor } from '@tinymce/tinymce-react'
|
|
3
|
-
import FormInputElement from '@/components/form/FormInputElement'
|
|
4
|
-
// import { useTheme } from 'next-themes'
|
|
5
|
-
import { useAxiosPrivate } from 'nextjs-cms/auth/hooks'
|
|
6
|
-
import { RichTextFieldClientConfig } from 'nextjs-cms/core/fields'
|
|
7
|
-
import { FieldError, useFormContext } from 'react-hook-form'
|
|
8
|
-
|
|
9
|
-
export default function RichTextFormInput({ input }: { input: RichTextFieldClientConfig }) {
|
|
10
|
-
const { setValue, register, formState } = useFormContext()
|
|
11
|
-
register(input.name)
|
|
12
|
-
const editorRef = useRef(null)
|
|
13
|
-
const value = input.value ? input.value : ''
|
|
14
|
-
const [initialValue, setInitialValue] = useState('')
|
|
15
|
-
const [key, setKey] = useState(0) // Step 1: Add key state
|
|
16
|
-
// const [editorPhotos, setEditorPhotos] = useState<string[]>([])
|
|
17
|
-
// const { theme } = useTheme()
|
|
18
|
-
const axiosPrivate = useAxiosPrivate()
|
|
19
|
-
|
|
20
|
-
/*useEffect(() => {
|
|
21
|
-
// Step 2: Update the key when the theme changes
|
|
22
|
-
setKey((prevKey) => prevKey + 1)
|
|
23
|
-
}, [theme])*/
|
|
24
|
-
|
|
25
|
-
const image_upload_handler = (blobInfo: any, progress: any) =>
|
|
26
|
-
new Promise<string>(async (resolve, reject) => {
|
|
27
|
-
const formData = new FormData()
|
|
28
|
-
formData.append('photo', blobInfo.blob(), blobInfo.filename())
|
|
29
|
-
formData.append('sectionName', 'editor')
|
|
30
|
-
formData.append('itemId', '123')
|
|
31
|
-
|
|
32
|
-
axiosPrivate
|
|
33
|
-
.post('/editor/photo', Object.fromEntries(formData.entries()), {
|
|
34
|
-
headers: {
|
|
35
|
-
'Content-Type': 'multipart/form-data',
|
|
36
|
-
},
|
|
37
|
-
})
|
|
38
|
-
.then((res) => {
|
|
39
|
-
// resolve(getPublicPhotoUrl(res.data.name, 'editor', 'large'))
|
|
40
|
-
resolve(res.data)
|
|
41
|
-
return
|
|
42
|
-
})
|
|
43
|
-
.catch((error) => {
|
|
44
|
-
if (error.response) {
|
|
45
|
-
reject({ message: 'Error: ' + error.response.data.error, remove: true })
|
|
46
|
-
} else if (error.request) {
|
|
47
|
-
reject({ message: 'Error: ' + error.request, remove: true })
|
|
48
|
-
} else {
|
|
49
|
-
reject({ message: 'Error: ' + error.message, remove: true })
|
|
50
|
-
}
|
|
51
|
-
})
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
const plugins = [
|
|
55
|
-
'directionality ',
|
|
56
|
-
'advlist',
|
|
57
|
-
'autolink',
|
|
58
|
-
'lists',
|
|
59
|
-
'link',
|
|
60
|
-
'charmap',
|
|
61
|
-
'anchor',
|
|
62
|
-
'searchreplace',
|
|
63
|
-
'visualblocks',
|
|
64
|
-
'code',
|
|
65
|
-
'fullscreen',
|
|
66
|
-
'insertdatetime',
|
|
67
|
-
'table',
|
|
68
|
-
'preview',
|
|
69
|
-
'help',
|
|
70
|
-
'wordcount',
|
|
71
|
-
]
|
|
72
|
-
|
|
73
|
-
let toolbar =
|
|
74
|
-
'undo redo | bold italic underline strikethrough formatselect | fontfamily fontsize blocks | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor permanentpen formatpainter removeformat | pagebreak | charmap emoticons | fullscreen preview save print | pageembed template link anchor codesample | a11ycheck ltr rtl | showcomments addcomment | footnotes '
|
|
75
|
-
if (input.allowMedia) {
|
|
76
|
-
plugins.push('media', 'image')
|
|
77
|
-
toolbar += ' | insertfile image media'
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return (
|
|
81
|
-
<FormInputElement
|
|
82
|
-
validationError={formState.errors[input.name] as FieldError}
|
|
83
|
-
value={<div style={{ maxHeight: '200px', overflow: 'auto', wordBreak: 'break-all' }}>{input.value}</div>}
|
|
84
|
-
readonly={input.readonly}
|
|
85
|
-
label={input.label}
|
|
86
|
-
required={input.required}
|
|
87
|
-
>
|
|
88
|
-
<Editor
|
|
89
|
-
api-key='no-api-key'
|
|
90
|
-
licenseKey='gpl'
|
|
91
|
-
// TODO: Is this expensive?
|
|
92
|
-
onEditorChange={(content) => {
|
|
93
|
-
setValue(input.name, content, { shouldValidate: true })
|
|
94
|
-
}}
|
|
95
|
-
key={key} // Step 3: Pass the key as a prop
|
|
96
|
-
/*
|
|
97
|
-
onEditorChange={(content, editor) => {
|
|
98
|
-
console.log('Content was updated:', content)
|
|
99
|
-
setValue(content)
|
|
100
|
-
}}*/
|
|
101
|
-
// onChange={(e) => setValue(e.target.getContent())}
|
|
102
|
-
tinymceScriptSrc={'/tinymce/tinymce.min.js?v=1.1'}
|
|
103
|
-
onInit={(evt, editor) => {
|
|
104
|
-
// @ts-ignore
|
|
105
|
-
editorRef.current = editor
|
|
106
|
-
setInitialValue(value ? value : input.value ? input.value : '')
|
|
107
|
-
}}
|
|
108
|
-
textareaName={input.name}
|
|
109
|
-
initialValue={initialValue}
|
|
110
|
-
init={{
|
|
111
|
-
promotion: false,
|
|
112
|
-
height: 500,
|
|
113
|
-
menubar: 'file edit view insert format tools table tc help',
|
|
114
|
-
plugins: plugins,
|
|
115
|
-
toolbar: toolbar,
|
|
116
|
-
|
|
117
|
-
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
|
|
118
|
-
// skin: theme === 'light' ? 'oxide' : 'oxide-dark',
|
|
119
|
-
// content_css: theme === 'light' ? 'default' : 'dark',
|
|
120
|
-
skin: 'oxide-dark',
|
|
121
|
-
content_css: 'dark',
|
|
122
|
-
relative_urls: false,
|
|
123
|
-
remove_script_host: false,
|
|
124
|
-
convert_urls: true,
|
|
125
|
-
extended_valid_elements: 'iframe[src|width|height|name|align|class|style],*[id|class|style]',
|
|
126
|
-
// valid_children: '+body[style],+div[*],+p[*]', // Allow any element to be a child of body, div, and p elements
|
|
127
|
-
// valid_elements: '*[*]', // Allow all elements and attributes
|
|
128
|
-
images_file_types: 'jpg,png,webp',
|
|
129
|
-
block_unsupported_drop: true,
|
|
130
|
-
images_upload_handler: image_upload_handler,
|
|
131
|
-
}}
|
|
132
|
-
/>
|
|
133
|
-
</FormInputElement>
|
|
134
|
-
)
|
|
135
|
-
}
|
|
1
|
+
import React, { useRef, useState } from 'react'
|
|
2
|
+
import { Editor } from '@tinymce/tinymce-react'
|
|
3
|
+
import FormInputElement from '@/components/form/FormInputElement'
|
|
4
|
+
// import { useTheme } from 'next-themes'
|
|
5
|
+
import { useAxiosPrivate } from 'nextjs-cms/auth/hooks'
|
|
6
|
+
import { RichTextFieldClientConfig } from 'nextjs-cms/core/fields'
|
|
7
|
+
import { FieldError, useFormContext } from 'react-hook-form'
|
|
8
|
+
|
|
9
|
+
export default function RichTextFormInput({ input }: { input: RichTextFieldClientConfig }) {
|
|
10
|
+
const { setValue, register, formState } = useFormContext()
|
|
11
|
+
register(input.name)
|
|
12
|
+
const editorRef = useRef(null)
|
|
13
|
+
const value = input.value ? input.value : ''
|
|
14
|
+
const [initialValue, setInitialValue] = useState('')
|
|
15
|
+
const [key, setKey] = useState(0) // Step 1: Add key state
|
|
16
|
+
// const [editorPhotos, setEditorPhotos] = useState<string[]>([])
|
|
17
|
+
// const { theme } = useTheme()
|
|
18
|
+
const axiosPrivate = useAxiosPrivate()
|
|
19
|
+
|
|
20
|
+
/*useEffect(() => {
|
|
21
|
+
// Step 2: Update the key when the theme changes
|
|
22
|
+
setKey((prevKey) => prevKey + 1)
|
|
23
|
+
}, [theme])*/
|
|
24
|
+
|
|
25
|
+
const image_upload_handler = (blobInfo: any, progress: any) =>
|
|
26
|
+
new Promise<string>(async (resolve, reject) => {
|
|
27
|
+
const formData = new FormData()
|
|
28
|
+
formData.append('photo', blobInfo.blob(), blobInfo.filename())
|
|
29
|
+
formData.append('sectionName', 'editor')
|
|
30
|
+
formData.append('itemId', '123')
|
|
31
|
+
|
|
32
|
+
axiosPrivate
|
|
33
|
+
.post('/editor/photo', Object.fromEntries(formData.entries()), {
|
|
34
|
+
headers: {
|
|
35
|
+
'Content-Type': 'multipart/form-data',
|
|
36
|
+
},
|
|
37
|
+
})
|
|
38
|
+
.then((res) => {
|
|
39
|
+
// resolve(getPublicPhotoUrl(res.data.name, 'editor', 'large'))
|
|
40
|
+
resolve(res.data)
|
|
41
|
+
return
|
|
42
|
+
})
|
|
43
|
+
.catch((error) => {
|
|
44
|
+
if (error.response) {
|
|
45
|
+
reject({ message: 'Error: ' + error.response.data.error, remove: true })
|
|
46
|
+
} else if (error.request) {
|
|
47
|
+
reject({ message: 'Error: ' + error.request, remove: true })
|
|
48
|
+
} else {
|
|
49
|
+
reject({ message: 'Error: ' + error.message, remove: true })
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const plugins = [
|
|
55
|
+
'directionality ',
|
|
56
|
+
'advlist',
|
|
57
|
+
'autolink',
|
|
58
|
+
'lists',
|
|
59
|
+
'link',
|
|
60
|
+
'charmap',
|
|
61
|
+
'anchor',
|
|
62
|
+
'searchreplace',
|
|
63
|
+
'visualblocks',
|
|
64
|
+
'code',
|
|
65
|
+
'fullscreen',
|
|
66
|
+
'insertdatetime',
|
|
67
|
+
'table',
|
|
68
|
+
'preview',
|
|
69
|
+
'help',
|
|
70
|
+
'wordcount',
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
let toolbar =
|
|
74
|
+
'undo redo | bold italic underline strikethrough formatselect | fontfamily fontsize blocks | alignleft aligncenter alignright alignjustify | outdent indent | numlist bullist | forecolor backcolor permanentpen formatpainter removeformat | pagebreak | charmap emoticons | fullscreen preview save print | pageembed template link anchor codesample | a11ycheck ltr rtl | showcomments addcomment | footnotes '
|
|
75
|
+
if (input.allowMedia) {
|
|
76
|
+
plugins.push('media', 'image')
|
|
77
|
+
toolbar += ' | insertfile image media'
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return (
|
|
81
|
+
<FormInputElement
|
|
82
|
+
validationError={formState.errors[input.name] as FieldError}
|
|
83
|
+
value={<div style={{ maxHeight: '200px', overflow: 'auto', wordBreak: 'break-all' }}>{input.value}</div>}
|
|
84
|
+
readonly={input.readonly}
|
|
85
|
+
label={input.label}
|
|
86
|
+
required={input.required}
|
|
87
|
+
>
|
|
88
|
+
<Editor
|
|
89
|
+
api-key='no-api-key'
|
|
90
|
+
licenseKey='gpl'
|
|
91
|
+
// TODO: Is this expensive?
|
|
92
|
+
onEditorChange={(content) => {
|
|
93
|
+
setValue(input.name, content, { shouldValidate: true })
|
|
94
|
+
}}
|
|
95
|
+
key={key} // Step 3: Pass the key as a prop
|
|
96
|
+
/*
|
|
97
|
+
onEditorChange={(content, editor) => {
|
|
98
|
+
console.log('Content was updated:', content)
|
|
99
|
+
setValue(content)
|
|
100
|
+
}}*/
|
|
101
|
+
// onChange={(e) => setValue(e.target.getContent())}
|
|
102
|
+
tinymceScriptSrc={'/tinymce/tinymce.min.js?v=1.1'}
|
|
103
|
+
onInit={(evt, editor) => {
|
|
104
|
+
// @ts-ignore
|
|
105
|
+
editorRef.current = editor
|
|
106
|
+
setInitialValue(value ? value : input.value ? input.value : '')
|
|
107
|
+
}}
|
|
108
|
+
textareaName={input.name}
|
|
109
|
+
initialValue={initialValue}
|
|
110
|
+
init={{
|
|
111
|
+
promotion: false,
|
|
112
|
+
height: 500,
|
|
113
|
+
menubar: 'file edit view insert format tools table tc help',
|
|
114
|
+
plugins: plugins,
|
|
115
|
+
toolbar: toolbar,
|
|
116
|
+
|
|
117
|
+
content_style: 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
|
|
118
|
+
// skin: theme === 'light' ? 'oxide' : 'oxide-dark',
|
|
119
|
+
// content_css: theme === 'light' ? 'default' : 'dark',
|
|
120
|
+
skin: 'oxide-dark',
|
|
121
|
+
content_css: 'dark',
|
|
122
|
+
relative_urls: false,
|
|
123
|
+
remove_script_host: false,
|
|
124
|
+
convert_urls: true,
|
|
125
|
+
extended_valid_elements: 'iframe[src|width|height|name|align|class|style],*[id|class|style]',
|
|
126
|
+
// valid_children: '+body[style],+div[*],+p[*]', // Allow any element to be a child of body, div, and p elements
|
|
127
|
+
// valid_elements: '*[*]', // Allow all elements and attributes
|
|
128
|
+
images_file_types: 'jpg,png,webp',
|
|
129
|
+
block_unsupported_drop: true,
|
|
130
|
+
images_upload_handler: image_upload_handler,
|
|
131
|
+
}}
|
|
132
|
+
/>
|
|
133
|
+
</FormInputElement>
|
|
134
|
+
)
|
|
135
|
+
}
|
|
@@ -1,175 +1,175 @@
|
|
|
1
|
-
import React, { useEffect, useState, useMemo } from 'react'
|
|
2
|
-
import FormInputElement from '@/components/form/FormInputElement'
|
|
3
|
-
import SelectBox from '@/components/SelectBox'
|
|
4
|
-
import LoadingSpinners from '@/components/LoadingSpinners'
|
|
5
|
-
import { useAutoAnimate } from '@formkit/auto-animate/react'
|
|
6
|
-
import { useI18n } from 'nextjs-cms/translations/client'
|
|
7
|
-
import { useSession } from 'nextjs-cms/auth/react'
|
|
8
|
-
import { trpc } from '@/app/_trpc/client'
|
|
9
|
-
import { SelectFieldClientConfig, SelectOption } from 'nextjs-cms/core/fields'
|
|
10
|
-
import { ConditionalFields } from '@/components/ConditionalFields'
|
|
11
|
-
import { useController, useFormContext } from 'react-hook-form'
|
|
12
|
-
|
|
13
|
-
export default function SelectFormInput({
|
|
14
|
-
input,
|
|
15
|
-
sectionName,
|
|
16
|
-
refetch,
|
|
17
|
-
level = 1,
|
|
18
|
-
}: {
|
|
19
|
-
input: SelectFieldClientConfig
|
|
20
|
-
sectionName: string
|
|
21
|
-
refetch?: any
|
|
22
|
-
level?: number
|
|
23
|
-
}) {
|
|
24
|
-
const t = useI18n()
|
|
25
|
-
const session = useSession()
|
|
26
|
-
const locale = session?.data?.user?.locale
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Create options array with translated placeholder option.
|
|
30
|
-
* This ensures the placeholder is always up-to-date when the language changes.
|
|
31
|
-
*/
|
|
32
|
-
const optionsWithPlaceholder = useMemo(() => {
|
|
33
|
-
if (!input.options) return []
|
|
34
|
-
|
|
35
|
-
// Filter out any existing placeholder options (value === undefined)
|
|
36
|
-
const optionsWithoutPlaceholder = input.options.filter((option) => option.value !== undefined)
|
|
37
|
-
|
|
38
|
-
// Add the translated placeholder option at the beginning
|
|
39
|
-
// @ts-ignore - This is a type-hack to add the placeholder `select` option with undefined value
|
|
40
|
-
return [{ value: undefined, label: t('select') as string }, ...optionsWithoutPlaceholder]
|
|
41
|
-
}, [input.options, t, locale])
|
|
42
|
-
|
|
43
|
-
const depth = input.section ? input.section.depth : 1
|
|
44
|
-
const [parent] = useAutoAnimate(/* optional config */)
|
|
45
|
-
const [isLoading, setIsLoading] = useState<boolean>(false)
|
|
46
|
-
const [value, setValue] = useState<SelectOption | undefined>(getValue(input.value))
|
|
47
|
-
const [child, setChild] = useState<{
|
|
48
|
-
input: SelectFieldClientConfig
|
|
49
|
-
level: number
|
|
50
|
-
} | null>(null)
|
|
51
|
-
|
|
52
|
-
const childrenMutation = trpc.categorySections.getChildren.useMutation({
|
|
53
|
-
onSuccess: (data) => {
|
|
54
|
-
if (data && data.options) {
|
|
55
|
-
const childInput = {
|
|
56
|
-
input: {
|
|
57
|
-
type: 'select' as const,
|
|
58
|
-
required: false,
|
|
59
|
-
readonly: false,
|
|
60
|
-
conditionalFields: [],
|
|
61
|
-
options: data.options,
|
|
62
|
-
section: input.section,
|
|
63
|
-
label: '|->',
|
|
64
|
-
name: input.name,
|
|
65
|
-
value:
|
|
66
|
-
input.value && Array.isArray(input.value) && input.value[1]
|
|
67
|
-
? input.value.slice(1)
|
|
68
|
-
: undefined,
|
|
69
|
-
defaultValue: input.value?.[0]?.value ?? undefined,
|
|
70
|
-
},
|
|
71
|
-
level: data.level,
|
|
72
|
-
}
|
|
73
|
-
setChild(childInput)
|
|
74
|
-
} else {
|
|
75
|
-
setChild(null)
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
setIsLoading(false)
|
|
79
|
-
},
|
|
80
|
-
onError: (error) => {},
|
|
81
|
-
// console.log('Error', error)
|
|
82
|
-
})
|
|
83
|
-
|
|
84
|
-
function getValue(value: SelectOption[] | undefined): SelectOption | undefined {
|
|
85
|
-
if (value && !Array.isArray(value)) {
|
|
86
|
-
return value
|
|
87
|
-
}
|
|
88
|
-
if (Array.isArray(value) && value.length > 0) {
|
|
89
|
-
return value[0]
|
|
90
|
-
}
|
|
91
|
-
return undefined
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const { control } = useFormContext()
|
|
95
|
-
const {
|
|
96
|
-
field,
|
|
97
|
-
fieldState: { invalid, isTouched, isDirty, error },
|
|
98
|
-
} = useController({
|
|
99
|
-
name: input.name,
|
|
100
|
-
control,
|
|
101
|
-
defaultValue: value?.value,
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
useEffect(() => {
|
|
105
|
-
setChild(null)
|
|
106
|
-
if (!input.section || !input.section.name) return
|
|
107
|
-
if (!value?.value) {
|
|
108
|
-
return
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* If the depth is less than or equal to 1, return
|
|
113
|
-
* If the level is greater than or equal to the depth, return
|
|
114
|
-
*/
|
|
115
|
-
if (depth && (depth <= 1 || level >= depth)) return
|
|
116
|
-
|
|
117
|
-
setIsLoading(true)
|
|
118
|
-
childrenMutation.mutate({
|
|
119
|
-
parentId: value.value,
|
|
120
|
-
sectionName: input.section.name,
|
|
121
|
-
level: level,
|
|
122
|
-
})
|
|
123
|
-
}, [value?.value])
|
|
124
|
-
|
|
125
|
-
return (
|
|
126
|
-
<>
|
|
127
|
-
<FormInputElement validationError={error} label={input.label} required={input.required}>
|
|
128
|
-
<input
|
|
129
|
-
type='hidden'
|
|
130
|
-
disabled={field.disabled}
|
|
131
|
-
name={field.name}
|
|
132
|
-
onBlur={field.onBlur}
|
|
133
|
-
ref={field.ref}
|
|
134
|
-
value={field.value}
|
|
135
|
-
/>
|
|
136
|
-
<SelectBox
|
|
137
|
-
defaultValue={value?.value ? value?.value.toString() : undefined}
|
|
138
|
-
// @ts-ignore - optionsWithPlaceholder includes placeholder with undefined value
|
|
139
|
-
items={optionsWithPlaceholder}
|
|
140
|
-
onChange={(value: SelectOption) => {
|
|
141
|
-
field.onChange(value.value)
|
|
142
|
-
if (value) setValue(value)
|
|
143
|
-
}}
|
|
144
|
-
classname='w-full shadow-xs'
|
|
145
|
-
/>
|
|
146
|
-
</FormInputElement>
|
|
147
|
-
<div ref={parent}>
|
|
148
|
-
{isLoading && <LoadingSpinners />}
|
|
149
|
-
{child && (
|
|
150
|
-
<div className='ps-4'>
|
|
151
|
-
{/* @ts-ignore no validators for child selects */}
|
|
152
|
-
<SelectFormInput
|
|
153
|
-
sectionName={sectionName}
|
|
154
|
-
refetch={() => {
|
|
155
|
-
refetch().then(() => {
|
|
156
|
-
setValue(undefined)
|
|
157
|
-
})
|
|
158
|
-
}}
|
|
159
|
-
level={child.level}
|
|
160
|
-
input={child.input}
|
|
161
|
-
/>
|
|
162
|
-
</div>
|
|
163
|
-
)}
|
|
164
|
-
|
|
165
|
-
{input.conditionalFields ? (
|
|
166
|
-
<ConditionalFields
|
|
167
|
-
sectionName={sectionName}
|
|
168
|
-
conditionalFields={input.conditionalFields}
|
|
169
|
-
value={value?.value}
|
|
170
|
-
/>
|
|
171
|
-
) : null}
|
|
172
|
-
</div>
|
|
173
|
-
</>
|
|
174
|
-
)
|
|
175
|
-
}
|
|
1
|
+
import React, { useEffect, useState, useMemo } from 'react'
|
|
2
|
+
import FormInputElement from '@/components/form/FormInputElement'
|
|
3
|
+
import SelectBox from '@/components/SelectBox'
|
|
4
|
+
import LoadingSpinners from '@/components/LoadingSpinners'
|
|
5
|
+
import { useAutoAnimate } from '@formkit/auto-animate/react'
|
|
6
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
7
|
+
import { useSession } from 'nextjs-cms/auth/react'
|
|
8
|
+
import { trpc } from '@/app/_trpc/client'
|
|
9
|
+
import { SelectFieldClientConfig, SelectOption } from 'nextjs-cms/core/fields'
|
|
10
|
+
import { ConditionalFields } from '@/components/ConditionalFields'
|
|
11
|
+
import { useController, useFormContext } from 'react-hook-form'
|
|
12
|
+
|
|
13
|
+
export default function SelectFormInput({
|
|
14
|
+
input,
|
|
15
|
+
sectionName,
|
|
16
|
+
refetch,
|
|
17
|
+
level = 1,
|
|
18
|
+
}: {
|
|
19
|
+
input: SelectFieldClientConfig
|
|
20
|
+
sectionName: string
|
|
21
|
+
refetch?: any
|
|
22
|
+
level?: number
|
|
23
|
+
}) {
|
|
24
|
+
const t = useI18n()
|
|
25
|
+
const session = useSession()
|
|
26
|
+
const locale = session?.data?.user?.locale
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Create options array with translated placeholder option.
|
|
30
|
+
* This ensures the placeholder is always up-to-date when the language changes.
|
|
31
|
+
*/
|
|
32
|
+
const optionsWithPlaceholder = useMemo(() => {
|
|
33
|
+
if (!input.options) return []
|
|
34
|
+
|
|
35
|
+
// Filter out any existing placeholder options (value === undefined)
|
|
36
|
+
const optionsWithoutPlaceholder = input.options.filter((option) => option.value !== undefined)
|
|
37
|
+
|
|
38
|
+
// Add the translated placeholder option at the beginning
|
|
39
|
+
// @ts-ignore - This is a type-hack to add the placeholder `select` option with undefined value
|
|
40
|
+
return [{ value: undefined, label: t('select') as string }, ...optionsWithoutPlaceholder]
|
|
41
|
+
}, [input.options, t, locale])
|
|
42
|
+
|
|
43
|
+
const depth = input.section ? input.section.depth : 1
|
|
44
|
+
const [parent] = useAutoAnimate(/* optional config */)
|
|
45
|
+
const [isLoading, setIsLoading] = useState<boolean>(false)
|
|
46
|
+
const [value, setValue] = useState<SelectOption | undefined>(getValue(input.value))
|
|
47
|
+
const [child, setChild] = useState<{
|
|
48
|
+
input: SelectFieldClientConfig
|
|
49
|
+
level: number
|
|
50
|
+
} | null>(null)
|
|
51
|
+
|
|
52
|
+
const childrenMutation = trpc.categorySections.getChildren.useMutation({
|
|
53
|
+
onSuccess: (data) => {
|
|
54
|
+
if (data && data.options) {
|
|
55
|
+
const childInput = {
|
|
56
|
+
input: {
|
|
57
|
+
type: 'select' as const,
|
|
58
|
+
required: false,
|
|
59
|
+
readonly: false,
|
|
60
|
+
conditionalFields: [],
|
|
61
|
+
options: data.options,
|
|
62
|
+
section: input.section,
|
|
63
|
+
label: '|->',
|
|
64
|
+
name: input.name,
|
|
65
|
+
value:
|
|
66
|
+
input.value && Array.isArray(input.value) && input.value[1]
|
|
67
|
+
? input.value.slice(1)
|
|
68
|
+
: undefined,
|
|
69
|
+
defaultValue: input.value?.[0]?.value ?? undefined,
|
|
70
|
+
},
|
|
71
|
+
level: data.level,
|
|
72
|
+
}
|
|
73
|
+
setChild(childInput)
|
|
74
|
+
} else {
|
|
75
|
+
setChild(null)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
setIsLoading(false)
|
|
79
|
+
},
|
|
80
|
+
onError: (error) => {},
|
|
81
|
+
// console.log('Error', error)
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
function getValue(value: SelectOption[] | undefined): SelectOption | undefined {
|
|
85
|
+
if (value && !Array.isArray(value)) {
|
|
86
|
+
return value
|
|
87
|
+
}
|
|
88
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
89
|
+
return value[0]
|
|
90
|
+
}
|
|
91
|
+
return undefined
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const { control } = useFormContext()
|
|
95
|
+
const {
|
|
96
|
+
field,
|
|
97
|
+
fieldState: { invalid, isTouched, isDirty, error },
|
|
98
|
+
} = useController({
|
|
99
|
+
name: input.name,
|
|
100
|
+
control,
|
|
101
|
+
defaultValue: value?.value,
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
useEffect(() => {
|
|
105
|
+
setChild(null)
|
|
106
|
+
if (!input.section || !input.section.name) return
|
|
107
|
+
if (!value?.value) {
|
|
108
|
+
return
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* If the depth is less than or equal to 1, return
|
|
113
|
+
* If the level is greater than or equal to the depth, return
|
|
114
|
+
*/
|
|
115
|
+
if (depth && (depth <= 1 || level >= depth)) return
|
|
116
|
+
|
|
117
|
+
setIsLoading(true)
|
|
118
|
+
childrenMutation.mutate({
|
|
119
|
+
parentId: value.value,
|
|
120
|
+
sectionName: input.section.name,
|
|
121
|
+
level: level,
|
|
122
|
+
})
|
|
123
|
+
}, [value?.value])
|
|
124
|
+
|
|
125
|
+
return (
|
|
126
|
+
<>
|
|
127
|
+
<FormInputElement validationError={error} label={input.label} required={input.required}>
|
|
128
|
+
<input
|
|
129
|
+
type='hidden'
|
|
130
|
+
disabled={field.disabled}
|
|
131
|
+
name={field.name}
|
|
132
|
+
onBlur={field.onBlur}
|
|
133
|
+
ref={field.ref}
|
|
134
|
+
value={field.value}
|
|
135
|
+
/>
|
|
136
|
+
<SelectBox
|
|
137
|
+
defaultValue={value?.value ? value?.value.toString() : undefined}
|
|
138
|
+
// @ts-ignore - optionsWithPlaceholder includes placeholder with undefined value
|
|
139
|
+
items={optionsWithPlaceholder}
|
|
140
|
+
onChange={(value: SelectOption) => {
|
|
141
|
+
field.onChange(value.value)
|
|
142
|
+
if (value) setValue(value)
|
|
143
|
+
}}
|
|
144
|
+
classname='w-full shadow-xs'
|
|
145
|
+
/>
|
|
146
|
+
</FormInputElement>
|
|
147
|
+
<div ref={parent}>
|
|
148
|
+
{isLoading && <LoadingSpinners />}
|
|
149
|
+
{child && (
|
|
150
|
+
<div className='ps-4'>
|
|
151
|
+
{/* @ts-ignore no validators for child selects */}
|
|
152
|
+
<SelectFormInput
|
|
153
|
+
sectionName={sectionName}
|
|
154
|
+
refetch={() => {
|
|
155
|
+
refetch().then(() => {
|
|
156
|
+
setValue(undefined)
|
|
157
|
+
})
|
|
158
|
+
}}
|
|
159
|
+
level={child.level}
|
|
160
|
+
input={child.input}
|
|
161
|
+
/>
|
|
162
|
+
</div>
|
|
163
|
+
)}
|
|
164
|
+
|
|
165
|
+
{input.conditionalFields ? (
|
|
166
|
+
<ConditionalFields
|
|
167
|
+
sectionName={sectionName}
|
|
168
|
+
conditionalFields={input.conditionalFields}
|
|
169
|
+
value={value?.value}
|
|
170
|
+
/>
|
|
171
|
+
) : null}
|
|
172
|
+
</div>
|
|
173
|
+
</>
|
|
174
|
+
)
|
|
175
|
+
}
|