@digitalygo/create-diggocms-app 0.1.1 → 0.1.3
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/README.md +96 -23
- package/bin/cli.js +52 -94
- package/package.json +1 -1
- package/templates/full/.env.local.example +3 -2
- package/templates/full/README.md +64 -22
- package/templates/full/components/PageLayout.tsx +40 -0
- package/templates/full/lib/data-fetching.ts +55 -10
- package/templates/full/lib/diggo-config.ts +4 -2
- package/templates/full/package.json +3 -1
- package/templates/full/pages/[...slug].tsx +93 -0
- package/templates/full/pages/_app.tsx +11 -0
- package/templates/full/pages/index.tsx +14 -0
- package/templates/full/postcss.config.js +6 -0
- package/templates/full/styles/globals.css +113 -0
- package/templates/full/tailwind.config.ts +14 -0
- package/templates/full/tsconfig.json +2 -1
- package/templates/minimal/.env.local.example +3 -2
- package/templates/minimal/README.md +44 -10
- package/templates/minimal/lib/data-fetching.ts +46 -12
- package/templates/minimal/lib/diggo-config.ts +2 -5
- package/templates/minimal/package.json +3 -1
- package/templates/minimal/pages/[...slug].tsx +73 -0
- package/templates/minimal/pages/_app.tsx +11 -0
- package/templates/minimal/pages/index.tsx +14 -0
- package/templates/minimal/postcss.config.js +6 -0
- package/templates/minimal/{app → styles}/globals.css +4 -0
- package/templates/minimal/tailwind.config.ts +14 -0
- package/templates/minimal/tsconfig.json +2 -1
- package/templates/with-mock/.env.local.example +3 -2
- package/templates/with-mock/README.md +61 -33
- package/templates/with-mock/components/PageLayout.tsx +40 -0
- package/templates/with-mock/lib/data-fetching.ts +56 -11
- package/templates/with-mock/lib/diggo-config.ts +4 -2
- package/templates/with-mock/package.json +7 -3
- package/templates/with-mock/pages/[...slug].tsx +117 -0
- package/templates/with-mock/pages/_app.tsx +11 -0
- package/templates/with-mock/pages/index.tsx +14 -0
- package/templates/with-mock/postcss.config.js +6 -0
- package/templates/with-mock/scripts/mock-server.ts +0 -6
- package/templates/with-mock/styles/globals.css +118 -0
- package/templates/with-mock/tailwind.config.ts +14 -0
- package/templates/with-mock/tsconfig.json +2 -1
- package/templates/full/app/[...slug]/page.tsx +0 -115
- package/templates/full/app/globals.css +0 -238
- package/templates/full/app/layout.tsx +0 -25
- package/templates/full/app/page.tsx +0 -6
- package/templates/full/tsconfig.tsbuildinfo +0 -1
- package/templates/minimal/app/[...slug]/page.tsx +0 -56
- package/templates/minimal/app/layout.tsx +0 -22
- package/templates/minimal/app/page.tsx +0 -6
- package/templates/with-mock/app/[...slug]/page.tsx +0 -115
- package/templates/with-mock/app/globals.css +0 -238
- package/templates/with-mock/app/layout.tsx +0 -25
- package/templates/with-mock/app/page.tsx +0 -6
- package/templates/with-mock/tsconfig.tsbuildinfo +0 -1
|
@@ -1,31 +1,59 @@
|
|
|
1
1
|
import type { PagePayload } from '@digitalygo/diggocms-sdk-core';
|
|
2
2
|
import { sdkConfig, isMockMode, hasBaseUrl } from './diggo-config';
|
|
3
3
|
|
|
4
|
+
const FETCH_TIMEOUT_MS = 5000;
|
|
5
|
+
|
|
4
6
|
export interface FetchPageResult {
|
|
5
7
|
page: PagePayload | null;
|
|
6
8
|
error: string | null;
|
|
7
9
|
}
|
|
8
10
|
|
|
11
|
+
async function fetchWithTimeout(
|
|
12
|
+
url: string,
|
|
13
|
+
timeoutMs: number
|
|
14
|
+
): Promise<Response> {
|
|
15
|
+
const controller = new AbortController();
|
|
16
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
17
|
+
|
|
18
|
+
try {
|
|
19
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
20
|
+
clearTimeout(timeoutId);
|
|
21
|
+
return response;
|
|
22
|
+
} catch (error) {
|
|
23
|
+
clearTimeout(timeoutId);
|
|
24
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
25
|
+
throw new Error(`Request timeout after ${timeoutMs}ms`);
|
|
26
|
+
}
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
9
31
|
export async function fetchPageData(slug: string): Promise<FetchPageResult> {
|
|
10
32
|
if (isMockMode()) {
|
|
11
|
-
// Mock mode: fetch from local mock API
|
|
12
33
|
try {
|
|
13
34
|
const mockApiUrl = process.env.MOCK_API_URL ?? 'http://localhost:3001';
|
|
14
|
-
const response = await
|
|
15
|
-
|
|
35
|
+
const response = await fetchWithTimeout(
|
|
36
|
+
`${mockApiUrl}/pages/${slug}`,
|
|
37
|
+
FETCH_TIMEOUT_MS
|
|
38
|
+
);
|
|
39
|
+
|
|
16
40
|
if (!response.ok) {
|
|
17
41
|
return {
|
|
18
42
|
page: null,
|
|
19
43
|
error: `Mock API returned ${response.status}`,
|
|
20
44
|
};
|
|
21
45
|
}
|
|
22
|
-
|
|
46
|
+
|
|
23
47
|
const page = (await response.json()) as PagePayload;
|
|
24
48
|
return { page, error: null };
|
|
25
|
-
} catch {
|
|
49
|
+
} catch (error) {
|
|
50
|
+
const errorMessage =
|
|
51
|
+
error instanceof Error ? error.message : 'Unknown error';
|
|
26
52
|
return {
|
|
27
53
|
page: null,
|
|
28
|
-
error:
|
|
54
|
+
error: errorMessage.includes('timeout')
|
|
55
|
+
? 'Mock API request timed out. Is the server running?'
|
|
56
|
+
: 'Failed to connect to mock API. Is it running?',
|
|
29
57
|
};
|
|
30
58
|
}
|
|
31
59
|
}
|
|
@@ -37,23 +65,29 @@ export async function fetchPageData(slug: string): Promise<FetchPageResult> {
|
|
|
37
65
|
};
|
|
38
66
|
}
|
|
39
67
|
|
|
40
|
-
// Real API mode: fetch from CMS
|
|
41
68
|
try {
|
|
42
|
-
const response = await
|
|
43
|
-
|
|
69
|
+
const response = await fetchWithTimeout(
|
|
70
|
+
`${sdkConfig.baseUrl}/pages/${slug}`,
|
|
71
|
+
FETCH_TIMEOUT_MS
|
|
72
|
+
);
|
|
73
|
+
|
|
44
74
|
if (!response.ok) {
|
|
45
75
|
return {
|
|
46
76
|
page: null,
|
|
47
77
|
error: `CMS returned ${response.status}`,
|
|
48
78
|
};
|
|
49
79
|
}
|
|
50
|
-
|
|
80
|
+
|
|
51
81
|
const page = (await response.json()) as PagePayload;
|
|
52
82
|
return { page, error: null };
|
|
53
|
-
} catch {
|
|
83
|
+
} catch (error) {
|
|
84
|
+
const errorMessage =
|
|
85
|
+
error instanceof Error ? error.message : 'Unknown error';
|
|
54
86
|
return {
|
|
55
87
|
page: null,
|
|
56
|
-
error: '
|
|
88
|
+
error: errorMessage.includes('timeout')
|
|
89
|
+
? 'Request to CMS timed out. Please check your BASE_URL and network connection.'
|
|
90
|
+
: 'Error connecting to CMS',
|
|
57
91
|
};
|
|
58
92
|
}
|
|
59
93
|
}
|
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
import type { ComponentRegistry, NavigationRegistry, DiggoConfig } from '@digitalygo/diggocms-sdk-core';
|
|
2
2
|
|
|
3
|
-
// Add your custom components here
|
|
4
3
|
export const componentsRegistry: ComponentRegistry = {
|
|
5
|
-
// Example: title: ExtendedTitle,
|
|
6
4
|
};
|
|
7
5
|
|
|
8
6
|
export const navigationRegistry: NavigationRegistry = {
|
|
9
|
-
// Example: MenuItem: ExtendedMenuItem,
|
|
10
7
|
};
|
|
11
8
|
|
|
12
9
|
const isMock = process.env.MOCK === '1';
|
|
@@ -17,7 +14,7 @@ function getBaseUrl(): string {
|
|
|
17
14
|
if (isMock) {
|
|
18
15
|
return mockApiUrl ?? 'http://localhost:3001';
|
|
19
16
|
}
|
|
20
|
-
return baseUrl ?? '
|
|
17
|
+
return baseUrl ?? '';
|
|
21
18
|
}
|
|
22
19
|
|
|
23
20
|
export const sdkConfig: DiggoConfig = {
|
|
@@ -31,5 +28,5 @@ export function isMockMode(): boolean {
|
|
|
31
28
|
}
|
|
32
29
|
|
|
33
30
|
export function hasBaseUrl(): boolean {
|
|
34
|
-
return !!baseUrl;
|
|
31
|
+
return !!baseUrl && baseUrl.length > 0;
|
|
35
32
|
}
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
"name": "my-diggocms-app",
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"private": true,
|
|
5
|
-
"type": "module",
|
|
6
5
|
"scripts": {
|
|
7
6
|
"dev": "next dev",
|
|
8
7
|
"build": "next build",
|
|
@@ -20,6 +19,9 @@
|
|
|
20
19
|
"@types/node": "^20.0.0",
|
|
21
20
|
"@types/react": "^18.3.0",
|
|
22
21
|
"@types/react-dom": "^18.3.0",
|
|
22
|
+
"autoprefixer": "^10.4.19",
|
|
23
|
+
"postcss": "^8.4.38",
|
|
24
|
+
"tailwindcss": "^3.4.4",
|
|
23
25
|
"typescript": "^5.5.0"
|
|
24
26
|
}
|
|
25
27
|
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { GetStaticPaths, GetStaticProps } from 'next';
|
|
2
|
+
import type { PagePayload } from '@digitalygo/diggocms-sdk-core';
|
|
3
|
+
import { useRouter } from 'next/router';
|
|
4
|
+
import Head from 'next/head';
|
|
5
|
+
import { renderPage } from '@digitalygo/diggocms-sdk-core/server';
|
|
6
|
+
import { componentsRegistry } from '@/lib/diggo-config';
|
|
7
|
+
import { fetchPageData, FetchPageResult } from '@/lib/data-fetching';
|
|
8
|
+
|
|
9
|
+
interface CatchAllPageProps {
|
|
10
|
+
page: PagePayload | null;
|
|
11
|
+
error: string | null;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export default function CatchAllPage({ page, error }: CatchAllPageProps) {
|
|
15
|
+
const router = useRouter();
|
|
16
|
+
|
|
17
|
+
if (router.isFallback) {
|
|
18
|
+
return <div className="p-8 text-center">Loading...</div>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (!page) {
|
|
22
|
+
return (
|
|
23
|
+
<main className="max-w-5xl mx-auto p-8">
|
|
24
|
+
<h1 className="text-2xl font-bold mb-4">Page Not Found</h1>
|
|
25
|
+
{error && <p className="text-red-600">{error}</p>}
|
|
26
|
+
</main>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<>
|
|
32
|
+
<Head>
|
|
33
|
+
<title>{page.title || 'My DiggoCMS App'}</title>
|
|
34
|
+
</Head>
|
|
35
|
+
<main className="max-w-5xl mx-auto p-8">
|
|
36
|
+
{error && (
|
|
37
|
+
<div className="bg-yellow-50 border border-yellow-200 text-yellow-800 p-4 mb-6 rounded">
|
|
38
|
+
<strong>Warning:</strong> {error}
|
|
39
|
+
</div>
|
|
40
|
+
)}
|
|
41
|
+
{renderPage(page, componentsRegistry)}
|
|
42
|
+
</main>
|
|
43
|
+
</>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const getStaticPaths: GetStaticPaths = async () => {
|
|
48
|
+
return {
|
|
49
|
+
paths: [],
|
|
50
|
+
fallback: 'blocking',
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const getStaticProps: GetStaticProps<CatchAllPageProps> = async ({ params }) => {
|
|
55
|
+
const slugArray = params?.slug as string[] | undefined;
|
|
56
|
+
const slug = Array.isArray(slugArray) ? slugArray.join('/') : 'home';
|
|
57
|
+
|
|
58
|
+
const { page, error }: FetchPageResult = await fetchPageData(slug);
|
|
59
|
+
|
|
60
|
+
if (!page) {
|
|
61
|
+
return {
|
|
62
|
+
notFound: true,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return {
|
|
67
|
+
props: {
|
|
68
|
+
page,
|
|
69
|
+
error,
|
|
70
|
+
},
|
|
71
|
+
revalidate: 60,
|
|
72
|
+
};
|
|
73
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AppProps } from 'next/app';
|
|
2
|
+
import { DiggoProvider } from '@/components/DiggoProvider';
|
|
3
|
+
import '@/styles/globals.css';
|
|
4
|
+
|
|
5
|
+
export default function App({ Component, pageProps }: AppProps) {
|
|
6
|
+
return (
|
|
7
|
+
<DiggoProvider>
|
|
8
|
+
<Component {...pageProps} />
|
|
9
|
+
</DiggoProvider>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GetStaticProps } from 'next';
|
|
2
|
+
|
|
3
|
+
export default function HomePage() {
|
|
4
|
+
return null;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export const getStaticProps: GetStaticProps = async () => {
|
|
8
|
+
return {
|
|
9
|
+
redirect: {
|
|
10
|
+
destination: '/home',
|
|
11
|
+
permanent: false,
|
|
12
|
+
},
|
|
13
|
+
};
|
|
14
|
+
};
|
|
@@ -14,10 +14,11 @@
|
|
|
14
14
|
"jsx": "preserve",
|
|
15
15
|
"incremental": true,
|
|
16
16
|
"plugins": [{ "name": "next" }],
|
|
17
|
+
"baseUrl": ".",
|
|
17
18
|
"paths": {
|
|
18
19
|
"@/*": ["./*"]
|
|
19
20
|
}
|
|
20
21
|
},
|
|
21
|
-
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"
|
|
22
|
+
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
|
22
23
|
"exclude": ["node_modules"]
|
|
23
24
|
}
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
# Copy this file to .env.local and update the values
|
|
3
3
|
|
|
4
4
|
# CMS API Configuration
|
|
5
|
-
# Set your DiggoCMS API base URL (
|
|
6
|
-
BASE_URL=https://
|
|
5
|
+
# Set your DiggoCMS API base URL (only needed when not using mock mode)
|
|
6
|
+
# Example: BASE_URL=https://cms.example.com
|
|
7
|
+
BASE_URL=
|
|
7
8
|
|
|
8
9
|
# Mock Mode
|
|
9
10
|
# Set to 1 to use the local mock API server
|
|
@@ -1,32 +1,32 @@
|
|
|
1
1
|
# My DiggoCMS App
|
|
2
2
|
|
|
3
|
-
A complete DiggoCMS application with mock API server for development.
|
|
3
|
+
A complete DiggoCMS application with mock API server for development, built with Next.js Pages Router.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
7
|
+
- Next.js 16 with Pages Router
|
|
8
|
+
- Static Site Generation (SSG) with `getStaticProps`/`getStaticPaths`
|
|
9
|
+
- Tailwind CSS for styling
|
|
10
|
+
- Hot reload via `next dev`
|
|
11
|
+
- Extended components for all supported types: title, image, text, video, gallery, and card
|
|
12
|
+
- Navigation components with dropdown support
|
|
13
|
+
- Header renders the main menu from mock API data (or CMS when configured)
|
|
14
|
+
- Built-in mock API server (runs with tsx, works with any package manager)
|
|
15
|
+
- Sample fixtures aligned with SDK payload model
|
|
16
|
+
- Mock mode indicator during development
|
|
14
17
|
|
|
15
18
|
## Quick Start
|
|
16
19
|
|
|
17
20
|
1. Copy `.env.local.example` to `.env.local`:
|
|
21
|
+
|
|
18
22
|
```bash
|
|
19
23
|
cp .env.local.example .env.local
|
|
20
24
|
```
|
|
21
25
|
|
|
22
|
-
2. Install dependencies
|
|
23
|
-
```bash
|
|
24
|
-
bun install
|
|
25
|
-
# or
|
|
26
|
-
npm install
|
|
27
|
-
```
|
|
26
|
+
2. Install dependencies using your chosen package manager.
|
|
28
27
|
|
|
29
28
|
3. Start the mock API and dev server:
|
|
29
|
+
|
|
30
30
|
```bash
|
|
31
31
|
bun run dev:mock
|
|
32
32
|
# or run separately:
|
|
@@ -34,13 +34,15 @@ A complete DiggoCMS application with mock API server for development.
|
|
|
34
34
|
bun run dev # Terminal 2
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
+
With npm, yarn, or pnpm, use: `npm run dev:mock`, `yarn dev:mock`, or `pnpm run dev:mock`.
|
|
38
|
+
|
|
37
39
|
4. Open [http://localhost:3000](http://localhost:3000) in your browser.
|
|
38
40
|
|
|
39
41
|
## Available Pages
|
|
40
42
|
|
|
41
43
|
The mock API includes these sample pages:
|
|
42
44
|
|
|
43
|
-
- `/` - Home page with title, text, image, video, gallery, and card payloads
|
|
45
|
+
- `/` or `/home` - Home page with title, text, image, video, gallery, and card payloads
|
|
44
46
|
- `/chi-siamo` - About page with title, text, and image payloads
|
|
45
47
|
- `/contatti` - Contact page with title, text, and card payloads
|
|
46
48
|
|
|
@@ -48,11 +50,13 @@ The mock API includes these sample pages:
|
|
|
48
50
|
|
|
49
51
|
| Script | Description |
|
|
50
52
|
|--------|-------------|
|
|
51
|
-
| `
|
|
52
|
-
| `
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
53
|
+
| `dev` | Start Next.js dev server only |
|
|
54
|
+
| `build` | Build for production |
|
|
55
|
+
| `start` | Start production server |
|
|
56
|
+
| `lint` | Run ESLint |
|
|
57
|
+
| `typecheck` | Run TypeScript check |
|
|
58
|
+
| `mock:api` | Start mock API server only |
|
|
59
|
+
| `dev:mock` | Start mock API and dev server together |
|
|
56
60
|
|
|
57
61
|
## Mock API Endpoints
|
|
58
62
|
|
|
@@ -65,13 +69,7 @@ The mock server provides these endpoints:
|
|
|
65
69
|
|
|
66
70
|
## Project Structure
|
|
67
71
|
|
|
68
|
-
```
|
|
69
|
-
app/ # Next.js app router
|
|
70
|
-
├── [...slug]/ # Dynamic catch-all route
|
|
71
|
-
├── layout.tsx # Root layout with DiggoProvider
|
|
72
|
-
├── page.tsx # Home redirect
|
|
73
|
-
└── globals.css # Global styles
|
|
74
|
-
|
|
72
|
+
```text
|
|
75
73
|
components/ # React components
|
|
76
74
|
├── DiggoProvider.tsx
|
|
77
75
|
├── server-components.ts
|
|
@@ -85,10 +83,15 @@ components/ # React components
|
|
|
85
83
|
└── ExtendedMenuContainer.tsx
|
|
86
84
|
|
|
87
85
|
lib/ # Utility functions
|
|
88
|
-
├── diggo-config.ts
|
|
89
|
-
└── data-fetching.ts
|
|
86
|
+
├── diggo-config.ts # SDK configuration
|
|
87
|
+
└── data-fetching.ts # Data fetching helpers
|
|
88
|
+
|
|
89
|
+
pages/ # Next.js pages (Pages Router)
|
|
90
|
+
├── _app.tsx # App wrapper with DiggoProvider
|
|
91
|
+
├── index.tsx # Home redirect
|
|
92
|
+
└── [...slug].tsx # Dynamic catch-all route
|
|
90
93
|
|
|
91
|
-
fixtures/ # Mock data
|
|
94
|
+
fixtures/ # Mock data (optional, CLI-configurable)
|
|
92
95
|
├── pages/
|
|
93
96
|
│ ├── index.json # Pages list
|
|
94
97
|
│ ├── home.json # Home page
|
|
@@ -99,6 +102,10 @@ fixtures/ # Mock data
|
|
|
99
102
|
|
|
100
103
|
scripts/
|
|
101
104
|
└── mock-server.ts # Mock API server
|
|
105
|
+
|
|
106
|
+
public/ # Static assets
|
|
107
|
+
styles/ # Global styles
|
|
108
|
+
└── globals.css # Tailwind + component styles
|
|
102
109
|
```
|
|
103
110
|
|
|
104
111
|
## Switching to Real API
|
|
@@ -106,26 +113,47 @@ scripts/
|
|
|
106
113
|
To use a real CMS API instead of mock data:
|
|
107
114
|
|
|
108
115
|
1. Update `.env.local`:
|
|
116
|
+
|
|
109
117
|
```env
|
|
110
118
|
BASE_URL=https://your-cms-api.com
|
|
111
119
|
MOCK=0
|
|
112
120
|
```
|
|
113
121
|
|
|
122
|
+
Note: `BASE_URL` defaults to empty. You must set a valid URL. Requests timeout after 5 seconds if the server is unreachable.
|
|
123
|
+
|
|
114
124
|
2. Restart the dev server:
|
|
125
|
+
|
|
115
126
|
```bash
|
|
116
127
|
bun run dev
|
|
117
128
|
```
|
|
118
129
|
|
|
130
|
+
Or use npm, yarn, or pnpm: `npm run dev`, `yarn dev`, `pnpm run dev`.
|
|
131
|
+
|
|
132
|
+
## CLI Options
|
|
133
|
+
|
|
134
|
+
When creating a project with `create-diggocms-app`, you can choose:
|
|
135
|
+
|
|
136
|
+
- **Template**: `minimal`, `full`, or `with-mock`
|
|
137
|
+
- **Package manager**: `bun` (default), `npm`, `yarn`, or `pnpm`
|
|
138
|
+
- **Fixtures** (with-mock only): Include or exclude sample fixtures
|
|
139
|
+
|
|
140
|
+
## SSG Configuration
|
|
141
|
+
|
|
142
|
+
Pages are automatically pre-generated at build time from the fixtures when using mock mode. For production with a real API, update `getStaticPaths` in `pages/[...slug].tsx` to fetch from your CMS.
|
|
143
|
+
|
|
119
144
|
## Customization
|
|
120
145
|
|
|
121
146
|
1. **Edit fixtures** - Modify files in `fixtures/` to change content
|
|
122
147
|
2. **Add components** - Create new components in `components/` and register in `lib/diggo-config.ts`
|
|
123
|
-
3. **Update styles** - Edit `
|
|
148
|
+
3. **Update styles** - Edit `styles/globals.css` or component styles
|
|
124
149
|
4. **Extend mock API** - Add new routes in `scripts/mock-server.ts`
|
|
125
150
|
|
|
126
|
-
|
|
151
|
+
## Fixture Notes
|
|
152
|
+
|
|
153
|
+
Page fixtures intentionally use only the current supported component types: `title`, `text`, `image`, `video`, `gallery`, and `card`. Deprecated types like `subtitle` and `richtext` (as standalone components) are excluded from page fixtures. Card components may still use card-specific fields like `richtext` as part of the card data structure.
|
|
127
154
|
|
|
128
155
|
## Documentation
|
|
129
156
|
|
|
130
157
|
- [DiggoCMS SDK Documentation](https://github.com/digitalygo/diggocms-sdk)
|
|
131
|
-
- [Next.js Documentation](https://nextjs.org/docs)
|
|
158
|
+
- [Next.js Pages Router Documentation](https://nextjs.org/docs/pages)
|
|
159
|
+
- [Tailwind CSS Documentation](https://tailwindcss.com/docs)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { MenuPayload } from '@digitalygo/diggocms-sdk-core';
|
|
2
|
+
import type { ReactElement, ReactNode } from 'react';
|
|
3
|
+
import { renderMenu } from '@digitalygo/diggocms-sdk-core/server';
|
|
4
|
+
import { navigationRegistry } from '@/lib/diggo-config';
|
|
5
|
+
|
|
6
|
+
interface PageLayoutProps {
|
|
7
|
+
children: ReactNode;
|
|
8
|
+
menu: MenuPayload | null;
|
|
9
|
+
menuError: string | null;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function PageLayout({ children, menu, menuError }: PageLayoutProps): ReactElement {
|
|
13
|
+
return (
|
|
14
|
+
<div className="page-shell">
|
|
15
|
+
<header className="site-header">
|
|
16
|
+
<div className="site-header__inner">
|
|
17
|
+
<a href="/home" className="site-brand">
|
|
18
|
+
DiggoCMS Demo
|
|
19
|
+
</a>
|
|
20
|
+
<div className="site-nav">
|
|
21
|
+
{menu ? (
|
|
22
|
+
renderMenu(menu, navigationRegistry)
|
|
23
|
+
) : (
|
|
24
|
+
<div className="menu-fallback" role="status">
|
|
25
|
+
Menu unavailable
|
|
26
|
+
</div>
|
|
27
|
+
)}
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
{menuError && (
|
|
31
|
+
<div className="menu-alert" role="status">
|
|
32
|
+
{menuError}
|
|
33
|
+
</div>
|
|
34
|
+
)}
|
|
35
|
+
</header>
|
|
36
|
+
|
|
37
|
+
<main className="page-content">{children}</main>
|
|
38
|
+
</div>
|
|
39
|
+
);
|
|
40
|
+
}
|
|
@@ -12,15 +12,39 @@ interface PageIndexItem {
|
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
const DEFAULT_MOCK_API_URL = 'http://localhost:3001';
|
|
15
|
+
const FETCH_TIMEOUT_MS = 5000;
|
|
15
16
|
|
|
16
17
|
function getMockApiBaseUrl(): string {
|
|
17
18
|
return process.env.MOCK_API_URL ?? DEFAULT_MOCK_API_URL;
|
|
18
19
|
}
|
|
19
20
|
|
|
21
|
+
async function fetchWithTimeout(
|
|
22
|
+
url: string,
|
|
23
|
+
timeoutMs: number
|
|
24
|
+
): Promise<Response> {
|
|
25
|
+
const controller = new AbortController();
|
|
26
|
+
const timeoutId = setTimeout(() => controller.abort(), timeoutMs);
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
const response = await fetch(url, { signal: controller.signal });
|
|
30
|
+
clearTimeout(timeoutId);
|
|
31
|
+
return response;
|
|
32
|
+
} catch (error) {
|
|
33
|
+
clearTimeout(timeoutId);
|
|
34
|
+
if (error instanceof Error && error.name === 'AbortError') {
|
|
35
|
+
throw new Error(`Request timeout after ${timeoutMs}ms`);
|
|
36
|
+
}
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
20
41
|
async function fetchFromMockApi<T>(endpoint: string): Promise<T | null> {
|
|
21
42
|
try {
|
|
22
43
|
const baseUrl = getMockApiBaseUrl();
|
|
23
|
-
const response = await
|
|
44
|
+
const response = await fetchWithTimeout(
|
|
45
|
+
`${baseUrl}${endpoint}`,
|
|
46
|
+
FETCH_TIMEOUT_MS
|
|
47
|
+
);
|
|
24
48
|
|
|
25
49
|
if (!response.ok) {
|
|
26
50
|
return null;
|
|
@@ -32,7 +56,7 @@ async function fetchFromMockApi<T>(endpoint: string): Promise<T | null> {
|
|
|
32
56
|
}
|
|
33
57
|
}
|
|
34
58
|
|
|
35
|
-
async function loadPagesIndex(): Promise<PageIndexItem[]> {
|
|
59
|
+
export async function loadPagesIndex(): Promise<PageIndexItem[]> {
|
|
36
60
|
const index = await fetchFromMockApi<PageIndexItem[]>('/pages');
|
|
37
61
|
return index ?? [];
|
|
38
62
|
}
|
|
@@ -99,7 +123,10 @@ export async function fetchPageData(slug: string): Promise<FetchPageResult> {
|
|
|
99
123
|
}
|
|
100
124
|
|
|
101
125
|
try {
|
|
102
|
-
const response = await
|
|
126
|
+
const response = await fetchWithTimeout(
|
|
127
|
+
`${sdkConfig.baseUrl}/pages/${slug}`,
|
|
128
|
+
FETCH_TIMEOUT_MS
|
|
129
|
+
);
|
|
103
130
|
|
|
104
131
|
if (!response.ok) {
|
|
105
132
|
return {
|
|
@@ -111,10 +138,14 @@ export async function fetchPageData(slug: string): Promise<FetchPageResult> {
|
|
|
111
138
|
|
|
112
139
|
const page = (await response.json()) as PagePayload;
|
|
113
140
|
return { page, error: null, usingMock: false };
|
|
114
|
-
} catch {
|
|
141
|
+
} catch (error) {
|
|
142
|
+
const errorMessage =
|
|
143
|
+
error instanceof Error ? error.message : 'Unknown error';
|
|
115
144
|
return {
|
|
116
145
|
page: null,
|
|
117
|
-
error: '
|
|
146
|
+
error: errorMessage.includes('timeout')
|
|
147
|
+
? 'Request to CMS timed out. Please check your BASE_URL and network connection.'
|
|
148
|
+
: 'Error connecting to CMS',
|
|
118
149
|
usingMock: false,
|
|
119
150
|
};
|
|
120
151
|
}
|
|
@@ -143,7 +174,10 @@ export async function fetchCollectionData(
|
|
|
143
174
|
}
|
|
144
175
|
|
|
145
176
|
try {
|
|
146
|
-
const response = await
|
|
177
|
+
const response = await fetchWithTimeout(
|
|
178
|
+
`${sdkConfig.baseUrl}/collections/${type}`,
|
|
179
|
+
FETCH_TIMEOUT_MS
|
|
180
|
+
);
|
|
147
181
|
|
|
148
182
|
if (!response.ok) {
|
|
149
183
|
return {
|
|
@@ -155,10 +189,14 @@ export async function fetchCollectionData(
|
|
|
155
189
|
|
|
156
190
|
const collection = (await response.json()) as CollectionPayload;
|
|
157
191
|
return { collection, error: null, usingMock: false };
|
|
158
|
-
} catch {
|
|
192
|
+
} catch (error) {
|
|
193
|
+
const errorMessage =
|
|
194
|
+
error instanceof Error ? error.message : 'Unknown error';
|
|
159
195
|
return {
|
|
160
196
|
collection: null,
|
|
161
|
-
error: '
|
|
197
|
+
error: errorMessage.includes('timeout')
|
|
198
|
+
? 'Request to CMS timed out. Please check your BASE_URL and network connection.'
|
|
199
|
+
: 'Error connecting to CMS',
|
|
162
200
|
usingMock: false,
|
|
163
201
|
};
|
|
164
202
|
}
|
|
@@ -183,7 +221,10 @@ export async function fetchMenuData(key: string): Promise<FetchMenuResult> {
|
|
|
183
221
|
}
|
|
184
222
|
|
|
185
223
|
try {
|
|
186
|
-
const response = await
|
|
224
|
+
const response = await fetchWithTimeout(
|
|
225
|
+
`${sdkConfig.baseUrl}/menus/${key}`,
|
|
226
|
+
FETCH_TIMEOUT_MS
|
|
227
|
+
);
|
|
187
228
|
|
|
188
229
|
if (!response.ok) {
|
|
189
230
|
return {
|
|
@@ -195,10 +236,14 @@ export async function fetchMenuData(key: string): Promise<FetchMenuResult> {
|
|
|
195
236
|
|
|
196
237
|
const menu = (await response.json()) as MenuPayload;
|
|
197
238
|
return { menu, error: null, usingMock: false };
|
|
198
|
-
} catch {
|
|
239
|
+
} catch (error) {
|
|
240
|
+
const errorMessage =
|
|
241
|
+
error instanceof Error ? error.message : 'Unknown error';
|
|
199
242
|
return {
|
|
200
243
|
menu: null,
|
|
201
|
-
error: '
|
|
244
|
+
error: errorMessage.includes('timeout')
|
|
245
|
+
? 'Request to CMS timed out. Please check your BASE_URL and network connection.'
|
|
246
|
+
: 'Error connecting to CMS',
|
|
202
247
|
usingMock: false,
|
|
203
248
|
};
|
|
204
249
|
}
|