@djangocfg/layouts 1.2.19 → 1.2.21

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "1.2.19",
3
+ "version": "1.2.21",
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.19",
57
- "@djangocfg/og-image": "^1.2.19",
58
- "@djangocfg/ui": "^1.2.19",
56
+ "@djangocfg/api": "^1.2.21",
57
+ "@djangocfg/og-image": "^1.2.21",
58
+ "@djangocfg/ui": "^1.2.21",
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.19",
79
+ "@djangocfg/typescript-config": "^1.2.21",
80
80
  "@types/node": "^24.7.2",
81
81
  "@types/react": "19.2.2",
82
82
  "@types/react-dom": "19.2.1",
@@ -93,25 +93,36 @@ const AuthProviderInternal: React.FC<AuthProviderProps> = ({ children, config })
93
93
 
94
94
  // Simple profile loading without retry - now uses AccountsContext
95
95
  const loadCurrentProfile = useCallback(async (): Promise<void> => {
96
+ // console.log('[AuthContext] loadCurrentProfile called');
96
97
  try {
97
98
  // Ensure API clients are properly initialized with current token
98
- if (!api.isAuthenticated()) {
99
+ const isAuth = api.isAuthenticated();
100
+ const token = api.getToken();
101
+ // console.log('[AuthContext] isAuthenticated:', isAuth, 'token:', token ? token.substring(0, 20) + '...' : 'null');
102
+
103
+ if (!isAuth) {
104
+ console.warn('[AuthContext] No valid authentication token, throwing error');
99
105
  throw new Error('No valid authentication token');
100
106
  }
101
107
 
108
+ // console.log('[AuthContext] Refreshing profile from AccountsContext...');
102
109
  // Refresh profile from AccountsContext
103
110
  const refreshedProfile = await accounts.refreshProfile();
104
111
 
105
112
  if (refreshedProfile) {
113
+ // console.log('[AuthContext] Profile loaded successfully:', refreshedProfile.id, 'is_staff:', refreshedProfile.is_staff, 'is_superuser:', refreshedProfile.is_superuser);
106
114
  authLogger.info('Profile loaded successfully:', refreshedProfile.id);
107
115
  } else {
116
+ console.warn('[AuthContext] Profile refresh returned undefined - but keeping tokens');
108
117
  authLogger.warn('Profile refresh returned undefined - but keeping tokens');
109
118
  }
110
119
 
111
120
  // Always mark as initialized if we have valid tokens
112
121
  // Don't clear tokens just because profile fetch failed
113
122
  setInitialized(true);
123
+ // console.log('[AuthContext] loadCurrentProfile completed, initialized=true');
114
124
  } catch (error) {
125
+ console.error('[AuthContext] Failed to load profile:', error);
115
126
  authLogger.error('Failed to load profile:', error);
116
127
  // Use global error handler first, fallback to clearing state
117
128
  if (!handleGlobalAuthError(error, 'loadCurrentProfile')) {
@@ -110,6 +110,19 @@ function LayoutRouter({
110
110
  // Admin routes: Always show loading during SSR and initial client render
111
111
  // This prevents hydration mismatch when isAuthenticated differs between server/client
112
112
  if (isAdminRoute && !forceLayout) {
113
+ // In embedded mode (iframe), render AdminLayout immediately to receive postMessage
114
+ const isEmbedded = typeof window !== 'undefined' && window !== window.parent;
115
+
116
+ if (isEmbedded) {
117
+ // Skip loading checks - AdminLayout will handle auth via postMessage
118
+ return (
119
+ <AdminLayout enableParentSync={true}>
120
+ {children}
121
+ </AdminLayout>
122
+ );
123
+ }
124
+
125
+ // Standalone mode: show loading during initialization
113
126
  if (!isMounted || isLoading) {
114
127
  return (
115
128
  <div className="min-h-screen flex items-center justify-center">
@@ -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-11-02T05:58:03.758Z
19
+ * Last updated: 2025-11-02T11:45:27.117Z
20
20
  */
21
21
  const PACKAGE_VERSIONS: PackageInfo[] = [
22
22
  {
23
23
  "name": "@djangocfg/ui",
24
- "version": "1.2.19"
24
+ "version": "1.2.21"
25
25
  },
26
26
  {
27
27
  "name": "@djangocfg/api",
28
- "version": "1.2.19"
28
+ "version": "1.2.21"
29
29
  },
30
30
  {
31
31
  "name": "@djangocfg/layouts",
32
- "version": "1.2.19"
32
+ "version": "1.2.21"
33
33
  },
34
34
  {
35
35
  "name": "@djangocfg/markdown",
36
- "version": "1.2.19"
36
+ "version": "1.2.21"
37
37
  },
38
38
  {
39
39
  "name": "@djangocfg/og-image",
40
- "version": "1.2.19"
40
+ "version": "1.2.21"
41
41
  },
42
42
  {
43
43
  "name": "@djangocfg/eslint-config",
44
- "version": "1.2.19"
44
+ "version": "1.2.21"
45
45
  },
46
46
  {
47
47
  "name": "@djangocfg/typescript-config",
48
- "version": "1.2.19"
48
+ "version": "1.2.21"
49
49
  }
50
50
  ];
51
51
 
@@ -82,27 +82,68 @@ export function AdminLayout({
82
82
  config,
83
83
  enableParentSync = true
84
84
  }: AdminLayoutProps) {
85
- const { user } = useAuth();
85
+ const [isMounted, setIsMounted] = React.useState(false);
86
+ const { user, isLoading, loadCurrentProfile } = useAuth();
87
+ // console.log('[AdminLayout] Rendering with user:', user, 'isLoading:', isLoading);
88
+
89
+ // Track mount state to prevent hydration mismatch
90
+ React.useEffect(() => {
91
+ setIsMounted(true);
92
+ }, []);
86
93
 
87
94
  // useCfgApp hook is called here to initialize iframe communication
88
95
  // Automatically sets tokens in API client when received from parent
89
96
  const { isEmbedded } = useCfgApp({
90
- onAuthTokenReceived: (authToken, refreshToken) => {
97
+ onAuthTokenReceived: async (authToken, refreshToken) => {
98
+ // console.log('[AdminLayout] onAuthTokenReceived called');
99
+ // console.log('[AdminLayout] authToken:', authToken.substring(0, 20) + '...', 'refreshToken:', refreshToken ? refreshToken.substring(0, 20) + '...' : 'null');
100
+
91
101
  // Always set tokens in API client
92
102
  api.setToken(authToken, refreshToken);
103
+ // console.log('[AdminLayout] Tokens set in API client');
104
+
105
+ // Load user profile after setting tokens
106
+ // console.log('[AdminLayout] Loading user profile...');
107
+ await loadCurrentProfile();
108
+ // console.log('[AdminLayout] User profile loaded');
93
109
 
94
110
  // Call custom handler if provided
95
111
  if (config?.onAuthTokenReceived) {
112
+ // console.log('[AdminLayout] Calling custom onAuthTokenReceived handler');
96
113
  config.onAuthTokenReceived(authToken, refreshToken);
97
114
  }
98
115
  }
99
116
  });
100
117
 
118
+ // console.log('[AdminLayout] isEmbedded:', isEmbedded);
119
+
120
+ // During SSR and initial render, show loading to prevent hydration mismatch
121
+ if (!isMounted) {
122
+ return (
123
+ <div className="min-h-screen flex items-center justify-center bg-background">
124
+ <div className="text-muted-foreground">Loading admin panel...</div>
125
+ </div>
126
+ );
127
+ }
128
+
129
+ // Show loading while auth is initializing (waiting for tokens from parent or profile loading)
130
+ if (isLoading) {
131
+ // console.log('[AdminLayout] Showing loading state - isLoading:', isLoading);
132
+ return (
133
+ <div className="min-h-screen flex items-center justify-center bg-background">
134
+ <div className="text-muted-foreground">Loading admin panel...</div>
135
+ </div>
136
+ );
137
+ }
138
+
101
139
  // Check if user has staff or superuser privileges
102
140
  const hasAdminAccess = user?.is_staff || user?.is_superuser;
141
+ // console.log('[AdminLayout] user:', user, 'hasAdminAccess:', hasAdminAccess, 'is_staff:', user?.is_staff, 'is_superuser:', user?.is_superuser);
103
142
 
104
143
  // Only render AdminLayout features for staff/superuser
105
144
  if (!hasAdminAccess) {
145
+ console.warn('[AdminLayout] Access denied - user does not have admin access');
146
+ // console.log('[AdminLayout] User details:', JSON.stringify(user, null, 2));
106
147
  return (
107
148
  <div className="min-h-screen flex items-center justify-center bg-background">
108
149
  <div className="text-center space-y-4 p-8">
@@ -9,6 +9,7 @@
9
9
  'use client';
10
10
 
11
11
  import { useEffect, useState } from 'react';
12
+ import { useRouter } from 'next/router';
12
13
  import { useAuth } from '../../../../../auth';
13
14
  import { useThemeContext } from '@djangocfg/ui';
14
15
  import { useCfgApp } from '../hooks/useApp';
@@ -47,15 +48,20 @@ export function ParentSync() {
47
48
  * Separated to avoid SSR issues during static export
48
49
  */
49
50
  function ParentSyncClient() {
51
+ const router = useRouter();
50
52
  const auth = useAuth();
51
53
  const { setTheme } = useThemeContext();
52
54
  const { isEmbedded, isMounted, parentTheme } = useCfgApp();
53
55
 
54
56
  // 1. Sync theme from parent → iframe
55
57
  useEffect(() => {
58
+ // console.log('[ParentSync] Theme sync effect triggered - isEmbedded:', isEmbedded, 'parentTheme:', parentTheme);
56
59
  if (isEmbedded && parentTheme) {
57
- // console.log('[ParentSync] 🎨 Syncing theme from parent:', parentTheme);
60
+ // console.log('[ParentSync] Syncing theme from parent:', parentTheme);
58
61
  setTheme(parentTheme);
62
+ // console.log('[ParentSync] setTheme called with:', parentTheme);
63
+ } else {
64
+ // console.log('[ParentSync] Skipping theme sync - isEmbedded:', isEmbedded, 'parentTheme:', parentTheme);
59
65
  }
60
66
  }, [isEmbedded, parentTheme, setTheme]);
61
67
 
@@ -89,94 +95,37 @@ function ParentSyncClient() {
89
95
  }
90
96
  }, [auth.isAuthenticated, auth.isLoading, auth.user, isEmbedded, isMounted]);
91
97
 
92
- // 3. Send iframe height changes to parent for auto-resize
98
+ // 3. iframe-resize removed - was causing log spam
99
+
100
+ // 4. Send navigation events to parent (for "Open in New Window" button)
93
101
  useEffect(() => {
94
102
  // Only send if embedded and mounted
95
103
  if (!isEmbedded || !isMounted) {
96
104
  return;
97
105
  }
98
106
 
99
- // Function to send height to parent
100
- const sendHeight = () => {
101
- const height = document.documentElement.scrollHeight;
102
- // console.log('[ParentSync] 📏 Sending height to parent:', height);
103
-
107
+ const handleRouteChange = (url: string) => {
104
108
  try {
105
109
  window.parent.postMessage({
106
- type: 'iframe-resize',
107
- data: { height }
110
+ type: 'iframe-navigation',
111
+ data: { path: url }
108
112
  }, '*');
109
113
  } catch (e) {
110
- console.error('[ParentSync] Failed to send height:', e);
114
+ console.error('[ParentSync] Failed to send navigation event:', e);
111
115
  }
112
116
  };
113
117
 
114
- // Send initial height
115
- sendHeight();
118
+ // Listen to Next.js router events
119
+ router.events.on('routeChangeComplete', handleRouteChange);
116
120
 
117
- // Watch for content height changes with ResizeObserver
118
- const resizeObserver = new ResizeObserver(() => {
119
- sendHeight();
120
- });
121
-
122
- // Observe body element for size changes
123
- resizeObserver.observe(document.body);
121
+ // Send initial route
122
+ handleRouteChange(router.asPath);
124
123
 
125
124
  // Cleanup
126
125
  return () => {
127
- resizeObserver.disconnect();
126
+ router.events.off('routeChangeComplete', handleRouteChange);
128
127
  };
129
- }, [isEmbedded, isMounted]);
130
-
131
- // 4. Send navigation events to parent (for "Open in New Window" button)
132
- useEffect(() => {
133
- // Only send if embedded and mounted
134
- if (!isEmbedded || !isMounted) {
135
- return;
136
- }
137
-
138
- // Import Next.js router dynamically
139
- import('next/router').then(({ default: Router }) => {
140
- const handleRouteChange = (url: string) => {
141
- // console.log('[ParentSync] 🔗 Route changed:', url);
142
-
143
- try {
144
- window.parent.postMessage({
145
- type: 'iframe-navigation',
146
- data: { path: url }
147
- }, '*');
148
- } catch (e) {
149
- console.error('[ParentSync] ❌ Failed to send navigation event:', e);
150
- }
151
-
152
- // Also send height update after route change
153
- setTimeout(() => {
154
- const height = document.documentElement.scrollHeight;
155
- try {
156
- window.parent.postMessage({
157
- type: 'iframe-resize',
158
- data: { height }
159
- }, '*');
160
- } catch (e) {
161
- console.error('[ParentSync] ❌ Failed to send height:', e);
162
- }
163
- }, 100);
164
- };
165
-
166
- // Listen to Next.js router events
167
- Router.events.on('routeChangeComplete', handleRouteChange);
168
-
169
- // Send initial route
170
- handleRouteChange(Router.asPath);
171
-
172
- // Cleanup
173
- return () => {
174
- Router.events.off('routeChangeComplete', handleRouteChange);
175
- };
176
- }).catch(err => {
177
- console.warn('[ParentSync] Failed to load next/router:', err);
178
- });
179
- }, [isEmbedded, isMounted]);
128
+ }, [isEmbedded, isMounted, router]);
180
129
 
181
130
  // This component doesn't render anything
182
131
  return null;
@@ -94,13 +94,10 @@ export function useCfgApp(options?: UseCfgAppOptions): UseCfgAppReturn {
94
94
  const [parentThemeMode, setParentThemeMode] = useState<string | null>(null);
95
95
 
96
96
  useEffect(() => {
97
- // Debug logging (uncomment for debugging)
98
- // console.log('[useCfgApp] Hook initializing...');
99
97
  setIsMounted(true);
100
98
 
101
99
  // Check if running in iframe
102
100
  const inIframe = window.self !== window.top;
103
- // console.log('[useCfgApp] Iframe detection:', { inIframe });
104
101
  setIsEmbedded(inIframe);
105
102
 
106
103
  // Check if running as standalone PWA
@@ -109,138 +106,84 @@ export function useCfgApp(options?: UseCfgAppOptions): UseCfgAppReturn {
109
106
 
110
107
  // Get referrer if embedded
111
108
  if (inIframe && document.referrer) {
112
- // console.log('[useCfgApp] Referrer:', document.referrer);
113
109
  setReferrer(document.referrer);
114
110
  }
115
111
 
116
- // Setup resize observer and interval for iframe height updates
117
- let resizeObserver: ResizeObserver | null = null;
118
- let checkHeightInterval: NodeJS.Timeout | null = null;
119
-
120
- // Notify parent window that iframe is ready
121
- if (inIframe) {
122
- try {
123
- // console.log('[useCfgApp] Sending iframe-ready message to parent');
124
- window.parent.postMessage({
125
- type: 'iframe-ready',
126
- data: {
127
- url: window.location.href,
128
- referrer: document.referrer
129
- }
130
- }, '*'); // Use '*' or specific origin for security
131
- // console.log('[useCfgApp] iframe-ready message sent');
132
- } catch (e) {
133
- console.error('[useCfgApp] Failed to notify parent:', e);
134
- }
135
-
136
- // Send current content height (both increases and decreases)
137
- const sendHeight = () => {
138
- try {
139
- // Use body.scrollHeight to avoid iframe height feedback loop
140
- // document.documentElement.scrollHeight includes iframe's own height!
141
- const bodyScrollHeight = document.body.scrollHeight;
142
- const bodyOffsetHeight = document.body.offsetHeight;
143
- const height = Math.max(bodyScrollHeight, bodyOffsetHeight);
144
-
145
- window.parent.postMessage({
146
- type: 'iframe-resize',
147
- data: { height }
148
- }, '*');
149
- } catch (e) {
150
- console.error('[useCfgApp] Failed to send height:', e);
151
- }
152
- };
153
-
154
- // Send height immediately after mount
155
- setTimeout(sendHeight, 100);
156
-
157
- // Watch for content size changes using ResizeObserver
158
- resizeObserver = new ResizeObserver(() => {
159
- sendHeight();
160
- });
161
-
162
- // Observe document body for size changes
163
- resizeObserver.observe(document.body);
164
-
165
- // Also observe on route changes and after data loads
166
- checkHeightInterval = setInterval(sendHeight, 1000);
167
- }
168
-
169
112
  // Listen for messages from parent window
170
113
  const handleMessage = (event: MessageEvent) => {
171
- // console.log('[useCfgApp] Message received:', {
114
+ // console.log('[useCfgApp] RAW message event:', {
172
115
  // origin: event.origin,
173
- // type: event.data?.type,
174
- // data: event.data?.data
116
+ // source: event.source === window.parent ? 'parent' : 'other',
117
+ // dataType: typeof event.data,
118
+ // data: event.data
175
119
  // });
176
120
 
177
- // Verify origin for security (optional - adjust for your needs)
178
- // if (event.origin !== window.location.origin) return;
179
-
180
121
  const { type, data } = event.data || {};
122
+ // console.log('[useCfgApp] Received message:', type, data);
181
123
 
182
124
  switch (type) {
183
125
  case 'parent-auth':
184
- // console.log('[useCfgApp] Processing parent-auth message');
126
+ // console.log('[useCfgApp] parent-auth message received');
185
127
  // Receive authentication tokens from parent
186
128
  if (data?.authToken && options?.onAuthTokenReceived) {
129
+ // console.log('[useCfgApp] Auth tokens found, calling onAuthTokenReceived callback');
130
+ // console.log('[useCfgApp] authToken:', data.authToken.substring(0, 20) + '...', 'refreshToken:', data.refreshToken ? data.refreshToken.substring(0, 20) + '...' : 'null');
187
131
  try {
188
- // console.log('[useCfgApp] Calling onAuthTokenReceived');
189
132
  options.onAuthTokenReceived(data.authToken, data.refreshToken);
190
- // console.log('[useCfgApp] Auth tokens received from parent');
133
+ // console.log('[useCfgApp] onAuthTokenReceived callback completed successfully');
191
134
  } catch (e) {
192
135
  console.error('[useCfgApp] Failed to process auth tokens:', e);
193
136
  }
137
+ } else {
138
+ console.warn('[useCfgApp] parent-auth message received but authToken or callback missing:', { hasToken: !!data?.authToken, hasCallback: !!options?.onAuthTokenReceived });
194
139
  }
195
140
  break;
196
141
 
197
142
  case 'parent-theme':
198
- // console.log('[useCfgApp] Processing parent-theme message');
143
+ // console.log('[useCfgApp] parent-theme message received:', data?.theme);
199
144
  // Receive theme from parent
200
145
  if (data?.theme) {
201
146
  try {
202
- // console.log('[useCfgApp] Setting theme:', {
203
- // theme: data.theme,
204
- // themeMode: data.themeMode
205
- // });
206
147
  setParentTheme(data.theme);
207
148
  if (data.themeMode) {
208
149
  setParentThemeMode(data.themeMode);
209
150
  }
210
- // console.log('[useCfgApp] Theme received from parent:', data.theme, 'mode:', data.themeMode);
151
+ // console.log('[useCfgApp] Theme set successfully:', data.theme);
211
152
  } catch (e) {
212
153
  console.error('[useCfgApp] Failed to process theme:', e);
213
154
  }
214
155
  }
215
156
  break;
216
157
 
217
- case 'parent-resize':
218
- // Handle parent window resize (optional)
219
- // console.log('[useCfgApp] Parent resized:', data);
220
- break;
221
-
222
158
  default:
223
- // if (type) {
224
- // console.log('[useCfgApp] Unknown message type:', type);
225
- // }
226
159
  break;
227
160
  }
228
161
  };
229
162
 
230
- // console.log('[useCfgApp] Adding message event listener');
231
163
  window.addEventListener('message', handleMessage);
164
+ // console.log('[useCfgApp] Message listener registered, isEmbedded:', inIframe);
165
+
166
+ // Send iframe-ready since listener is registered
167
+ if (inIframe) {
168
+ try {
169
+ // console.log('[useCfgApp] Sending iframe-ready message to parent');
170
+ window.parent.postMessage({
171
+ type: 'iframe-ready',
172
+ data: {
173
+ url: window.location.href,
174
+ referrer: document.referrer
175
+ }
176
+ }, '*');
177
+ // console.log('[useCfgApp] iframe-ready message sent');
178
+ } catch (e) {
179
+ console.error('[useCfgApp] Failed to notify parent about ready state:', e);
180
+ }
181
+ } else {
182
+ // console.log('[useCfgApp] Not in iframe, skipping iframe-ready message');
183
+ }
232
184
 
233
185
  return () => {
234
- // console.log('[useCfgApp] Cleaning up message event listener');
235
186
  window.removeEventListener('message', handleMessage);
236
-
237
- // Cleanup resize observer and interval
238
- if (resizeObserver) {
239
- resizeObserver.disconnect();
240
- }
241
- if (checkHeightInterval) {
242
- clearInterval(checkHeightInterval);
243
- }
244
187
  };
245
188
  }, [options]);
246
189
 
@@ -47,7 +47,7 @@ export function DashboardHeader({ isAdmin = false }: DashboardHeaderProps) {
47
47
 
48
48
  // Notification handler - TODO: implement notification system
49
49
  const handleNotificationClick = () => {
50
- console.log('Notifications clicked');
50
+ // console.log('Notifications clicked');
51
51
  };
52
52
 
53
53
  return (