create-nextjs-cms 0.9.5 → 0.9.7
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/dist/helpers/check-directory.d.ts +1 -0
- package/dist/helpers/check-directory.d.ts.map +1 -1
- package/dist/helpers/check-directory.js +98 -23
- package/dist/index.js +13 -8
- package/dist/lib/create-project.js +1 -1
- package/package.json +3 -3
- package/templates/default/app/(auth)/auth-language-provider.tsx +34 -34
- package/templates/default/components/AnalyticsPage.tsx +22 -6
- package/templates/default/components/CategorySectionSelectInput.tsx +2 -2
- package/templates/default/components/form/Form.tsx +12 -2
- package/templates/default/components/form/FormInputs.tsx +25 -16
- package/templates/default/components/form/inputs/DateFormInput.tsx +110 -53
- package/templates/default/components/form/inputs/DateRangeFormInput.tsx +175 -0
- package/templates/default/components/form/inputs/TagsFormInput.tsx +6 -5
- package/templates/default/next-env.d.ts +1 -1
- package/templates/default/package.json +3 -3
- package/templates/default/proxy.ts +2 -2
- package/templates/default/app/(rootLayout)/dashboard-new/page.tsx +0 -7
- package/templates/default/components/DashboardNewPage.tsx +0 -253
- package/templates/default/components/DashboardPage.tsx +0 -188
- package/templates/default/components/EmailCard.tsx +0 -138
- package/templates/default/components/EmailPasswordForm.tsx +0 -85
- package/templates/default/components/EmailQuotaForm.tsx +0 -73
- package/templates/default/components/EmailsPage.tsx +0 -49
- package/templates/default/components/NewEmailForm.tsx +0 -132
- package/templates/default/components/form/DateRangeFormInput.tsx +0 -57
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check-directory.d.ts","sourceRoot":"","sources":["../../src/helpers/check-directory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"check-directory.d.ts","sourceRoot":"","sources":["../../src/helpers/check-directory.ts"],"names":[],"mappings":"AA2HA,eAAO,MAAM,gBAAgB,GACzB,SAAS,MAAM,KAChB,OAAO,CAAC;IACP,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,OAAO,CAAA;IACpB,mBAAmB,EAAE,OAAO,CAAA;CAC/B,CAqCA,CAAA"}
|
|
@@ -6,6 +6,85 @@ import fs from 'fs-extra';
|
|
|
6
6
|
import { isEmptyDir } from './utils.js';
|
|
7
7
|
import * as p from '@clack/prompts';
|
|
8
8
|
import chalk from 'chalk';
|
|
9
|
+
import ora from 'ora';
|
|
10
|
+
const abortInstallation = () => {
|
|
11
|
+
p.cancel('Aborting installation');
|
|
12
|
+
process.exit(1);
|
|
13
|
+
};
|
|
14
|
+
const abortDirectoryCleanup = (targetDir, error) => {
|
|
15
|
+
p.log.error(`Failed to empty ${targetDir === process.cwd() ? 'the current directory' : `"${targetDir}"`}.`);
|
|
16
|
+
p.log.message('Some files may already have been deleted.');
|
|
17
|
+
p.log.message(`Please review and clean this directory manually: ${targetDir}`);
|
|
18
|
+
p.log.message(`Reason: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
19
|
+
return abortInstallation();
|
|
20
|
+
};
|
|
21
|
+
const getDirectoryEntriesToDelete = async (targetDir, keepGit) => {
|
|
22
|
+
const entries = await fs.readdir(targetDir);
|
|
23
|
+
return entries.filter((entry) => !(keepGit && entry === '.git'));
|
|
24
|
+
};
|
|
25
|
+
const emptyDirectory = async (targetDir, entries, onEntryDeleted) => {
|
|
26
|
+
let deletedCount = 0;
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
await fs.remove(path.join(targetDir, entry));
|
|
29
|
+
deletedCount += 1;
|
|
30
|
+
onEntryDeleted?.(deletedCount, entries.length);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const runDirectoryCleanup = async ({ targetDir, targetIsCwd, keepGit, }) => {
|
|
34
|
+
const spinnerText = keepGit
|
|
35
|
+
? `Emptying ${targetIsCwd ? 'the current directory' : `"${targetDir}"`} and keeping .git...`
|
|
36
|
+
: `Emptying ${targetIsCwd ? 'the current directory' : `"${targetDir}"`}...`;
|
|
37
|
+
const successMessage = keepGit
|
|
38
|
+
? `Emptied ${targetIsCwd ? 'the current directory' : `"${targetDir}"`} and kept ${chalk.green('.git')}.`
|
|
39
|
+
: `Emptied ${targetIsCwd ? 'the current directory' : `"${targetDir}"`}.`;
|
|
40
|
+
const entriesToDelete = await getDirectoryEntriesToDelete(targetDir, keepGit);
|
|
41
|
+
const spinner = ora(entriesToDelete.length > 0 ? `${spinnerText} (0/${entriesToDelete.length})` : spinnerText).start();
|
|
42
|
+
try {
|
|
43
|
+
await emptyDirectory(targetDir, entriesToDelete, (deletedCount, totalCount) => {
|
|
44
|
+
spinner.text = `${spinnerText} (${deletedCount}/${totalCount})`;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
spinner.stop();
|
|
49
|
+
abortDirectoryCleanup(targetDir, error);
|
|
50
|
+
}
|
|
51
|
+
spinner.stop();
|
|
52
|
+
p.log.info(successMessage);
|
|
53
|
+
};
|
|
54
|
+
const promptForNonEmptyDirectoryAction = async (targetDir, targetIsCwd) => {
|
|
55
|
+
if (targetIsCwd) {
|
|
56
|
+
p.log.error('Current directory is not empty.');
|
|
57
|
+
p.log.message(chalk.gray('Tip: You can also specify a directory name like this: \n') +
|
|
58
|
+
chalk.green('pnpm create nextjs-cms ') +
|
|
59
|
+
chalk.italic.magenta('my-app'));
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
p.log.error(`Directory "${targetDir}" is not empty.`);
|
|
63
|
+
}
|
|
64
|
+
p.log.message('Choose how you want to proceed:');
|
|
65
|
+
const action = await p.select({
|
|
66
|
+
message: 'How would you like to proceed?',
|
|
67
|
+
initialValue: 'abort',
|
|
68
|
+
options: [
|
|
69
|
+
{ value: 'abort', label: 'Abort' },
|
|
70
|
+
{ value: 'empty', label: 'Empty directory ⚠️', hint: 'Delete everything in the target directory' },
|
|
71
|
+
{
|
|
72
|
+
value: 'empty-keep-git',
|
|
73
|
+
label: 'Empty directory and keep .git folder ⚠️',
|
|
74
|
+
hint: 'Delete everything except the root .git folder',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
value: 'continue',
|
|
78
|
+
label: 'Continue anyway ⚠️',
|
|
79
|
+
hint: 'Existing files may be overwritten and the app may be unstable',
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
if (p.isCancel(action) || action === 'abort') {
|
|
84
|
+
abortInstallation();
|
|
85
|
+
}
|
|
86
|
+
return action;
|
|
87
|
+
};
|
|
9
88
|
export const resolveDirectory = async (appName) => {
|
|
10
89
|
// Resolve target path from the caller's CWD
|
|
11
90
|
const rawTarget = expandHome(appName);
|
|
@@ -13,35 +92,31 @@ export const resolveDirectory = async (appName) => {
|
|
|
13
92
|
// Derive package name from final path
|
|
14
93
|
const projectName = basename(targetDir);
|
|
15
94
|
const targetIsCwd = path.normalize(targetDir) === path.normalize(process.cwd());
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
await fs.ensureDir(targetDir);
|
|
20
|
-
}
|
|
21
|
-
else if (!(await isEmptyDir(targetDir))) {
|
|
22
|
-
p.log.error('Current directory is not empty. Choose an empty folder or a new directory name.');
|
|
23
|
-
p.log.message(chalk.gray('Tip: You can also specify a directory name like this: \n') +
|
|
24
|
-
chalk.green('pnpm create nextjs-cms ') +
|
|
25
|
-
chalk.italic.magenta('my-app'));
|
|
26
|
-
p.log.message(' ');
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
95
|
+
const targetExistedBefore = await fs.pathExists(targetDir);
|
|
96
|
+
if (!targetExistedBefore) {
|
|
97
|
+
await fs.ensureDir(targetDir);
|
|
29
98
|
}
|
|
30
|
-
else {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
99
|
+
else if (!(await isEmptyDir(targetDir))) {
|
|
100
|
+
const action = await promptForNonEmptyDirectoryAction(targetDir, targetIsCwd);
|
|
101
|
+
switch (action) {
|
|
102
|
+
case 'empty':
|
|
103
|
+
await runDirectoryCleanup({ targetDir, targetIsCwd, keepGit: false });
|
|
104
|
+
break;
|
|
105
|
+
case 'empty-keep-git':
|
|
106
|
+
await runDirectoryCleanup({ targetDir, targetIsCwd, keepGit: true });
|
|
107
|
+
break;
|
|
108
|
+
case 'continue':
|
|
109
|
+
p.log.warn('Continuing in a non-empty directory. Existing files may be overwritten.');
|
|
110
|
+
break;
|
|
111
|
+
default:
|
|
112
|
+
action;
|
|
113
|
+
break;
|
|
40
114
|
}
|
|
41
115
|
}
|
|
42
116
|
return {
|
|
43
117
|
targetDir,
|
|
44
118
|
projectName,
|
|
45
119
|
targetIsCwd,
|
|
120
|
+
targetExistedBefore,
|
|
46
121
|
};
|
|
47
122
|
};
|
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ async function createNextjsCms() {
|
|
|
21
21
|
// Run CLI to get user input
|
|
22
22
|
const { appName, sectionsToAdd, git /*, databaseProvider*/ } = await runCli();
|
|
23
23
|
// Resolve directory
|
|
24
|
-
const { targetDir, projectName, targetIsCwd } = await resolveDirectory(appName);
|
|
24
|
+
const { targetDir, projectName, targetIsCwd, targetExistedBefore } = await resolveDirectory(appName);
|
|
25
25
|
try {
|
|
26
26
|
// Detect package manager
|
|
27
27
|
const preferredPM = detectPackageManager();
|
|
@@ -63,14 +63,19 @@ async function createNextjsCms() {
|
|
|
63
63
|
p.outro(chalk.red(`❌ Failed to create project: ${error instanceof Error ? error.message : 'Unknown error'}`));
|
|
64
64
|
// Clean up partial installation on error
|
|
65
65
|
if ((await fs.pathExists(targetDir)) && !targetIsCwd) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
await fs.remove(targetDir);
|
|
69
|
-
p.log.success('✅ Cleanup completed.');
|
|
66
|
+
if (targetExistedBefore) {
|
|
67
|
+
p.log.warn('Project creation failed in a pre-existing directory. Existing files were left in place.');
|
|
70
68
|
}
|
|
71
|
-
|
|
72
|
-
p.log.
|
|
73
|
-
|
|
69
|
+
else {
|
|
70
|
+
p.log.info('🧹 Cleaning up partial installation...');
|
|
71
|
+
try {
|
|
72
|
+
await fs.remove(targetDir);
|
|
73
|
+
p.log.success('✅ Cleanup completed.');
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
p.log.error('⚠️ Could not clean up partial installation.');
|
|
77
|
+
p.log.message(` Please manually remove: ${targetDir}`);
|
|
78
|
+
}
|
|
74
79
|
}
|
|
75
80
|
}
|
|
76
81
|
process.exit(1);
|
|
@@ -22,7 +22,7 @@ export const createProject = async ({ targetDir, sectionsToAdd, projectName, pre
|
|
|
22
22
|
!rel.startsWith('.turbo') &&
|
|
23
23
|
!rel.includes('tsconfig.tsbuildinfo'));
|
|
24
24
|
},
|
|
25
|
-
// Overwrite is
|
|
25
|
+
// Overwrite is intentional because the target is new, user-approved, or was explicitly emptied
|
|
26
26
|
overwrite: true,
|
|
27
27
|
errorOnExist: false,
|
|
28
28
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-nextjs-cms",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.7",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,8 +29,8 @@
|
|
|
29
29
|
"tsx": "^4.20.6",
|
|
30
30
|
"typescript": "^5.9.2",
|
|
31
31
|
"@lzcms/eslint-config": "0.3.0",
|
|
32
|
-
"@lzcms/
|
|
33
|
-
"@lzcms/
|
|
32
|
+
"@lzcms/tsconfig": "0.1.0",
|
|
33
|
+
"@lzcms/prettier-config": "0.1.0"
|
|
34
34
|
},
|
|
35
35
|
"prettier": "@lzcms/prettier-config",
|
|
36
36
|
"scripts": {
|
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { createContext, useContext, type ReactNode } from 'react'
|
|
4
|
-
|
|
5
|
-
export interface AuthLanguageValue {
|
|
6
|
-
supportedLanguages: readonly string[]
|
|
7
|
-
fallbackLanguage: string
|
|
8
|
-
initialLanguage: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const AuthLanguageContext = createContext<AuthLanguageValue | null>(null)
|
|
12
|
-
|
|
13
|
-
export function AuthLanguageProvider({
|
|
14
|
-
supportedLanguages,
|
|
15
|
-
fallbackLanguage,
|
|
16
|
-
initialLanguage,
|
|
17
|
-
children,
|
|
18
|
-
}: AuthLanguageValue & { children: ReactNode }) {
|
|
19
|
-
return (
|
|
20
|
-
<AuthLanguageContext.Provider
|
|
21
|
-
value={{ supportedLanguages, fallbackLanguage, initialLanguage }}
|
|
22
|
-
>
|
|
23
|
-
{children}
|
|
24
|
-
</AuthLanguageContext.Provider>
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export function useAuthLanguage(): AuthLanguageValue {
|
|
29
|
-
const ctx = useContext(AuthLanguageContext)
|
|
30
|
-
if (!ctx) {
|
|
31
|
-
throw new Error('useAuthLanguage must be used within AuthLanguageProvider')
|
|
32
|
-
}
|
|
33
|
-
return ctx
|
|
34
|
-
}
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createContext, useContext, type ReactNode } from 'react'
|
|
4
|
+
|
|
5
|
+
export interface AuthLanguageValue {
|
|
6
|
+
supportedLanguages: readonly string[]
|
|
7
|
+
fallbackLanguage: string
|
|
8
|
+
initialLanguage: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const AuthLanguageContext = createContext<AuthLanguageValue | null>(null)
|
|
12
|
+
|
|
13
|
+
export function AuthLanguageProvider({
|
|
14
|
+
supportedLanguages,
|
|
15
|
+
fallbackLanguage,
|
|
16
|
+
initialLanguage,
|
|
17
|
+
children,
|
|
18
|
+
}: AuthLanguageValue & { children: ReactNode }) {
|
|
19
|
+
return (
|
|
20
|
+
<AuthLanguageContext.Provider
|
|
21
|
+
value={{ supportedLanguages, fallbackLanguage, initialLanguage }}
|
|
22
|
+
>
|
|
23
|
+
{children}
|
|
24
|
+
</AuthLanguageContext.Provider>
|
|
25
|
+
)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function useAuthLanguage(): AuthLanguageValue {
|
|
29
|
+
const ctx = useContext(AuthLanguageContext)
|
|
30
|
+
if (!ctx) {
|
|
31
|
+
throw new Error('useAuthLanguage must be used within AuthLanguageProvider')
|
|
32
|
+
}
|
|
33
|
+
return ctx
|
|
34
|
+
}
|
|
@@ -3,7 +3,7 @@ import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'
|
|
|
3
3
|
import React, { useEffect, useRef } from 'react'
|
|
4
4
|
import { useI18n } from 'nextjs-cms/translations/client'
|
|
5
5
|
import { Chart as ChartJS, ArcElement, Tooltip, Legend } from 'chart.js'
|
|
6
|
-
import
|
|
6
|
+
import DateRangeFormInput from '@/components/form/inputs/DateRangeFormInput'
|
|
7
7
|
import { MonthlyPageViews } from '@/components/analytics/MonthlyPageViews'
|
|
8
8
|
import { TopSources } from '@/components/analytics/TopSources'
|
|
9
9
|
import { TopMediums } from '@/components/analytics/TopMediums'
|
|
@@ -15,17 +15,19 @@ import { BounceRate } from '@/components/analytics/BounceRate'
|
|
|
15
15
|
import { LivePageViews } from '@/components/analytics/LivePageViews'
|
|
16
16
|
import { TopCountries } from '@/components/analytics/TopCountries'
|
|
17
17
|
import { TopDevices } from '@/components/analytics/TopDevices'
|
|
18
|
-
import { trpc } from '@/app/_trpc/client'
|
|
18
|
+
// import { trpc } from '@/app/_trpc/client'
|
|
19
19
|
ChartJS.register(ArcElement, Tooltip, Legend)
|
|
20
20
|
|
|
21
|
+
// TODO: MOVE THIS TO THE PLUGIN
|
|
22
|
+
|
|
21
23
|
const AnalyticsPage = () => {
|
|
22
24
|
const t = useI18n()
|
|
23
25
|
const controller = new AbortController()
|
|
24
26
|
const datePickerRangeRef = useRef<HTMLDivElement>(null)
|
|
25
|
-
const [fromDate, setFromDate] = React.useState<
|
|
26
|
-
const [toDate, setToDate] = React.useState<
|
|
27
|
+
const [fromDate, setFromDate] = React.useState<string>('30daysAgo')
|
|
28
|
+
const [toDate, setToDate] = React.useState<string>('today')
|
|
27
29
|
|
|
28
|
-
const { data, isError } = trpc.googleAnalytics.test.useQuery()
|
|
30
|
+
// const { data, isError } = trpc.googleAnalytics.test.useQuery()
|
|
29
31
|
|
|
30
32
|
useEffect(() => {
|
|
31
33
|
return () => {
|
|
@@ -92,7 +94,21 @@ const AnalyticsPage = () => {
|
|
|
92
94
|
</Tabs>
|
|
93
95
|
</Tabs>
|
|
94
96
|
<div ref={datePickerRangeRef} className='hidden'>
|
|
95
|
-
<
|
|
97
|
+
<DateRangeFormInput input={{
|
|
98
|
+
name: 'dateRange',
|
|
99
|
+
label: 'Date Range',
|
|
100
|
+
required: true,
|
|
101
|
+
conditionalFields: [],
|
|
102
|
+
readonly: false,
|
|
103
|
+
defaultValue: undefined,
|
|
104
|
+
value: undefined,
|
|
105
|
+
startName: 'fromDate',
|
|
106
|
+
endName: 'toDate',
|
|
107
|
+
format: 'date',
|
|
108
|
+
startValue: fromDate,
|
|
109
|
+
endValue: toDate,
|
|
110
|
+
type: 'date_range',
|
|
111
|
+
}} />
|
|
96
112
|
</div>
|
|
97
113
|
</div>
|
|
98
114
|
<div className='my-2 grid grid-cols-2 gap-4 sm-sidebar:grid-cols-2 md-sidebar:grid-cols-2 lg-sidebar:grid-cols-4'>
|
|
@@ -25,9 +25,9 @@ export default function CategorySectionSelectInput({
|
|
|
25
25
|
/**
|
|
26
26
|
* Check and add the select option if it does not exist, the select option has a value of undefined
|
|
27
27
|
*/
|
|
28
|
-
const exists = input
|
|
28
|
+
const exists = input?.options?.filter((option) => option.value === undefined)
|
|
29
29
|
if (!exists || exists.length === 0) {
|
|
30
|
-
if (input
|
|
30
|
+
if (input?.options) {
|
|
31
31
|
// @ts-ignore - This is a type-hack to add the placeholder `select` option to the options array
|
|
32
32
|
input.options.unshift({ value: undefined, label: t('select') })
|
|
33
33
|
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
CheckboxFieldClientConfig,
|
|
17
17
|
ColorFieldClientConfig,
|
|
18
18
|
DateFieldClientConfig,
|
|
19
|
+
DateRangeFieldClientConfig,
|
|
19
20
|
DocumentFieldClientConfig,
|
|
20
21
|
MapFieldClientConfig,
|
|
21
22
|
NumberFieldClientConfig,
|
|
@@ -36,6 +37,7 @@ import {
|
|
|
36
37
|
selectFieldSchema,
|
|
37
38
|
selectMultipleFieldSchema,
|
|
38
39
|
dateFieldSchema,
|
|
40
|
+
dateRangeFieldSchema,
|
|
39
41
|
checkboxFieldSchema,
|
|
40
42
|
textareaFieldSchema,
|
|
41
43
|
richTextFieldSchema,
|
|
@@ -79,6 +81,8 @@ export default function Form({
|
|
|
79
81
|
section: {
|
|
80
82
|
name: string
|
|
81
83
|
gallery?: boolean
|
|
84
|
+
title?: { section?: string; singular?: string; plural?: string }
|
|
85
|
+
configFile?: string
|
|
82
86
|
}
|
|
83
87
|
inputGroups:
|
|
84
88
|
| {
|
|
@@ -152,6 +156,12 @@ export default function Form({
|
|
|
152
156
|
})
|
|
153
157
|
break
|
|
154
158
|
|
|
159
|
+
case 'date_range':
|
|
160
|
+
schema = schema.extend(
|
|
161
|
+
dateRangeFieldSchema(input as DateRangeFieldClientConfig, language),
|
|
162
|
+
)
|
|
163
|
+
break
|
|
164
|
+
|
|
155
165
|
case 'checkbox':
|
|
156
166
|
schema = schema.extend({
|
|
157
167
|
[input.name]: checkboxFieldSchema(input as CheckboxFieldClientConfig, language),
|
|
@@ -263,7 +273,7 @@ export default function Form({
|
|
|
263
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'>
|
|
264
274
|
<h3 className='font-semibold'>{t('noLocalizedFields')}</h3>
|
|
265
275
|
<p className='mt-1'>
|
|
266
|
-
{t('noLocalizedFieldsHint', { section: data.section.title.section, file: data.section.configFile })}
|
|
276
|
+
{t('noLocalizedFieldsHint', { section: typeof data.section.title === 'object' ? (data.section.title?.section ?? '') : '', file: data.section.configFile })}
|
|
267
277
|
</p>
|
|
268
278
|
</div>
|
|
269
279
|
) : (
|
|
@@ -286,7 +296,7 @@ export default function Form({
|
|
|
286
296
|
{data.section.gallery && !isTranslationMode ? (
|
|
287
297
|
<>
|
|
288
298
|
<div className='w-full'>
|
|
289
|
-
<PhotoGallery sectionName={data.section.name} gallery={data.gallery} />
|
|
299
|
+
<PhotoGallery sectionName={data.section.name} gallery={'gallery' in data ? data.gallery : []} />
|
|
290
300
|
</div>
|
|
291
301
|
<div className='w-full'>
|
|
292
302
|
<Dropzone ref={dropzoneRef} />
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import React from 'react'
|
|
1
|
+
import React from 'react'
|
|
2
2
|
import ColorFormInput from '@/components/form/inputs/ColorFormInput'
|
|
3
3
|
import NumberFormInput from '@/components/form/inputs/NumberFormInput'
|
|
4
4
|
import TextFormInput from '@/components/form/inputs/TextFormInput'
|
|
5
5
|
import DateFormInput from '@/components/form/inputs/DateFormInput'
|
|
6
|
+
import DateRangeFormInput from '@/components/form/inputs/DateRangeFormInput'
|
|
6
7
|
import RichTextFormInput from '@/components/form/inputs/RichTextFormInput'
|
|
7
8
|
import SelectFormInput from '@/components/form/inputs/SelectFormInput'
|
|
8
9
|
import MultipleSelectFormInput from '@/components/form/inputs/MultipleSelectFormInput'
|
|
@@ -20,6 +21,7 @@ import {
|
|
|
20
21
|
CheckboxFieldClientConfig,
|
|
21
22
|
ColorFieldClientConfig,
|
|
22
23
|
DateFieldClientConfig,
|
|
24
|
+
DateRangeFieldClientConfig,
|
|
23
25
|
DocumentFieldClientConfig,
|
|
24
26
|
MapFieldClientConfig,
|
|
25
27
|
NumberFieldClientConfig,
|
|
@@ -35,15 +37,15 @@ import {
|
|
|
35
37
|
SlugFieldClientConfig,
|
|
36
38
|
} from 'nextjs-cms/core/fields'
|
|
37
39
|
|
|
38
|
-
export default function FormInputs({
|
|
39
|
-
inputs,
|
|
40
|
-
sectionName,
|
|
41
|
-
submitSuccessCount = 0,
|
|
42
|
-
}: {
|
|
43
|
-
inputs: FieldClientConfig[]
|
|
44
|
-
sectionName: string
|
|
45
|
-
submitSuccessCount?: number
|
|
46
|
-
}) {
|
|
40
|
+
export default function FormInputs({
|
|
41
|
+
inputs,
|
|
42
|
+
sectionName,
|
|
43
|
+
submitSuccessCount = 0,
|
|
44
|
+
}: {
|
|
45
|
+
inputs: FieldClientConfig[]
|
|
46
|
+
sectionName: string
|
|
47
|
+
submitSuccessCount?: number
|
|
48
|
+
}) {
|
|
47
49
|
return (
|
|
48
50
|
<>
|
|
49
51
|
{inputs?.length > 0 &&
|
|
@@ -57,6 +59,13 @@ export default function FormInputs({
|
|
|
57
59
|
return <TextFormInput input={input as TextFieldClientConfig} key={input.name} />
|
|
58
60
|
case 'date':
|
|
59
61
|
return <DateFormInput input={input as DateFieldClientConfig} key={input.name} />
|
|
62
|
+
case 'date_range':
|
|
63
|
+
return (
|
|
64
|
+
<DateRangeFormInput
|
|
65
|
+
input={input as DateRangeFieldClientConfig}
|
|
66
|
+
key={(input as DateRangeFieldClientConfig).startName}
|
|
67
|
+
/>
|
|
68
|
+
)
|
|
60
69
|
case 'rich_text':
|
|
61
70
|
return <RichTextFormInput input={input as RichTextFieldClientConfig} key={input.name} />
|
|
62
71
|
case 'textarea':
|
|
@@ -78,12 +87,12 @@ export default function FormInputs({
|
|
|
78
87
|
)
|
|
79
88
|
case 'photo':
|
|
80
89
|
return (
|
|
81
|
-
<PhotoFormInput
|
|
82
|
-
sectionName={sectionName}
|
|
83
|
-
input={input as PhotoFieldClientConfig}
|
|
84
|
-
submitSuccessCount={submitSuccessCount}
|
|
85
|
-
key={input.name}
|
|
86
|
-
/>
|
|
90
|
+
<PhotoFormInput
|
|
91
|
+
sectionName={sectionName}
|
|
92
|
+
input={input as PhotoFieldClientConfig}
|
|
93
|
+
submitSuccessCount={submitSuccessCount}
|
|
94
|
+
key={input.name}
|
|
95
|
+
/>
|
|
87
96
|
)
|
|
88
97
|
case 'video':
|
|
89
98
|
return (
|