@djangocfg/layouts 1.4.30 → 2.0.2

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 (119) hide show
  1. package/README.md +277 -18
  2. package/package.json +15 -24
  3. package/src/auth/context/AuthContext.tsx +5 -5
  4. package/src/auth/hooks/useAuthGuard.ts +1 -1
  5. package/src/auth/hooks/useAutoAuth.ts +8 -7
  6. package/src/components/ErrorBoundary.tsx +78 -0
  7. package/src/components/JsonLd.tsx +31 -0
  8. package/src/components/LucideIcon.tsx +91 -0
  9. package/src/components/PageProgress.tsx +127 -0
  10. package/src/components/Suspense.tsx +29 -0
  11. package/src/{layouts/AppLayout/components → components}/UpdateNotifier/UpdateNotifier.tsx +56 -49
  12. package/src/components/index.ts +10 -0
  13. package/src/index.ts +25 -7
  14. package/src/layouts/AdminLayout/AdminLayout.tsx +46 -0
  15. package/src/layouts/AdminLayout/index.ts +7 -0
  16. package/src/layouts/AppLayout/AppLayout.tsx +278 -326
  17. package/src/layouts/AppLayout/index.ts +2 -39
  18. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthContext.tsx +3 -2
  19. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthHelp.tsx +1 -0
  20. package/src/layouts/AuthLayout/AuthLayout.tsx +61 -0
  21. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/IdentifierForm.tsx +47 -34
  22. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/OTPForm.tsx +2 -3
  23. package/src/layouts/AuthLayout/index.ts +24 -0
  24. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/types.ts +1 -0
  25. package/src/layouts/PrivateLayout/PrivateLayout.tsx +144 -0
  26. package/src/layouts/PrivateLayout/components/PrivateContent.tsx +32 -0
  27. package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +57 -0
  28. package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +141 -0
  29. package/src/layouts/PrivateLayout/components/index.ts +8 -0
  30. package/src/layouts/PrivateLayout/index.ts +7 -0
  31. package/src/layouts/ProfileLayout/ProfileLayout.tsx +15 -7
  32. package/src/layouts/PublicLayout/PublicLayout.tsx +121 -0
  33. package/src/layouts/PublicLayout/components/PublicFooter.tsx +190 -0
  34. package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +117 -0
  35. package/src/layouts/PublicLayout/components/PublicNavigation.tsx +101 -0
  36. package/src/layouts/PublicLayout/components/index.ts +8 -0
  37. package/src/layouts/PublicLayout/index.ts +7 -0
  38. package/src/layouts/_components/UserMenu.tsx +160 -0
  39. package/src/layouts/_components/index.ts +7 -0
  40. package/src/layouts/index.ts +15 -8
  41. package/src/snippets/Analytics/AnalyticsProvider.tsx +8 -4
  42. package/src/snippets/Analytics/useAnalytics.ts +11 -21
  43. package/src/snippets/Chat/ChatWidget.tsx +4 -4
  44. package/src/snippets/ContactForm/ContactFormProvider.tsx +32 -19
  45. package/src/snippets/ContactForm/ContactPage.tsx +2 -4
  46. package/src/snippets/ContactForm/types.ts +3 -2
  47. package/src/snippets/index.ts +0 -1
  48. package/src/layouts/AppLayout/README.md +0 -204
  49. package/src/layouts/AppLayout/SUMMARY.md +0 -240
  50. package/src/layouts/AppLayout/USAGE.md +0 -312
  51. package/src/layouts/AppLayout/components/ErrorBoundary.tsx +0 -112
  52. package/src/layouts/AppLayout/components/PageProgress.tsx +0 -123
  53. package/src/layouts/AppLayout/components/Seo.tsx +0 -171
  54. package/src/layouts/AppLayout/components/UserMenu.tsx +0 -385
  55. package/src/layouts/AppLayout/components/index.ts +0 -11
  56. package/src/layouts/AppLayout/context/AppContext.tsx +0 -151
  57. package/src/layouts/AppLayout/context/index.ts +0 -5
  58. package/src/layouts/AppLayout/hooks/index.ts +0 -8
  59. package/src/layouts/AppLayout/hooks/useLayoutMode.ts +0 -26
  60. package/src/layouts/AppLayout/hooks/useNavigation.ts +0 -51
  61. package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +0 -224
  62. package/src/layouts/AppLayout/layouts/AdminLayout/README.md +0 -409
  63. package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.example.tsx +0 -98
  64. package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.tsx +0 -149
  65. package/src/layouts/AppLayout/layouts/AdminLayout/components/ParentSync.tsx +0 -146
  66. package/src/layouts/AppLayout/layouts/AdminLayout/components/index.ts +0 -3
  67. package/src/layouts/AppLayout/layouts/AdminLayout/context/CfgAppContext.tsx +0 -48
  68. package/src/layouts/AppLayout/layouts/AdminLayout/context/index.ts +0 -2
  69. package/src/layouts/AppLayout/layouts/AdminLayout/hooks/index.ts +0 -6
  70. package/src/layouts/AppLayout/layouts/AdminLayout/hooks/useApp.ts +0 -279
  71. package/src/layouts/AppLayout/layouts/AdminLayout/index.ts +0 -24
  72. package/src/layouts/AppLayout/layouts/AdminLayout/lottie/energizing.json +0 -1
  73. package/src/layouts/AppLayout/layouts/AdminLayout/types/index.ts +0 -45
  74. package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +0 -41
  75. package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +0 -15
  76. package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +0 -82
  77. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +0 -62
  78. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +0 -89
  79. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +0 -181
  80. package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +0 -9
  81. package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +0 -5
  82. package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +0 -44
  83. package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +0 -242
  84. package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileDrawer.tsx +0 -150
  85. package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +0 -169
  86. package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +0 -5
  87. package/src/layouts/AppLayout/layouts/index.ts +0 -7
  88. package/src/layouts/AppLayout/providers/CoreProviders.tsx +0 -80
  89. package/src/layouts/AppLayout/providers/index.ts +0 -5
  90. package/src/layouts/AppLayout/types/config.ts +0 -79
  91. package/src/layouts/AppLayout/types/index.ts +0 -11
  92. package/src/layouts/AppLayout/types/layout.ts +0 -54
  93. package/src/layouts/AppLayout/types/navigation.ts +0 -43
  94. package/src/layouts/AppLayout/types/page.ts +0 -80
  95. package/src/layouts/AppLayout/types/routes.ts +0 -43
  96. package/src/layouts/AppLayout/utils/index.ts +0 -5
  97. package/src/layouts/AppLayout/utils/routeDetection.ts +0 -31
  98. package/src/layouts/ErrorLayout/ErrorLayout.tsx +0 -173
  99. package/src/layouts/ErrorLayout/errorConfig.tsx +0 -152
  100. package/src/layouts/ErrorLayout/index.ts +0 -8
  101. package/src/layouts/SimpleLayout/SimpleLayout.tsx +0 -72
  102. package/src/layouts/SimpleLayout/index.ts +0 -3
  103. package/src/snippets/VideoPlayer/README.md +0 -238
  104. package/src/snippets/VideoPlayer/VideoControls.tsx +0 -137
  105. package/src/snippets/VideoPlayer/VideoPlayer.tsx +0 -248
  106. package/src/snippets/VideoPlayer/index.ts +0 -8
  107. package/src/snippets/VideoPlayer/types.ts +0 -61
  108. package/src/types/index.ts +0 -2
  109. package/src/types/pageConfig.ts +0 -100
  110. /package/src/{validation → components/ErrorsTracker}/README.md +0 -0
  111. /package/src/{validation → components/ErrorsTracker}/components/ErrorButtons.tsx +0 -0
  112. /package/src/{validation → components/ErrorsTracker}/components/ErrorToast.tsx +0 -0
  113. /package/src/{validation → components/ErrorsTracker}/hooks.ts +0 -0
  114. /package/src/{validation → components/ErrorsTracker}/index.ts +0 -0
  115. /package/src/{validation → components/ErrorsTracker}/providers/ErrorTrackingProvider.tsx +0 -0
  116. /package/src/{validation → components/ErrorsTracker}/types.ts +0 -0
  117. /package/src/{validation → components/ErrorsTracker}/utils/curl-generator.ts +0 -0
  118. /package/src/{validation → components/ErrorsTracker}/utils/formatters.ts +0 -0
  119. /package/src/{layouts/AppLayout/components → components}/UpdateNotifier/index.ts +0 -0
@@ -1,146 +0,0 @@
1
- // ============================================================================
2
- // Parent Sync Component
3
- // ============================================================================
4
- // Handles all synchronization between iframe and parent window:
5
- // - Theme sync (parent → iframe)
6
- // - Auth status sync (iframe → parent)
7
- // Must be used inside AuthProvider and ThemeProvider contexts
8
-
9
- 'use client';
10
-
11
- import { useEffect, useState } from 'react';
12
- import { useRouter } from 'next/router';
13
- import { useAuth } from '../../../../../auth';
14
- import { useThemeContext } from '@djangocfg/ui';
15
- import { useCfgAppContext } from '../context';
16
- import { authLogger } from '../../../../../utils/logger';
17
-
18
- /**
19
- * ParentSync Component
20
- *
21
- * Handles bidirectional communication with parent window when embedded:
22
- * 1. Receives theme updates from parent (Django Unfold) and applies to ThemeProvider
23
- * 2. Sends auth status to parent when authentication state changes
24
- *
25
- * Usage:
26
- * - Place inside AppLayout (after AuthProvider and ThemeProvider)
27
- * - Automatically handles all iframe ↔ parent communication
28
- *
29
- * Note: Safe for static export - only runs on client-side after hydration
30
- */
31
- export function ParentSync() {
32
- const [isClient, setIsClient] = useState(false);
33
-
34
- // Wait for client-side hydration to avoid SSR/static export errors
35
- useEffect(() => {
36
- setIsClient(true);
37
- }, []);
38
-
39
- // Early return during SSR/static export
40
- if (!isClient) {
41
- return null;
42
- }
43
-
44
- return <ParentSyncClient />;
45
- }
46
-
47
- /**
48
- * Client-only component that uses auth context
49
- * Separated to avoid SSR issues during static export
50
- */
51
- function ParentSyncClient() {
52
- const router = useRouter();
53
- const auth = useAuth();
54
- const { setTheme } = useThemeContext();
55
- const { isEmbedded, isMounted, parentTheme } = useCfgAppContext();
56
-
57
- // 1. Sync theme from parent → iframe
58
- useEffect(() => {
59
- // console.log('[ParentSync] Theme sync effect triggered - isEmbedded:', isEmbedded, 'parentTheme:', parentTheme);
60
- if (isEmbedded && parentTheme) {
61
- // console.log('[ParentSync] Syncing theme from parent:', parentTheme);
62
- setTheme(parentTheme);
63
- // console.log('[ParentSync] setTheme called with:', parentTheme);
64
- } else {
65
- // console.log('[ParentSync] Skipping theme sync - isEmbedded:', isEmbedded, 'parentTheme:', parentTheme);
66
- }
67
- }, [isEmbedded, parentTheme, setTheme]);
68
-
69
- // 2. Send auth status from iframe → parent (debounced to prevent spam)
70
- useEffect(() => {
71
- // Only send if embedded and mounted
72
- if (!isEmbedded || !isMounted) {
73
- // console.log('[ParentSync] Skipping auth sync - not embedded or not mounted:', {
74
- // isEmbedded,
75
- // isMounted
76
- // });
77
- return;
78
- }
79
-
80
- // Use primitive values to avoid unnecessary re-renders from object reference changes
81
- const isAuthenticated = auth.isAuthenticated;
82
- const isLoading = auth.isLoading;
83
- const hasUser = !!auth.user;
84
-
85
- // console.log('[ParentSync] 📤 Sending auth status to parent:', { isAuthenticated, isLoading, hasUser });
86
-
87
- // Debounce the postMessage to prevent excessive calls
88
- const timeoutId = setTimeout(() => {
89
- try {
90
- window.parent.postMessage({
91
- type: 'iframe-auth-status',
92
- data: { isAuthenticated, isLoading, hasUser }
93
- }, '*');
94
- // authLogger.debug('Auth status sent successfully');
95
- } catch (e) {
96
- authLogger.error('Failed to send auth status:', e);
97
- }
98
- }, 100); // 100ms debounce
99
-
100
- return () => clearTimeout(timeoutId);
101
- }, [
102
- auth.isAuthenticated,
103
- auth.isLoading,
104
- !!auth.user, // Convert to boolean to prevent object reference changes
105
- isEmbedded,
106
- isMounted
107
- ]);
108
-
109
- // 3. iframe-resize removed - was causing log spam
110
-
111
- // 4. Send navigation events to parent (for "Open in New Window" button)
112
- useEffect(() => {
113
- // Only send if embedded and mounted
114
- if (!isEmbedded || !isMounted) {
115
- return;
116
- }
117
-
118
- const handleRouteChange = (url: string) => {
119
- try {
120
- window.parent.postMessage({
121
- type: 'iframe-navigation',
122
- data: { path: url }
123
- }, '*');
124
- } catch (e) {
125
- authLogger.error('Failed to send navigation event:', e);
126
- }
127
- };
128
-
129
- // Listen to Next.js router events
130
- router.events.on('routeChangeComplete', handleRouteChange);
131
-
132
- // Send initial route
133
- handleRouteChange(router.asPath);
134
-
135
- // Cleanup
136
- return () => {
137
- router.events.off('routeChangeComplete', handleRouteChange);
138
- };
139
- }, [isEmbedded, isMounted, router]);
140
-
141
- // This component doesn't render anything
142
- return null;
143
- }
144
-
145
- // Export with old name for backward compatibility
146
- export { ParentSync as AuthStatusSync };
@@ -1,3 +0,0 @@
1
- export { ParentSync, AuthStatusSync } from './ParentSync';
2
- export { PagePreloader, PagePreloaderDark } from './PagePreloader';
3
- export type { PagePreloaderProps } from './PagePreloader';
@@ -1,48 +0,0 @@
1
- // ============================================================================
2
- // CfgApp Context - Shared App State
3
- // ============================================================================
4
- // Provides useCfgApp state to child components without duplicate hook calls
5
-
6
- 'use client';
7
-
8
- import React, { createContext, useContext, ReactNode } from 'react';
9
- import { UseCfgAppReturn, useCfgApp, UseCfgAppOptions } from '../hooks/useApp';
10
-
11
- const CfgAppContext = createContext<UseCfgAppReturn | null>(null);
12
-
13
- export interface CfgAppProviderProps {
14
- children: ReactNode;
15
- options?: UseCfgAppOptions;
16
- }
17
-
18
- /**
19
- * Provider for CfgApp state
20
- *
21
- * Should be used once at the top level (e.g., AdminLayout)
22
- * to avoid multiple useCfgApp() calls which cause duplicate
23
- * message listeners and iframe-ready signals
24
- */
25
- export function CfgAppProvider({ children, options }: CfgAppProviderProps) {
26
- const cfgApp = useCfgApp(options);
27
-
28
- return (
29
- <CfgAppContext.Provider value={cfgApp}>
30
- {children}
31
- </CfgAppContext.Provider>
32
- );
33
- }
34
-
35
- /**
36
- * Hook to consume CfgApp state
37
- *
38
- * @throws Error if used outside CfgAppProvider
39
- */
40
- export function useCfgAppContext(): UseCfgAppReturn {
41
- const context = useContext(CfgAppContext);
42
-
43
- if (!context) {
44
- throw new Error('useCfgAppContext must be used within CfgAppProvider');
45
- }
46
-
47
- return context;
48
- }
@@ -1,2 +0,0 @@
1
- export { CfgAppProvider, useCfgAppContext } from './CfgAppContext';
2
- export type { CfgAppProviderProps } from './CfgAppContext';
@@ -1,6 +0,0 @@
1
- export { useCfgApp } from './useApp';
2
- export type { UseCfgAppReturn, UseCfgAppOptions } from './useApp';
3
-
4
- // Backward compatibility alias
5
- export { useCfgApp as useApp } from './useApp';
6
- export type { UseCfgAppReturn as UseAppReturn, UseCfgAppOptions as UseAppOptions } from './useApp';
@@ -1,279 +0,0 @@
1
- // ============================================================================
2
- // CfgApp Hook - Application State and Embedding Detection
3
- // ============================================================================
4
-
5
- 'use client';
6
-
7
- import React from 'react';
8
- import { useState, useEffect } from 'react';
9
- import { useRouter } from 'next/router';
10
- import { authLogger } from '../../../../../utils/logger';
11
- import { consola } from 'consola';
12
-
13
- export interface UseCfgAppReturn {
14
- /**
15
- * Whether the app is embedded in an iframe
16
- */
17
- isEmbedded: boolean;
18
-
19
- /**
20
- * Whether the app is running in standalone mode (PWA)
21
- */
22
- isStandalone: boolean;
23
-
24
- /**
25
- * Whether to disable the layout (useful for iframes)
26
- */
27
- disableLayout: boolean;
28
-
29
- /**
30
- * The referrer URL if embedded
31
- */
32
- referrer: string | null;
33
-
34
- /**
35
- * Whether the app has been mounted on the client
36
- */
37
- isMounted: boolean;
38
-
39
- /**
40
- * The theme received from parent (if embedded)
41
- */
42
- parentTheme: 'light' | 'dark' | null;
43
-
44
- /**
45
- * The theme mode from parent ('dark', 'light', 'auto')
46
- */
47
- parentThemeMode: string | null;
48
-
49
- /**
50
- * Handler for setting auth token (used by parent window)
51
- */
52
- setToken?: (authToken: string, refreshToken?: string) => void;
53
- }
54
-
55
- export interface UseCfgAppOptions {
56
- /**
57
- * Handler for setting auth token when received from parent
58
- */
59
- onAuthTokenReceived?: (authToken: string, refreshToken?: string) => void;
60
- }
61
-
62
- /**
63
- * Custom hook for detecting app embedding and providing app-level state
64
- *
65
- * Features:
66
- * - Detects iframe embedding
67
- * - Detects URL-based embedding (`?embed=true`)
68
- * - Detects standalone PWA mode
69
- * - Provides referrer information
70
- * - Handles postMessage communication with parent window
71
- * - Syncs theme from parent window
72
- * - Receives auth tokens from parent window
73
- * - SSR-safe (returns default values on server)
74
- *
75
- * Usage:
76
- * ```tsx
77
- * const { isEmbedded, disableLayout, referrer, parentTheme } = useCfgApp({
78
- * onAuthTokenReceived: (authToken, refreshToken) => {
79
- * api.setToken(authToken, refreshToken);
80
- * }
81
- * });
82
- *
83
- * return (
84
- * <AppLayout disableLayout={disableLayout}>
85
- * {isEmbedded && <div>Running in embedded mode from {referrer}</div>}
86
- * </AppLayout>
87
- * );
88
- * ```
89
- */
90
- // Global flag to track if iframe-ready was sent (persists across component remounts)
91
- let iframeReadySent = false;
92
-
93
- export function useCfgApp(options?: UseCfgAppOptions): UseCfgAppReturn {
94
- const router = useRouter();
95
- const [isMounted, setIsMounted] = useState(false);
96
- const [isEmbedded, setIsEmbedded] = useState(false);
97
- const [isStandalone, setIsStandalone] = useState(false);
98
- const [referrer, setReferrer] = useState<string | null>(null);
99
- const [parentTheme, setParentTheme] = useState<'light' | 'dark' | null>(null);
100
- const [parentThemeMode, setParentThemeMode] = useState<string | null>(null);
101
-
102
- // Store callback in ref OUTSIDE useEffect so it's always available
103
- const callbackRef = React.useRef(options?.onAuthTokenReceived);
104
-
105
- // Update callback ref when options change (but don't re-register listener)
106
- React.useEffect(() => {
107
- callbackRef.current = options?.onAuthTokenReceived;
108
- }, [options?.onAuthTokenReceived]);
109
-
110
- useEffect(() => {
111
- setIsMounted(true);
112
-
113
- // Check if running in iframe
114
- const inIframe = window.self !== window.top;
115
- setIsEmbedded(inIframe);
116
-
117
- // Check if running as standalone PWA
118
- const standalone = window.matchMedia('(display-mode: standalone)').matches;
119
- setIsStandalone(standalone);
120
-
121
- // Get referrer if embedded
122
- if (inIframe && document.referrer) {
123
- setReferrer(document.referrer);
124
- }
125
-
126
- // Debounce timeout for parent-auth messages
127
- let authTokenTimeout: NodeJS.Timeout | null = null;
128
- // Track if we've already processed auth tokens
129
- let authTokenProcessed = false;
130
-
131
- // Listen for messages from parent window
132
- const handleMessage = (event: MessageEvent) => {
133
- // console.log('[useCfgApp] RAW message event:', {
134
- // origin: event.origin,
135
- // source: event.source === window.parent ? 'parent' : 'other',
136
- // dataType: typeof event.data,
137
- // data: event.data
138
- // });
139
-
140
- const { type, data } = event.data || {};
141
- // console.log('[useCfgApp] Received message:', type, data);
142
-
143
- switch (type) {
144
- case 'parent-auth':
145
- consola.info('[useCfgApp] parent-auth message received', { authTokenProcessed });
146
-
147
- // Prevent processing if already handled
148
- if (authTokenProcessed) {
149
- consola.warn('[useCfgApp] Auth tokens already processed, ignoring duplicate message');
150
- return;
151
- }
152
-
153
- // Cancel previous timeout to debounce rapid auth messages
154
- if (authTokenTimeout) {
155
- clearTimeout(authTokenTimeout);
156
- }
157
-
158
- // Debounce auth token processing to prevent rapid calls (300ms)
159
- authTokenTimeout = setTimeout(() => {
160
- // Double-check still not processed (race condition protection)
161
- if (authTokenProcessed) {
162
- consola.warn('[useCfgApp] Auth tokens already processed during debounce, skipping');
163
- return;
164
- }
165
-
166
- // Receive authentication tokens from parent
167
- if (data?.authToken && callbackRef.current) {
168
- consola.success('[useCfgApp] Auth tokens found, calling onAuthTokenReceived callback');
169
- consola.debug('[useCfgApp] Tokens:', {
170
- authToken: data.authToken.substring(0, 20) + '...',
171
- refreshToken: data.refreshToken ? data.refreshToken.substring(0, 20) + '...' : 'null'
172
- });
173
-
174
- // Mark as processed BEFORE calling callback
175
- authTokenProcessed = true;
176
-
177
- try {
178
- callbackRef.current(data.authToken, data.refreshToken);
179
- consola.success('[useCfgApp] onAuthTokenReceived callback completed successfully');
180
- } catch (e) {
181
- authLogger.error('Failed to process auth tokens:', e);
182
- // Reset on error to allow retry
183
- authTokenProcessed = false;
184
- }
185
- } else {
186
- authLogger.warn('parent-auth message received but authToken or callback missing:', { hasToken: !!data?.authToken, hasCallback: !!callbackRef.current });
187
- }
188
- }, 300); // 300ms debounce
189
- break;
190
-
191
- case 'parent-theme':
192
- // console.log('[useCfgApp] parent-theme message received:', data?.theme);
193
- // Receive theme from parent
194
- if (data?.theme) {
195
- try {
196
- setParentTheme(data.theme);
197
- if (data.themeMode) {
198
- setParentThemeMode(data.themeMode);
199
- }
200
- // authLogger.debug('Theme set successfully:', data.theme);
201
- } catch (e) {
202
- authLogger.error('Failed to process theme:', e);
203
- }
204
- }
205
- break;
206
-
207
- default:
208
- break;
209
- }
210
- };
211
-
212
- window.addEventListener('message', handleMessage);
213
- // console.log('[useCfgApp] Message listener registered, isEmbedded:', inIframe);
214
-
215
- // Send iframe-ready ONLY ONCE (even if component remounts)
216
- if (inIframe && !iframeReadySent) {
217
- try {
218
- consola.start('[useCfgApp] Sending iframe-ready message to parent (FIRST TIME)');
219
- window.parent.postMessage({
220
- type: 'iframe-ready',
221
- data: {
222
- url: window.location.href,
223
- referrer: document.referrer
224
- }
225
- }, '*');
226
- iframeReadySent = true; // Mark as sent to prevent duplicates
227
- consola.success('[useCfgApp] iframe-ready message sent');
228
- } catch (e) {
229
- authLogger.error('Failed to notify parent about ready state:', e);
230
- }
231
- } else if (inIframe && iframeReadySent) {
232
- consola.info('[useCfgApp] iframe-ready already sent, skipping to prevent loop');
233
- }
234
-
235
- return () => {
236
- window.removeEventListener('message', handleMessage);
237
- // Clear timeout on cleanup
238
- if (authTokenTimeout) {
239
- clearTimeout(authTokenTimeout);
240
- }
241
- };
242
- }, []); // ← EMPTY DEPS - register listener ONCE
243
-
244
- // Notify parent about route changes
245
- useEffect(() => {
246
- if (!isEmbedded || !isMounted) return;
247
-
248
- try {
249
- window.parent.postMessage({
250
- type: 'iframe-navigation',
251
- data: {
252
- path: router.asPath,
253
- route: router.pathname
254
- }
255
- }, '*');
256
- } catch (e) {
257
- authLogger.error('Failed to notify parent about navigation:', e);
258
- }
259
- }, [router.asPath, router.pathname, isEmbedded, isMounted]);
260
-
261
- // Check URL parameter for embed mode
262
- const embedParam = router.query.embed === 'true' || router.query.embed === '1';
263
-
264
- // Determine if layout should be disabled
265
- // Disable layout if:
266
- // 1. Running in iframe AND no explicit embed=false param
267
- // 2. URL has embed=true param
268
- const shouldDisableLayout = (isEmbedded && router.query.embed !== 'false') || embedParam;
269
-
270
- return {
271
- isEmbedded,
272
- isStandalone,
273
- disableLayout: shouldDisableLayout,
274
- referrer,
275
- isMounted,
276
- parentTheme,
277
- parentThemeMode,
278
- };
279
- }
@@ -1,24 +0,0 @@
1
- /**
2
- * AdminLayout - Django CFG Layout with iframe Integration
3
- *
4
- * Universal layout component for Django CFG applications
5
- * Handles iframe embedding, theme sync, and auth communication
6
- */
7
-
8
- // Main component
9
- export { AdminLayout } from './AdminLayout';
10
- export type { AdminLayoutProps } from './AdminLayout';
11
-
12
- // Hooks
13
- export { useCfgApp, useApp } from './hooks';
14
- export type { UseCfgAppReturn, UseCfgAppOptions, UseAppReturn, UseAppOptions } from './hooks';
15
-
16
- // Context
17
- export { CfgAppProvider, useCfgAppContext } from './context';
18
- export type { CfgAppProviderProps } from './context';
19
-
20
- // Components
21
- export { ParentSync, AuthStatusSync } from './components';
22
-
23
- // Types
24
- export type { AdminLayoutConfig } from './types';
@@ -1 +0,0 @@
1
- {"nm":"Rander","ddd":0,"h":512,"w":512,"meta":{"g":"LottieFiles AE 2.0.4"},"layers":[{"ty":4,"nm":"Capa 2","sr":1,"st":0,"op":46,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[256.002,256,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.956,"y":0},"i":{"x":0.237,"y":1},"s":[120,120,100],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.395,"y":1},"s":[60,60,100],"t":40},{"s":[120,120,100],"t":46}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[256.002,256,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 1","ix":1,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.638,-9.168],[0,0],[0,0],[-8.437,0],[0,0],[-5.944,5.985],[0,0]],"o":[[0,0],[0,0],[-5.944,5.985],[0,0],[-8.437,0],[0,0],[6.562,-6.613]],"v":[[75.715,-65.189],[73.984,-55.526],[-40.87,60.214],[-34.133,76.388],[-66.367,76.388],[-73.104,60.214],[59.634,-73.544]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,0.8196,0.3569],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[226.809,194.352],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[1.061,-5.831],[0,0],[0,0],[-1.679,9.179],[0,0],[5.924,0],[0,0]],"o":[[0,0],[0,0],[-6.562,6.624],[0,0],[1.061,-5.831],[0,0],[5.934,0]],"v":[[25.334,-50.448],[8.686,40.722],[-9.249,58.802],[-25.32,50.416],[-6.9,-50.448],[-16.244,-61.646],[15.99,-61.646]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,0.8196,0.3569],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[233.82,332.386],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"tr","a":{"a":0,"k":[233.82,332.386],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[233.82,332.386],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Group 2","ix":2,"cix":2,"np":2,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[6.558,-6.608],[0,0],[-8.439,0],[0,0],[1.064,-5.829],[0,0],[-6.57,6.624],[0,0],[8.2,0.413],[0,0],[-1.005,5.629],[0,0]],"o":[[0,0],[-5.945,5.99],[0,0],[5.925,0],[0,0],[-1.676,9.178],[0,0],[5.782,-5.829],[0,0],[-5.71,-0.288],[0,0],[1.639,-9.166]],"v":[[30.44,-135.194],[-102.297,-1.434],[-95.559,14.745],[-38.427,14.745],[-29.089,25.943],[-47.507,126.802],[-31.43,135.192],[102.301,0.367],[96.039,-15.798],[38.063,-18.72],[29.196,-29.87],[46.521,-126.837]]},"ix":2}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[0.9961,0.8941,0.3529],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[256.002,256],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1},{"ty":4,"nm":"Shape Layer 1","sr":1,"st":-41,"op":46,"ip":-39,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[6,22,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0,0,100],"t":13},{"s":[800,800,100],"t":30}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[252.5,252.5,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":13},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":23},{"s":[0],"t":30}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[30,30],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":4},"w":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":13},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[3],"t":23},{"s":[0],"t":30}],"ix":5},"c":{"a":0,"k":[1,0.9647,0],"ix":3}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[6,22],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":2}],"v":"4.8.0","fr":30,"op":46,"ip":0,"assets":[]}
@@ -1,45 +0,0 @@
1
- import { ReactNode } from 'react';
2
-
3
- /**
4
- * Configuration for AdminLayout
5
- * All options are optional - AdminLayout works out of the box with zero config
6
- */
7
- export interface AdminLayoutConfig {
8
- /**
9
- * Optional handler called when auth tokens are received from parent window
10
- *
11
- * Note: Tokens are ALWAYS automatically set in API client via `api.setToken()`.
12
- * Use this callback only if you need additional custom logic.
13
- *
14
- * @example
15
- * ```tsx
16
- * <AdminLayout config={{
17
- * onAuthTokenReceived: (authToken, refreshToken) => {
18
- * console.log('Tokens received and set in API client');
19
- * // Additional custom logic here
20
- * }
21
- * }}>
22
- * ```
23
- */
24
- onAuthTokenReceived?: (authToken: string, refreshToken?: string) => void;
25
-
26
- /**
27
- * Whether to automatically sync theme from parent window
28
- * @default true
29
- */
30
- enableThemeSync?: boolean;
31
-
32
- /**
33
- * Whether to automatically send auth status to parent window
34
- * @default true
35
- */
36
- enableAuthStatusSync?: boolean;
37
- }
38
-
39
- /**
40
- * Props for AdminLayout component
41
- */
42
- export interface AdminLayoutProps {
43
- children: ReactNode;
44
- config?: AdminLayoutConfig;
45
- }
@@ -1,41 +0,0 @@
1
- import React from 'react';
2
-
3
- import { AuthProvider, useAuthContext } from './AuthContext';
4
- import { IdentifierForm } from './IdentifierForm';
5
- import { OTPForm } from './OTPForm';
6
-
7
- import type { AuthProps } from './types';
8
-
9
- export const AuthLayout: React.FC<AuthProps> = (props) => {
10
-
11
- return (
12
- <AuthProvider {...props}>
13
- <div
14
- className={`min-h-screen flex flex-col items-center justify-center bg-background py-6 px-4 sm:py-12 sm:px-6 lg:px-8 ${props.className || ''}`}
15
- >
16
- <div className="w-full sm:max-w-md space-y-8">
17
- {props.children}
18
-
19
- <AuthContent />
20
- </div>
21
- </div>
22
- </AuthProvider>
23
- );
24
- };
25
-
26
- // Separate component to use the context
27
- const AuthContent: React.FC = () => {
28
- const { step, error } = useAuthContext();
29
-
30
- return (
31
- <>
32
- {/* {error && (
33
- <div className="bg-destructive/10 border border-destructive/20 text-destructive px-4 py-3 rounded-md">
34
- {error}
35
- </div>
36
- )} */}
37
-
38
- <div>{step === 'identifier' ? <IdentifierForm /> : <OTPForm />}</div>
39
- </>
40
- );
41
- };
@@ -1,15 +0,0 @@
1
- 'use client';
2
-
3
- export { AuthLayout } from './AuthLayout';
4
- export {
5
- AuthProvider as AuthLayoutProvider,
6
- useAuthContext as useAuthLayoutContext
7
- } from './AuthContext';
8
- export { IdentifierForm } from './IdentifierForm';
9
- export { OTPForm } from './OTPForm';
10
- export { AuthHelp } from './AuthHelp';
11
- export type {
12
- AuthProps,
13
- AuthContextType as AuthLayoutContextType,
14
- AuthHelpProps
15
- } from './types';