@digitalygo/create-diggocms-app 0.1.0

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.
Files changed (65) hide show
  1. package/README.md +152 -0
  2. package/bin/cli.js +392 -0
  3. package/package.json +44 -0
  4. package/templates/full/.env.local.example +13 -0
  5. package/templates/full/README.md +101 -0
  6. package/templates/full/app/[...slug]/page.tsx +115 -0
  7. package/templates/full/app/globals.css +187 -0
  8. package/templates/full/app/layout.tsx +25 -0
  9. package/templates/full/app/page.tsx +6 -0
  10. package/templates/full/components/DiggoProvider.tsx +15 -0
  11. package/templates/full/components/ExtendedCard.tsx +15 -0
  12. package/templates/full/components/ExtendedImage.tsx +16 -0
  13. package/templates/full/components/ExtendedMenuContainer.tsx +10 -0
  14. package/templates/full/components/ExtendedMenuItem.tsx +22 -0
  15. package/templates/full/components/ExtendedRichtext.tsx +15 -0
  16. package/templates/full/components/ExtendedSubtitle.tsx +10 -0
  17. package/templates/full/components/ExtendedTitle.tsx +10 -0
  18. package/templates/full/components/server-components.ts +8 -0
  19. package/templates/full/lib/data-fetching.ts +205 -0
  20. package/templates/full/lib/diggo-config.ts +48 -0
  21. package/templates/full/next-env.d.ts +2 -0
  22. package/templates/full/next.config.ts +7 -0
  23. package/templates/full/package.json +25 -0
  24. package/templates/full/tsconfig.json +23 -0
  25. package/templates/minimal/.env.local.example +13 -0
  26. package/templates/minimal/README.md +67 -0
  27. package/templates/minimal/app/[...slug]/page.tsx +56 -0
  28. package/templates/minimal/app/globals.css +11 -0
  29. package/templates/minimal/app/layout.tsx +22 -0
  30. package/templates/minimal/app/page.tsx +6 -0
  31. package/templates/minimal/components/DiggoProvider.tsx +15 -0
  32. package/templates/minimal/lib/data-fetching.ts +59 -0
  33. package/templates/minimal/lib/diggo-config.ts +35 -0
  34. package/templates/minimal/next-env.d.ts +2 -0
  35. package/templates/minimal/next.config.ts +7 -0
  36. package/templates/minimal/package.json +25 -0
  37. package/templates/minimal/tsconfig.json +23 -0
  38. package/templates/with-mock/.env.local.example +13 -0
  39. package/templates/with-mock/README.md +128 -0
  40. package/templates/with-mock/app/[...slug]/page.tsx +115 -0
  41. package/templates/with-mock/app/globals.css +187 -0
  42. package/templates/with-mock/app/layout.tsx +25 -0
  43. package/templates/with-mock/app/page.tsx +6 -0
  44. package/templates/with-mock/components/DiggoProvider.tsx +15 -0
  45. package/templates/with-mock/components/ExtendedCard.tsx +15 -0
  46. package/templates/with-mock/components/ExtendedImage.tsx +16 -0
  47. package/templates/with-mock/components/ExtendedMenuContainer.tsx +10 -0
  48. package/templates/with-mock/components/ExtendedMenuItem.tsx +22 -0
  49. package/templates/with-mock/components/ExtendedRichtext.tsx +15 -0
  50. package/templates/with-mock/components/ExtendedSubtitle.tsx +10 -0
  51. package/templates/with-mock/components/ExtendedTitle.tsx +10 -0
  52. package/templates/with-mock/components/server-components.ts +8 -0
  53. package/templates/with-mock/fixtures/collection.json +28 -0
  54. package/templates/with-mock/fixtures/menu.json +29 -0
  55. package/templates/with-mock/fixtures/pages/chi-siamo.json +37 -0
  56. package/templates/with-mock/fixtures/pages/contatti.json +33 -0
  57. package/templates/with-mock/fixtures/pages/home.json +62 -0
  58. package/templates/with-mock/fixtures/pages/index.json +17 -0
  59. package/templates/with-mock/lib/data-fetching.ts +205 -0
  60. package/templates/with-mock/lib/diggo-config.ts +48 -0
  61. package/templates/with-mock/next-env.d.ts +2 -0
  62. package/templates/with-mock/next.config.ts +7 -0
  63. package/templates/with-mock/package.json +27 -0
  64. package/templates/with-mock/scripts/mock-server.ts +231 -0
  65. package/templates/with-mock/tsconfig.json +23 -0
@@ -0,0 +1,115 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { Metadata } from 'next';
3
+ import Link from 'next/link';
4
+ import { notFound } from 'next/navigation';
5
+ import { fetchPageData, fetchMenuData } from '@/lib/data-fetching';
6
+ import {
7
+ componentsRegistry,
8
+ navigationRegistry,
9
+ isMockMode,
10
+ hasBaseUrl,
11
+ } from '@/lib/diggo-config';
12
+ import { renderPage, renderMenu } from '@digitalygo/diggocms-sdk-core/server';
13
+
14
+ export const dynamic = 'force-dynamic';
15
+
16
+ interface CatchAllPageProps {
17
+ params: Promise<{ slug: string[] }>;
18
+ }
19
+
20
+ export async function generateMetadata({
21
+ params,
22
+ }: CatchAllPageProps): Promise<Metadata> {
23
+ const { slug: slugParam } = await params;
24
+ const slug = (Array.isArray(slugParam) ? slugParam : []).join('/') || 'home';
25
+ const { page } = await fetchPageData(slug);
26
+
27
+ return {
28
+ title: page?.title,
29
+ };
30
+ }
31
+
32
+ export default async function CatchAllPage({
33
+ params,
34
+ }: CatchAllPageProps): Promise<ReactElement> {
35
+ const { slug: slugParam } = await params;
36
+ const slug = (Array.isArray(slugParam) ? slugParam : []).join('/') || 'home';
37
+
38
+ const [{ page, error, usingMock }, { menu }] = await Promise.all([
39
+ fetchPageData(slug),
40
+ fetchMenuData('main'),
41
+ ]);
42
+
43
+ const showMockIndicator = usingMock || isMockMode();
44
+ const showError = error && !page;
45
+
46
+ if (!page) {
47
+ if (!hasBaseUrl() && !isMockMode()) {
48
+ return (
49
+ <div className="container">
50
+ <header className="header">
51
+ <div className="header-content container">
52
+ <div className="logo">My DiggoCMS App</div>
53
+ </div>
54
+ </header>
55
+ <main className="main container">
56
+ <div className="error-banner">
57
+ <strong>Error:</strong> BASE_URL is not configured and MOCK mode
58
+ is disabled.
59
+ </div>
60
+ <p>
61
+ Please configure your environment variables. Copy{' '}
62
+ <code>.env.local.example</code> to <code>.env.local</code> and set
63
+ either:
64
+ </p>
65
+ <ul style={{ marginLeft: '1.5rem', marginTop: '0.5rem' }}>
66
+ <li>
67
+ <code>MOCK=1</code> to use local fixtures
68
+ </li>
69
+ <li>
70
+ <code>BASE_URL</code> to your CMS API endpoint
71
+ </li>
72
+ </ul>
73
+ </main>
74
+ </div>
75
+ );
76
+ }
77
+
78
+ notFound();
79
+ }
80
+
81
+ return (
82
+ <div>
83
+ <header className="header">
84
+ <div className="header-content container">
85
+ <Link href="/" className="logo">
86
+ My DiggoCMS App
87
+ </Link>
88
+ {menu && renderMenu(menu, navigationRegistry)}
89
+ </div>
90
+ </header>
91
+
92
+ <main className="main container">
93
+ {showError && !showMockIndicator && (
94
+ <div className="error-banner">
95
+ <strong>Warning:</strong> {error}
96
+ </div>
97
+ )}
98
+
99
+ {showMockIndicator && (
100
+ <div className="mock-badge">
101
+ Mock Mode
102
+ </div>
103
+ )}
104
+
105
+ {renderPage(page, componentsRegistry)}
106
+ </main>
107
+
108
+ <footer className="footer">
109
+ <div className="container">
110
+ <p>My DiggoCMS App &copy; {new Date().getFullYear()}</p>
111
+ </div>
112
+ </footer>
113
+ </div>
114
+ );
115
+ }
@@ -0,0 +1,187 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ body {
8
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
9
+ line-height: 1.6;
10
+ color: #333;
11
+ }
12
+
13
+ .container {
14
+ max-width: 1200px;
15
+ margin: 0 auto;
16
+ padding: 0 1rem;
17
+ }
18
+
19
+ /* Header Styles */
20
+ .header {
21
+ background: #fff;
22
+ border-bottom: 1px solid #e5e7eb;
23
+ padding: 1rem 0;
24
+ margin-bottom: 2rem;
25
+ }
26
+
27
+ .header-content {
28
+ display: flex;
29
+ justify-content: space-between;
30
+ align-items: center;
31
+ flex-wrap: wrap;
32
+ gap: 1rem;
33
+ }
34
+
35
+ .logo {
36
+ font-size: 1.5rem;
37
+ font-weight: 700;
38
+ color: #3b82f6;
39
+ text-decoration: none;
40
+ }
41
+
42
+ /* Main Content */
43
+ .main {
44
+ min-height: calc(100vh - 200px);
45
+ padding-bottom: 3rem;
46
+ }
47
+
48
+ /* Footer Styles */
49
+ .footer {
50
+ background: #f9fafb;
51
+ border-top: 1px solid #e5e7eb;
52
+ padding: 2rem 0;
53
+ text-align: center;
54
+ color: #6b7280;
55
+ }
56
+
57
+ /* Error Banner */
58
+ .error-banner {
59
+ background: #fef3c7;
60
+ border: 1px solid #fbbf24;
61
+ border-radius: 0.375rem;
62
+ padding: 1rem;
63
+ margin-bottom: 1.5rem;
64
+ color: #92400e;
65
+ }
66
+
67
+ /* Mock Badge */
68
+ .mock-badge {
69
+ display: inline-block;
70
+ background: #dbeafe;
71
+ color: #1e40af;
72
+ padding: 0.25rem 0.75rem;
73
+ border-radius: 9999px;
74
+ font-size: 0.75rem;
75
+ font-weight: 500;
76
+ margin-bottom: 1rem;
77
+ }
78
+
79
+ /* Component Styles */
80
+ .diggo-title {
81
+ font-size: 2.5rem;
82
+ font-weight: 700;
83
+ color: #111827;
84
+ margin-bottom: 1rem;
85
+ line-height: 1.2;
86
+ }
87
+
88
+ .diggo-subtitle {
89
+ font-size: 1.5rem;
90
+ font-weight: 600;
91
+ color: #374151;
92
+ margin-bottom: 1rem;
93
+ margin-top: 2rem;
94
+ line-height: 1.3;
95
+ }
96
+
97
+ .diggo-image {
98
+ max-width: 100%;
99
+ height: auto;
100
+ border-radius: 0.5rem;
101
+ margin: 1rem 0;
102
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
103
+ }
104
+
105
+ .diggo-richtext {
106
+ margin: 1rem 0;
107
+ }
108
+
109
+ .diggo-richtext p {
110
+ margin-bottom: 1rem;
111
+ }
112
+
113
+ .diggo-card {
114
+ background: #fff;
115
+ border: 1px solid #e5e7eb;
116
+ border-radius: 0.5rem;
117
+ padding: 1.5rem;
118
+ margin: 1.5rem 0;
119
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
120
+ }
121
+
122
+ .diggo-card .diggo-title {
123
+ font-size: 1.25rem;
124
+ margin-bottom: 0.5rem;
125
+ }
126
+
127
+ .diggo-card .diggo-subtitle {
128
+ font-size: 1rem;
129
+ color: #6b7280;
130
+ margin-top: 0;
131
+ margin-bottom: 1rem;
132
+ }
133
+
134
+ /* Navigation Styles */
135
+ .menu {
136
+ display: flex;
137
+ }
138
+
139
+ .menu-list {
140
+ display: flex;
141
+ list-style: none;
142
+ gap: 1.5rem;
143
+ flex-wrap: wrap;
144
+ }
145
+
146
+ .menu-item {
147
+ position: relative;
148
+ }
149
+
150
+ .menu-link {
151
+ color: #374151;
152
+ text-decoration: none;
153
+ font-weight: 500;
154
+ padding: 0.5rem 0;
155
+ transition: color 0.2s;
156
+ }
157
+
158
+ .menu-link:hover {
159
+ color: #3b82f6;
160
+ }
161
+
162
+ .menu-link[aria-current='page'] {
163
+ color: #3b82f6;
164
+ border-bottom: 2px solid #3b82f6;
165
+ }
166
+
167
+ .menu-item ul {
168
+ display: none;
169
+ position: absolute;
170
+ top: 100%;
171
+ left: 0;
172
+ background: #fff;
173
+ border: 1px solid #e5e7eb;
174
+ border-radius: 0.375rem;
175
+ padding: 0.5rem 0;
176
+ min-width: 150px;
177
+ list-style: none;
178
+ box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
179
+ }
180
+
181
+ .menu-item:hover > ul {
182
+ display: block;
183
+ }
184
+
185
+ .menu-item ul li {
186
+ padding: 0.25rem 1rem;
187
+ }
@@ -0,0 +1,25 @@
1
+ import type { ReactElement } from 'react';
2
+ import { DiggoProvider } from '@/components/DiggoProvider';
3
+ import './globals.css';
4
+
5
+ export const metadata = {
6
+ title: {
7
+ template: '%s | My DiggoCMS App',
8
+ default: 'My DiggoCMS App',
9
+ },
10
+ description: 'Built with DiggoCMS SDK',
11
+ };
12
+
13
+ export default function RootLayout({
14
+ children,
15
+ }: {
16
+ children: React.ReactNode;
17
+ }): ReactElement {
18
+ return (
19
+ <html lang="en">
20
+ <body>
21
+ <DiggoProvider>{children}</DiggoProvider>
22
+ </body>
23
+ </html>
24
+ );
25
+ }
@@ -0,0 +1,6 @@
1
+ import type { ReactElement } from 'react';
2
+ import { redirect } from 'next/navigation';
3
+
4
+ export default function HomePage(): ReactElement {
5
+ redirect('/home');
6
+ }
@@ -0,0 +1,15 @@
1
+ 'use client';
2
+
3
+ import type { ReactElement, ReactNode } from 'react';
4
+ import { DiggoProvider as SDKProvider } from '@digitalygo/diggocms-sdk-core';
5
+ import { sdkConfig } from '@/lib/diggo-config';
6
+
7
+ interface DiggoProviderWrapperProps {
8
+ children: ReactNode;
9
+ }
10
+
11
+ export function DiggoProvider({
12
+ children,
13
+ }: DiggoProviderWrapperProps): ReactElement {
14
+ return <SDKProvider config={sdkConfig}>{children}</SDKProvider>;
15
+ }
@@ -0,0 +1,15 @@
1
+ import type { CardProps } from '@digitalygo/diggocms-sdk-core';
2
+ import type { ReactElement } from 'react';
3
+ import { ExtendedTitle } from './ExtendedTitle';
4
+ import { ExtendedSubtitle } from './ExtendedSubtitle';
5
+ import { ExtendedRichtext } from './ExtendedRichtext';
6
+
7
+ export function ExtendedCard({ title, subtitle, content }: CardProps): ReactElement | null {
8
+ return (
9
+ <div className="diggo-card">
10
+ {title && <ExtendedTitle content={title} />}
11
+ {subtitle && <ExtendedSubtitle content={subtitle} />}
12
+ {content && <ExtendedRichtext content={content} />}
13
+ </div>
14
+ );
15
+ }
@@ -0,0 +1,16 @@
1
+ import type { ImageProps } from '@digitalygo/diggocms-sdk-core';
2
+ import type { ReactElement } from 'react';
3
+
4
+ export function ExtendedImage({ src, alt }: ImageProps): ReactElement | null {
5
+ if (!src) {
6
+ return null;
7
+ }
8
+
9
+ return (
10
+ <img
11
+ src={src}
12
+ alt={alt || ''}
13
+ className="diggo-image"
14
+ />
15
+ );
16
+ }
@@ -0,0 +1,10 @@
1
+ import type { MenuContainerProps } from '@digitalygo/diggocms-sdk-core';
2
+ import type { ReactElement } from 'react';
3
+
4
+ export function ExtendedMenuContainer({ children }: MenuContainerProps): ReactElement {
5
+ return (
6
+ <nav className="menu">
7
+ <ul className="menu-list">{children}</ul>
8
+ </nav>
9
+ );
10
+ }
@@ -0,0 +1,22 @@
1
+ import type { MenuItemProps } from '@digitalygo/diggocms-sdk-core';
2
+ import type { ReactElement, ReactNode } from 'react';
3
+
4
+ interface ExtendedMenuItemProps extends MenuItemProps {
5
+ children?: ReactNode;
6
+ }
7
+
8
+ export function ExtendedMenuItem({
9
+ label,
10
+ href,
11
+ current,
12
+ children,
13
+ }: ExtendedMenuItemProps): ReactElement {
14
+ return (
15
+ <li className="menu-item">
16
+ <a href={href} aria-current={current ? 'page' : undefined} className="menu-link">
17
+ {label}
18
+ </a>
19
+ {children}
20
+ </li>
21
+ );
22
+ }
@@ -0,0 +1,15 @@
1
+ import type { RichtextProps } from '@digitalygo/diggocms-sdk-core';
2
+ import type { ReactElement } from 'react';
3
+
4
+ export function ExtendedRichtext({ content }: RichtextProps): ReactElement | null {
5
+ if (!content) {
6
+ return null;
7
+ }
8
+
9
+ return (
10
+ <div
11
+ className="diggo-richtext"
12
+ dangerouslySetInnerHTML={{ __html: content }}
13
+ />
14
+ );
15
+ }
@@ -0,0 +1,10 @@
1
+ import type { SubtitleProps } from '@digitalygo/diggocms-sdk-core';
2
+ import type { ReactElement } from 'react';
3
+
4
+ export function ExtendedSubtitle({ content }: SubtitleProps): ReactElement | null {
5
+ if (!content) {
6
+ return null;
7
+ }
8
+
9
+ return <h2 className="diggo-subtitle">{content}</h2>;
10
+ }
@@ -0,0 +1,10 @@
1
+ import type { TitleProps } from '@digitalygo/diggocms-sdk-core';
2
+ import type { ReactElement } from 'react';
3
+
4
+ export function ExtendedTitle({ content }: TitleProps): ReactElement | null {
5
+ if (!content) {
6
+ return null;
7
+ }
8
+
9
+ return <h1 className="diggo-title">{content}</h1>;
10
+ }
@@ -0,0 +1,8 @@
1
+ // Re-export components for server-side usage (they're server-safe pure components)
2
+ export { ExtendedTitle } from './ExtendedTitle';
3
+ export { ExtendedSubtitle } from './ExtendedSubtitle';
4
+ export { ExtendedImage } from './ExtendedImage';
5
+ export { ExtendedRichtext } from './ExtendedRichtext';
6
+ export { ExtendedCard } from './ExtendedCard';
7
+ export { ExtendedMenuItem } from './ExtendedMenuItem';
8
+ export { ExtendedMenuContainer } from './ExtendedMenuContainer';
@@ -0,0 +1,205 @@
1
+ import type {
2
+ PagePayload,
3
+ CollectionPayload,
4
+ MenuPayload,
5
+ } from '@digitalygo/diggocms-sdk-core';
6
+ import { sdkConfig, isMockMode, hasBaseUrl } from './diggo-config';
7
+
8
+ interface PageIndexItem {
9
+ id: string;
10
+ slug: string;
11
+ title: string;
12
+ }
13
+
14
+ const DEFAULT_MOCK_API_URL = 'http://localhost:3001';
15
+
16
+ function getMockApiBaseUrl(): string {
17
+ return process.env.MOCK_API_URL ?? DEFAULT_MOCK_API_URL;
18
+ }
19
+
20
+ async function fetchFromMockApi<T>(endpoint: string): Promise<T | null> {
21
+ try {
22
+ const baseUrl = getMockApiBaseUrl();
23
+ const response = await fetch(`${baseUrl}${endpoint}`);
24
+
25
+ if (!response.ok) {
26
+ return null;
27
+ }
28
+
29
+ return (await response.json()) as T;
30
+ } catch {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ async function loadPagesIndex(): Promise<PageIndexItem[]> {
36
+ const index = await fetchFromMockApi<PageIndexItem[]>('/pages');
37
+ return index ?? [];
38
+ }
39
+
40
+ export interface FetchPageResult {
41
+ page: PagePayload | null;
42
+ error: string | null;
43
+ usingMock: boolean;
44
+ }
45
+
46
+ export interface FetchCollectionResult {
47
+ collection: CollectionPayload | null;
48
+ error: string | null;
49
+ usingMock: boolean;
50
+ }
51
+
52
+ export interface FetchMenuResult {
53
+ menu: MenuPayload | null;
54
+ error: string | null;
55
+ usingMock: boolean;
56
+ }
57
+
58
+ function normalizeSlug(slug: string): string {
59
+ if (slug === 'home' || slug === '') {
60
+ return '';
61
+ }
62
+ return slug;
63
+ }
64
+
65
+ async function loadPageFromMockApi(slug: string): Promise<PagePayload | null> {
66
+ const normalizedSlug = normalizeSlug(slug);
67
+ const index = await loadPagesIndex();
68
+
69
+ const pageExists = index.some(
70
+ (item) => normalizeSlug(item.slug) === normalizedSlug
71
+ );
72
+
73
+ if (!pageExists) {
74
+ return null;
75
+ }
76
+
77
+ const endpoint =
78
+ normalizedSlug === '' ? '/pages/home' : `/pages/${normalizedSlug}`;
79
+
80
+ return fetchFromMockApi<PagePayload>(endpoint);
81
+ }
82
+
83
+ export async function fetchPageData(slug: string): Promise<FetchPageResult> {
84
+ if (isMockMode()) {
85
+ const page = await loadPageFromMockApi(slug);
86
+ return {
87
+ page,
88
+ error: page ? null : 'Failed to load mock page from API',
89
+ usingMock: true,
90
+ };
91
+ }
92
+
93
+ if (!hasBaseUrl()) {
94
+ return {
95
+ page: null,
96
+ error: 'BASE_URL is not configured and MOCK mode is not enabled.',
97
+ usingMock: false,
98
+ };
99
+ }
100
+
101
+ try {
102
+ const response = await fetch(`${sdkConfig.baseUrl}/pages/${slug}`);
103
+
104
+ if (!response.ok) {
105
+ return {
106
+ page: null,
107
+ error: `Failed to fetch page: ${response.statusText}`,
108
+ usingMock: false,
109
+ };
110
+ }
111
+
112
+ const page = (await response.json()) as PagePayload;
113
+ return { page, error: null, usingMock: false };
114
+ } catch {
115
+ return {
116
+ page: null,
117
+ error: 'Error connecting to CMS',
118
+ usingMock: false,
119
+ };
120
+ }
121
+ }
122
+
123
+ export async function fetchCollectionData(
124
+ type: string
125
+ ): Promise<FetchCollectionResult> {
126
+ if (isMockMode()) {
127
+ const collection = await fetchFromMockApi<CollectionPayload>(
128
+ `/collections/${type}`
129
+ );
130
+ return {
131
+ collection,
132
+ error: collection ? null : 'Failed to load mock collection from API',
133
+ usingMock: true,
134
+ };
135
+ }
136
+
137
+ if (!hasBaseUrl()) {
138
+ return {
139
+ collection: null,
140
+ error: 'BASE_URL is not configured and MOCK mode is not enabled.',
141
+ usingMock: false,
142
+ };
143
+ }
144
+
145
+ try {
146
+ const response = await fetch(`${sdkConfig.baseUrl}/collections/${type}`);
147
+
148
+ if (!response.ok) {
149
+ return {
150
+ collection: null,
151
+ error: `Failed to fetch collection: ${response.statusText}`,
152
+ usingMock: false,
153
+ };
154
+ }
155
+
156
+ const collection = (await response.json()) as CollectionPayload;
157
+ return { collection, error: null, usingMock: false };
158
+ } catch {
159
+ return {
160
+ collection: null,
161
+ error: 'Error connecting to CMS',
162
+ usingMock: false,
163
+ };
164
+ }
165
+ }
166
+
167
+ export async function fetchMenuData(key: string): Promise<FetchMenuResult> {
168
+ if (isMockMode()) {
169
+ const menu = await fetchFromMockApi<MenuPayload>(`/menus/${key}`);
170
+ return {
171
+ menu,
172
+ error: menu ? null : 'Failed to load mock menu from API',
173
+ usingMock: true,
174
+ };
175
+ }
176
+
177
+ if (!hasBaseUrl()) {
178
+ return {
179
+ menu: null,
180
+ error: 'BASE_URL is not configured and MOCK mode is not enabled.',
181
+ usingMock: false,
182
+ };
183
+ }
184
+
185
+ try {
186
+ const response = await fetch(`${sdkConfig.baseUrl}/menus/${key}`);
187
+
188
+ if (!response.ok) {
189
+ return {
190
+ menu: null,
191
+ error: `Failed to fetch menu: ${response.statusText}`,
192
+ usingMock: false,
193
+ };
194
+ }
195
+
196
+ const menu = (await response.json()) as MenuPayload;
197
+ return { menu, error: null, usingMock: false };
198
+ } catch {
199
+ return {
200
+ menu: null,
201
+ error: 'Error connecting to CMS',
202
+ usingMock: false,
203
+ };
204
+ }
205
+ }