@djangocfg/layouts 1.2.5 → 1.2.7
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 +5 -5
- package/src/auth/context/AuthContext.tsx +10 -6
- package/src/layouts/AppLayout/AppLayout.tsx +82 -55
- package/src/layouts/AppLayout/components/PackageVersions/packageVersions.config.ts +8 -8
- package/src/layouts/AppLayout/index.ts +12 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/CfgLayout.tsx +104 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/README.md +389 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/components/ParentSync.tsx +149 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/components/index.ts +1 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/hooks/index.ts +6 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/hooks/useApp.ts +282 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/index.ts +20 -0
- package/src/layouts/AppLayout/layouts/CfgLayout/types/index.ts +45 -0
- package/src/layouts/UILayout/config/components/data.config.tsx +125 -0
- package/src/layouts/UILayout/config/components/feedback.config.tsx +44 -0
- package/src/layouts/UILayout/config/components/forms.config.tsx +194 -0
- package/src/layouts/UILayout/config/components/layout.config.tsx +95 -0
- package/src/layouts/UILayout/config/components/navigation.config.tsx +50 -0
- package/src/layouts/UILayout/config/components/specialized.config.tsx +250 -0
- package/src/layouts/index.ts +3 -0
- package/src/utils/logger.ts +4 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.7",
|
|
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.
|
|
57
|
-
"@djangocfg/og-image": "^1.2.
|
|
58
|
-
"@djangocfg/ui": "^1.2.
|
|
56
|
+
"@djangocfg/api": "^1.2.7",
|
|
57
|
+
"@djangocfg/og-image": "^1.2.7",
|
|
58
|
+
"@djangocfg/ui": "^1.2.7",
|
|
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.
|
|
79
|
+
"@djangocfg/typescript-config": "^1.2.7",
|
|
80
80
|
"@types/node": "^24.7.2",
|
|
81
81
|
"@types/react": "19.2.2",
|
|
82
82
|
"@types/react-dom": "19.2.1",
|
|
@@ -101,14 +101,16 @@ const AuthProviderInternal: React.FC<AuthProviderProps> = ({ children, config })
|
|
|
101
101
|
|
|
102
102
|
// Refresh profile from AccountsContext
|
|
103
103
|
const refreshedProfile = await accounts.refreshProfile();
|
|
104
|
-
|
|
104
|
+
|
|
105
105
|
if (refreshedProfile) {
|
|
106
|
-
setInitialized(true);
|
|
107
106
|
authLogger.info('Profile loaded successfully:', refreshedProfile.id);
|
|
108
107
|
} else {
|
|
109
|
-
authLogger.warn('Profile refresh returned undefined');
|
|
110
|
-
clearAuthState('loadCurrentProfile:noProfile');
|
|
108
|
+
authLogger.warn('Profile refresh returned undefined - but keeping tokens');
|
|
111
109
|
}
|
|
110
|
+
|
|
111
|
+
// Always mark as initialized if we have valid tokens
|
|
112
|
+
// Don't clear tokens just because profile fetch failed
|
|
113
|
+
setInitialized(true);
|
|
112
114
|
} catch (error) {
|
|
113
115
|
authLogger.error('Failed to load profile:', error);
|
|
114
116
|
// Use global error handler first, fallback to clearing state
|
|
@@ -166,7 +168,8 @@ const AuthProviderInternal: React.FC<AuthProviderProps> = ({ children, config })
|
|
|
166
168
|
useEffect(() => {
|
|
167
169
|
if (!initialized) return;
|
|
168
170
|
|
|
169
|
-
|
|
171
|
+
// Consider authenticated if we have valid tokens, even without profile
|
|
172
|
+
const isAuthenticated = api.isAuthenticated();
|
|
170
173
|
const authRoute = config?.routes?.auth || defaultRoutes.auth;
|
|
171
174
|
const isAuthPage = router.pathname === authRoute;
|
|
172
175
|
|
|
@@ -408,7 +411,8 @@ const AuthProviderInternal: React.FC<AuthProviderProps> = ({ children, config })
|
|
|
408
411
|
() => ({
|
|
409
412
|
user,
|
|
410
413
|
isLoading,
|
|
411
|
-
|
|
414
|
+
// Consider authenticated if we have valid tokens, even without user profile
|
|
415
|
+
isAuthenticated: api.isAuthenticated(),
|
|
412
416
|
loadCurrentProfile,
|
|
413
417
|
checkAuthAndRedirect,
|
|
414
418
|
getToken: () => api.getToken(),
|
|
@@ -6,12 +6,19 @@
|
|
|
6
6
|
* - Applies correct layout automatically
|
|
7
7
|
* - Manages all state through context
|
|
8
8
|
* - Zero prop drilling
|
|
9
|
+
* - Optional Django CFG admin mode with iframe integration
|
|
9
10
|
*
|
|
10
11
|
* Usage in _app.tsx:
|
|
11
12
|
* ```tsx
|
|
13
|
+
* // Standard usage
|
|
12
14
|
* <AppLayout config={appLayoutConfig}>
|
|
13
15
|
* <Component {...pageProps} />
|
|
14
16
|
* </AppLayout>
|
|
17
|
+
*
|
|
18
|
+
* // Django CFG admin mode (with iframe integration)
|
|
19
|
+
* <AppLayout config={appLayoutConfig} isCfgAdmin={true}>
|
|
20
|
+
* <Component {...pageProps} />
|
|
21
|
+
* </AppLayout>
|
|
15
22
|
* ```
|
|
16
23
|
*/
|
|
17
24
|
|
|
@@ -25,6 +32,7 @@ import { Seo, PageProgress, ErrorBoundary } from './components';
|
|
|
25
32
|
import { PublicLayout } from './layouts/PublicLayout';
|
|
26
33
|
import { PrivateLayout } from './layouts/PrivateLayout';
|
|
27
34
|
import { AuthLayout } from './layouts/AuthLayout';
|
|
35
|
+
import { CfgLayout } from './layouts/CfgLayout';
|
|
28
36
|
import { determineLayoutMode, getRedirectUrl } from './utils';
|
|
29
37
|
import { useAuth } from '../../auth';
|
|
30
38
|
import type { AppLayoutConfig } from './types';
|
|
@@ -41,9 +49,13 @@ export interface AppLayoutProps {
|
|
|
41
49
|
/**
|
|
42
50
|
* Force a specific layout regardless of route
|
|
43
51
|
* Overrides automatic layout detection
|
|
52
|
+
*
|
|
44
53
|
* @example forceLayout="public" - always use PublicLayout
|
|
54
|
+
* @example forceLayout="private" - always use PrivateLayout
|
|
55
|
+
* @example forceLayout="auth" - always use AuthLayout
|
|
56
|
+
* @example forceLayout="admin" - Django CFG admin mode with iframe integration
|
|
45
57
|
*/
|
|
46
|
-
forceLayout?: 'public' | 'private' | 'auth';
|
|
58
|
+
forceLayout?: 'public' | 'private' | 'auth' | 'admin';
|
|
47
59
|
/**
|
|
48
60
|
* Font family to apply globally
|
|
49
61
|
* Accepts Next.js font object or CSS font-family string
|
|
@@ -73,7 +85,7 @@ function LayoutRouter({
|
|
|
73
85
|
}: {
|
|
74
86
|
children: ReactNode;
|
|
75
87
|
disableLayout?: boolean;
|
|
76
|
-
forceLayout?: 'public' | 'private' | 'auth';
|
|
88
|
+
forceLayout?: 'public' | 'private' | 'auth' | 'admin';
|
|
77
89
|
config: AppLayoutConfig;
|
|
78
90
|
}) {
|
|
79
91
|
const router = useRouter();
|
|
@@ -85,29 +97,49 @@ function LayoutRouter({
|
|
|
85
97
|
setIsMounted(true);
|
|
86
98
|
}, []);
|
|
87
99
|
|
|
100
|
+
const isAdminMode = forceLayout === 'admin';
|
|
88
101
|
// If layout is disabled, render children directly (providers still active!)
|
|
89
|
-
if (disableLayout) {
|
|
102
|
+
if (disableLayout || isAdminMode) {
|
|
90
103
|
return <>{children}</>;
|
|
91
104
|
}
|
|
92
105
|
|
|
93
|
-
//
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
if (forceLayout) return forceLayout;
|
|
106
|
+
// Check route type (synchronous - works with SSR)
|
|
107
|
+
const isAuthRoute = config.routes.detectors.isAuthRoute(router.pathname);
|
|
108
|
+
const isPrivateRoute = config.routes.detectors.isPrivateRoute(router.pathname);
|
|
97
109
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
110
|
+
// Private routes: Always show loading during SSR and initial client render
|
|
111
|
+
// This prevents hydration mismatch when isAuthenticated differs between server/client
|
|
112
|
+
if (isPrivateRoute && !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
|
+
}
|
|
101
120
|
|
|
102
|
-
|
|
121
|
+
// After mount: check authentication
|
|
122
|
+
if (!isAuthenticated) {
|
|
123
|
+
// Redirect to auth (handled in PrivateLayout)
|
|
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
|
+
}
|
|
103
133
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
return 'private';
|
|
107
|
-
};
|
|
108
|
-
return 'auth';
|
|
109
|
-
};
|
|
134
|
+
return <PrivateLayout>{children}</PrivateLayout>;
|
|
135
|
+
}
|
|
110
136
|
|
|
137
|
+
// Determine layout mode for non-private routes
|
|
138
|
+
const getLayoutMode = (): 'public' | 'auth' | 'admin' => {
|
|
139
|
+
if (forceLayout === 'auth') return 'auth';
|
|
140
|
+
if (forceLayout === 'public') return 'public';
|
|
141
|
+
if (isAuthRoute) return 'auth';
|
|
142
|
+
if (isAdminMode) return 'admin';
|
|
111
143
|
return 'public';
|
|
112
144
|
};
|
|
113
145
|
|
|
@@ -115,15 +147,15 @@ function LayoutRouter({
|
|
|
115
147
|
|
|
116
148
|
// Render appropriate layout
|
|
117
149
|
switch (layoutMode) {
|
|
150
|
+
case 'admin':
|
|
151
|
+
return <CfgLayout>{children}</CfgLayout>;
|
|
152
|
+
break;
|
|
118
153
|
// Public routes: render immediately (SSR enabled)
|
|
119
154
|
case 'public':
|
|
120
155
|
return <PublicLayout>{children}</PublicLayout>;
|
|
121
156
|
|
|
122
157
|
// Auth routes: render inside AuthLayout
|
|
123
158
|
case 'auth':
|
|
124
|
-
// Check if we're on a private route that requires auth
|
|
125
|
-
const isPrivateRoute = config.routes.detectors.isPrivateRoute(router.pathname);
|
|
126
|
-
|
|
127
159
|
return (
|
|
128
160
|
<AuthLayout
|
|
129
161
|
termsUrl={config.auth?.termsUrl}
|
|
@@ -131,22 +163,10 @@ function LayoutRouter({
|
|
|
131
163
|
supportUrl={config.auth?.supportUrl}
|
|
132
164
|
enablePhoneAuth={config.auth?.enablePhoneAuth}
|
|
133
165
|
>
|
|
134
|
-
{
|
|
135
|
-
{!isPrivateRoute && children}
|
|
166
|
+
{children}
|
|
136
167
|
</AuthLayout>
|
|
137
168
|
);
|
|
138
169
|
|
|
139
|
-
// Private routes: wait for client-side hydration and auth check
|
|
140
|
-
case 'private':
|
|
141
|
-
if (!isMounted || isLoading) {
|
|
142
|
-
return (
|
|
143
|
-
<div className="min-h-screen flex items-center justify-center">
|
|
144
|
-
<div className="text-muted-foreground">Loading...</div>
|
|
145
|
-
</div>
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
return <PrivateLayout>{children}</PrivateLayout>;
|
|
149
|
-
|
|
150
170
|
default:
|
|
151
171
|
return <PublicLayout>{children}</PublicLayout>;
|
|
152
172
|
}
|
|
@@ -189,6 +209,9 @@ export function AppLayout({ children, config, disableLayout = false, forceLayout
|
|
|
189
209
|
const supportEmail = config.errors?.supportEmail;
|
|
190
210
|
const onError = config.errors?.onError;
|
|
191
211
|
|
|
212
|
+
// Determine if admin mode is enabled (Django CFG iframe integration)
|
|
213
|
+
const isAdminMode = forceLayout === 'admin';
|
|
214
|
+
|
|
192
215
|
const content = (
|
|
193
216
|
<>
|
|
194
217
|
{/* Global Font Styles */}
|
|
@@ -199,29 +222,33 @@ export function AppLayout({ children, config, disableLayout = false, forceLayout
|
|
|
199
222
|
)}
|
|
200
223
|
|
|
201
224
|
<CoreProviders config={config}>
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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={{
|
|
209
232
|
title: config.app.name,
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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>
|
|
225
252
|
</CoreProviders>
|
|
226
253
|
</>
|
|
227
254
|
);
|
|
@@ -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-
|
|
19
|
+
* Last updated: 2025-10-28T04:15:24.707Z
|
|
20
20
|
*/
|
|
21
21
|
const PACKAGE_VERSIONS: PackageInfo[] = [
|
|
22
22
|
{
|
|
23
23
|
"name": "@djangocfg/ui",
|
|
24
|
-
"version": "1.2.
|
|
24
|
+
"version": "1.2.7"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"name": "@djangocfg/api",
|
|
28
|
-
"version": "1.2.
|
|
28
|
+
"version": "1.2.7"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"name": "@djangocfg/layouts",
|
|
32
|
-
"version": "1.2.
|
|
32
|
+
"version": "1.2.7"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"name": "@djangocfg/markdown",
|
|
36
|
-
"version": "1.2.
|
|
36
|
+
"version": "1.2.7"
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
"name": "@djangocfg/og-image",
|
|
40
|
-
"version": "1.2.
|
|
40
|
+
"version": "1.2.7"
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"name": "@djangocfg/eslint-config",
|
|
44
|
-
"version": "1.2.
|
|
44
|
+
"version": "1.2.7"
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"name": "@djangocfg/typescript-config",
|
|
48
|
-
"version": "1.2.
|
|
48
|
+
"version": "1.2.7"
|
|
49
49
|
}
|
|
50
50
|
];
|
|
51
51
|
|
|
@@ -29,3 +29,15 @@ export { useLayoutMode, useNavigation } from './hooks';
|
|
|
29
29
|
export { PublicLayout } from './layouts/PublicLayout';
|
|
30
30
|
export { PrivateLayout } from './layouts/PrivateLayout';
|
|
31
31
|
export { AuthLayout } from './layouts/AuthLayout';
|
|
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';
|
|
37
|
+
export type {
|
|
38
|
+
CfgLayoutConfig,
|
|
39
|
+
UseCfgAppReturn,
|
|
40
|
+
UseCfgAppOptions,
|
|
41
|
+
UseAppReturn,
|
|
42
|
+
UseAppOptions
|
|
43
|
+
} from './layouts/CfgLayout';
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
// ============================================================================
|
|
2
|
+
// CfgLayout - Django CFG Layout with iframe Integration
|
|
3
|
+
// ============================================================================
|
|
4
|
+
// Universal layout component that handles:
|
|
5
|
+
// - iframe embedding detection
|
|
6
|
+
// - Parent ↔ iframe communication (postMessage)
|
|
7
|
+
// - Theme synchronization from Django Unfold
|
|
8
|
+
// - Auth token passing from parent window (automatically sets in API client)
|
|
9
|
+
// - Auth status reporting to parent window
|
|
10
|
+
// - Automatic layout disable in iframe mode
|
|
11
|
+
//
|
|
12
|
+
// This is a lightweight wrapper that can be used with any layout system
|
|
13
|
+
// (AppLayout, custom layouts, etc.)
|
|
14
|
+
|
|
15
|
+
'use client';
|
|
16
|
+
|
|
17
|
+
import React, { ReactNode } from 'react';
|
|
18
|
+
import { ParentSync } from './components';
|
|
19
|
+
import { useCfgApp } from './hooks';
|
|
20
|
+
import type { CfgLayoutConfig } from './types';
|
|
21
|
+
import { api } from '@djangocfg/api';
|
|
22
|
+
|
|
23
|
+
export interface CfgLayoutProps {
|
|
24
|
+
children: ReactNode;
|
|
25
|
+
config?: CfgLayoutConfig;
|
|
26
|
+
/**
|
|
27
|
+
* Whether to render ParentSync component
|
|
28
|
+
* Set to false if you want to handle sync manually
|
|
29
|
+
* @default true
|
|
30
|
+
*/
|
|
31
|
+
enableParentSync?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* CfgLayout - Universal Layout Component for Django CFG
|
|
36
|
+
*
|
|
37
|
+
* Provides iframe integration features:
|
|
38
|
+
* - Auto-detects iframe embedding
|
|
39
|
+
* - Syncs theme from parent window (Django Unfold)
|
|
40
|
+
* - Receives auth tokens from parent window and automatically sets them in API client
|
|
41
|
+
* - Sends auth status to parent window
|
|
42
|
+
* - Provides useApp hook data via context
|
|
43
|
+
*
|
|
44
|
+
* Usage:
|
|
45
|
+
* ```tsx
|
|
46
|
+
* // Wrap your app in _app.tsx - no config needed!
|
|
47
|
+
* <CfgLayout>
|
|
48
|
+
* <AppLayout config={appLayoutConfig}>
|
|
49
|
+
* <Component {...pageProps} />
|
|
50
|
+
* </AppLayout>
|
|
51
|
+
* </CfgLayout>
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* Or with custom auth handler:
|
|
55
|
+
* ```tsx
|
|
56
|
+
* <CfgLayout config={{
|
|
57
|
+
* onAuthTokenReceived: (authToken, refreshToken) => {
|
|
58
|
+
* // Custom logic before/after setting tokens
|
|
59
|
+
* console.log('Tokens received');
|
|
60
|
+
* }
|
|
61
|
+
* }}>
|
|
62
|
+
* <AppLayout config={appLayoutConfig}>
|
|
63
|
+
* <Component {...pageProps} />
|
|
64
|
+
* </AppLayout>
|
|
65
|
+
* </CfgLayout>
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* Use useCfgApp hook directly:
|
|
69
|
+
* ```tsx
|
|
70
|
+
* import { useCfgApp } from '@djangocfg/layouts/CfgLayout';
|
|
71
|
+
*
|
|
72
|
+
* function MyComponent() {
|
|
73
|
+
* const { isEmbedded, disableLayout, parentTheme } = useCfgApp();
|
|
74
|
+
* // ...
|
|
75
|
+
* }
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export function CfgLayout({
|
|
79
|
+
children,
|
|
80
|
+
config,
|
|
81
|
+
enableParentSync = true
|
|
82
|
+
}: CfgLayoutProps) {
|
|
83
|
+
// useCfgApp hook is called here to initialize iframe communication
|
|
84
|
+
// Automatically sets tokens in API client when received from parent
|
|
85
|
+
useCfgApp({
|
|
86
|
+
onAuthTokenReceived: (authToken, refreshToken) => {
|
|
87
|
+
// Always set tokens in API client
|
|
88
|
+
api.setToken(authToken, refreshToken);
|
|
89
|
+
|
|
90
|
+
// Call custom handler if provided
|
|
91
|
+
if (config?.onAuthTokenReceived) {
|
|
92
|
+
config.onAuthTokenReceived(authToken, refreshToken);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return (
|
|
98
|
+
<>
|
|
99
|
+
{/* ParentSync handles theme sync and auth status reporting */}
|
|
100
|
+
{enableParentSync && <ParentSync />}
|
|
101
|
+
{children}
|
|
102
|
+
</>
|
|
103
|
+
);
|
|
104
|
+
}
|