create-nextjs-cms 0.9.30 → 0.9.32
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 +1 -1
- 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-language-provider.tsx +34 -34
- package/templates/default/app/(auth)/layout.tsx +81 -81
- package/templates/default/app/(rootLayout)/(plugins)/[...slug]/error.tsx +64 -0
- package/templates/default/app/(rootLayout)/(plugins)/[...slug]/page.tsx +59 -47
- package/templates/default/app/(rootLayout)/(plugins)/[...slug]/plugin-server-registry.ts +14 -16
- 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 +68 -70
- package/templates/default/app/(rootLayout)/edit/[section]/[itemId]/page.tsx +20 -20
- 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 +5 -7
- package/templates/default/app/(rootLayout)/new/[section]/page.tsx +15 -15
- package/templates/default/app/(rootLayout)/section/[section]/page.tsx +19 -19
- package/templates/default/app/(rootLayout)/settings/page.tsx +11 -13
- 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/route.ts +49 -49
- package/templates/default/app/api/auth/session/route.ts +20 -20
- package/templates/default/app/api/document/route.ts +165 -165
- 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 +95 -95
- package/templates/default/app/api/submit/section/item/route.ts +56 -56
- package/templates/default/app/api/submit/section/simple/route.ts +86 -86
- package/templates/default/app/api/video/route.ts +174 -174
- package/templates/default/app/globals.css +236 -236
- package/templates/default/cms.config.ts +56 -56
- package/templates/default/components/admin/admin-card.tsx +165 -165
- package/templates/default/components/admin/admin-edit-page.tsx +124 -124
- package/templates/default/components/admin/admin-privilege-card.tsx +184 -184
- package/templates/default/components/admin/new-admin-form.tsx +172 -172
- package/templates/default/components/container-box.tsx +24 -24
- package/templates/default/components/dnd-kit/draggable.tsx +21 -21
- package/templates/default/components/dnd-kit/droppable.tsx +20 -20
- package/templates/default/components/dnd-kit/sortable-item.tsx +18 -18
- package/templates/default/components/feedback/error-component.tsx +16 -16
- package/templates/default/components/feedback/info-card.tsx +93 -93
- package/templates/default/components/feedback/loading-spinners.tsx +67 -67
- package/templates/default/components/feedback/modal.tsx +166 -166
- package/templates/default/components/feedback/progress-bar.tsx +48 -48
- package/templates/default/components/feedback/tooltip-component.tsx +27 -27
- package/templates/default/components/form/form-input-element.tsx +70 -70
- 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/checkbox-form-input.tsx +46 -46
- package/templates/default/components/form/inputs/color-form-input.tsx +44 -44
- package/templates/default/components/form/inputs/date-form-input.tsx +93 -93
- package/templates/default/components/form/inputs/map-form-input.tsx +141 -141
- package/templates/default/components/form/inputs/multiple-select-form-input.tsx +85 -85
- package/templates/default/components/form/inputs/number-form-input.tsx +43 -43
- package/templates/default/components/form/inputs/password-form-input.tsx +47 -47
- package/templates/default/components/form/inputs/photo-form-input.tsx +279 -279
- package/templates/default/components/form/inputs/rich-text-form-input.tsx +148 -148
- package/templates/default/components/form/inputs/select-form-input.tsx +159 -159
- package/templates/default/components/form/inputs/slug-form-input.tsx +131 -131
- package/templates/default/components/form/inputs/tags-form-input.tsx +255 -255
- package/templates/default/components/form/inputs/text-form-input.tsx +61 -61
- package/templates/default/components/form/inputs/textarea-form-input.tsx +61 -61
- package/templates/default/components/layout/default-nav-items.tsx +3 -3
- package/templates/default/components/layout/layout.tsx +84 -84
- package/templates/default/components/layout/navbar.tsx +258 -258
- package/templates/default/components/layout/sidebar-dropdown-item.tsx +83 -83
- package/templates/default/components/layout/sidebar-item.tsx +24 -24
- package/templates/default/components/layout/sidebar.tsx +229 -229
- package/templates/default/components/layout/theme-provider.tsx +8 -8
- package/templates/default/components/layout/theme-toggle.tsx +39 -39
- package/templates/default/components/locale/locale-switcher.tsx +98 -98
- package/templates/default/components/media/dropzone.tsx +154 -154
- package/templates/default/components/media/protected-document.tsx +44 -44
- package/templates/default/components/media/protected-image.tsx +143 -143
- package/templates/default/components/media/protected-video.tsx +76 -76
- package/templates/default/components/multi-select.tsx +1150 -1150
- package/templates/default/components/pages/admins-page.tsx +43 -43
- package/templates/default/components/pages/browse-page.tsx +106 -106
- package/templates/default/components/pages/categorized-section-page.tsx +31 -31
- package/templates/default/components/pages/dashboard-page-alt.tsx +45 -45
- package/templates/default/components/pages/item-edit-page.tsx +267 -267
- package/templates/default/components/pages/log-page.tsx +107 -107
- package/templates/default/components/pages/new-page.tsx +183 -183
- package/templates/default/components/pages/section-page.tsx +203 -203
- package/templates/default/components/pages/settings-page.tsx +232 -232
- package/templates/default/components/pagination/pagination-buttons.tsx +147 -147
- package/templates/default/components/pagination/pagination.tsx +36 -36
- package/templates/default/components/sections/category-delete-confirm-page.tsx +130 -130
- package/templates/default/components/sections/category-section-select-input.tsx +139 -139
- package/templates/default/components/sections/conditional-fields.tsx +49 -49
- package/templates/default/components/sections/section-icon.tsx +8 -8
- package/templates/default/components/sections/section-item-card.tsx +143 -143
- package/templates/default/components/sections/section-item-status-badge.tsx +17 -17
- package/templates/default/components/sections/select-input-buttons.tsx +125 -125
- package/templates/default/components/select-box.tsx +98 -98
- package/templates/default/components/ui/accordion.tsx +53 -53
- package/templates/default/components/ui/alert-dialog.tsx +113 -113
- package/templates/default/components/ui/alert.tsx +47 -47
- package/templates/default/components/ui/badge.tsx +38 -38
- package/templates/default/components/ui/card.tsx +43 -43
- 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/direction.tsx +22 -22
- 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/spinner.tsx +16 -16
- 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 +187 -187
- package/templates/default/drizzle.config.ts +4 -4
- package/templates/default/dynamic-schemas/schema.ts +225 -75
- package/templates/default/env/env.ts +46 -46
- package/templates/default/envConfig.ts +4 -4
- 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 +24 -24
- package/templates/default/package.json +1 -1
- package/templates/default/postcss.config.mjs +6 -6
- package/templates/default/proxy.ts +32 -32
- package/templates/default/tsconfig.json +48 -48
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect } from 'react'
|
|
4
|
+
import { Button } from '@/components/ui/button'
|
|
5
|
+
import { AlertTriangle, Bug, RefreshCw } from 'lucide-react'
|
|
6
|
+
import { useI18n } from 'nextjs-cms/translations/client'
|
|
7
|
+
import { Badge } from '@/components/ui/badge'
|
|
8
|
+
|
|
9
|
+
export default function PluginError({ error, reset }: { error: Error & { digest?: string }; reset: () => void }) {
|
|
10
|
+
const t = useI18n()
|
|
11
|
+
const errorMessage = error.message || t('unexpectedPluginError')
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
// Surface plugin failures in the console; error.digest correlates with server logs.
|
|
15
|
+
console.error('[plugin]', error)
|
|
16
|
+
}, [error])
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div className='flex min-h-[360px] items-center justify-center p-4 sm:p-6'>
|
|
20
|
+
<section className='bg-card text-card-foreground w-full max-w-3xl overflow-hidden rounded-lg border shadow-sm'>
|
|
21
|
+
<div className='from-destructive/10 via-background to-background border-b bg-linear-to-r px-5 py-5 sm:px-6'>
|
|
22
|
+
<div className='flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between'>
|
|
23
|
+
<div className='flex items-start gap-4'>
|
|
24
|
+
<div className='bg-destructive/10 dark:bg-destructive/30 text-destructive ring-destructive/20 flex size-12 shrink-0 items-center justify-center rounded-lg ring-1'>
|
|
25
|
+
<AlertTriangle className='size-6' aria-hidden='true' />
|
|
26
|
+
</div>
|
|
27
|
+
<div>
|
|
28
|
+
<Badge variant='destructive' className='mb-2'>
|
|
29
|
+
{t('pluginError')}
|
|
30
|
+
</Badge>
|
|
31
|
+
<h2 className='text-xl font-semibold tracking-tight'>{t('pluginFailedToLoad')}</h2>
|
|
32
|
+
<p className='text-muted-foreground mt-2 max-w-2xl text-sm'>
|
|
33
|
+
{t('pluginFailedToLoadDescription')}
|
|
34
|
+
</p>
|
|
35
|
+
</div>
|
|
36
|
+
</div>
|
|
37
|
+
|
|
38
|
+
<Button type='button' size='sm' onClick={() => reset()} className='self-start'>
|
|
39
|
+
<RefreshCw className='size-4' aria-hidden='true' />
|
|
40
|
+
{t('retry')}
|
|
41
|
+
</Button>
|
|
42
|
+
</div>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<dl className='divide-y text-sm'>
|
|
46
|
+
<div className='grid gap-2 px-5 py-4 sm:grid-cols-[10rem_1fr] sm:px-6'>
|
|
47
|
+
<dt className='text-foreground flex items-center gap-2 font-medium'>
|
|
48
|
+
<Bug className='text-muted-foreground size-4' aria-hidden='true' />
|
|
49
|
+
{t('details')}
|
|
50
|
+
</dt>
|
|
51
|
+
<dd className='text-muted-foreground wrap-break-word'>{errorMessage}</dd>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
{error.digest && (
|
|
55
|
+
<div className='grid gap-2 px-5 py-4 sm:grid-cols-[10rem_1fr] sm:px-6'>
|
|
56
|
+
<dt className='text-foreground font-medium'>{t('errorDigest')}</dt>
|
|
57
|
+
<dd className='text-muted-foreground font-mono text-xs break-all'>{error.digest}</dd>
|
|
58
|
+
</div>
|
|
59
|
+
)}
|
|
60
|
+
</dl>
|
|
61
|
+
</section>
|
|
62
|
+
</div>
|
|
63
|
+
)
|
|
64
|
+
}
|
|
@@ -1,47 +1,59 @@
|
|
|
1
|
-
import { notFound } from 'next/navigation'
|
|
2
|
-
import auth from 'nextjs-cms/auth'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
const
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
await
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
1
|
+
import { notFound } from 'next/navigation'
|
|
2
|
+
import auth from 'nextjs-cms/auth'
|
|
3
|
+
import { Suspense } from 'react'
|
|
4
|
+
import { getAdminPrivileges } from 'nextjs-cms/api/server/actions'
|
|
5
|
+
import { findPluginRouteByPath, isDashboardOverridePlugin, runRoutePrefetches } from 'nextjs-cms/plugins/server'
|
|
6
|
+
import { api, HydrateClient } from '@/app/_trpc/server'
|
|
7
|
+
import { getPluginServerComponent } from './plugin-server-registry'
|
|
8
|
+
import LoadingSpinners from '@/components/feedback/loading-spinners'
|
|
9
|
+
|
|
10
|
+
type Params = Promise<{ slug?: string[] }>
|
|
11
|
+
|
|
12
|
+
export default async function Page(props: { params: Params }) {
|
|
13
|
+
const params = await props.params
|
|
14
|
+
const slug = params.slug ?? []
|
|
15
|
+
|
|
16
|
+
const path = `/${slug.join('/')}`
|
|
17
|
+
const session = await auth()
|
|
18
|
+
if (!session?.user) {
|
|
19
|
+
notFound()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const plugin = await findPluginRouteByPath(path)
|
|
23
|
+
if (!plugin) {
|
|
24
|
+
notFound()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Bypass privilege check for dashboard override plugin
|
|
28
|
+
const isDashboardPlugin = await isDashboardOverridePlugin(plugin.pluginName)
|
|
29
|
+
if (!isDashboardPlugin) {
|
|
30
|
+
const privilegeSet = await getAdminPrivileges(session.user.id)
|
|
31
|
+
if (!privilegeSet.has(plugin.pluginName)) {
|
|
32
|
+
notFound()
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Fire-and-forget: HydrateClient streams resolved query data as it becomes available,
|
|
37
|
+
// so awaiting here would block the shell on slow upstream APIs (cPanel, GA, etc.)
|
|
38
|
+
void runRoutePrefetches(api, plugin)
|
|
39
|
+
|
|
40
|
+
const PluginComponent = await getPluginServerComponent(plugin.pluginName, plugin.component)
|
|
41
|
+
if (!PluginComponent) {
|
|
42
|
+
notFound()
|
|
43
|
+
}
|
|
44
|
+
return (
|
|
45
|
+
<HydrateClient>
|
|
46
|
+
{/* This way, or use useSuspenseQuery
|
|
47
|
+
in each plugin client component and remove the <Suspense /> here */}
|
|
48
|
+
<Suspense
|
|
49
|
+
fallback={
|
|
50
|
+
<div className='p-20'>
|
|
51
|
+
<LoadingSpinners />
|
|
52
|
+
</div>
|
|
53
|
+
}
|
|
54
|
+
>
|
|
55
|
+
<PluginComponent />
|
|
56
|
+
</Suspense>
|
|
57
|
+
</HydrateClient>
|
|
58
|
+
)
|
|
59
|
+
}
|
|
@@ -1,22 +1,20 @@
|
|
|
1
|
-
import type { ComponentType } from 'react'
|
|
2
|
-
|
|
1
|
+
import type { ComponentType } from 'react'
|
|
2
|
+
|
|
3
3
|
export const pluginNamesMap: Record<string, string> = {
|
|
4
4
|
'cpanel-dashboard': '@nextjscms/plugin-cpanel-dashboard/server',
|
|
5
5
|
'cpanel-emails': '@nextjscms/plugin-cpanel-emails/server',
|
|
6
6
|
'google-analytics': '@nextjscms/plugin-google-analytics/server',
|
|
7
7
|
// A workaround to avoid importing error if no plugins are installed
|
|
8
8
|
blank: 'nextjs-cms/plugins/blank-component',
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const getPluginServerComponent = async (
|
|
12
|
-
pluginName: string,
|
|
13
|
-
componentName: string | undefined,
|
|
14
|
-
): Promise<ComponentType | null> => {
|
|
15
|
-
const modulePath = pluginNamesMap[pluginName]
|
|
16
|
-
if (!modulePath) return null
|
|
17
|
-
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
return component ?? null
|
|
22
|
-
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const getPluginServerComponent = async (
|
|
12
|
+
pluginName: string,
|
|
13
|
+
componentName: string | undefined,
|
|
14
|
+
): Promise<ComponentType | null> => {
|
|
15
|
+
const modulePath = pluginNamesMap[pluginName]
|
|
16
|
+
if (!modulePath) return null
|
|
17
|
+
const mod = await import(modulePath)
|
|
18
|
+
const Component = componentName ? mod[componentName] : mod.default
|
|
19
|
+
return (Component as ComponentType) ?? null
|
|
20
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import AdminsPage from '@/components/pages/admins-page'
|
|
2
|
-
import { api, HydrateClient } from '@/app/_trpc/server'
|
|
3
|
-
export default async function Page() {
|
|
4
|
-
await api.admins.list.prefetch()
|
|
5
|
-
return (
|
|
6
|
-
<HydrateClient>
|
|
7
|
-
<AdminsPage />
|
|
8
|
-
</HydrateClient>
|
|
9
|
-
)
|
|
10
|
-
}
|
|
1
|
+
import AdminsPage from '@/components/pages/admins-page'
|
|
2
|
+
import { api, HydrateClient } from '@/app/_trpc/server'
|
|
3
|
+
export default async function Page() {
|
|
4
|
+
await api.admins.list.prefetch()
|
|
5
|
+
return (
|
|
6
|
+
<HydrateClient>
|
|
7
|
+
<AdminsPage />
|
|
8
|
+
</HydrateClient>
|
|
9
|
+
)
|
|
10
|
+
}
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
import BrowsePage from '@/components/pages/browse-page'
|
|
2
|
-
import { api, HydrateClient } from '@/app/_trpc/server'
|
|
3
|
-
type Params = Promise<{ section: string; page: string }>
|
|
4
|
-
type SearchParams = Promise<{ q: string | undefined }>
|
|
5
|
-
export default async function Page(props: { params: Params; searchParams: SearchParams }) {
|
|
6
|
-
const params = await props.params
|
|
7
|
-
const searchParams = await props.searchParams
|
|
8
|
-
const { q } = searchParams
|
|
9
|
-
const { section, page } = params
|
|
10
|
-
|
|
11
|
-
await api.hasItemsSections.listItems.prefetch({
|
|
12
|
-
sectionName: section,
|
|
13
|
-
page: parseInt(page) ?? 1,
|
|
14
|
-
q: q ?? '',
|
|
15
|
-
})
|
|
16
|
-
|
|
17
|
-
return (
|
|
18
|
-
<HydrateClient>
|
|
19
|
-
<BrowsePage page={params.page} section={params.section} />
|
|
20
|
-
</HydrateClient>
|
|
21
|
-
)
|
|
22
|
-
}
|
|
1
|
+
import BrowsePage from '@/components/pages/browse-page'
|
|
2
|
+
import { api, HydrateClient } from '@/app/_trpc/server'
|
|
3
|
+
type Params = Promise<{ section: string; page: string }>
|
|
4
|
+
type SearchParams = Promise<{ q: string | undefined }>
|
|
5
|
+
export default async function Page(props: { params: Params; searchParams: SearchParams }) {
|
|
6
|
+
const params = await props.params
|
|
7
|
+
const searchParams = await props.searchParams
|
|
8
|
+
const { q } = searchParams
|
|
9
|
+
const { section, page } = params
|
|
10
|
+
|
|
11
|
+
await api.hasItemsSections.listItems.prefetch({
|
|
12
|
+
sectionName: section,
|
|
13
|
+
page: parseInt(page) ?? 1,
|
|
14
|
+
q: q ?? '',
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<HydrateClient>
|
|
19
|
+
<BrowsePage page={params.page} section={params.section} />
|
|
20
|
+
</HydrateClient>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import CategorizedSectionPage from '@/components/pages/categorized-section-page'
|
|
2
|
-
import { api, HydrateClient } from '@/app/_trpc/server'
|
|
3
|
-
type Params = Promise<{ section: string }>
|
|
4
|
-
|
|
5
|
-
export default async function Page(props: { params: Params }) {
|
|
6
|
-
const params = await props.params
|
|
7
|
-
await api.categorySections.get.prefetch({
|
|
8
|
-
sectionName: params.section,
|
|
9
|
-
})
|
|
10
|
-
return (
|
|
11
|
-
<HydrateClient>
|
|
12
|
-
<CategorizedSectionPage section={params.section} />
|
|
13
|
-
</HydrateClient>
|
|
14
|
-
)
|
|
15
|
-
}
|
|
1
|
+
import CategorizedSectionPage from '@/components/pages/categorized-section-page'
|
|
2
|
+
import { api, HydrateClient } from '@/app/_trpc/server'
|
|
3
|
+
type Params = Promise<{ section: string }>
|
|
4
|
+
|
|
5
|
+
export default async function Page(props: { params: Params }) {
|
|
6
|
+
const params = await props.params
|
|
7
|
+
await api.categorySections.get.prefetch({
|
|
8
|
+
sectionName: params.section,
|
|
9
|
+
})
|
|
10
|
+
return (
|
|
11
|
+
<HydrateClient>
|
|
12
|
+
<CategorizedSectionPage section={params.section} />
|
|
13
|
+
</HydrateClient>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
@@ -1,70 +1,68 @@
|
|
|
1
|
-
import { getDashboardOverride, runRoutePrefetches } from 'nextjs-cms/plugins/server'
|
|
2
|
-
import { api, HydrateClient } from '@/app/_trpc/server'
|
|
3
|
-
import { getPluginServerComponent } from '../(plugins)/[...slug]/plugin-server-registry'
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
)
|
|
70
|
-
}
|
|
1
|
+
import { getDashboardOverride, runRoutePrefetches } from 'nextjs-cms/plugins/server'
|
|
2
|
+
import { api, HydrateClient } from '@/app/_trpc/server'
|
|
3
|
+
import { getPluginServerComponent } from '../(plugins)/[...slug]/plugin-server-registry'
|
|
4
|
+
|
|
5
|
+
function DefaultDashboard() {
|
|
6
|
+
return (
|
|
7
|
+
<div className='w-full'>
|
|
8
|
+
<div className='text-foreground bg-linear-to-r from-amber-200 via-orange-200 to-rose-200 p-8 dark:from-amber-900 dark:via-orange-900 dark:to-rose-900'>
|
|
9
|
+
<h1 className='text-3xl font-extrabold tracking-tight'>Welcome to Mission Control</h1>
|
|
10
|
+
<p className='text-muted-foreground mt-2 text-base'>
|
|
11
|
+
Your CMS is ready. Chart a course, publish boldly, and keep the lights green.
|
|
12
|
+
</p>
|
|
13
|
+
</div>
|
|
14
|
+
|
|
15
|
+
<div className='space-y-6 p-6'>
|
|
16
|
+
<section className='bg-card text-card-foreground rounded-lg border p-6 shadow-sm'>
|
|
17
|
+
<h2 className='text-xl font-semibold'>Today's focus</h2>
|
|
18
|
+
<p className='text-muted-foreground mt-2 text-sm'>
|
|
19
|
+
Start with one small win: verify a section, ship a quick update, or polish a headline.
|
|
20
|
+
</p>
|
|
21
|
+
</section>
|
|
22
|
+
|
|
23
|
+
<section className='grid gap-4 md:grid-cols-3'>
|
|
24
|
+
<div className='bg-card text-card-foreground rounded-lg border p-4 shadow-sm'>
|
|
25
|
+
<div className='text-sm font-semibold'>Ship with confidence</div>
|
|
26
|
+
<div className='text-muted-foreground mt-2 text-sm'>
|
|
27
|
+
Keep changes tight and reversible. Small releases, fast feedback.
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
<div className='bg-card text-card-foreground rounded-lg border p-4 shadow-sm'>
|
|
31
|
+
<div className='text-sm font-semibold'>Stay organized</div>
|
|
32
|
+
<div className='text-muted-foreground mt-2 text-sm'>
|
|
33
|
+
Group your content by intent, not just by type.
|
|
34
|
+
</div>
|
|
35
|
+
</div>
|
|
36
|
+
<div className='bg-card text-card-foreground rounded-lg border p-4 shadow-sm'>
|
|
37
|
+
<div className='text-sm font-semibold'>Delight users</div>
|
|
38
|
+
<div className='text-muted-foreground mt-2 text-sm'>
|
|
39
|
+
A clean headline and a single strong image can do the heavy lifting.
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</section>
|
|
43
|
+
</div>
|
|
44
|
+
</div>
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default async function DashboardPage() {
|
|
49
|
+
// Check for dashboard override configuration
|
|
50
|
+
const override = await getDashboardOverride()
|
|
51
|
+
if (!override) {
|
|
52
|
+
return <DefaultDashboard />
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Render the override plugin component (no privilege check for dashboard override)
|
|
56
|
+
const PluginComponent = await getPluginServerComponent(override.pluginName, override.component)
|
|
57
|
+
if (!PluginComponent) {
|
|
58
|
+
return <DefaultDashboard />
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
await runRoutePrefetches(api, override)
|
|
62
|
+
|
|
63
|
+
return (
|
|
64
|
+
<HydrateClient>
|
|
65
|
+
<PluginComponent />
|
|
66
|
+
</HydrateClient>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
import ItemEditPage from '@/components/pages/item-edit-page'
|
|
2
|
-
import { api, HydrateClient } from '@/app/_trpc/server'
|
|
3
|
-
type Params = Promise<{ section: string; itemId: string }>
|
|
4
|
-
type SearchParams = Promise<{ locale?: string }>
|
|
5
|
-
|
|
6
|
-
export default async function Page(props: { params: Params; searchParams: SearchParams }) {
|
|
7
|
-
const params = await props.params
|
|
8
|
-
const searchParams = await props.searchParams
|
|
9
|
-
await api.hasItemsSections.editItem.prefetch({
|
|
10
|
-
sectionName: params.section,
|
|
11
|
-
sectionItemId: params.itemId,
|
|
12
|
-
locale: searchParams.locale,
|
|
13
|
-
})
|
|
14
|
-
|
|
15
|
-
return (
|
|
16
|
-
<HydrateClient>
|
|
17
|
-
<ItemEditPage sectionType='has_items' section={params.section} itemId={params.itemId} />
|
|
18
|
-
</HydrateClient>
|
|
19
|
-
)
|
|
20
|
-
}
|
|
1
|
+
import ItemEditPage from '@/components/pages/item-edit-page'
|
|
2
|
+
import { api, HydrateClient } from '@/app/_trpc/server'
|
|
3
|
+
type Params = Promise<{ section: string; itemId: string }>
|
|
4
|
+
type SearchParams = Promise<{ locale?: string }>
|
|
5
|
+
|
|
6
|
+
export default async function Page(props: { params: Params; searchParams: SearchParams }) {
|
|
7
|
+
const params = await props.params
|
|
8
|
+
const searchParams = await props.searchParams
|
|
9
|
+
await api.hasItemsSections.editItem.prefetch({
|
|
10
|
+
sectionName: params.section,
|
|
11
|
+
sectionItemId: params.itemId,
|
|
12
|
+
locale: searchParams.locale,
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<HydrateClient>
|
|
17
|
+
<ItemEditPage sectionType='has_items' section={params.section} itemId={params.itemId} />
|
|
18
|
+
</HydrateClient>
|
|
19
|
+
)
|
|
20
|
+
}
|