@djangocfg/layouts 1.4.30 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +277 -18
  2. package/package.json +15 -24
  3. package/src/auth/context/AuthContext.tsx +5 -5
  4. package/src/auth/hooks/useAuthGuard.ts +1 -1
  5. package/src/auth/hooks/useAutoAuth.ts +8 -7
  6. package/src/components/ErrorBoundary.tsx +78 -0
  7. package/src/components/JsonLd.tsx +31 -0
  8. package/src/components/LucideIcon.tsx +91 -0
  9. package/src/components/PageProgress.tsx +127 -0
  10. package/src/components/Suspense.tsx +29 -0
  11. package/src/{layouts/AppLayout/components → components}/UpdateNotifier/UpdateNotifier.tsx +56 -49
  12. package/src/components/index.ts +10 -0
  13. package/src/index.ts +25 -7
  14. package/src/layouts/AdminLayout/AdminLayout.tsx +46 -0
  15. package/src/layouts/AdminLayout/index.ts +7 -0
  16. package/src/layouts/AppLayout/AppLayout.tsx +278 -326
  17. package/src/layouts/AppLayout/index.ts +2 -39
  18. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthContext.tsx +3 -2
  19. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthHelp.tsx +1 -0
  20. package/src/layouts/AuthLayout/AuthLayout.tsx +61 -0
  21. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/IdentifierForm.tsx +47 -34
  22. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/OTPForm.tsx +2 -3
  23. package/src/layouts/AuthLayout/index.ts +24 -0
  24. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/types.ts +1 -0
  25. package/src/layouts/PrivateLayout/PrivateLayout.tsx +144 -0
  26. package/src/layouts/PrivateLayout/components/PrivateContent.tsx +32 -0
  27. package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +57 -0
  28. package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +141 -0
  29. package/src/layouts/PrivateLayout/components/index.ts +8 -0
  30. package/src/layouts/PrivateLayout/index.ts +7 -0
  31. package/src/layouts/ProfileLayout/ProfileLayout.tsx +15 -7
  32. package/src/layouts/PublicLayout/PublicLayout.tsx +121 -0
  33. package/src/layouts/PublicLayout/components/PublicFooter.tsx +190 -0
  34. package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +117 -0
  35. package/src/layouts/PublicLayout/components/PublicNavigation.tsx +101 -0
  36. package/src/layouts/PublicLayout/components/index.ts +8 -0
  37. package/src/layouts/PublicLayout/index.ts +7 -0
  38. package/src/layouts/_components/UserMenu.tsx +160 -0
  39. package/src/layouts/_components/index.ts +7 -0
  40. package/src/layouts/index.ts +15 -8
  41. package/src/snippets/Analytics/AnalyticsProvider.tsx +8 -4
  42. package/src/snippets/Analytics/useAnalytics.ts +11 -21
  43. package/src/snippets/Chat/ChatWidget.tsx +4 -4
  44. package/src/snippets/ContactForm/ContactFormProvider.tsx +32 -19
  45. package/src/snippets/ContactForm/ContactPage.tsx +2 -4
  46. package/src/snippets/ContactForm/types.ts +3 -2
  47. package/src/snippets/index.ts +0 -1
  48. package/src/layouts/AppLayout/README.md +0 -204
  49. package/src/layouts/AppLayout/SUMMARY.md +0 -240
  50. package/src/layouts/AppLayout/USAGE.md +0 -312
  51. package/src/layouts/AppLayout/components/ErrorBoundary.tsx +0 -112
  52. package/src/layouts/AppLayout/components/PageProgress.tsx +0 -123
  53. package/src/layouts/AppLayout/components/Seo.tsx +0 -171
  54. package/src/layouts/AppLayout/components/UserMenu.tsx +0 -385
  55. package/src/layouts/AppLayout/components/index.ts +0 -11
  56. package/src/layouts/AppLayout/context/AppContext.tsx +0 -151
  57. package/src/layouts/AppLayout/context/index.ts +0 -5
  58. package/src/layouts/AppLayout/hooks/index.ts +0 -8
  59. package/src/layouts/AppLayout/hooks/useLayoutMode.ts +0 -26
  60. package/src/layouts/AppLayout/hooks/useNavigation.ts +0 -51
  61. package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +0 -224
  62. package/src/layouts/AppLayout/layouts/AdminLayout/README.md +0 -409
  63. package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.example.tsx +0 -98
  64. package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.tsx +0 -149
  65. package/src/layouts/AppLayout/layouts/AdminLayout/components/ParentSync.tsx +0 -146
  66. package/src/layouts/AppLayout/layouts/AdminLayout/components/index.ts +0 -3
  67. package/src/layouts/AppLayout/layouts/AdminLayout/context/CfgAppContext.tsx +0 -48
  68. package/src/layouts/AppLayout/layouts/AdminLayout/context/index.ts +0 -2
  69. package/src/layouts/AppLayout/layouts/AdminLayout/hooks/index.ts +0 -6
  70. package/src/layouts/AppLayout/layouts/AdminLayout/hooks/useApp.ts +0 -279
  71. package/src/layouts/AppLayout/layouts/AdminLayout/index.ts +0 -24
  72. package/src/layouts/AppLayout/layouts/AdminLayout/lottie/energizing.json +0 -1
  73. package/src/layouts/AppLayout/layouts/AdminLayout/types/index.ts +0 -45
  74. package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +0 -41
  75. package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +0 -15
  76. package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +0 -82
  77. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +0 -62
  78. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +0 -89
  79. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +0 -181
  80. package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +0 -9
  81. package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +0 -5
  82. package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +0 -44
  83. package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +0 -242
  84. package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileDrawer.tsx +0 -150
  85. package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +0 -169
  86. package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +0 -5
  87. package/src/layouts/AppLayout/layouts/index.ts +0 -7
  88. package/src/layouts/AppLayout/providers/CoreProviders.tsx +0 -80
  89. package/src/layouts/AppLayout/providers/index.ts +0 -5
  90. package/src/layouts/AppLayout/types/config.ts +0 -79
  91. package/src/layouts/AppLayout/types/index.ts +0 -11
  92. package/src/layouts/AppLayout/types/layout.ts +0 -54
  93. package/src/layouts/AppLayout/types/navigation.ts +0 -43
  94. package/src/layouts/AppLayout/types/page.ts +0 -80
  95. package/src/layouts/AppLayout/types/routes.ts +0 -43
  96. package/src/layouts/AppLayout/utils/index.ts +0 -5
  97. package/src/layouts/AppLayout/utils/routeDetection.ts +0 -31
  98. package/src/layouts/ErrorLayout/ErrorLayout.tsx +0 -173
  99. package/src/layouts/ErrorLayout/errorConfig.tsx +0 -152
  100. package/src/layouts/ErrorLayout/index.ts +0 -8
  101. package/src/layouts/SimpleLayout/SimpleLayout.tsx +0 -72
  102. package/src/layouts/SimpleLayout/index.ts +0 -3
  103. package/src/snippets/VideoPlayer/README.md +0 -238
  104. package/src/snippets/VideoPlayer/VideoControls.tsx +0 -137
  105. package/src/snippets/VideoPlayer/VideoPlayer.tsx +0 -248
  106. package/src/snippets/VideoPlayer/index.ts +0 -8
  107. package/src/snippets/VideoPlayer/types.ts +0 -61
  108. package/src/types/index.ts +0 -2
  109. package/src/types/pageConfig.ts +0 -100
  110. /package/src/{validation → components/ErrorsTracker}/README.md +0 -0
  111. /package/src/{validation → components/ErrorsTracker}/components/ErrorButtons.tsx +0 -0
  112. /package/src/{validation → components/ErrorsTracker}/components/ErrorToast.tsx +0 -0
  113. /package/src/{validation → components/ErrorsTracker}/hooks.ts +0 -0
  114. /package/src/{validation → components/ErrorsTracker}/index.ts +0 -0
  115. /package/src/{validation → components/ErrorsTracker}/providers/ErrorTrackingProvider.tsx +0 -0
  116. /package/src/{validation → components/ErrorsTracker}/types.ts +0 -0
  117. /package/src/{validation → components/ErrorsTracker}/utils/curl-generator.ts +0 -0
  118. /package/src/{validation → components/ErrorsTracker}/utils/formatters.ts +0 -0
  119. /package/src/{layouts/AppLayout/components → components}/UpdateNotifier/index.ts +0 -0
@@ -0,0 +1,91 @@
1
+ /**
2
+ * LucideIcon Component
3
+ *
4
+ * Smart component for rendering Lucide icons
5
+ * Supports both string names and icon components
6
+ *
7
+ * Usage:
8
+ * ```tsx
9
+ * // By name (string)
10
+ * <LucideIcon icon="CloudLightning" className="w-4 h-4" />
11
+ *
12
+ * // By component
13
+ * import { CloudLightning } from 'lucide-react';
14
+ * <LucideIcon icon={CloudLightning} className="w-4 h-4" />
15
+ * ```
16
+ */
17
+
18
+ 'use client';
19
+
20
+ import React from 'react';
21
+ import * as LucideIcons from 'lucide-react';
22
+ import type { LucideIcon as LucideIconType } from 'lucide-react';
23
+ import { cn } from '@djangocfg/ui';
24
+
25
+ export interface LucideIconProps extends Omit<React.SVGProps<SVGSVGElement>, 'children'> {
26
+ /**
27
+ * Icon name (string) or icon component (LucideIcon)
28
+ * If string, will be resolved from lucide-react
29
+ * If component, will be used directly
30
+ */
31
+ icon?: LucideIconType | string;
32
+ /**
33
+ * Fallback icon if name not found
34
+ * @default CloudLightning
35
+ */
36
+ fallback?: LucideIconType;
37
+ /**
38
+ * Additional CSS classes
39
+ */
40
+ className?: string;
41
+ }
42
+
43
+ /**
44
+ * Get Lucide icon by name or return the icon component
45
+ * @param icon - Icon name (string) or LucideIcon component
46
+ * @param fallback - Fallback icon if name not found (default: CloudLightning)
47
+ * @returns LucideIcon component
48
+ */
49
+ function getLucideIcon(
50
+ icon: LucideIconType | string | undefined,
51
+ fallback: LucideIconType = LucideIcons.CloudLightning
52
+ ): LucideIconType {
53
+ if (!icon) {
54
+ return fallback;
55
+ }
56
+
57
+ if (typeof icon === 'string') {
58
+ const IconComponent = LucideIcons[icon as keyof typeof LucideIcons] as LucideIconType | undefined;
59
+ return IconComponent || fallback;
60
+ }
61
+
62
+ return icon;
63
+ }
64
+
65
+ /**
66
+ * LucideIcon Component
67
+ *
68
+ * Renders a Lucide icon either by name (string) or by component reference.
69
+ * Automatically resolves icon names from lucide-react package.
70
+ *
71
+ * Usage:
72
+ * ```tsx
73
+ * // By name (string)
74
+ * <LucideIcon icon="CloudLightning" className="w-4 h-4" />
75
+ *
76
+ * // By component
77
+ * import { CloudLightning } from 'lucide-react';
78
+ * <LucideIcon icon={CloudLightning} className="w-4 h-4" />
79
+ * ```
80
+ */
81
+ export function LucideIcon({
82
+ icon,
83
+ fallback = LucideIcons.CloudLightning,
84
+ className,
85
+ ...props
86
+ }: LucideIconProps) {
87
+ const IconComponent = getLucideIcon(icon, fallback);
88
+
89
+ return <IconComponent className={cn(className)} {...props} />;
90
+ }
91
+
@@ -0,0 +1,127 @@
1
+ /**
2
+ * PageProgress - Loading progress bar
3
+ *
4
+ * Shows a progress bar at the top of the page during route transitions
5
+ */
6
+
7
+ 'use client';
8
+
9
+ import { usePathname } from 'next/navigation';
10
+ import { useEffect, useRef, useState } from 'react';
11
+
12
+ export function PageProgress() {
13
+ const pathname = usePathname();
14
+ const [loading, setLoading] = useState(false);
15
+ const [progress, setProgress] = useState(0);
16
+ const [mounted, setMounted] = useState(false);
17
+ const progressTimer = useRef<NodeJS.Timeout | null>(null);
18
+ const prevPathname = useRef<string | null>(null);
19
+
20
+ useEffect(() => {
21
+ setMounted(true);
22
+ }, []);
23
+
24
+ // Simulate realistic progress
25
+ const startFakeProgress = () => {
26
+ // Clear any existing timer
27
+ if (progressTimer.current) {
28
+ clearInterval(progressTimer.current);
29
+ }
30
+
31
+ setProgress(0);
32
+
33
+ // Quickly go to 20% to show immediate feedback
34
+ setTimeout(() => setProgress(20), 50);
35
+
36
+ // Then slowly increase to 90% (never reach 100% until actually complete)
37
+ progressTimer.current = setInterval(() => {
38
+ setProgress((prevProgress) => {
39
+ if (prevProgress >= 90) {
40
+ if (progressTimer.current) {
41
+ clearInterval(progressTimer.current);
42
+ }
43
+ return 90;
44
+ }
45
+
46
+ // Slow down as we get closer to 90%
47
+ const increment = 90 - prevProgress;
48
+ return prevProgress + (increment / 10);
49
+ });
50
+ }, 300);
51
+ };
52
+
53
+ const completeProgress = () => {
54
+ // Clear any existing timer
55
+ if (progressTimer.current) {
56
+ clearInterval(progressTimer.current);
57
+ progressTimer.current = null;
58
+ }
59
+
60
+ // Jump to 100% and then hide after showing completion
61
+ setProgress(100);
62
+ setTimeout(() => {
63
+ setLoading(false);
64
+ setTimeout(() => {
65
+ setProgress(0);
66
+ }, 300); // Wait for fade out animation
67
+ }, 500); // Show 100% for half a second
68
+ };
69
+
70
+ // Track pathname changes (App Router equivalent of routeChangeStart/Complete)
71
+ useEffect(() => {
72
+ // Skip on initial mount
73
+ if (prevPathname.current === null) {
74
+ prevPathname.current = pathname;
75
+ return;
76
+ }
77
+
78
+ // Only trigger if pathname actually changed
79
+ if (prevPathname.current !== pathname) {
80
+ setLoading(true);
81
+ startFakeProgress();
82
+
83
+ // Complete progress after a short delay (simulating route change)
84
+ const timeout = setTimeout(() => {
85
+ completeProgress();
86
+ prevPathname.current = pathname;
87
+ }, 100);
88
+
89
+ return () => {
90
+ clearTimeout(timeout);
91
+ if (progressTimer.current) {
92
+ clearInterval(progressTimer.current);
93
+ }
94
+ };
95
+ }
96
+ }, [pathname]);
97
+
98
+ if (!mounted) {
99
+ return null;
100
+ }
101
+
102
+ return (
103
+ <div
104
+ data-page-progress="root"
105
+ data-loading={loading}
106
+ data-progress={progress}
107
+ className={`fixed top-0 left-0 w-full transition-opacity duration-300 ${
108
+ loading ? 'opacity-100' : 'opacity-0'
109
+ }`}
110
+ style={{
111
+ zIndex: 99999,
112
+ height: '3px',
113
+ }}
114
+ >
115
+ <div
116
+ className="h-full transition-all duration-200 ease-linear"
117
+ style={{
118
+ width: `${progress}%`,
119
+ background: 'linear-gradient(90deg, #3b82f6 0%, #60a5fa 50%, #3b82f6 100%)',
120
+ boxShadow: '0 0 10px rgba(59, 130, 246, 0.6), 0 0 20px rgba(59, 130, 246, 0.4), 0 0 30px rgba(59, 130, 246, 0.2)',
121
+ filter: 'drop-shadow(0 0 8px rgba(59, 130, 246, 0.8))',
122
+ }}
123
+ />
124
+ </div>
125
+ );
126
+ }
127
+
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Suspense Wrapper Component
3
+ *
4
+ * Reusable Suspense component with consistent loading fallback
5
+ * Used for wrapping layouts that may use useSearchParams or other async hooks
6
+ */
7
+
8
+ 'use client';
9
+
10
+ import { Suspense as ReactSuspense, ReactNode } from 'react';
11
+
12
+ interface SuspenseProps {
13
+ children: ReactNode;
14
+ fallback?: ReactNode;
15
+ }
16
+
17
+ const defaultFallback = (
18
+ <div className="flex items-center justify-center min-h-screen">
19
+ <div className="text-center">
20
+ <div className="inline-block h-8 w-8 animate-spin rounded-full border-4 border-solid border-current border-r-transparent align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]" />
21
+ <p className="mt-4 text-sm text-muted-foreground">Loading...</p>
22
+ </div>
23
+ </div>
24
+ );
25
+
26
+ export function Suspense({ children, fallback = defaultFallback }: SuspenseProps) {
27
+ return <ReactSuspense fallback={fallback}>{children}</ReactSuspense>;
28
+ }
29
+
@@ -8,9 +8,10 @@
8
8
 
9
9
  'use client';
10
10
 
11
- import React, { useEffect, useState } from 'react';
11
+ import React, { useEffect, useState, useRef } from 'react';
12
12
  import consola from 'consola';
13
13
  import { toast } from '@djangocfg/ui/hooks';
14
+ import { useLocalStorage } from '@djangocfg/ui/hooks';
14
15
 
15
16
  const PACKAGE_NAME = '@djangocfg/layouts';
16
17
  const CHECK_INTERVAL_MS = 24 * 60 * 60 * 1000; // 24 hours
@@ -22,16 +23,34 @@ interface UpdateCheckCache {
22
23
  dismissed: boolean;
23
24
  }
24
25
 
26
+ const defaultCache: UpdateCheckCache = {
27
+ lastCheck: 0,
28
+ latestVersion: '',
29
+ dismissed: false,
30
+ };
31
+
25
32
  export interface UpdateNotifierProps {
26
33
  /**
27
34
  * Enable update notifications
28
35
  * @default false
29
36
  */
30
37
  enabled?: boolean;
31
- /**
32
- * Current package version (auto-injected from package.json)
33
- */
34
- currentVersion?: string;
38
+ }
39
+
40
+ /**
41
+ * Get current package version from package.json
42
+ * Uses require in runtime to avoid TypeScript rootDir issues
43
+ */
44
+ function getCurrentVersion(): string | null {
45
+ try {
46
+ // Use require in runtime (works in both dev and build)
47
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
48
+ const packageJson = require('../../../package.json');
49
+ return packageJson.version || null;
50
+ } catch (error) {
51
+ consola.warn('[UpdateNotifier] Failed to load package.json:', error);
52
+ return null;
53
+ }
35
54
  }
36
55
 
37
56
  /**
@@ -69,53 +88,36 @@ async function fetchLatestVersion(): Promise<string | null> {
69
88
  }
70
89
  }
71
90
 
72
- /**
73
- * Get cached update check data
74
- */
75
- function getCache(): UpdateCheckCache | null {
76
- if (typeof window === 'undefined') return null;
77
-
78
- try {
79
- const cached = localStorage.getItem(CACHE_KEY);
80
- if (!cached) return null;
81
-
82
- const data: UpdateCheckCache = JSON.parse(cached);
83
- return data;
84
- } catch {
85
- return null;
86
- }
87
- }
88
-
89
- /**
90
- * Save update check data to cache
91
- */
92
- function setCache(data: UpdateCheckCache): void {
93
- if (typeof window === 'undefined') return;
94
-
95
- try {
96
- localStorage.setItem(CACHE_KEY, JSON.stringify(data));
97
- } catch (error) {
98
- consola.warn('[UpdateNotifier] Failed to cache update check:', error);
99
- }
100
- }
101
-
102
- export function UpdateNotifier({ enabled = false, currentVersion }: UpdateNotifierProps) {
91
+ export function UpdateNotifier({ enabled = false }: UpdateNotifierProps) {
103
92
  const [checked, setChecked] = useState(false);
93
+ const [cache, setCache] = useLocalStorage<UpdateCheckCache | null>(CACHE_KEY, null);
94
+ const cacheRef = useRef(cache);
95
+
96
+ // Keep cacheRef in sync with cache
97
+ useEffect(() => {
98
+ cacheRef.current = cache;
99
+ }, [cache]);
104
100
 
105
101
  useEffect(() => {
106
102
  if (!enabled || checked || typeof window === 'undefined') return;
107
- if (!currentVersion) return;
108
103
 
109
104
  const checkForUpdates = async () => {
110
- // Check cache first
111
- const cache = getCache();
105
+ // Get current version from package.json
106
+ const currentVersion = getCurrentVersion();
107
+ if (!currentVersion) {
108
+ setChecked(true);
109
+ return;
110
+ }
111
+
112
+ // Check cache first (use ref to get latest value)
112
113
  const now = Date.now();
114
+ const cachedData = cacheRef.current || defaultCache;
113
115
 
114
116
  // If we checked recently, skip
115
- if (cache && (now - cache.lastCheck) < CHECK_INTERVAL_MS) {
117
+ if (cachedData && cachedData.lastCheck > 0 && (now - cachedData.lastCheck) < CHECK_INTERVAL_MS) {
116
118
  // Show notification if there's an update and it wasn't dismissed
117
- if (cache.latestVersion && !cache.dismissed && isNewerVersion(currentVersion, cache.latestVersion)) {
118
- showUpdateNotification(currentVersion, cache.latestVersion);
119
+ if (cachedData.latestVersion && !cachedData.dismissed && isNewerVersion(currentVersion, cachedData.latestVersion)) {
120
+ showUpdateNotification(currentVersion, cachedData.latestVersion, setCache);
119
121
  }
120
122
  setChecked(true);
121
123
  return;
@@ -138,7 +140,7 @@ export function UpdateNotifier({ enabled = false, currentVersion }: UpdateNotifi
138
140
 
139
141
  // Show notification if newer version available
140
142
  if (isNewerVersion(currentVersion, latestVersion)) {
141
- showUpdateNotification(currentVersion, latestVersion);
143
+ showUpdateNotification(currentVersion, latestVersion, setCache);
142
144
  }
143
145
 
144
146
  setChecked(true);
@@ -148,7 +150,8 @@ export function UpdateNotifier({ enabled = false, currentVersion }: UpdateNotifi
148
150
  const timer = setTimeout(checkForUpdates, 2000);
149
151
 
150
152
  return () => clearTimeout(timer);
151
- }, [enabled, checked, currentVersion]);
153
+ // eslint-disable-next-line react-hooks/exhaustive-deps
154
+ }, [enabled, checked]);
152
155
 
153
156
  return null; // This component doesn't render anything
154
157
  }
@@ -156,7 +159,11 @@ export function UpdateNotifier({ enabled = false, currentVersion }: UpdateNotifi
156
159
  /**
157
160
  * Show update notification toast
158
161
  */
159
- function showUpdateNotification(currentVersion: string, latestVersion: string) {
162
+ function showUpdateNotification(
163
+ currentVersion: string,
164
+ latestVersion: string,
165
+ setCache: (value: UpdateCheckCache | null | ((val: UpdateCheckCache | null) => UpdateCheckCache | null)) => void
166
+ ) {
160
167
  toast({
161
168
  title: `📦 Update Available`,
162
169
  description: `New version ${latestVersion} of @djangocfg packages is available. You're using ${currentVersion}. Run: pnpm update @djangocfg/layouts@latest`,
@@ -164,8 +171,8 @@ function showUpdateNotification(currentVersion: string, latestVersion: string) {
164
171
  });
165
172
 
166
173
  // Mark as dismissed in cache after showing
167
- const cache = getCache();
168
- if (cache) {
169
- setCache({ ...cache, dismissed: true });
170
- }
174
+ setCache((prev) => {
175
+ if (!prev) return null;
176
+ return { ...prev, dismissed: true };
177
+ });
171
178
  }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Components exports
3
+ */
4
+
5
+ export { JsonLd } from './JsonLd';
6
+ export { LucideIcon } from './LucideIcon';
7
+ export type { LucideIconProps } from './LucideIcon';
8
+ export { PageProgress } from './PageProgress';
9
+
10
+ export { Suspense } from './Suspense';
package/src/index.ts CHANGED
@@ -1,7 +1,28 @@
1
1
  /**
2
2
  * @djangocfg/layouts
3
- *
4
- * Reusable layout components and authentication system
3
+ *
4
+ * Simple, straightforward layout components for Next.js
5
+ * Import and use directly with props - no complex configs needed!
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { PublicLayout, PrivateLayout, AuthLayout } from '@djangocfg/layouts';
10
+ *
11
+ * // Public page
12
+ * <PublicLayout logo="/logo.svg" siteName="My App" navigation={navItems}>
13
+ * {children}
14
+ * </PublicLayout>
15
+ *
16
+ * // Private page
17
+ * <PrivateLayout sidebar={{ items: menuItems }} header={{ title: 'Dashboard' }}>
18
+ * {children}
19
+ * </PrivateLayout>
20
+ *
21
+ * // Auth page
22
+ * <AuthLayout logo="/logo.svg" title="Sign In">
23
+ * <LoginForm />
24
+ * </AuthLayout>
25
+ * ```
5
26
  */
6
27
 
7
28
  // Auth system
@@ -10,12 +31,9 @@ export * from './auth';
10
31
  // Layout components
11
32
  export * from './layouts';
12
33
 
13
- // Types
14
- export * from './types';
15
-
16
34
  // Snippets - Reusable UI components (includes Analytics)
17
35
  export * from './snippets';
18
36
 
19
- // Validation error tracking
20
- export * from './validation';
37
+ // Components (includes ErrorTrackingProvider)
38
+ export * from './components';
21
39
 
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Admin Layout
3
+ *
4
+ * Layout for admin dashboard pages
5
+ * Import and use directly with props - no complex configs needed!
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * import { AdminLayout } from '@djangocfg/layouts';
10
+ *
11
+ * <AdminLayout
12
+ * sidebar={{
13
+ * items: [
14
+ * { label: 'Overview', href: '/admin', icon: 'LayoutDashboard' },
15
+ * { label: 'Users', href: '/admin/users', icon: 'Users' }
16
+ * ]
17
+ * }}
18
+ * header={{
19
+ * title: 'Admin Dashboard',
20
+ * profilePath: '/profile' // Optional
21
+ * }}
22
+ * >
23
+ * {children}
24
+ * </AdminLayout>
25
+ * ```
26
+ */
27
+
28
+ 'use client';
29
+
30
+ import { ReactNode } from 'react';
31
+ import { PrivateLayout, type PrivateLayoutProps } from '../PrivateLayout/PrivateLayout';
32
+
33
+ export interface AdminLayoutProps extends PrivateLayoutProps {
34
+ children: ReactNode;
35
+ }
36
+
37
+ /**
38
+ * Admin Layout Component
39
+ *
40
+ * Wrapper around PrivateLayout with admin-specific defaults
41
+ * Same API as PrivateLayout - just a convenience export
42
+ */
43
+ export function AdminLayout(props: AdminLayoutProps) {
44
+ return <PrivateLayout {...props} />;
45
+ }
46
+
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Admin Layout exports
3
+ */
4
+
5
+ export { AdminLayout } from './AdminLayout';
6
+ export type { AdminLayoutProps } from './AdminLayout';
7
+