@djangocfg/layouts 1.2.20 → 1.2.22
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 +12 -1
- package/src/layouts/AppLayout/AppLayout.tsx +13 -0
- package/src/layouts/AppLayout/components/PackageVersions/packageVersions.config.ts +8 -8
- package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +43 -2
- package/src/layouts/AppLayout/layouts/AdminLayout/components/ParentSync.tsx +20 -71
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/useApp.ts +21 -4
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +1 -1
- package/src/layouts/UILayout/TOOLS_INTEGRATION.md +216 -0
- package/src/layouts/UILayout/config/categories.config.tsx +8 -0
- package/src/layouts/UILayout/config/components/index.ts +3 -0
- package/src/layouts/UILayout/config/components/tools.config.tsx +204 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/layouts",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.22",
|
|
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.22",
|
|
57
|
+
"@djangocfg/og-image": "^1.2.22",
|
|
58
|
+
"@djangocfg/ui": "^1.2.22",
|
|
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.22",
|
|
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
|
-
|
|
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-
|
|
19
|
+
* Last updated: 2025-11-03T06:46:24.174Z
|
|
20
20
|
*/
|
|
21
21
|
const PACKAGE_VERSIONS: PackageInfo[] = [
|
|
22
22
|
{
|
|
23
23
|
"name": "@djangocfg/ui",
|
|
24
|
-
"version": "1.2.
|
|
24
|
+
"version": "1.2.22"
|
|
25
25
|
},
|
|
26
26
|
{
|
|
27
27
|
"name": "@djangocfg/api",
|
|
28
|
-
"version": "1.2.
|
|
28
|
+
"version": "1.2.22"
|
|
29
29
|
},
|
|
30
30
|
{
|
|
31
31
|
"name": "@djangocfg/layouts",
|
|
32
|
-
"version": "1.2.
|
|
32
|
+
"version": "1.2.22"
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
"name": "@djangocfg/markdown",
|
|
36
|
-
"version": "1.2.
|
|
36
|
+
"version": "1.2.22"
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
"name": "@djangocfg/og-image",
|
|
40
|
-
"version": "1.2.
|
|
40
|
+
"version": "1.2.22"
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"name": "@djangocfg/eslint-config",
|
|
44
|
-
"version": "1.2.
|
|
44
|
+
"version": "1.2.22"
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"name": "@djangocfg/typescript-config",
|
|
48
|
-
"version": "1.2.
|
|
48
|
+
"version": "1.2.22"
|
|
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
|
|
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]
|
|
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.
|
|
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
|
-
|
|
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-
|
|
107
|
-
data: {
|
|
110
|
+
type: 'iframe-navigation',
|
|
111
|
+
data: { path: url }
|
|
108
112
|
}, '*');
|
|
109
113
|
} catch (e) {
|
|
110
|
-
console.error('[ParentSync]
|
|
114
|
+
console.error('[ParentSync] Failed to send navigation event:', e);
|
|
111
115
|
}
|
|
112
116
|
};
|
|
113
117
|
|
|
114
|
-
//
|
|
115
|
-
|
|
118
|
+
// Listen to Next.js router events
|
|
119
|
+
router.events.on('routeChangeComplete', handleRouteChange);
|
|
116
120
|
|
|
117
|
-
//
|
|
118
|
-
|
|
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
|
-
|
|
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;
|
|
@@ -111,21 +111,36 @@ export function useCfgApp(options?: UseCfgAppOptions): UseCfgAppReturn {
|
|
|
111
111
|
|
|
112
112
|
// Listen for messages from parent window
|
|
113
113
|
const handleMessage = (event: MessageEvent) => {
|
|
114
|
+
// console.log('[useCfgApp] RAW message event:', {
|
|
115
|
+
// origin: event.origin,
|
|
116
|
+
// source: event.source === window.parent ? 'parent' : 'other',
|
|
117
|
+
// dataType: typeof event.data,
|
|
118
|
+
// data: event.data
|
|
119
|
+
// });
|
|
120
|
+
|
|
114
121
|
const { type, data } = event.data || {};
|
|
122
|
+
// console.log('[useCfgApp] Received message:', type, data);
|
|
115
123
|
|
|
116
124
|
switch (type) {
|
|
117
125
|
case 'parent-auth':
|
|
126
|
+
// console.log('[useCfgApp] parent-auth message received');
|
|
118
127
|
// Receive authentication tokens from parent
|
|
119
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');
|
|
120
131
|
try {
|
|
121
132
|
options.onAuthTokenReceived(data.authToken, data.refreshToken);
|
|
133
|
+
// console.log('[useCfgApp] onAuthTokenReceived callback completed successfully');
|
|
122
134
|
} catch (e) {
|
|
123
135
|
console.error('[useCfgApp] Failed to process auth tokens:', e);
|
|
124
136
|
}
|
|
137
|
+
} else {
|
|
138
|
+
console.warn('[useCfgApp] parent-auth message received but authToken or callback missing:', { hasToken: !!data?.authToken, hasCallback: !!options?.onAuthTokenReceived });
|
|
125
139
|
}
|
|
126
140
|
break;
|
|
127
141
|
|
|
128
142
|
case 'parent-theme':
|
|
143
|
+
// console.log('[useCfgApp] parent-theme message received:', data?.theme);
|
|
129
144
|
// Receive theme from parent
|
|
130
145
|
if (data?.theme) {
|
|
131
146
|
try {
|
|
@@ -133,26 +148,25 @@ export function useCfgApp(options?: UseCfgAppOptions): UseCfgAppReturn {
|
|
|
133
148
|
if (data.themeMode) {
|
|
134
149
|
setParentThemeMode(data.themeMode);
|
|
135
150
|
}
|
|
151
|
+
// console.log('[useCfgApp] Theme set successfully:', data.theme);
|
|
136
152
|
} catch (e) {
|
|
137
153
|
console.error('[useCfgApp] Failed to process theme:', e);
|
|
138
154
|
}
|
|
139
155
|
}
|
|
140
156
|
break;
|
|
141
157
|
|
|
142
|
-
case 'parent-resize':
|
|
143
|
-
// Handle parent window resize (optional)
|
|
144
|
-
break;
|
|
145
|
-
|
|
146
158
|
default:
|
|
147
159
|
break;
|
|
148
160
|
}
|
|
149
161
|
};
|
|
150
162
|
|
|
151
163
|
window.addEventListener('message', handleMessage);
|
|
164
|
+
// console.log('[useCfgApp] Message listener registered, isEmbedded:', inIframe);
|
|
152
165
|
|
|
153
166
|
// Send iframe-ready since listener is registered
|
|
154
167
|
if (inIframe) {
|
|
155
168
|
try {
|
|
169
|
+
// console.log('[useCfgApp] Sending iframe-ready message to parent');
|
|
156
170
|
window.parent.postMessage({
|
|
157
171
|
type: 'iframe-ready',
|
|
158
172
|
data: {
|
|
@@ -160,9 +174,12 @@ export function useCfgApp(options?: UseCfgAppOptions): UseCfgAppReturn {
|
|
|
160
174
|
referrer: document.referrer
|
|
161
175
|
}
|
|
162
176
|
}, '*');
|
|
177
|
+
// console.log('[useCfgApp] iframe-ready message sent');
|
|
163
178
|
} catch (e) {
|
|
164
179
|
console.error('[useCfgApp] Failed to notify parent about ready state:', e);
|
|
165
180
|
}
|
|
181
|
+
} else {
|
|
182
|
+
// console.log('[useCfgApp] Not in iframe, skipping iframe-ready message');
|
|
166
183
|
}
|
|
167
184
|
|
|
168
185
|
return () => {
|
|
@@ -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 (
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# Developer Tools Integration
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
Successfully integrated three developer tool components into the UILayout showcase:
|
|
6
|
+
|
|
7
|
+
1. **JsonTree** - Interactive JSON viewer with expand/collapse functionality
|
|
8
|
+
2. **PrettyCode** - Syntax-highlighted code display
|
|
9
|
+
3. **Mermaid** - Diagram renderer with fullscreen support
|
|
10
|
+
|
|
11
|
+
## Changes Made
|
|
12
|
+
|
|
13
|
+
### 1. Created Tools Configuration
|
|
14
|
+
|
|
15
|
+
**File**: `config/components/tools.config.tsx`
|
|
16
|
+
|
|
17
|
+
- Defined 3 component configurations with examples and previews
|
|
18
|
+
- Added sample data for demonstrations
|
|
19
|
+
- Included comprehensive usage examples
|
|
20
|
+
|
|
21
|
+
### 2. Updated Components Index
|
|
22
|
+
|
|
23
|
+
**File**: `config/components/index.ts`
|
|
24
|
+
|
|
25
|
+
- Added `TOOLS_COMPONENTS` export
|
|
26
|
+
- Integrated tools into `COMPONENTS_CONFIG` array
|
|
27
|
+
- Tools now available in component registry
|
|
28
|
+
|
|
29
|
+
### 3. Added Developer Tools Category
|
|
30
|
+
|
|
31
|
+
**File**: `config/categories.config.tsx`
|
|
32
|
+
|
|
33
|
+
- Created new "Developer Tools" category
|
|
34
|
+
- Added Wrench icon from lucide-react
|
|
35
|
+
- Positioned between "Specialized" and "Blocks" categories
|
|
36
|
+
- Description: "Developer utilities: JSON viewer, code syntax highlighting, Mermaid diagrams"
|
|
37
|
+
|
|
38
|
+
### 4. Updated Tools Exports
|
|
39
|
+
|
|
40
|
+
**File**: `packages/ui/src/tools/index.ts`
|
|
41
|
+
|
|
42
|
+
- Ensured all tools are properly exported as named exports
|
|
43
|
+
- Exported TypeScript types (Language, JsonTreeConfig)
|
|
44
|
+
|
|
45
|
+
## Component Features
|
|
46
|
+
|
|
47
|
+
### JsonTree
|
|
48
|
+
|
|
49
|
+
**Features:**
|
|
50
|
+
- Expand/collapse all functionality
|
|
51
|
+
- Smart auto-expansion based on depth and collection size
|
|
52
|
+
- Collection info display (array length, object keys count)
|
|
53
|
+
- Copy to clipboard
|
|
54
|
+
- Download as JSON file
|
|
55
|
+
- Theme-aware colors using CSS variables
|
|
56
|
+
- String truncation for long values
|
|
57
|
+
- Configurable expansion limits
|
|
58
|
+
|
|
59
|
+
**Configuration Options:**
|
|
60
|
+
- `maxAutoExpandDepth` - Maximum depth to auto-expand (default: 2)
|
|
61
|
+
- `maxAutoExpandArrayItems` - Max array items to auto-expand (default: 10)
|
|
62
|
+
- `maxAutoExpandObjectKeys` - Max object keys to auto-expand (default: 5)
|
|
63
|
+
- `maxStringLength` - String truncation threshold (default: 200)
|
|
64
|
+
- `collectionLimit` - Collection size limit for performance (default: 50)
|
|
65
|
+
- `showCollectionInfo` - Display collection metadata
|
|
66
|
+
- `showExpandControls` - Show expand/collapse buttons
|
|
67
|
+
- `showActionButtons` - Show copy/download buttons
|
|
68
|
+
|
|
69
|
+
### PrettyCode
|
|
70
|
+
|
|
71
|
+
**Features:**
|
|
72
|
+
- Syntax highlighting with Prism
|
|
73
|
+
- Automatic language detection
|
|
74
|
+
- Theme support (dark/light)
|
|
75
|
+
- Inline and block modes
|
|
76
|
+
- Language badge display
|
|
77
|
+
- Support for 15+ languages
|
|
78
|
+
- Custom background colors
|
|
79
|
+
- JSON auto-formatting
|
|
80
|
+
|
|
81
|
+
**Supported Languages:**
|
|
82
|
+
- JavaScript/TypeScript
|
|
83
|
+
- Python
|
|
84
|
+
- Bash/Shell
|
|
85
|
+
- JSON, YAML, XML
|
|
86
|
+
- HTML, CSS
|
|
87
|
+
- SQL
|
|
88
|
+
- Markdown
|
|
89
|
+
- And more...
|
|
90
|
+
|
|
91
|
+
### Mermaid
|
|
92
|
+
|
|
93
|
+
**Features:**
|
|
94
|
+
- Fullscreen modal view
|
|
95
|
+
- Click to expand
|
|
96
|
+
- ESC key to close
|
|
97
|
+
- Theme-aware rendering
|
|
98
|
+
- Semantic color variables
|
|
99
|
+
- Support for all Mermaid diagram types
|
|
100
|
+
- Responsive SVG sizing
|
|
101
|
+
- Error handling with user feedback
|
|
102
|
+
|
|
103
|
+
**Supported Diagrams:**
|
|
104
|
+
- Flowchart / Graph
|
|
105
|
+
- Sequence diagram
|
|
106
|
+
- Class diagram
|
|
107
|
+
- State diagram
|
|
108
|
+
- Entity Relationship diagram
|
|
109
|
+
- User Journey
|
|
110
|
+
- Gantt chart
|
|
111
|
+
- Pie chart
|
|
112
|
+
- Git graph
|
|
113
|
+
|
|
114
|
+
## Usage in UILayout
|
|
115
|
+
|
|
116
|
+
The tools are now accessible through the UILayout navigation:
|
|
117
|
+
|
|
118
|
+
1. Navigate to **Developer Tools** category in sidebar
|
|
119
|
+
2. Click on any tool (JsonTree, PrettyCode, Mermaid)
|
|
120
|
+
3. View live preview and usage examples
|
|
121
|
+
4. Copy code examples for use in your project
|
|
122
|
+
|
|
123
|
+
## Import Examples
|
|
124
|
+
|
|
125
|
+
```tsx
|
|
126
|
+
// Individual imports
|
|
127
|
+
import { JsonTree } from '@djangocfg/ui';
|
|
128
|
+
import { PrettyCode } from '@djangocfg/ui';
|
|
129
|
+
import { Mermaid } from '@djangocfg/ui';
|
|
130
|
+
|
|
131
|
+
// All at once
|
|
132
|
+
import { JsonTree, PrettyCode, Mermaid } from '@djangocfg/ui';
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
## Preview Examples
|
|
136
|
+
|
|
137
|
+
### JsonTree Preview
|
|
138
|
+
```tsx
|
|
139
|
+
<JsonTree
|
|
140
|
+
title="User Data"
|
|
141
|
+
data={{
|
|
142
|
+
user: {
|
|
143
|
+
id: 1,
|
|
144
|
+
name: "John Doe",
|
|
145
|
+
profile: { bio: "Software engineer" }
|
|
146
|
+
}
|
|
147
|
+
}}
|
|
148
|
+
config={{
|
|
149
|
+
maxAutoExpandDepth: 2,
|
|
150
|
+
showCollectionInfo: true,
|
|
151
|
+
showExpandControls: true,
|
|
152
|
+
}}
|
|
153
|
+
/>
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### PrettyCode Preview
|
|
157
|
+
```tsx
|
|
158
|
+
<PrettyCode
|
|
159
|
+
data={pythonCode}
|
|
160
|
+
language="python"
|
|
161
|
+
/>
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### Mermaid Preview
|
|
165
|
+
```tsx
|
|
166
|
+
<Mermaid
|
|
167
|
+
chart={`
|
|
168
|
+
graph TD
|
|
169
|
+
A[Start] --> B{Decision}
|
|
170
|
+
B -->|Yes| C[OK]
|
|
171
|
+
B -->|No| D[End]
|
|
172
|
+
`}
|
|
173
|
+
/>
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## File Structure
|
|
177
|
+
|
|
178
|
+
```
|
|
179
|
+
UILayout/
|
|
180
|
+
├── config/
|
|
181
|
+
│ ├── components/
|
|
182
|
+
│ │ ├── tools.config.tsx # NEW - Tools configurations
|
|
183
|
+
│ │ └── index.ts # UPDATED - Added tools export
|
|
184
|
+
│ └── categories.config.tsx # UPDATED - Added tools category
|
|
185
|
+
└── TOOLS_INTEGRATION.md # THIS FILE
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Testing
|
|
189
|
+
|
|
190
|
+
To verify the integration:
|
|
191
|
+
|
|
192
|
+
1. Start the development server
|
|
193
|
+
2. Navigate to UILayout showcase
|
|
194
|
+
3. Check sidebar for "Developer Tools" category
|
|
195
|
+
4. Verify all 3 components render correctly
|
|
196
|
+
5. Test interactive features:
|
|
197
|
+
- JsonTree: expand/collapse, copy, download
|
|
198
|
+
- PrettyCode: syntax highlighting in different themes
|
|
199
|
+
- Mermaid: fullscreen view, ESC key close
|
|
200
|
+
|
|
201
|
+
## Next Steps
|
|
202
|
+
|
|
203
|
+
Potential enhancements:
|
|
204
|
+
- Add search functionality to JsonTree
|
|
205
|
+
- Add more code language examples to PrettyCode
|
|
206
|
+
- Add more diagram type examples to Mermaid
|
|
207
|
+
- Create composite examples combining multiple tools
|
|
208
|
+
- Add performance benchmarks section
|
|
209
|
+
|
|
210
|
+
## Notes
|
|
211
|
+
|
|
212
|
+
- All components are theme-aware using CSS variables
|
|
213
|
+
- All components support both light and dark modes
|
|
214
|
+
- All components are fully responsive
|
|
215
|
+
- No breaking changes to existing components
|
|
216
|
+
- Backward compatible with existing UILayout structure
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
Boxes,
|
|
17
17
|
Code2,
|
|
18
18
|
Palette,
|
|
19
|
+
Wrench,
|
|
19
20
|
} from 'lucide-react';
|
|
20
21
|
import { getComponentCount } from './components';
|
|
21
22
|
|
|
@@ -83,6 +84,13 @@ export const CATEGORIES: ComponentCategory[] = [
|
|
|
83
84
|
count: getComponentCount('specialized'),
|
|
84
85
|
description: 'Advanced components like sidebar navigation and image handling',
|
|
85
86
|
},
|
|
87
|
+
{
|
|
88
|
+
id: 'tools',
|
|
89
|
+
label: 'Developer Tools',
|
|
90
|
+
icon: <Wrench className="h-4 w-4" />,
|
|
91
|
+
count: getComponentCount('tools'),
|
|
92
|
+
description: 'Developer utilities: JSON viewer, code syntax highlighting, Mermaid diagrams',
|
|
93
|
+
},
|
|
86
94
|
{
|
|
87
95
|
id: 'blocks',
|
|
88
96
|
label: 'Blocks',
|
|
@@ -11,6 +11,7 @@ export { OVERLAY_COMPONENTS } from './overlay.config';
|
|
|
11
11
|
export { FEEDBACK_COMPONENTS } from './feedback.config';
|
|
12
12
|
export { DATA_COMPONENTS } from './data.config';
|
|
13
13
|
export { SPECIALIZED_COMPONENTS } from './specialized.config';
|
|
14
|
+
export { TOOLS_COMPONENTS } from './tools.config';
|
|
14
15
|
export { BLOCKS } from './blocks.config';
|
|
15
16
|
export { HOOKS } from './hooks.config';
|
|
16
17
|
|
|
@@ -21,6 +22,7 @@ import { OVERLAY_COMPONENTS } from './overlay.config';
|
|
|
21
22
|
import { FEEDBACK_COMPONENTS } from './feedback.config';
|
|
22
23
|
import { DATA_COMPONENTS } from './data.config';
|
|
23
24
|
import { SPECIALIZED_COMPONENTS } from './specialized.config';
|
|
25
|
+
import { TOOLS_COMPONENTS } from './tools.config';
|
|
24
26
|
import { BLOCKS } from './blocks.config';
|
|
25
27
|
import { HOOKS } from './hooks.config';
|
|
26
28
|
import type { ComponentConfig } from './types';
|
|
@@ -36,6 +38,7 @@ export const COMPONENTS_CONFIG: ComponentConfig[] = [
|
|
|
36
38
|
...FEEDBACK_COMPONENTS,
|
|
37
39
|
...DATA_COMPONENTS,
|
|
38
40
|
...SPECIALIZED_COMPONENTS,
|
|
41
|
+
...TOOLS_COMPONENTS,
|
|
39
42
|
...BLOCKS,
|
|
40
43
|
...HOOKS,
|
|
41
44
|
];
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Developer Tools Components Configuration
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
import { JsonTree, PrettyCode, Mermaid } from '@djangocfg/ui';
|
|
7
|
+
import type { ComponentConfig } from './types';
|
|
8
|
+
|
|
9
|
+
// Sample data for demos
|
|
10
|
+
const sampleJsonData = {
|
|
11
|
+
user: {
|
|
12
|
+
id: 1,
|
|
13
|
+
name: "John Doe",
|
|
14
|
+
email: "john@example.com",
|
|
15
|
+
profile: {
|
|
16
|
+
bio: "Software engineer",
|
|
17
|
+
location: "San Francisco",
|
|
18
|
+
interests: ["coding", "music", "travel"]
|
|
19
|
+
},
|
|
20
|
+
posts: [
|
|
21
|
+
{ id: 1, title: "First Post", views: 120 },
|
|
22
|
+
{ id: 2, title: "Second Post", views: 89 }
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
settings: {
|
|
26
|
+
theme: "dark",
|
|
27
|
+
notifications: true,
|
|
28
|
+
language: "en"
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const samplePythonCode = `def fibonacci(n):
|
|
33
|
+
"""Calculate the nth Fibonacci number."""
|
|
34
|
+
if n <= 1:
|
|
35
|
+
return n
|
|
36
|
+
return fibonacci(n - 1) + fibonacci(n - 2)
|
|
37
|
+
|
|
38
|
+
# Example usage
|
|
39
|
+
result = fibonacci(10)
|
|
40
|
+
print(f"The 10th Fibonacci number is: {result}")`;
|
|
41
|
+
|
|
42
|
+
const sampleMermaidChart = `graph TD
|
|
43
|
+
A[Start] --> B{Is it?}
|
|
44
|
+
B -->|Yes| C[OK]
|
|
45
|
+
C --> D[Rethink]
|
|
46
|
+
D --> B
|
|
47
|
+
B ---->|No| E[End]`;
|
|
48
|
+
|
|
49
|
+
export const TOOLS_COMPONENTS: ComponentConfig[] = [
|
|
50
|
+
{
|
|
51
|
+
name: 'JsonTree',
|
|
52
|
+
category: 'tools',
|
|
53
|
+
description: 'Interactive JSON tree viewer with expand/collapse, search, and export functionality',
|
|
54
|
+
importPath: `import { JsonTree } from '@djangocfg/ui';`,
|
|
55
|
+
example: `<JsonTree
|
|
56
|
+
title="User Data"
|
|
57
|
+
data={{
|
|
58
|
+
user: {
|
|
59
|
+
id: 1,
|
|
60
|
+
name: "John Doe",
|
|
61
|
+
email: "john@example.com",
|
|
62
|
+
profile: {
|
|
63
|
+
bio: "Software engineer",
|
|
64
|
+
interests: ["coding", "music"]
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}}
|
|
68
|
+
config={{
|
|
69
|
+
maxAutoExpandDepth: 2,
|
|
70
|
+
showCollectionInfo: true,
|
|
71
|
+
showExpandControls: true,
|
|
72
|
+
showActionButtons: true,
|
|
73
|
+
}}
|
|
74
|
+
/>`,
|
|
75
|
+
preview: (
|
|
76
|
+
<div className="h-[500px]">
|
|
77
|
+
<JsonTree
|
|
78
|
+
title="User Data"
|
|
79
|
+
data={sampleJsonData}
|
|
80
|
+
config={{
|
|
81
|
+
maxAutoExpandDepth: 2,
|
|
82
|
+
showCollectionInfo: true,
|
|
83
|
+
showExpandControls: true,
|
|
84
|
+
showActionButtons: true,
|
|
85
|
+
}}
|
|
86
|
+
/>
|
|
87
|
+
</div>
|
|
88
|
+
),
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'PrettyCode',
|
|
92
|
+
category: 'tools',
|
|
93
|
+
description: 'Syntax-highlighted code display with automatic language detection and theme support',
|
|
94
|
+
importPath: `import { PrettyCode } from '@djangocfg/ui';`,
|
|
95
|
+
example: `// Code as string
|
|
96
|
+
<PrettyCode
|
|
97
|
+
data={pythonCode}
|
|
98
|
+
language="python"
|
|
99
|
+
/>
|
|
100
|
+
|
|
101
|
+
// JSON object (auto-formatted)
|
|
102
|
+
<PrettyCode
|
|
103
|
+
data={{ user: "John", age: 30 }}
|
|
104
|
+
language="json"
|
|
105
|
+
/>
|
|
106
|
+
|
|
107
|
+
// Inline code
|
|
108
|
+
<PrettyCode
|
|
109
|
+
data="const x = 42;"
|
|
110
|
+
language="javascript"
|
|
111
|
+
inline
|
|
112
|
+
/>`,
|
|
113
|
+
preview: (
|
|
114
|
+
<div className="space-y-4">
|
|
115
|
+
{/* Block code example */}
|
|
116
|
+
<div className="h-[300px]">
|
|
117
|
+
<PrettyCode
|
|
118
|
+
data={samplePythonCode}
|
|
119
|
+
language="python"
|
|
120
|
+
/>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
{/* Inline code example */}
|
|
124
|
+
<div className="p-4 border rounded-md bg-muted/50">
|
|
125
|
+
<p className="text-sm text-muted-foreground mb-2">
|
|
126
|
+
Inline code example:
|
|
127
|
+
</p>
|
|
128
|
+
<div className="flex items-center gap-2">
|
|
129
|
+
<span className="text-sm">Run</span>
|
|
130
|
+
<PrettyCode
|
|
131
|
+
data="npm install @djangocfg/ui"
|
|
132
|
+
language="bash"
|
|
133
|
+
inline
|
|
134
|
+
/>
|
|
135
|
+
<span className="text-sm">to install</span>
|
|
136
|
+
</div>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
),
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'Mermaid',
|
|
143
|
+
category: 'tools',
|
|
144
|
+
description: 'Interactive Mermaid diagram renderer with fullscreen view and theme support',
|
|
145
|
+
importPath: `import { Mermaid } from '@djangocfg/ui';`,
|
|
146
|
+
example: `<Mermaid
|
|
147
|
+
chart={\`
|
|
148
|
+
graph TD
|
|
149
|
+
A[Start] --> B{Is it?}
|
|
150
|
+
B -->|Yes| C[OK]
|
|
151
|
+
C --> D[Rethink]
|
|
152
|
+
D --> B
|
|
153
|
+
B -->|No| E[End]
|
|
154
|
+
\`}
|
|
155
|
+
/>
|
|
156
|
+
|
|
157
|
+
// Sequence diagram
|
|
158
|
+
<Mermaid
|
|
159
|
+
chart={\`
|
|
160
|
+
sequenceDiagram
|
|
161
|
+
participant A as Alice
|
|
162
|
+
participant B as Bob
|
|
163
|
+
A->>B: Hello Bob!
|
|
164
|
+
B->>A: Hello Alice!
|
|
165
|
+
\`}
|
|
166
|
+
/>
|
|
167
|
+
|
|
168
|
+
// Flowchart
|
|
169
|
+
<Mermaid
|
|
170
|
+
chart={\`
|
|
171
|
+
flowchart LR
|
|
172
|
+
A[Hard edge] -->|Link text| B(Round edge)
|
|
173
|
+
B --> C{Decision}
|
|
174
|
+
C -->|One| D[Result one]
|
|
175
|
+
C -->|Two| E[Result two]
|
|
176
|
+
\`}
|
|
177
|
+
/>`,
|
|
178
|
+
preview: (
|
|
179
|
+
<div className="space-y-4">
|
|
180
|
+
<Mermaid chart={sampleMermaidChart} />
|
|
181
|
+
|
|
182
|
+
<div className="p-4 border rounded-md bg-muted/50">
|
|
183
|
+
<p className="text-sm text-muted-foreground mb-2">
|
|
184
|
+
Supported diagram types:
|
|
185
|
+
</p>
|
|
186
|
+
<ul className="space-y-1 text-sm text-muted-foreground">
|
|
187
|
+
<li>• Flowchart / Graph</li>
|
|
188
|
+
<li>• Sequence diagram</li>
|
|
189
|
+
<li>• Class diagram</li>
|
|
190
|
+
<li>• State diagram</li>
|
|
191
|
+
<li>• Entity Relationship diagram</li>
|
|
192
|
+
<li>• User Journey</li>
|
|
193
|
+
<li>• Gantt chart</li>
|
|
194
|
+
<li>• Pie chart</li>
|
|
195
|
+
<li>• Git graph</li>
|
|
196
|
+
</ul>
|
|
197
|
+
<p className="text-xs text-muted-foreground mt-3">
|
|
198
|
+
Click on diagram to view in fullscreen mode
|
|
199
|
+
</p>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
),
|
|
203
|
+
},
|
|
204
|
+
];
|