@djangocfg/layouts 1.2.8 → 1.2.10

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 (20) hide show
  1. package/package.json +5 -5
  2. package/src/layouts/AppLayout/AppLayout.tsx +67 -35
  3. package/src/layouts/AppLayout/components/PackageVersions/packageVersions.config.ts +8 -8
  4. package/src/layouts/AppLayout/index.ts +6 -6
  5. package/src/layouts/AppLayout/layouts/{CfgLayout/CfgLayout.tsx → AdminLayout/AdminLayout.tsx} +39 -13
  6. package/src/layouts/AppLayout/layouts/{CfgLayout → AdminLayout}/README.md +23 -23
  7. package/src/layouts/AppLayout/layouts/{CfgLayout → AdminLayout}/index.ts +4 -4
  8. package/src/layouts/AppLayout/layouts/{CfgLayout → AdminLayout}/types/index.ts +7 -7
  9. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +2 -0
  10. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +5 -1
  11. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +24 -4
  12. package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +2 -0
  13. package/src/layouts/AppLayout/types/config.ts +4 -1
  14. package/src/layouts/AppLayout/types/layout.ts +7 -0
  15. package/src/layouts/AppLayout/types/navigation.ts +2 -0
  16. package/src/layouts/AppLayout/types/routes.ts +4 -1
  17. /package/src/layouts/AppLayout/layouts/{CfgLayout → AdminLayout}/components/ParentSync.tsx +0 -0
  18. /package/src/layouts/AppLayout/layouts/{CfgLayout → AdminLayout}/components/index.ts +0 -0
  19. /package/src/layouts/AppLayout/layouts/{CfgLayout → AdminLayout}/hooks/index.ts +0 -0
  20. /package/src/layouts/AppLayout/layouts/{CfgLayout → AdminLayout}/hooks/useApp.ts +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "1.2.8",
3
+ "version": "1.2.10",
4
4
  "description": "Layout system and components for Unrealon applications",
5
5
  "author": {
6
6
  "name": "DjangoCFG",
@@ -53,9 +53,9 @@
53
53
  "check": "tsc --noEmit"
54
54
  },
55
55
  "peerDependencies": {
56
- "@djangocfg/api": "^1.2.8",
57
- "@djangocfg/og-image": "^1.2.8",
58
- "@djangocfg/ui": "^1.2.8",
56
+ "@djangocfg/api": "^1.2.10",
57
+ "@djangocfg/og-image": "^1.2.10",
58
+ "@djangocfg/ui": "^1.2.10",
59
59
  "@hookform/resolvers": "^5.2.0",
60
60
  "consola": "^3.4.2",
61
61
  "lucide-react": "^0.468.0",
@@ -76,7 +76,7 @@
76
76
  "vidstack": "0.6.15"
77
77
  },
78
78
  "devDependencies": {
79
- "@djangocfg/typescript-config": "^1.2.8",
79
+ "@djangocfg/typescript-config": "^1.2.10",
80
80
  "@types/node": "^24.7.2",
81
81
  "@types/react": "19.2.2",
82
82
  "@types/react-dom": "19.2.1",
@@ -32,7 +32,7 @@ import { Seo, PageProgress, ErrorBoundary } from './components';
32
32
  import { PublicLayout } from './layouts/PublicLayout';
33
33
  import { PrivateLayout } from './layouts/PrivateLayout';
34
34
  import { AuthLayout } from './layouts/AuthLayout';
35
- import { CfgLayout } from './layouts/CfgLayout';
35
+ import { AdminLayout } from './layouts/AdminLayout';
36
36
  import { determineLayoutMode, getRedirectUrl } from './utils';
37
37
  import { useAuth } from '../../auth';
38
38
  import type { AppLayoutConfig } from './types';
@@ -97,15 +97,47 @@ function LayoutRouter({
97
97
  setIsMounted(true);
98
98
  }, []);
99
99
 
100
- const isAdminMode = forceLayout === 'admin';
101
100
  // If layout is disabled, render children directly (providers still active!)
102
- if (disableLayout || isAdminMode) {
101
+ if (disableLayout) {
103
102
  return <>{children}</>;
104
103
  }
105
104
 
106
105
  // Check route type (synchronous - works with SSR)
107
106
  const isAuthRoute = config.routes.detectors.isAuthRoute(router.pathname);
108
107
  const isPrivateRoute = config.routes.detectors.isPrivateRoute(router.pathname);
108
+ const isAdminRoute = config.routes.detectors.isAdminRoute(router.pathname);
109
+
110
+ // Admin routes: Always show loading during SSR and initial client render
111
+ // This prevents hydration mismatch when isAuthenticated differs between server/client
112
+ if (isAdminRoute && !forceLayout) {
113
+ if (!isMounted || isLoading) {
114
+ return (
115
+ <div className="min-h-screen flex items-center justify-center">
116
+ <div className="text-muted-foreground">Loading...</div>
117
+ </div>
118
+ );
119
+ }
120
+
121
+ // After mount: check authentication
122
+ if (!isAuthenticated) {
123
+ // Redirect to auth
124
+ return (
125
+ <AuthLayout
126
+ termsUrl={config.auth?.termsUrl}
127
+ privacyUrl={config.auth?.privacyUrl}
128
+ supportUrl={config.auth?.supportUrl}
129
+ enablePhoneAuth={config.auth?.enablePhoneAuth}
130
+ />
131
+ );
132
+ }
133
+
134
+ // Admin routes use AdminLayout with ParentSync
135
+ return (
136
+ <AdminLayout enableParentSync={true}>
137
+ {children}
138
+ </AdminLayout>
139
+ );
140
+ }
109
141
 
110
142
  // Private routes: Always show loading during SSR and initial client render
111
143
  // This prevents hydration mismatch when isAuthenticated differs between server/client
@@ -138,8 +170,8 @@ function LayoutRouter({
138
170
  const getLayoutMode = (): 'public' | 'auth' | 'admin' => {
139
171
  if (forceLayout === 'auth') return 'auth';
140
172
  if (forceLayout === 'public') return 'public';
173
+ if (forceLayout === 'admin') return 'admin';
141
174
  if (isAuthRoute) return 'auth';
142
- if (isAdminMode) return 'admin';
143
175
  return 'public';
144
176
  };
145
177
 
@@ -148,8 +180,11 @@ function LayoutRouter({
148
180
  // Render appropriate layout
149
181
  switch (layoutMode) {
150
182
  case 'admin':
151
- return <CfgLayout>{children}</CfgLayout>;
152
- break;
183
+ return (
184
+ <AdminLayout enableParentSync={true}>
185
+ {children}
186
+ </AdminLayout>
187
+ );
153
188
  // Public routes: render immediately (SSR enabled)
154
189
  case 'public':
155
190
  return <PublicLayout>{children}</PublicLayout>;
@@ -209,8 +244,31 @@ export function AppLayout({ children, config, disableLayout = false, forceLayout
209
244
  const supportEmail = config.errors?.supportEmail;
210
245
  const onError = config.errors?.onError;
211
246
 
212
- // Determine if admin mode is enabled (Django CFG iframe integration)
213
- const isAdminMode = forceLayout === 'admin';
247
+ const appContent = (
248
+ <AppContextProvider config={config} showPackageVersions={showPackageVersions}>
249
+ {/* SEO Meta Tags */}
250
+ <Seo
251
+ pageConfig={{
252
+ title: config.app.name,
253
+ description: config.app.description,
254
+ ogImage: {
255
+ title: config.app.name,
256
+ subtitle: config.app.description,
257
+ },
258
+ }}
259
+ icons={config.app.icons}
260
+ siteUrl={config.app.siteUrl}
261
+ />
262
+
263
+ {/* Loading Progress Bar */}
264
+ <PageProgress />
265
+
266
+ {/* Smart Layout Router */}
267
+ <LayoutRouter disableLayout={disableLayout} forceLayout={forceLayout} config={config}>
268
+ {children}
269
+ </LayoutRouter>
270
+ </AppContextProvider>
271
+ );
214
272
 
215
273
  const content = (
216
274
  <>
@@ -222,33 +280,7 @@ export function AppLayout({ children, config, disableLayout = false, forceLayout
222
280
  )}
223
281
 
224
282
  <CoreProviders config={config}>
225
- {/* CfgLayout must be inside CoreProviders to access AuthProvider */}
226
- {/* Only enable ParentSync when in admin mode */}
227
- <CfgLayout enableParentSync={isAdminMode}>
228
- <AppContextProvider config={config} showPackageVersions={showPackageVersions}>
229
- {/* SEO Meta Tags */}
230
- <Seo
231
- pageConfig={{
232
- title: config.app.name,
233
- description: config.app.description,
234
- ogImage: {
235
- title: config.app.name,
236
- subtitle: config.app.description,
237
- },
238
- }}
239
- icons={config.app.icons}
240
- siteUrl={config.app.siteUrl}
241
- />
242
-
243
- {/* Loading Progress Bar */}
244
- <PageProgress />
245
-
246
- {/* Smart Layout Router */}
247
- <LayoutRouter disableLayout={disableLayout} forceLayout={forceLayout} config={config}>
248
- {children}
249
- </LayoutRouter>
250
- </AppContextProvider>
251
- </CfgLayout>
283
+ {appContent}
252
284
  </CoreProviders>
253
285
  </>
254
286
  );
@@ -16,36 +16,36 @@ export interface PackageInfo {
16
16
  /**
17
17
  * Package versions registry
18
18
  * Auto-synced from package.json files
19
- * Last updated: 2025-10-28T04:50:15.743Z
19
+ * Last updated: 2025-10-29T03:56:18.725Z
20
20
  */
21
21
  const PACKAGE_VERSIONS: PackageInfo[] = [
22
22
  {
23
23
  "name": "@djangocfg/ui",
24
- "version": "1.2.8"
24
+ "version": "1.2.10"
25
25
  },
26
26
  {
27
27
  "name": "@djangocfg/api",
28
- "version": "1.2.8"
28
+ "version": "1.2.10"
29
29
  },
30
30
  {
31
31
  "name": "@djangocfg/layouts",
32
- "version": "1.2.8"
32
+ "version": "1.2.10"
33
33
  },
34
34
  {
35
35
  "name": "@djangocfg/markdown",
36
- "version": "1.2.8"
36
+ "version": "1.2.10"
37
37
  },
38
38
  {
39
39
  "name": "@djangocfg/og-image",
40
- "version": "1.2.8"
40
+ "version": "1.2.10"
41
41
  },
42
42
  {
43
43
  "name": "@djangocfg/eslint-config",
44
- "version": "1.2.8"
44
+ "version": "1.2.10"
45
45
  },
46
46
  {
47
47
  "name": "@djangocfg/typescript-config",
48
- "version": "1.2.8"
48
+ "version": "1.2.10"
49
49
  }
50
50
  ];
51
51
 
@@ -30,14 +30,14 @@ export { PublicLayout } from './layouts/PublicLayout';
30
30
  export { PrivateLayout } from './layouts/PrivateLayout';
31
31
  export { AuthLayout } from './layouts/AuthLayout';
32
32
 
33
- // CfgLayout - Django CFG iframe integration
34
- export { CfgLayout } from './layouts/CfgLayout';
35
- export { useCfgApp, useApp } from './layouts/CfgLayout';
36
- export { ParentSync, AuthStatusSync } from './layouts/CfgLayout';
33
+ // AdminLayout - Django CFG iframe integration
34
+ export { AdminLayout } from './layouts/AdminLayout';
35
+ export { useCfgApp, useApp } from './layouts/AdminLayout';
36
+ export { ParentSync, AuthStatusSync } from './layouts/AdminLayout';
37
37
  export type {
38
- CfgLayoutConfig,
38
+ AdminLayoutConfig,
39
39
  UseCfgAppReturn,
40
40
  UseCfgAppOptions,
41
41
  UseAppReturn,
42
42
  UseAppOptions
43
- } from './layouts/CfgLayout';
43
+ } from './layouts/AdminLayout';
@@ -1,5 +1,5 @@
1
1
  // ============================================================================
2
- // CfgLayout - Django CFG Layout with iframe Integration
2
+ // AdminLayout - Django CFG Layout with iframe Integration
3
3
  // ============================================================================
4
4
  // Universal layout component that handles:
5
5
  // - iframe embedding detection
@@ -15,14 +15,16 @@
15
15
  'use client';
16
16
 
17
17
  import React, { ReactNode } from 'react';
18
+ import { ShieldAlert } from 'lucide-react';
18
19
  import { ParentSync } from './components';
19
20
  import { useCfgApp } from './hooks';
20
- import type { CfgLayoutConfig } from './types';
21
+ import type { AdminLayoutConfig } from './types';
21
22
  import { api } from '@djangocfg/api';
23
+ import { useAuth } from '../../../../auth';
22
24
 
23
- export interface CfgLayoutProps {
25
+ export interface AdminLayoutProps {
24
26
  children: ReactNode;
25
- config?: CfgLayoutConfig;
27
+ config?: AdminLayoutConfig;
26
28
  /**
27
29
  * Whether to render ParentSync component
28
30
  * Set to false if you want to handle sync manually
@@ -32,7 +34,7 @@ export interface CfgLayoutProps {
32
34
  }
33
35
 
34
36
  /**
35
- * CfgLayout - Universal Layout Component for Django CFG
37
+ * AdminLayout - Universal Layout Component for Django CFG
36
38
  *
37
39
  * Provides iframe integration features:
38
40
  * - Auto-detects iframe embedding
@@ -44,16 +46,16 @@ export interface CfgLayoutProps {
44
46
  * Usage:
45
47
  * ```tsx
46
48
  * // Wrap your app in _app.tsx - no config needed!
47
- * <CfgLayout>
49
+ * <AdminLayout>
48
50
  * <AppLayout config={appLayoutConfig}>
49
51
  * <Component {...pageProps} />
50
52
  * </AppLayout>
51
- * </CfgLayout>
53
+ * </AdminLayout>
52
54
  * ```
53
55
  *
54
56
  * Or with custom auth handler:
55
57
  * ```tsx
56
- * <CfgLayout config={{
58
+ * <AdminLayout config={{
57
59
  * onAuthTokenReceived: (authToken, refreshToken) => {
58
60
  * // Custom logic before/after setting tokens
59
61
  * console.log('Tokens received');
@@ -62,12 +64,12 @@ export interface CfgLayoutProps {
62
64
  * <AppLayout config={appLayoutConfig}>
63
65
  * <Component {...pageProps} />
64
66
  * </AppLayout>
65
- * </CfgLayout>
67
+ * </AdminLayout>
66
68
  * ```
67
69
  *
68
70
  * Use useCfgApp hook directly:
69
71
  * ```tsx
70
- * import { useCfgApp } from '@djangocfg/layouts/CfgLayout';
72
+ * import { useCfgApp } from '@djangocfg/layouts/AdminLayout';
71
73
  *
72
74
  * function MyComponent() {
73
75
  * const { isEmbedded, disableLayout, parentTheme } = useCfgApp();
@@ -75,11 +77,13 @@ export interface CfgLayoutProps {
75
77
  * }
76
78
  * ```
77
79
  */
78
- export function CfgLayout({
80
+ export function AdminLayout({
79
81
  children,
80
82
  config,
81
83
  enableParentSync = true
82
- }: CfgLayoutProps) {
84
+ }: AdminLayoutProps) {
85
+ const { user } = useAuth();
86
+
83
87
  // useCfgApp hook is called here to initialize iframe communication
84
88
  // Automatically sets tokens in API client when received from parent
85
89
  const { isEmbedded } = useCfgApp({
@@ -94,13 +98,35 @@ export function CfgLayout({
94
98
  }
95
99
  });
96
100
 
101
+ // Check if user has staff or superuser privileges
102
+ const hasAdminAccess = user?.is_staff || user?.is_superuser;
103
+
104
+ // Only render AdminLayout features for staff/superuser
105
+ if (!hasAdminAccess) {
106
+ return (
107
+ <div className="min-h-screen flex items-center justify-center bg-background">
108
+ <div className="text-center space-y-4 p-8">
109
+ <div className="flex justify-center">
110
+ <ShieldAlert className="w-16 h-16 text-destructive" />
111
+ </div>
112
+ <h1 className="text-2xl font-bold text-foreground">Access Denied</h1>
113
+ <p className="text-muted-foreground max-w-md">
114
+ You don't have permission to access this admin panel.
115
+ <br />
116
+ Staff or superuser privileges are required.
117
+ </p>
118
+ </div>
119
+ </div>
120
+ );
121
+ }
122
+
97
123
  return (
98
124
  <>
99
125
  {/* ParentSync handles theme sync and auth status reporting */}
100
126
  {enableParentSync && <ParentSync />}
101
127
 
102
128
  {/* Apply padding only when NOT in iframe */}
103
- <div className={isEmbedded ? '' : 'p-6'}>
129
+ <div className="p-6">
104
130
  {children}
105
131
  </div>
106
132
  </>
@@ -1,8 +1,8 @@
1
- # CfgLayout - Django CFG Layout with iframe Integration
1
+ # AdminLayout - Django CFG Layout with iframe Integration
2
2
 
3
3
  Universal layout component for Django CFG applications that handles iframe embedding, theme synchronization, and authentication communication.
4
4
 
5
- **Note:** CfgLayout is now integrated into AppLayout. For most use cases, use `AppLayout` with `isCfgAdmin={true}` instead of using CfgLayout directly.
5
+ **Note:** AdminLayout is now integrated into AppLayout. For most use cases, use `AppLayout` with `isCfgAdmin={true}` instead of using AdminLayout directly.
6
6
 
7
7
  ## Features
8
8
 
@@ -38,28 +38,28 @@ function MyApp({ Component, pageProps }) {
38
38
  ```
39
39
 
40
40
  **That's it!** Setting `isCfgAdmin={true}` automatically:
41
- - Wraps your app with CfgLayout
41
+ - Wraps your app with AdminLayout
42
42
  - Sets up iframe communication
43
43
  - Handles auth token passing
44
44
  - Syncs theme from parent window
45
45
 
46
46
  ## Direct Usage (Advanced)
47
47
 
48
- If you need more control, you can use CfgLayout directly:
48
+ If you need more control, you can use AdminLayout directly:
49
49
 
50
- ### 1. Wrap your app with CfgLayout (Zero Config!)
50
+ ### 1. Wrap your app with AdminLayout (Zero Config!)
51
51
 
52
52
  ```tsx
53
53
  // apps/admin/src/pages/_app.tsx
54
- import { CfgLayout, AppLayout } from '@djangocfg/layouts';
54
+ import { AdminLayout, AppLayout } from '@djangocfg/layouts';
55
55
 
56
56
  function MyApp({ Component, pageProps }) {
57
57
  return (
58
- <CfgLayout>
58
+ <AdminLayout>
59
59
  <AppLayout config={appLayoutConfig}>
60
60
  <Component {...pageProps} />
61
61
  </AppLayout>
62
- </CfgLayout>
62
+ </AdminLayout>
63
63
  );
64
64
  }
65
65
  ```
@@ -70,7 +70,7 @@ function MyApp({ Component, pageProps }) {
70
70
 
71
71
  ```tsx
72
72
  // Only if you need additional logic when tokens are received
73
- <CfgLayout
73
+ <AdminLayout
74
74
  config={{
75
75
  onAuthTokenReceived: (authToken, refreshToken) => {
76
76
  // Tokens are already set in API client automatically
@@ -82,7 +82,7 @@ function MyApp({ Component, pageProps }) {
82
82
  <AppLayout config={appLayoutConfig}>
83
83
  <Component {...pageProps} />
84
84
  </AppLayout>
85
- </CfgLayout>
85
+ </AdminLayout>
86
86
  ```
87
87
 
88
88
  ## Hook Usage
@@ -140,20 +140,20 @@ function MyApp({ Component, pageProps }) {
140
140
  </AppLayout>
141
141
  ```
142
142
 
143
- ### CfgLayout Props
143
+ ### AdminLayout Props
144
144
 
145
145
  | Prop | Type | Default | Description |
146
146
  |------|------|---------|-------------|
147
147
  | `children` | `ReactNode` | Required | Your app content |
148
- | `config` | `CfgLayoutConfig` | `{}` | Configuration object |
148
+ | `config` | `AdminLayoutConfig` | `{}` | Configuration object |
149
149
  | `enableParentSync` | `boolean` | `true` | Enable automatic theme/auth sync |
150
150
 
151
- ### CfgLayoutConfig
151
+ ### AdminLayoutConfig
152
152
 
153
- **All options are optional!** CfgLayout works with zero configuration.
153
+ **All options are optional!** AdminLayout works with zero configuration.
154
154
 
155
155
  ```typescript
156
- interface CfgLayoutConfig {
156
+ interface AdminLayoutConfig {
157
157
  // Optional: Called when auth tokens are received from parent
158
158
  // Note: Tokens are ALWAYS automatically set in API client
159
159
  // Use this only if you need additional custom logic
@@ -230,9 +230,9 @@ function MyThemeSync() {
230
230
  ### Disable Auto Sync
231
231
 
232
232
  ```tsx
233
- <CfgLayout enableParentSync={false}>
233
+ <AdminLayout enableParentSync={false}>
234
234
  {/* Handle sync manually */}
235
- </CfgLayout>
235
+ </AdminLayout>
236
236
  ```
237
237
 
238
238
  ### URL Override
@@ -289,7 +289,7 @@ import { ParentSync } from '@djangocfg/layouts';
289
289
 
290
290
  **Old approach:**
291
291
  ```tsx
292
- import { AppLayout, CfgLayout, useCfgApp } from '@djangocfg/layouts';
292
+ import { AppLayout, AdminLayout, useCfgApp } from '@djangocfg/layouts';
293
293
 
294
294
  function AppLayoutWrapper() {
295
295
  const { disableLayout, isEmbedded } = useCfgApp();
@@ -302,11 +302,11 @@ function AppLayoutWrapper() {
302
302
 
303
303
  export default function App() {
304
304
  return (
305
- <CfgLayout>
305
+ <AdminLayout>
306
306
  <AppLayoutWrapper>
307
307
  {children}
308
308
  </AppLayoutWrapper>
309
- </CfgLayout>
309
+ </AdminLayout>
310
310
  );
311
311
  }
312
312
  ```
@@ -368,7 +368,7 @@ export default function App({ Component, pageProps }) {
368
368
 
369
369
  **With custom auth logic:**
370
370
  ```tsx
371
- <CfgLayout
371
+ <AdminLayout
372
372
  config={{
373
373
  onAuthTokenReceived: (token, refresh) => {
374
374
  console.log('Tokens received from parent');
@@ -379,11 +379,11 @@ export default function App({ Component, pageProps }) {
379
379
  <AppLayout config={config}>
380
380
  <Component {...pageProps} />
381
381
  </AppLayout>
382
- </CfgLayout>
382
+ </AdminLayout>
383
383
  ```
384
384
 
385
385
  ## Related Files
386
386
 
387
387
  - Django Template: `src/django_cfg/templates/admin/index.html`
388
388
  - Django Views: `src/django_cfg/apps/frontend/views.py`
389
- - Example App: `src/@frontend/apps/admin/src/pages/_app.tsx`
389
+ - Example App: `src/django_admin/apps/admin/src/pages/_app.tsx`
@@ -1,13 +1,13 @@
1
1
  /**
2
- * CfgLayout - Django CFG Layout with iframe Integration
2
+ * AdminLayout - Django CFG Layout with iframe Integration
3
3
  *
4
4
  * Universal layout component for Django CFG applications
5
5
  * Handles iframe embedding, theme sync, and auth communication
6
6
  */
7
7
 
8
8
  // Main component
9
- export { CfgLayout } from './CfgLayout';
10
- export type { CfgLayoutProps } from './CfgLayout';
9
+ export { AdminLayout } from './AdminLayout';
10
+ export type { AdminLayoutProps } from './AdminLayout';
11
11
 
12
12
  // Hooks
13
13
  export { useCfgApp, useApp } from './hooks';
@@ -17,4 +17,4 @@ export type { UseCfgAppReturn, UseCfgAppOptions, UseAppReturn, UseAppOptions } f
17
17
  export { ParentSync, AuthStatusSync } from './components';
18
18
 
19
19
  // Types
20
- export type { CfgLayoutConfig } from './types';
20
+ export type { AdminLayoutConfig } from './types';
@@ -1,10 +1,10 @@
1
1
  import { ReactNode } from 'react';
2
2
 
3
3
  /**
4
- * Configuration for CfgLayout
5
- * All options are optional - CfgLayout works out of the box with zero config
4
+ * Configuration for AdminLayout
5
+ * All options are optional - AdminLayout works out of the box with zero config
6
6
  */
7
- export interface CfgLayoutConfig {
7
+ export interface AdminLayoutConfig {
8
8
  /**
9
9
  * Optional handler called when auth tokens are received from parent window
10
10
  *
@@ -13,7 +13,7 @@ export interface CfgLayoutConfig {
13
13
  *
14
14
  * @example
15
15
  * ```tsx
16
- * <CfgLayout config={{
16
+ * <AdminLayout config={{
17
17
  * onAuthTokenReceived: (authToken, refreshToken) => {
18
18
  * console.log('Tokens received and set in API client');
19
19
  * // Additional custom logic here
@@ -37,9 +37,9 @@ export interface CfgLayoutConfig {
37
37
  }
38
38
 
39
39
  /**
40
- * Props for CfgLayout component
40
+ * Props for AdminLayout component
41
41
  */
42
- export interface CfgLayoutProps {
42
+ export interface AdminLayoutProps {
43
43
  children: ReactNode;
44
- config?: CfgLayoutConfig;
44
+ config?: AdminLayoutConfig;
45
45
  }
@@ -14,6 +14,7 @@ import { useAppContext } from '../../../context';
14
14
  interface DashboardContentProps {
15
15
  children: React.ReactNode;
16
16
  className?: string;
17
+ isAdmin?: boolean;
17
18
  }
18
19
 
19
20
  const paddingVariants = {
@@ -37,6 +38,7 @@ const paddingVariants = {
37
38
  export function DashboardContent({
38
39
  children,
39
40
  className,
41
+ isAdmin = false,
40
42
  }: DashboardContentProps) {
41
43
  const { config } = useAppContext();
42
44
  const { privateLayout } = config;
@@ -20,6 +20,10 @@ import { useAppContext } from '../../../context';
20
20
  import { useNavigation } from '../../../hooks';
21
21
  import { UserMenu } from '../../../components';
22
22
 
23
+ export interface DashboardHeaderProps {
24
+ isAdmin?: boolean;
25
+ }
26
+
23
27
  /**
24
28
  * Dashboard Header Component
25
29
  *
@@ -34,7 +38,7 @@ import { UserMenu } from '../../../components';
34
38
  *
35
39
  * All data from context!
36
40
  */
37
- export function DashboardHeader() {
41
+ export function DashboardHeader({ isAdmin = false }: DashboardHeaderProps) {
38
42
  const { config } = useAppContext();
39
43
  const { getPageTitle } = useNavigation();
40
44
 
@@ -31,6 +31,10 @@ import { useAppContext } from '../../../context';
31
31
  import { useNavigation } from '../../../hooks';
32
32
  import { PackageVersions } from '../../../components';
33
33
 
34
+ export interface DashboardSidebarProps {
35
+ isAdmin?: boolean;
36
+ }
37
+
34
38
  /**
35
39
  * Dashboard Sidebar Component
36
40
  *
@@ -44,12 +48,28 @@ import { PackageVersions } from '../../../components';
44
48
  *
45
49
  * All data from context!
46
50
  */
47
- export function DashboardSidebar() {
51
+ export function DashboardSidebar({ isAdmin = false }: DashboardSidebarProps) {
48
52
  const { config, showPackageVersions } = useAppContext();
49
53
  const { currentPath } = useNavigation();
50
54
  const { state, isMobile } = useSidebar();
51
55
 
52
- const { app, privateLayout } = config;
56
+ const { app, privateLayout, adminLayout } = config;
57
+
58
+ // Admin layout: use adminLayout.menuSections converted to menuGroups, fallback to privateLayout
59
+ const menuGroups = isAdmin && adminLayout
60
+ ? adminLayout.menuSections.map((section, idx) => ({
61
+ label: section.title,
62
+ order: idx + 1,
63
+ items: section.items.map(item => ({
64
+ path: item.path,
65
+ label: item.label,
66
+ icon: item.icon || (() => null),
67
+ badge: item.badge,
68
+ })),
69
+ }))
70
+ : privateLayout.menuGroups;
71
+
72
+ const homeHref = privateLayout.homeHref;
53
73
 
54
74
  const isActiveRoute = (path: string) => {
55
75
  // Only exact match - no prefix matching
@@ -73,7 +93,7 @@ export function DashboardSidebar() {
73
93
  transition: 'padding 200ms ease-in-out'
74
94
  }}
75
95
  >
76
- <Link href={privateLayout.homeHref}>
96
+ <Link href={homeHref}>
77
97
  <div className="flex items-center gap-3">
78
98
  {app.logoPath ? (
79
99
  <img
@@ -99,7 +119,7 @@ export function DashboardSidebar() {
99
119
  </SidebarHeader>
100
120
 
101
121
  <SidebarContent>
102
- {privateLayout.menuGroups.map((group) => (
122
+ {menuGroups.map((group) => (
103
123
  <SidebarGroup key={group.label}>
104
124
  <SidebarGroupLabel>{group.label}</SidebarGroupLabel>
105
125
  <SidebarGroupContent>
@@ -3,5 +3,7 @@
3
3
  */
4
4
 
5
5
  export { DashboardHeader } from './DashboardHeader';
6
+ export type { DashboardHeaderProps } from './DashboardHeader';
6
7
  export { DashboardSidebar } from './DashboardSidebar';
8
+ export type { DashboardSidebarProps } from './DashboardSidebar';
7
9
  export { DashboardContent } from './DashboardContent';
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import type { ReactNode } from 'react';
6
- import type { PublicLayoutConfig, PrivateLayoutConfig } from './layout';
6
+ import type { PublicLayoutConfig, PrivateLayoutConfig, AdminLayoutConfig } from './layout';
7
7
  import type { RouteConfig } from './routes';
8
8
 
9
9
  /**
@@ -38,6 +38,9 @@ export interface AppLayoutConfig {
38
38
  /** Private layout configuration */
39
39
  privateLayout: PrivateLayoutConfig;
40
40
 
41
+ /** Admin layout configuration (optional) */
42
+ adminLayout?: AdminLayoutConfig;
43
+
41
44
  /** Error handling configuration */
42
45
  errors?: {
43
46
  /** Enable automatic error boundary (default: true) */
@@ -45,3 +45,10 @@ export interface PrivateLayoutConfig {
45
45
  contentPadding?: 'default' | 'none';
46
46
  headerActions?: ReactNode;
47
47
  }
48
+
49
+ /**
50
+ * Admin layout configuration
51
+ */
52
+ export interface AdminLayoutConfig {
53
+ menuSections: NavigationSection[];
54
+ }
@@ -10,6 +10,8 @@ import type { LucideIcon } from 'lucide-react';
10
10
  export interface NavigationItem {
11
11
  label: string;
12
12
  path: string;
13
+ icon?: LucideIcon;
14
+ badge?: string | number;
13
15
  }
14
16
 
15
17
  /**
@@ -29,6 +29,9 @@ export interface RouteDetectors {
29
29
  /** Check if route is private/protected */
30
30
  isPrivateRoute: (path: string) => boolean;
31
31
 
32
+ /** Check if route is admin */
33
+ isAdminRoute: (path: string) => boolean;
34
+
32
35
  /** Check if route is auth page */
33
36
  isAuthRoute: (path: string) => boolean;
34
37
 
@@ -42,4 +45,4 @@ export interface RouteDetectors {
42
45
  /**
43
46
  * Layout mode based on route
44
47
  */
45
- export type LayoutMode = 'public' | 'private' | 'auth';
48
+ export type LayoutMode = 'public' | 'private' | 'admin' | 'auth';