create-nextjs-cms 0.5.69 → 0.5.71
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/package.json +3 -3
- package/templates/default/app/(rootLayout)/(plugins)/[...slug]/page.tsx +36 -36
- package/templates/default/app/(rootLayout)/(plugins)/[...slug]/plugin-server-registry.ts +22 -25
- package/templates/default/app/(rootLayout)/browse/[section]/[page]/page.tsx +1 -0
- package/templates/default/app/(rootLayout)/dashboard/page.tsx +44 -44
- package/templates/default/app/(rootLayout)/layout.tsx +3 -1
- package/templates/default/app/_trpc/client.ts +3 -3
- package/templates/default/app/api/trpc/[trpc]/route.ts +3 -3
- package/templates/default/app/layout.tsx +12 -6
- package/templates/default/cms.config.ts +1 -2
- package/templates/default/components/AdminsPage.tsx +6 -7
- package/templates/default/components/BarChartBox.tsx +1 -2
- package/templates/default/components/BrowsePage.tsx +10 -24
- package/templates/default/components/CategorizedSectionPage.tsx +5 -10
- package/templates/default/components/EmailsPage.tsx +1 -1
- package/templates/default/components/ErrorComponent.tsx +17 -0
- package/templates/default/components/GalleryPhoto.tsx +1 -2
- package/templates/default/components/InfoCard.tsx +0 -1
- package/templates/default/components/ItemEditPage.tsx +26 -30
- package/templates/default/components/Layout.tsx +3 -2
- package/templates/default/components/LogPage.tsx +0 -1
- package/templates/default/components/Modal.tsx +64 -7
- package/templates/default/components/Navbar.tsx +170 -17
- package/templates/default/components/NewPage.tsx +9 -11
- package/templates/default/components/PhotoGallery.tsx +0 -1
- package/templates/default/components/SectionItemCard.tsx +1 -1
- package/templates/default/components/SectionPage.tsx +8 -8
- package/templates/default/components/SelectBox.tsx +62 -77
- package/templates/default/components/SettingsPage.tsx +2 -9
- package/templates/default/components/Sidebar.tsx +5 -22
- package/templates/default/components/SidebarDropdownItem.tsx +2 -2
- package/templates/default/components/form/helpers/_section-hot-reload.js +1 -1
- package/templates/default/components/ui/alert-dialog.tsx +157 -0
- package/templates/default/components/ui/command.tsx +137 -184
- package/templates/default/components/ui/custom-alert-dialog.tsx +113 -0
- package/templates/default/components/ui/custom-dialog.tsx +123 -0
- package/templates/default/components/ui/dialog.tsx +123 -143
- package/templates/default/components/ui/popover.tsx +28 -34
- package/templates/default/components/ui/sheet.tsx +103 -107
- package/templates/default/next.config.ts +4 -0
- package/templates/default/package.json +6 -12
- package/templates/default/public/favicon.ico +0 -0
- package/templates/default/public/nextjscms.webp +0 -0
- package/templates/default/styles/globals.css +2 -1
- package/templates/default/app/(rootLayout)/advanced/page.tsx +0 -11
- package/templates/default/app/(rootLayout)/page.tsx +0 -10
- package/templates/default/components/AdvancedSettingsPage.tsx +0 -167
- package/templates/default/components/NavbarAlt.tsx +0 -182
- package/templates/default/components/TempPage.tsx +0 -12
- package/templates/default/public/lazemni_logo.png +0 -0
- package/templates/default/public/next.svg +0 -1
- package/templates/default/public/vercel.svg +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-nextjs-cms",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.71",
|
|
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/prettier-config": "0.1.0",
|
|
33
|
+
"@lzcms/tsconfig": "0.1.0"
|
|
34
34
|
},
|
|
35
35
|
"prettier": "@lzcms/prettier-config",
|
|
36
36
|
"scripts": {
|
|
@@ -1,36 +1,36 @@
|
|
|
1
|
-
import { notFound } from 'next/navigation'
|
|
2
|
-
import auth from 'nextjs-cms/auth'
|
|
3
|
-
import { getAdminPrivileges } from 'nextjs-cms/api/helpers'
|
|
4
|
-
import { findPluginRouteByPath } from 'nextjs-cms/plugins/server'
|
|
5
|
-
import { getPluginServerComponent } from './plugin-server-registry'
|
|
6
|
-
|
|
7
|
-
export const dynamic = 'force-dynamic'
|
|
8
|
-
type Params = Promise<{ slug?: string[] }>
|
|
9
|
-
|
|
10
|
-
export default async function Page(props: { params: Params }) {
|
|
11
|
-
const params = await props.params
|
|
12
|
-
const slug = params.slug ?? []
|
|
13
|
-
|
|
14
|
-
const path = `/${slug.join('/')}`
|
|
15
|
-
const session = await auth()
|
|
16
|
-
if (!session?.user) {
|
|
17
|
-
notFound()
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const plugin = await findPluginRouteByPath(path)
|
|
21
|
-
if (!plugin) {
|
|
22
|
-
notFound()
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const privilegeSet = await getAdminPrivileges(session.user.id)
|
|
26
|
-
if (!privilegeSet.has(plugin.pluginName)) {
|
|
27
|
-
notFound()
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const PluginComponent = await getPluginServerComponent(plugin.
|
|
31
|
-
if (!PluginComponent) {
|
|
32
|
-
notFound()
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return <PluginComponent />
|
|
36
|
-
}
|
|
1
|
+
import { notFound } from 'next/navigation'
|
|
2
|
+
import auth from 'nextjs-cms/auth'
|
|
3
|
+
import { getAdminPrivileges } from 'nextjs-cms/api/helpers'
|
|
4
|
+
import { findPluginRouteByPath } from 'nextjs-cms/plugins/server'
|
|
5
|
+
import { getPluginServerComponent } from './plugin-server-registry'
|
|
6
|
+
|
|
7
|
+
export const dynamic = 'force-dynamic'
|
|
8
|
+
type Params = Promise<{ slug?: string[] }>
|
|
9
|
+
|
|
10
|
+
export default async function Page(props: { params: Params }) {
|
|
11
|
+
const params = await props.params
|
|
12
|
+
const slug = params.slug ?? []
|
|
13
|
+
|
|
14
|
+
const path = `/${slug.join('/')}`
|
|
15
|
+
const session = await auth()
|
|
16
|
+
if (!session?.user) {
|
|
17
|
+
notFound()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const plugin = await findPluginRouteByPath(path)
|
|
21
|
+
if (!plugin) {
|
|
22
|
+
notFound()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const privilegeSet = await getAdminPrivileges(session.user.id)
|
|
26
|
+
if (!privilegeSet.has(plugin.pluginName)) {
|
|
27
|
+
notFound()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const PluginComponent = await getPluginServerComponent(plugin.pluginRegistryName, plugin.component)
|
|
31
|
+
if (!PluginComponent) {
|
|
32
|
+
notFound()
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return <PluginComponent />
|
|
36
|
+
}
|
|
@@ -1,25 +1,22 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export const getPluginServerComponent = async (
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
return resolved
|
|
25
|
-
}
|
|
1
|
+
import dynamic from 'next/dynamic'
|
|
2
|
+
|
|
3
|
+
export const pluginNamesMap: Record<string, string> = {
|
|
4
|
+
'cpanel-dashboard': '@nextjs-cms-plugins/cpanel-dashboard/server',
|
|
5
|
+
'cpanel-emails': '@nextjs-cms-plugins/cpanel-emails/server',
|
|
6
|
+
'google-analytics': '@nextjs-cms-plugins/google-analytics/server',
|
|
7
|
+
// A workaround to avoid importing error if no plugins are installed
|
|
8
|
+
blank: 'nextjs-cms/plugins/blank-component',
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const getPluginServerComponent = async (registryName: string, componentName: string | undefined) => {
|
|
12
|
+
const pluginName = pluginNamesMap[registryName]
|
|
13
|
+
const ClientComponent = dynamic(
|
|
14
|
+
() =>
|
|
15
|
+
import(pluginName ?? 'nextjs-cms/plugins/blank-component').then((mod) =>
|
|
16
|
+
componentName ? mod[componentName] : mod.default,
|
|
17
|
+
),
|
|
18
|
+
{ ssr: true },
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
return ClientComponent
|
|
22
|
+
}
|
|
@@ -7,6 +7,7 @@ export default async function Page(props: { params: Params; searchParams: Search
|
|
|
7
7
|
const searchParams = await props.searchParams
|
|
8
8
|
const { q } = searchParams
|
|
9
9
|
const { section, page } = params
|
|
10
|
+
|
|
10
11
|
await api.hasItemsSections.listItems.prefetch({
|
|
11
12
|
sectionName: section,
|
|
12
13
|
page: parseInt(page) ?? 1,
|
|
@@ -1,44 +1,44 @@
|
|
|
1
|
-
export const dynamic = 'force-dynamic'
|
|
2
|
-
|
|
3
|
-
export default function DashboardPage() {
|
|
4
|
-
return (
|
|
5
|
-
<div className='w-full'>
|
|
6
|
-
<div className='bg-linear-to-r from-amber-200 via-orange-200 to-rose-200 p-8 text-foreground dark:from-amber-900 dark:via-orange-900 dark:to-rose-900'>
|
|
7
|
-
<h1 className='text-3xl font-extrabold tracking-tight'>Welcome to Mission Control</h1>
|
|
8
|
-
<p className='mt-2 text-base text-muted-foreground'>
|
|
9
|
-
Your CMS is ready. Chart a course, publish boldly, and keep the lights green.
|
|
10
|
-
</p>
|
|
11
|
-
</div>
|
|
12
|
-
|
|
13
|
-
<div className='space-y-6 p-6'>
|
|
14
|
-
<section className='rounded-lg border bg-card p-6 text-card-foreground shadow-sm'>
|
|
15
|
-
<h2 className='text-xl font-semibold'>Today's focus</h2>
|
|
16
|
-
<p className='mt-2 text-sm text-muted-foreground'>
|
|
17
|
-
Start with one small win: verify a section, ship a quick update, or polish a headline.
|
|
18
|
-
</p>
|
|
19
|
-
</section>
|
|
20
|
-
|
|
21
|
-
<section className='grid gap-4 md:grid-cols-3'>
|
|
22
|
-
<div className='rounded-lg border bg-card p-4 text-card-foreground shadow-sm'>
|
|
23
|
-
<div className='text-sm font-semibold'>Ship with confidence</div>
|
|
24
|
-
<div className='mt-2 text-sm text-muted-foreground'>
|
|
25
|
-
Keep changes tight and reversible. Small releases, fast feedback.
|
|
26
|
-
</div>
|
|
27
|
-
</div>
|
|
28
|
-
<div className='rounded-lg border bg-card p-4 text-card-foreground shadow-sm'>
|
|
29
|
-
<div className='text-sm font-semibold'>Stay organized</div>
|
|
30
|
-
<div className='mt-2 text-sm text-muted-foreground'>
|
|
31
|
-
Group your content by intent, not just by type.
|
|
32
|
-
</div>
|
|
33
|
-
</div>
|
|
34
|
-
<div className='rounded-lg border bg-card p-4 text-card-foreground shadow-sm'>
|
|
35
|
-
<div className='text-sm font-semibold'>Delight users</div>
|
|
36
|
-
<div className='mt-2 text-sm text-muted-foreground'>
|
|
37
|
-
A clean headline and a single strong image can do the heavy lifting.
|
|
38
|
-
</div>
|
|
39
|
-
</div>
|
|
40
|
-
</section>
|
|
41
|
-
</div>
|
|
42
|
-
</div>
|
|
43
|
-
)
|
|
44
|
-
}
|
|
1
|
+
export const dynamic = 'force-dynamic'
|
|
2
|
+
|
|
3
|
+
export default function DashboardPage() {
|
|
4
|
+
return (
|
|
5
|
+
<div className='w-full'>
|
|
6
|
+
<div className='bg-linear-to-r from-amber-200 via-orange-200 to-rose-200 p-8 text-foreground dark:from-amber-900 dark:via-orange-900 dark:to-rose-900'>
|
|
7
|
+
<h1 className='text-3xl font-extrabold tracking-tight'>Welcome to Mission Control</h1>
|
|
8
|
+
<p className='mt-2 text-base text-muted-foreground'>
|
|
9
|
+
Your CMS is ready. Chart a course, publish boldly, and keep the lights green.
|
|
10
|
+
</p>
|
|
11
|
+
</div>
|
|
12
|
+
|
|
13
|
+
<div className='space-y-6 p-6'>
|
|
14
|
+
<section className='rounded-lg border bg-card p-6 text-card-foreground shadow-sm'>
|
|
15
|
+
<h2 className='text-xl font-semibold'>Today's focus</h2>
|
|
16
|
+
<p className='mt-2 text-sm text-muted-foreground'>
|
|
17
|
+
Start with one small win: verify a section, ship a quick update, or polish a headline.
|
|
18
|
+
</p>
|
|
19
|
+
</section>
|
|
20
|
+
|
|
21
|
+
<section className='grid gap-4 md:grid-cols-3'>
|
|
22
|
+
<div className='rounded-lg border bg-card p-4 text-card-foreground shadow-sm'>
|
|
23
|
+
<div className='text-sm font-semibold'>Ship with confidence</div>
|
|
24
|
+
<div className='mt-2 text-sm text-muted-foreground'>
|
|
25
|
+
Keep changes tight and reversible. Small releases, fast feedback.
|
|
26
|
+
</div>
|
|
27
|
+
</div>
|
|
28
|
+
<div className='rounded-lg border bg-card p-4 text-card-foreground shadow-sm'>
|
|
29
|
+
<div className='text-sm font-semibold'>Stay organized</div>
|
|
30
|
+
<div className='mt-2 text-sm text-muted-foreground'>
|
|
31
|
+
Group your content by intent, not just by type.
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
<div className='rounded-lg border bg-card p-4 text-card-foreground shadow-sm'>
|
|
35
|
+
<div className='text-sm font-semibold'>Delight users</div>
|
|
36
|
+
<div className='mt-2 text-sm text-muted-foreground'>
|
|
37
|
+
A clean headline and a single strong image can do the heavy lifting.
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</section>
|
|
41
|
+
</div>
|
|
42
|
+
</div>
|
|
43
|
+
)
|
|
44
|
+
}
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import Layout from '@/components/Layout'
|
|
2
2
|
import { api, HydrateClient } from 'nextjs-cms/api/trpc/server'
|
|
3
|
+
import { getCMSConfig } from 'nextjs-cms/core'
|
|
3
4
|
|
|
4
5
|
export default async function CMSLayout({ children }: { children: React.ReactNode }) {
|
|
5
6
|
await api.navigation.getSidebar.prefetch()
|
|
7
|
+
const cmsConfig = await getCMSConfig()
|
|
6
8
|
|
|
7
9
|
return (
|
|
8
10
|
<HydrateClient>
|
|
9
|
-
<Layout>{children}</Layout>
|
|
11
|
+
<Layout logoUrlPath={cmsConfig.ui.logo}>{children}</Layout>
|
|
10
12
|
</HydrateClient>
|
|
11
13
|
)
|
|
12
14
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
export { trpc } from 'nextjs-cms/api/trpc/client'
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
export { trpc } from 'nextjs-cms/api/trpc/client'
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { fetchRequestHandler } from '@trpc/server/adapters/fetch'
|
|
2
|
-
import { createTRPCContext, getAppRouter } from 'nextjs-cms/api'
|
|
2
|
+
import { createTRPCContext, getAppRouter } from 'nextjs-cms/api'
|
|
3
3
|
import { NextRequest } from 'next/server'
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -13,11 +13,11 @@ const context = async (req: NextRequest) => {
|
|
|
13
13
|
})
|
|
14
14
|
}
|
|
15
15
|
|
|
16
|
-
const handler = async (req: NextRequest) =>
|
|
16
|
+
const handler = async (req: NextRequest) =>
|
|
17
17
|
fetchRequestHandler({
|
|
18
18
|
endpoint: '/api/trpc',
|
|
19
19
|
req,
|
|
20
|
-
router: await getAppRouter(),
|
|
20
|
+
router: await getAppRouter(),
|
|
21
21
|
createContext: () => context(req),
|
|
22
22
|
/*onError(opts) {
|
|
23
23
|
return opts.error
|
|
@@ -5,23 +5,29 @@ import { cn } from '@/lib/utils'
|
|
|
5
5
|
import { ThemeProvider } from '@/components/ThemeProvider'
|
|
6
6
|
import Providers from '@/app/providers'
|
|
7
7
|
import auth from 'nextjs-cms/auth'
|
|
8
|
-
|
|
9
|
-
export const metadata: Metadata = {
|
|
10
|
-
title: 'nextjs-cms',
|
|
11
|
-
description: 'nextjs-cms',
|
|
12
|
-
}
|
|
8
|
+
import { getCMSConfig } from 'nextjs-cms/core'
|
|
13
9
|
|
|
14
10
|
const inter = Inter({
|
|
15
11
|
subsets: ['latin'],
|
|
16
12
|
variable: '--font-sans',
|
|
17
13
|
})
|
|
18
14
|
|
|
15
|
+
|
|
16
|
+
export async function generateMetadata(): Promise<Metadata> {
|
|
17
|
+
const cmsConfig = await getCMSConfig()
|
|
18
|
+
return {
|
|
19
|
+
title: cmsConfig.ui.title,
|
|
20
|
+
description: 'nextjs-cms',
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
19
24
|
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
|
20
25
|
const session = await auth()
|
|
26
|
+
const cmsConfig = await getCMSConfig()
|
|
21
27
|
return (
|
|
22
28
|
<html lang='en' suppressHydrationWarning>
|
|
23
29
|
<body className={cn('bg-background min-h-screen font-sans antialiased', inter.variable)}>
|
|
24
|
-
<ThemeProvider attribute='class' defaultTheme=
|
|
30
|
+
<ThemeProvider attribute='class' defaultTheme={cmsConfig.ui.defaultTheme} enableSystem disableTransitionOnChange>
|
|
25
31
|
<Providers session={session ?? undefined}>{children}</Providers>
|
|
26
32
|
</ThemeProvider>
|
|
27
33
|
</body>
|
|
@@ -4,12 +4,16 @@ import AdminCard from '@/components/AdminCard'
|
|
|
4
4
|
import getString from 'nextjs-cms/translations'
|
|
5
5
|
import NewAdminForm from '@/components/NewAdminForm'
|
|
6
6
|
import ContainerBox from '@/components/ContainerBox'
|
|
7
|
-
import LoadingSpinners from '@/components/LoadingSpinners'
|
|
8
7
|
import { trpc } from '@/app/_trpc/client'
|
|
8
|
+
import ErrorComponent from './ErrorComponent'
|
|
9
9
|
|
|
10
10
|
const AdminsPage = () => {
|
|
11
|
-
const
|
|
11
|
+
const [data, {refetch}] = trpc.admins.list.useSuspenseQuery()
|
|
12
12
|
|
|
13
|
+
if (data.error) {
|
|
14
|
+
return <ErrorComponent message={data.error.message} />
|
|
15
|
+
}
|
|
16
|
+
|
|
13
17
|
return (
|
|
14
18
|
<div className='w-full'>
|
|
15
19
|
<div className='bg-linear-to-r from-sky-200 via-emerald-300 to-blue-600 p-8 font-extrabold text-foreground dark:from-blue-800 dark:via-amber-700 dark:to-rose-900'>
|
|
@@ -21,11 +25,6 @@ const AdminsPage = () => {
|
|
|
21
25
|
{data && data.privileges && <NewAdminForm action={() => refetch()} privileges={data.privileges} />}
|
|
22
26
|
</ContainerBox>
|
|
23
27
|
|
|
24
|
-
{isLoading && (
|
|
25
|
-
<div>
|
|
26
|
-
<LoadingSpinners />
|
|
27
|
-
</div>
|
|
28
|
-
)}
|
|
29
28
|
{data && data.admins && data.admins.length > 0 && (
|
|
30
29
|
<ContainerBox title={getString('admins_list')}>
|
|
31
30
|
<div className='mt-2 grid w-full grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3'>
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import React from 'react'
|
|
2
1
|
import ContainerBox from '@/components/ContainerBox'
|
|
3
|
-
import { BarChart, Bar,
|
|
2
|
+
import { BarChart, Bar, XAxis, YAxis, ResponsiveContainer } from 'recharts'
|
|
4
3
|
import { BarChartDataItem } from 'nextjs-cms/core/types'
|
|
5
4
|
import LoadingSpinners from '@/components/LoadingSpinners'
|
|
6
5
|
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import
|
|
3
|
+
import { useRef } from 'react'
|
|
4
4
|
import getString from 'nextjs-cms/translations'
|
|
5
5
|
import SectionItemCard from '@/components/SectionItemCard'
|
|
6
|
-
import LoadingSpinners from '@/components/LoadingSpinners'
|
|
7
6
|
import ContainerBox from '@/components/ContainerBox'
|
|
8
7
|
import Pagination from '@/components/pagination/Pagination'
|
|
9
8
|
import { Alert, AlertDescription } from '@/components/ui/alert'
|
|
@@ -12,8 +11,8 @@ import { Input } from './ui/input'
|
|
|
12
11
|
import { Button } from './ui/button'
|
|
13
12
|
import { useRouter, useSearchParams } from 'next/navigation'
|
|
14
13
|
import { trpc } from '@/app/_trpc/client'
|
|
15
|
-
import { ExclamationTriangleIcon } from '@radix-ui/react-icons'
|
|
16
14
|
import { capitalizeWords } from 'nextjs-cms/utils'
|
|
15
|
+
import ErrorComponent from '@/components/ErrorComponent'
|
|
17
16
|
|
|
18
17
|
export default function BrowsePage({ section, page }: { section: string; page: string }) {
|
|
19
18
|
const pageInt = parseInt(page) ?? 1
|
|
@@ -21,13 +20,15 @@ export default function BrowsePage({ section, page }: { section: string; page: s
|
|
|
21
20
|
const router = useRouter()
|
|
22
21
|
const q = params.get('q') ?? ''
|
|
23
22
|
const searchInputRef = useRef<HTMLInputElement>(null)
|
|
24
|
-
const
|
|
23
|
+
const [data, {refetch}] = trpc.hasItemsSections.listItems.useSuspenseQuery({
|
|
25
24
|
sectionName: section,
|
|
26
25
|
page: pageInt,
|
|
27
26
|
q,
|
|
28
27
|
})
|
|
29
28
|
|
|
30
|
-
|
|
29
|
+
if (data.error) {
|
|
30
|
+
return <ErrorComponent message={data.error.message} />
|
|
31
|
+
}
|
|
31
32
|
|
|
32
33
|
return (
|
|
33
34
|
<div>
|
|
@@ -67,23 +68,9 @@ export default function BrowsePage({ section, page }: { section: string; page: s
|
|
|
67
68
|
</Card>
|
|
68
69
|
) : null}
|
|
69
70
|
<ContainerBox title={getString('browse')}>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
</div>
|
|
74
|
-
) : null}
|
|
75
|
-
|
|
76
|
-
{data ? (
|
|
77
|
-
<>
|
|
78
|
-
{data.error ? (
|
|
79
|
-
<Alert variant='destructive' className='my-8'>
|
|
80
|
-
<AlertDescription className='text-md font-bold'>
|
|
81
|
-
<div className='flex items-center gap-1.5'>
|
|
82
|
-
<ExclamationTriangleIcon className='h-5 w-5' /> {data.error.message}
|
|
83
|
-
</div>
|
|
84
|
-
</AlertDescription>
|
|
85
|
-
</Alert>
|
|
86
|
-
) : data.items && data.items.length > 0 ? (
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
{ data.items && data.items.length > 0 ? (
|
|
87
74
|
<div className='mt-2 grid w-full grid-cols-1 gap-8 md:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4'>
|
|
88
75
|
{data.items.map((item, index: number) => (
|
|
89
76
|
<SectionItemCard
|
|
@@ -99,8 +86,7 @@ export default function BrowsePage({ section, page }: { section: string; page: s
|
|
|
99
86
|
<AlertDescription className='font-bold'>{getString('noItems')}</AlertDescription>
|
|
100
87
|
</Alert>
|
|
101
88
|
)}
|
|
102
|
-
|
|
103
|
-
) : null}
|
|
89
|
+
|
|
104
90
|
</ContainerBox>
|
|
105
91
|
{data?.totalCount && data?.totalCount > 0 ? (
|
|
106
92
|
<Pagination
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import React, { useEffect } from 'react'
|
|
4
|
-
import LoadingSpinners from '@/components/LoadingSpinners'
|
|
5
3
|
import { trpc } from '@/app/_trpc/client'
|
|
6
4
|
import CategorySectionSelectInput from '@/components/CategorySectionSelectInput'
|
|
5
|
+
import ErrorComponent from '@/components/ErrorComponent'
|
|
7
6
|
|
|
8
7
|
export default function CategorizedSectionPage({ section }: { section: string }) {
|
|
9
|
-
const
|
|
8
|
+
const [data, {refetch}] = trpc.categorySections.get.useSuspenseQuery({
|
|
10
9
|
sectionName: section,
|
|
11
10
|
})
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
if (data.error) {
|
|
13
|
+
return <ErrorComponent message={data.error.message} />
|
|
14
|
+
}
|
|
14
15
|
|
|
15
16
|
return (
|
|
16
17
|
<div>
|
|
@@ -21,12 +22,6 @@ export default function CategorizedSectionPage({ section }: { section: string })
|
|
|
21
22
|
</div>
|
|
22
23
|
<div className='flex w-full flex-col'>
|
|
23
24
|
<div className='rounded-xl p-4 shadow-sm'>
|
|
24
|
-
{isLoading && (
|
|
25
|
-
<div>
|
|
26
|
-
<LoadingSpinners />
|
|
27
|
-
</div>
|
|
28
|
-
)}
|
|
29
|
-
|
|
30
25
|
{data && data.data ? <CategorySectionSelectInput refetch={refetch} input={data.data} /> : null}
|
|
31
26
|
</div>
|
|
32
27
|
</div>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Alert, AlertDescription } from './ui/alert'
|
|
2
|
+
import { ExclamationTriangleIcon } from '@radix-ui/react-icons'
|
|
3
|
+
|
|
4
|
+
export default function ErrorComponent({ message }: { message: string }) {
|
|
5
|
+
return (
|
|
6
|
+
<div className='px-4'>
|
|
7
|
+
<Alert variant='info' className='my-8'>
|
|
8
|
+
<AlertDescription className='text-md font-bold'>
|
|
9
|
+
<div className='flex items-center gap-1.5'>
|
|
10
|
+
<ExclamationTriangleIcon className='size-5' /> {message}
|
|
11
|
+
</div>
|
|
12
|
+
</AlertDescription>
|
|
13
|
+
</Alert>
|
|
14
|
+
</div>
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
}
|
|
@@ -2,13 +2,12 @@ import { PhotoGalleryItem } from 'nextjs-cms/core/types'
|
|
|
2
2
|
import getString from 'nextjs-cms/translations'
|
|
3
3
|
import { MinusIcon } from '@radix-ui/react-icons'
|
|
4
4
|
import ProtectedImage from '@/components/ProtectedImage'
|
|
5
|
-
import React from 'react'
|
|
6
5
|
import useModal from '@/hooks/useModal'
|
|
7
6
|
import { useToast } from '@/components/ui/use-toast'
|
|
8
7
|
import { trpc } from '@/app/_trpc/client'
|
|
9
8
|
|
|
10
9
|
const GalleryPhoto = ({ item, sectionName, action }: { item: PhotoGalleryItem; sectionName: string; action?: any }) => {
|
|
11
|
-
const { setModal,
|
|
10
|
+
const { setModal, setModalResponse } = useModal()
|
|
12
11
|
const deleteMutation = trpc.gallery.deletePhoto.useMutation({
|
|
13
12
|
onError: (error) => {
|
|
14
13
|
toast({
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
3
|
import { useAxiosPrivate } from 'nextjs-cms/auth/hooks'
|
|
4
|
-
import
|
|
4
|
+
import { RefObject, useEffect, useRef, useState } from 'react'
|
|
5
5
|
import { SectionType } from 'nextjs-cms/core/types'
|
|
6
6
|
import getString from 'nextjs-cms/translations'
|
|
7
7
|
import useModal from '@/hooks/useModal'
|
|
8
8
|
import { AxiosError } from 'axios'
|
|
9
9
|
import InfoCard from '@/components/InfoCard'
|
|
10
10
|
import { useRouter } from 'next/navigation'
|
|
11
|
-
import LoadingSpinners from '@/components/LoadingSpinners'
|
|
12
11
|
import { useToast } from '@/components/ui/use-toast'
|
|
13
12
|
import { DropzoneHandles } from '@/components/Dropzone'
|
|
14
13
|
import { VariantHandles } from '@/components/NewVariantComponent'
|
|
15
14
|
import { trpc } from '@/app/_trpc/client'
|
|
16
15
|
import Form from '@/components/form/Form'
|
|
16
|
+
import ErrorComponent from '@/components/ErrorComponent'
|
|
17
17
|
|
|
18
18
|
export default function ItemEditPage({
|
|
19
19
|
section,
|
|
@@ -42,7 +42,7 @@ export default function ItemEditPage({
|
|
|
42
42
|
const router = useRouter()
|
|
43
43
|
const dropzoneRef: RefObject<DropzoneHandles | null> = useRef(null)
|
|
44
44
|
const variantRef: RefObject<VariantHandles[]> = useRef([])
|
|
45
|
-
const
|
|
45
|
+
const [data, {refetch}] = trpc.hasItemsSections.editItem.useSuspenseQuery({
|
|
46
46
|
sectionName: section,
|
|
47
47
|
sectionItemId: itemId,
|
|
48
48
|
})
|
|
@@ -182,36 +182,32 @@ export default function ItemEditPage({
|
|
|
182
182
|
}
|
|
183
183
|
}, [])
|
|
184
184
|
|
|
185
|
+
if (data.error) {
|
|
186
|
+
return <ErrorComponent message={data.error.message} />
|
|
187
|
+
}
|
|
188
|
+
|
|
185
189
|
return (
|
|
186
190
|
<div className='flex w-full flex-col overflow-hidden'>
|
|
187
|
-
|
|
188
|
-
<div>
|
|
189
|
-
<
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
<div className='flex w-full flex-col'>
|
|
195
|
-
<div className='relative z-1 border-b-2 p-8 pt-12 font-extrabold text-foreground'>
|
|
196
|
-
<div className='absolute left-0 top-0 z-2 h-4 w-full bg-linear-to-r from-emerald-800 via-emerald-400 to-sky-600'></div>
|
|
197
|
-
<h1 className='pb-4 text-4xl'>{data.section?.title.section}</h1>
|
|
198
|
-
<span>
|
|
199
|
-
/{getString('edit')} {data.section?.title.singular}
|
|
200
|
-
</span>
|
|
201
|
-
</div>
|
|
202
|
-
<Form
|
|
203
|
-
formType='edit'
|
|
204
|
-
progressVariant={progressVariant}
|
|
205
|
-
response={response}
|
|
206
|
-
progress={progress}
|
|
207
|
-
data={data}
|
|
208
|
-
dropzoneRef={dropzoneRef}
|
|
209
|
-
variantRef={variantRef}
|
|
210
|
-
handleSubmit={handleSubmit}
|
|
211
|
-
isSubmitting={isSubmitting}
|
|
212
|
-
/>
|
|
191
|
+
<div className='flex w-full flex-col'>
|
|
192
|
+
<div className='relative z-1 border-b-2 p-8 pt-12 font-extrabold text-foreground'>
|
|
193
|
+
<div className='absolute left-0 top-0 z-2 h-4 w-full bg-linear-to-r from-emerald-800 via-emerald-400 to-sky-600'></div>
|
|
194
|
+
<h1 className='pb-4 text-4xl'>{data.section?.title.section}</h1>
|
|
195
|
+
<span>
|
|
196
|
+
/{getString('edit')} {data.section?.title.singular}
|
|
197
|
+
</span>
|
|
213
198
|
</div>
|
|
214
|
-
|
|
199
|
+
<Form
|
|
200
|
+
formType='edit'
|
|
201
|
+
progressVariant={progressVariant}
|
|
202
|
+
response={response}
|
|
203
|
+
progress={progress}
|
|
204
|
+
data={data}
|
|
205
|
+
dropzoneRef={dropzoneRef}
|
|
206
|
+
variantRef={variantRef}
|
|
207
|
+
handleSubmit={handleSubmit}
|
|
208
|
+
isSubmitting={isSubmitting}
|
|
209
|
+
/>
|
|
210
|
+
</div>
|
|
215
211
|
</div>
|
|
216
212
|
)
|
|
217
213
|
}
|