@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.
- package/README.md +277 -18
- package/package.json +15 -24
- package/src/auth/context/AuthContext.tsx +5 -5
- package/src/auth/hooks/useAuthGuard.ts +1 -1
- package/src/auth/hooks/useAutoAuth.ts +8 -7
- package/src/components/ErrorBoundary.tsx +78 -0
- package/src/components/JsonLd.tsx +31 -0
- package/src/components/LucideIcon.tsx +91 -0
- package/src/components/PageProgress.tsx +127 -0
- package/src/components/Suspense.tsx +29 -0
- package/src/{layouts/AppLayout/components → components}/UpdateNotifier/UpdateNotifier.tsx +56 -49
- package/src/components/index.ts +10 -0
- package/src/index.ts +25 -7
- package/src/layouts/AdminLayout/AdminLayout.tsx +46 -0
- package/src/layouts/AdminLayout/index.ts +7 -0
- package/src/layouts/AppLayout/AppLayout.tsx +278 -326
- package/src/layouts/AppLayout/index.ts +2 -39
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthContext.tsx +3 -2
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthHelp.tsx +1 -0
- package/src/layouts/AuthLayout/AuthLayout.tsx +61 -0
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/IdentifierForm.tsx +47 -34
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/OTPForm.tsx +2 -3
- package/src/layouts/AuthLayout/index.ts +24 -0
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/types.ts +1 -0
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +144 -0
- package/src/layouts/PrivateLayout/components/PrivateContent.tsx +32 -0
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +57 -0
- package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +141 -0
- package/src/layouts/PrivateLayout/components/index.ts +8 -0
- package/src/layouts/PrivateLayout/index.ts +7 -0
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +15 -7
- package/src/layouts/PublicLayout/PublicLayout.tsx +121 -0
- package/src/layouts/PublicLayout/components/PublicFooter.tsx +190 -0
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +117 -0
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +101 -0
- package/src/layouts/PublicLayout/components/index.ts +8 -0
- package/src/layouts/PublicLayout/index.ts +7 -0
- package/src/layouts/_components/UserMenu.tsx +160 -0
- package/src/layouts/_components/index.ts +7 -0
- package/src/layouts/index.ts +15 -8
- package/src/snippets/Analytics/AnalyticsProvider.tsx +8 -4
- package/src/snippets/Analytics/useAnalytics.ts +11 -21
- package/src/snippets/Chat/ChatWidget.tsx +4 -4
- package/src/snippets/ContactForm/ContactFormProvider.tsx +32 -19
- package/src/snippets/ContactForm/ContactPage.tsx +2 -4
- package/src/snippets/ContactForm/types.ts +3 -2
- package/src/snippets/index.ts +0 -1
- package/src/layouts/AppLayout/README.md +0 -204
- package/src/layouts/AppLayout/SUMMARY.md +0 -240
- package/src/layouts/AppLayout/USAGE.md +0 -312
- package/src/layouts/AppLayout/components/ErrorBoundary.tsx +0 -112
- package/src/layouts/AppLayout/components/PageProgress.tsx +0 -123
- package/src/layouts/AppLayout/components/Seo.tsx +0 -171
- package/src/layouts/AppLayout/components/UserMenu.tsx +0 -385
- package/src/layouts/AppLayout/components/index.ts +0 -11
- package/src/layouts/AppLayout/context/AppContext.tsx +0 -151
- package/src/layouts/AppLayout/context/index.ts +0 -5
- package/src/layouts/AppLayout/hooks/index.ts +0 -8
- package/src/layouts/AppLayout/hooks/useLayoutMode.ts +0 -26
- package/src/layouts/AppLayout/hooks/useNavigation.ts +0 -51
- package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +0 -224
- package/src/layouts/AppLayout/layouts/AdminLayout/README.md +0 -409
- package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.example.tsx +0 -98
- package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.tsx +0 -149
- package/src/layouts/AppLayout/layouts/AdminLayout/components/ParentSync.tsx +0 -146
- package/src/layouts/AppLayout/layouts/AdminLayout/components/index.ts +0 -3
- package/src/layouts/AppLayout/layouts/AdminLayout/context/CfgAppContext.tsx +0 -48
- package/src/layouts/AppLayout/layouts/AdminLayout/context/index.ts +0 -2
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/index.ts +0 -6
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/useApp.ts +0 -279
- package/src/layouts/AppLayout/layouts/AdminLayout/index.ts +0 -24
- package/src/layouts/AppLayout/layouts/AdminLayout/lottie/energizing.json +0 -1
- package/src/layouts/AppLayout/layouts/AdminLayout/types/index.ts +0 -45
- package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +0 -41
- package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +0 -15
- package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +0 -82
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +0 -62
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +0 -89
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +0 -181
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +0 -9
- package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +0 -5
- package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +0 -44
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +0 -242
- package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileDrawer.tsx +0 -150
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +0 -169
- package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +0 -5
- package/src/layouts/AppLayout/layouts/index.ts +0 -7
- package/src/layouts/AppLayout/providers/CoreProviders.tsx +0 -80
- package/src/layouts/AppLayout/providers/index.ts +0 -5
- package/src/layouts/AppLayout/types/config.ts +0 -79
- package/src/layouts/AppLayout/types/index.ts +0 -11
- package/src/layouts/AppLayout/types/layout.ts +0 -54
- package/src/layouts/AppLayout/types/navigation.ts +0 -43
- package/src/layouts/AppLayout/types/page.ts +0 -80
- package/src/layouts/AppLayout/types/routes.ts +0 -43
- package/src/layouts/AppLayout/utils/index.ts +0 -5
- package/src/layouts/AppLayout/utils/routeDetection.ts +0 -31
- package/src/layouts/ErrorLayout/ErrorLayout.tsx +0 -173
- package/src/layouts/ErrorLayout/errorConfig.tsx +0 -152
- package/src/layouts/ErrorLayout/index.ts +0 -8
- package/src/layouts/SimpleLayout/SimpleLayout.tsx +0 -72
- package/src/layouts/SimpleLayout/index.ts +0 -3
- package/src/snippets/VideoPlayer/README.md +0 -238
- package/src/snippets/VideoPlayer/VideoControls.tsx +0 -137
- package/src/snippets/VideoPlayer/VideoPlayer.tsx +0 -248
- package/src/snippets/VideoPlayer/index.ts +0 -8
- package/src/snippets/VideoPlayer/types.ts +0 -61
- package/src/types/index.ts +0 -2
- package/src/types/pageConfig.ts +0 -100
- /package/src/{validation → components/ErrorsTracker}/README.md +0 -0
- /package/src/{validation → components/ErrorsTracker}/components/ErrorButtons.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/components/ErrorToast.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/hooks.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/index.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/providers/ErrorTrackingProvider.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/types.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/utils/curl-generator.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/utils/formatters.ts +0 -0
- /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,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,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';
|