@djangocfg/layouts 1.4.30 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +277 -18
  2. package/package.json +15 -24
  3. package/src/auth/context/AuthContext.tsx +5 -5
  4. package/src/auth/hooks/useAuthGuard.ts +1 -1
  5. package/src/auth/hooks/useAutoAuth.ts +8 -7
  6. package/src/components/ErrorBoundary.tsx +78 -0
  7. package/src/components/JsonLd.tsx +31 -0
  8. package/src/components/LucideIcon.tsx +91 -0
  9. package/src/components/PageProgress.tsx +127 -0
  10. package/src/components/Suspense.tsx +29 -0
  11. package/src/{layouts/AppLayout/components → components}/UpdateNotifier/UpdateNotifier.tsx +56 -49
  12. package/src/components/index.ts +10 -0
  13. package/src/index.ts +25 -7
  14. package/src/layouts/AdminLayout/AdminLayout.tsx +46 -0
  15. package/src/layouts/AdminLayout/index.ts +7 -0
  16. package/src/layouts/AppLayout/AppLayout.tsx +278 -326
  17. package/src/layouts/AppLayout/index.ts +2 -39
  18. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthContext.tsx +3 -2
  19. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthHelp.tsx +1 -0
  20. package/src/layouts/AuthLayout/AuthLayout.tsx +61 -0
  21. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/IdentifierForm.tsx +47 -34
  22. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/OTPForm.tsx +2 -3
  23. package/src/layouts/AuthLayout/index.ts +24 -0
  24. package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/types.ts +1 -0
  25. package/src/layouts/PrivateLayout/PrivateLayout.tsx +144 -0
  26. package/src/layouts/PrivateLayout/components/PrivateContent.tsx +32 -0
  27. package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +57 -0
  28. package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +141 -0
  29. package/src/layouts/PrivateLayout/components/index.ts +8 -0
  30. package/src/layouts/PrivateLayout/index.ts +7 -0
  31. package/src/layouts/ProfileLayout/ProfileLayout.tsx +15 -7
  32. package/src/layouts/PublicLayout/PublicLayout.tsx +121 -0
  33. package/src/layouts/PublicLayout/components/PublicFooter.tsx +190 -0
  34. package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +117 -0
  35. package/src/layouts/PublicLayout/components/PublicNavigation.tsx +101 -0
  36. package/src/layouts/PublicLayout/components/index.ts +8 -0
  37. package/src/layouts/PublicLayout/index.ts +7 -0
  38. package/src/layouts/_components/UserMenu.tsx +160 -0
  39. package/src/layouts/_components/index.ts +7 -0
  40. package/src/layouts/index.ts +15 -8
  41. package/src/snippets/Analytics/AnalyticsProvider.tsx +8 -4
  42. package/src/snippets/Analytics/useAnalytics.ts +11 -21
  43. package/src/snippets/Chat/ChatWidget.tsx +4 -4
  44. package/src/snippets/ContactForm/ContactFormProvider.tsx +32 -19
  45. package/src/snippets/ContactForm/ContactPage.tsx +2 -4
  46. package/src/snippets/ContactForm/types.ts +3 -2
  47. package/src/snippets/index.ts +0 -1
  48. package/src/layouts/AppLayout/README.md +0 -204
  49. package/src/layouts/AppLayout/SUMMARY.md +0 -240
  50. package/src/layouts/AppLayout/USAGE.md +0 -312
  51. package/src/layouts/AppLayout/components/ErrorBoundary.tsx +0 -112
  52. package/src/layouts/AppLayout/components/PageProgress.tsx +0 -123
  53. package/src/layouts/AppLayout/components/Seo.tsx +0 -171
  54. package/src/layouts/AppLayout/components/UserMenu.tsx +0 -385
  55. package/src/layouts/AppLayout/components/index.ts +0 -11
  56. package/src/layouts/AppLayout/context/AppContext.tsx +0 -151
  57. package/src/layouts/AppLayout/context/index.ts +0 -5
  58. package/src/layouts/AppLayout/hooks/index.ts +0 -8
  59. package/src/layouts/AppLayout/hooks/useLayoutMode.ts +0 -26
  60. package/src/layouts/AppLayout/hooks/useNavigation.ts +0 -51
  61. package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +0 -224
  62. package/src/layouts/AppLayout/layouts/AdminLayout/README.md +0 -409
  63. package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.example.tsx +0 -98
  64. package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.tsx +0 -149
  65. package/src/layouts/AppLayout/layouts/AdminLayout/components/ParentSync.tsx +0 -146
  66. package/src/layouts/AppLayout/layouts/AdminLayout/components/index.ts +0 -3
  67. package/src/layouts/AppLayout/layouts/AdminLayout/context/CfgAppContext.tsx +0 -48
  68. package/src/layouts/AppLayout/layouts/AdminLayout/context/index.ts +0 -2
  69. package/src/layouts/AppLayout/layouts/AdminLayout/hooks/index.ts +0 -6
  70. package/src/layouts/AppLayout/layouts/AdminLayout/hooks/useApp.ts +0 -279
  71. package/src/layouts/AppLayout/layouts/AdminLayout/index.ts +0 -24
  72. package/src/layouts/AppLayout/layouts/AdminLayout/lottie/energizing.json +0 -1
  73. package/src/layouts/AppLayout/layouts/AdminLayout/types/index.ts +0 -45
  74. package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +0 -41
  75. package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +0 -15
  76. package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +0 -82
  77. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +0 -62
  78. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +0 -89
  79. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +0 -181
  80. package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +0 -9
  81. package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +0 -5
  82. package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +0 -44
  83. package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +0 -242
  84. package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileDrawer.tsx +0 -150
  85. package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +0 -169
  86. package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +0 -5
  87. package/src/layouts/AppLayout/layouts/index.ts +0 -7
  88. package/src/layouts/AppLayout/providers/CoreProviders.tsx +0 -80
  89. package/src/layouts/AppLayout/providers/index.ts +0 -5
  90. package/src/layouts/AppLayout/types/config.ts +0 -79
  91. package/src/layouts/AppLayout/types/index.ts +0 -11
  92. package/src/layouts/AppLayout/types/layout.ts +0 -54
  93. package/src/layouts/AppLayout/types/navigation.ts +0 -43
  94. package/src/layouts/AppLayout/types/page.ts +0 -80
  95. package/src/layouts/AppLayout/types/routes.ts +0 -43
  96. package/src/layouts/AppLayout/utils/index.ts +0 -5
  97. package/src/layouts/AppLayout/utils/routeDetection.ts +0 -31
  98. package/src/layouts/ErrorLayout/ErrorLayout.tsx +0 -173
  99. package/src/layouts/ErrorLayout/errorConfig.tsx +0 -152
  100. package/src/layouts/ErrorLayout/index.ts +0 -8
  101. package/src/layouts/SimpleLayout/SimpleLayout.tsx +0 -72
  102. package/src/layouts/SimpleLayout/index.ts +0 -3
  103. package/src/snippets/VideoPlayer/README.md +0 -238
  104. package/src/snippets/VideoPlayer/VideoControls.tsx +0 -137
  105. package/src/snippets/VideoPlayer/VideoPlayer.tsx +0 -248
  106. package/src/snippets/VideoPlayer/index.ts +0 -8
  107. package/src/snippets/VideoPlayer/types.ts +0 -61
  108. package/src/types/index.ts +0 -2
  109. package/src/types/pageConfig.ts +0 -100
  110. /package/src/{validation → components/ErrorsTracker}/README.md +0 -0
  111. /package/src/{validation → components/ErrorsTracker}/components/ErrorButtons.tsx +0 -0
  112. /package/src/{validation → components/ErrorsTracker}/components/ErrorToast.tsx +0 -0
  113. /package/src/{validation → components/ErrorsTracker}/hooks.ts +0 -0
  114. /package/src/{validation → components/ErrorsTracker}/index.ts +0 -0
  115. /package/src/{validation → components/ErrorsTracker}/providers/ErrorTrackingProvider.tsx +0 -0
  116. /package/src/{validation → components/ErrorsTracker}/types.ts +0 -0
  117. /package/src/{validation → components/ErrorsTracker}/utils/curl-generator.ts +0 -0
  118. /package/src/{validation → components/ErrorsTracker}/utils/formatters.ts +0 -0
  119. /package/src/{layouts/AppLayout/components → components}/UpdateNotifier/index.ts +0 -0
@@ -1,82 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * Private Layout
4
- *
5
- * Dashboard layout for authenticated pages
6
- * Refactored from _old/DashboardLayout - uses context only!
7
- */
8
-
9
- 'use client';
10
-
11
- import React, { ReactNode, useEffect } from 'react';
12
- import { SidebarInset, SidebarProvider } from '@djangocfg/ui/components';
13
- import { useAuth } from '../../../../auth';
14
- import { KnowledgeChat } from '../../../../snippets/Chat';
15
- import { useAppContext } from '../../context';
16
- import { DashboardContent, DashboardHeader, DashboardSidebar } from './components';
17
-
18
- export interface PrivateLayoutProps {
19
- children: ReactNode;
20
- }
21
-
22
- /**
23
- * Private Layout Component
24
- *
25
- * Features:
26
- * - SidebarProvider for collapsible sidebar
27
- * - DashboardSidebar with navigation
28
- * - DashboardHeader with user menu, notifications, theme toggle
29
- * - DashboardContent with configurable padding
30
- * - KnowledgeChat (optional)
31
- * - Auth loading state
32
- * - Auth redirect for unauthenticated users
33
- *
34
- * All data from useAppContext() - no props!
35
- */
36
- export function PrivateLayout({ children }: PrivateLayoutProps) {
37
- const { config } = useAppContext();
38
- const { isLoading, isAuthenticated } = useAuth();
39
-
40
- const { privateLayout, routes } = config;
41
-
42
- // Redirect unauthenticated users (only when not loading)
43
- useEffect(() => {
44
- if (!isLoading && !isAuthenticated) {
45
- const redirect = routes.detectors.getUnauthenticatedRedirect(
46
- window.location.pathname
47
- );
48
- if (redirect) {
49
- window.location.href = redirect;
50
- }
51
- }
52
- }, [isLoading, isAuthenticated, routes]);
53
-
54
- // Don't render content if user is not authenticated
55
- // Note: Loading state is now handled in AppLayout, so we don't show it here
56
- if (!isAuthenticated) {
57
- return null;
58
- }
59
-
60
- return (
61
- <>
62
- <SidebarProvider defaultOpen={true}>
63
- {/* Sidebar */}
64
- <DashboardSidebar />
65
-
66
- {/* Main content area */}
67
- <SidebarInset className="flex flex-col">
68
- {/* Header */}
69
- <DashboardHeader />
70
-
71
- {/* Page content */}
72
- <div className="flex-1 overflow-y-auto">
73
- <DashboardContent>{children}</DashboardContent>
74
- </div>
75
- </SidebarInset>
76
- </SidebarProvider>
77
-
78
- {/* Chat positioned outside main layout for proper fixed positioning */}
79
- {privateLayout.showChat && <KnowledgeChat />}
80
- </>
81
- );
82
- }
@@ -1,62 +0,0 @@
1
- /**
2
- * Dashboard Content
3
- *
4
- * Main content wrapper for dashboard pages
5
- * Refactored from _old/DashboardLayout - uses context only!
6
- */
7
-
8
- 'use client';
9
-
10
- import React from 'react';
11
- import { cn } from '@djangocfg/ui/lib';
12
- import { useAppContext } from '../../../context';
13
-
14
- interface DashboardContentProps {
15
- children: React.ReactNode;
16
- className?: string;
17
- isAdmin?: boolean;
18
- }
19
-
20
- const paddingVariants = {
21
- none: '',
22
- default: 'p-6',
23
- sm: 'p-4',
24
- md: 'p-6',
25
- lg: 'p-8',
26
- };
27
-
28
- /**
29
- * Dashboard Content Component
30
- *
31
- * Features:
32
- * - Configurable padding from context
33
- * - Full-width container
34
- * - Custom className support
35
- *
36
- * Padding controlled by config.privateLayout.contentPadding
37
- */
38
- export function DashboardContent({
39
- children,
40
- className,
41
- isAdmin = false,
42
- }: DashboardContentProps) {
43
- const { config } = useAppContext();
44
- const { privateLayout } = config;
45
-
46
- const padding =
47
- paddingVariants[
48
- privateLayout.contentPadding as keyof typeof paddingVariants
49
- ] || paddingVariants.default;
50
-
51
- return (
52
- <main
53
- className={cn(
54
- 'w-full bg-background relative min-h-full',
55
- padding,
56
- className
57
- )}
58
- >
59
- {children}
60
- </main>
61
- );
62
- }
@@ -1,89 +0,0 @@
1
- /**
2
- * Dashboard Header
3
- *
4
- * Header for private/dashboard layout
5
- * Refactored from _old/DashboardLayout - uses context only!
6
- */
7
-
8
- 'use client';
9
-
10
- import { Bell } from 'lucide-react';
11
- import React from 'react';
12
-
13
- import {
14
- Button,
15
- Separator,
16
- SidebarTrigger,
17
- } from '@djangocfg/ui';
18
- import { ThemeToggle } from '@djangocfg/ui/theme';
19
- import { useAppContext } from '../../../context';
20
- import { useNavigation } from '../../../hooks';
21
- import { UserMenu } from '../../../components';
22
-
23
- export interface DashboardHeaderProps {
24
- isAdmin?: boolean;
25
- }
26
-
27
- /**
28
- * Dashboard Header Component
29
- *
30
- * Features:
31
- * - Sidebar trigger (mobile)
32
- * - Page title
33
- * - Custom header actions
34
- * - Notifications button with badge
35
- * - Theme toggle
36
- * - User dropdown with avatar, profile, logout
37
- * - Login button for guests
38
- *
39
- * All data from context!
40
- */
41
- export function DashboardHeader({ isAdmin = false }: DashboardHeaderProps) {
42
- const { config } = useAppContext();
43
- const { getPageTitle } = useNavigation();
44
-
45
- const { privateLayout } = config;
46
- const pageTitle = getPageTitle();
47
-
48
- // Notification handler - TODO: implement notification system
49
- const handleNotificationClick = () => {
50
- // console.log('Notifications clicked');
51
- };
52
-
53
- return (
54
- <header className="sticky top-0 py-2 z-10 h-16 flex items-center justify-between px-4 shrink-0 bg-background border-b border-border">
55
- {/* Left side */}
56
- <div className="flex items-center gap-4">
57
- <SidebarTrigger className="-ml-1" />
58
- <Separator orientation="vertical" className="mr-2 h-4" />
59
-
60
- {pageTitle && (
61
- <h1 className="text-lg font-semibold text-foreground">{pageTitle}</h1>
62
- )}
63
- </div>
64
-
65
- {/* Right side */}
66
- <div className="flex items-center gap-3">
67
- {/* Custom header actions */}
68
- {privateLayout.headerActions}
69
-
70
- {/* Notifications */}
71
- <Button
72
- variant="ghost"
73
- size="icon"
74
- className="relative"
75
- onClick={handleNotificationClick}
76
- >
77
- <Bell className="h-5 w-5" />
78
- {/* TODO: implement notification count from context */}
79
- </Button>
80
-
81
- {/* Theme Toggle */}
82
- <ThemeToggle />
83
-
84
- {/* User menu - unified component */}
85
- <UserMenu variant="desktop" />
86
- </div>
87
- </header>
88
- );
89
- }
@@ -1,181 +0,0 @@
1
- /**
2
- * Dashboard Sidebar
3
- *
4
- * Sidebar navigation for private/dashboard layout
5
- * Refactored from _old/DashboardLayout - uses context only!
6
- */
7
-
8
- 'use client';
9
-
10
- import Link from 'next/link';
11
- import React from 'react';
12
-
13
- import {
14
- Sidebar,
15
- SidebarContent,
16
- SidebarFooter,
17
- SidebarGroup,
18
- SidebarGroupContent,
19
- SidebarGroupLabel,
20
- SidebarHeader,
21
- SidebarMenu,
22
- SidebarMenuBadge,
23
- SidebarMenuButton,
24
- SidebarMenuItem,
25
- SidebarMenuSub,
26
- SidebarMenuSubButton,
27
- SidebarMenuSubItem,
28
- useSidebar,
29
- } from '@djangocfg/ui/components';
30
- import { useAppContext } from '../../../context';
31
- import { useNavigation } from '../../../hooks';
32
-
33
- export interface DashboardSidebarProps {
34
- isAdmin?: boolean;
35
- }
36
-
37
- /**
38
- * Dashboard Sidebar Component
39
- *
40
- * Features:
41
- * - Project logo and name (clickable to home)
42
- * - Menu groups with labels
43
- * - Menu items with icons, labels, badges
44
- * - Sub-menu items (nested navigation)
45
- * - Active state detection
46
- * - Optional footer content
47
- *
48
- * All data from context!
49
- */
50
- export function DashboardSidebar({ isAdmin = false }: DashboardSidebarProps) {
51
- const { config } = useAppContext();
52
- const { currentPath } = useNavigation();
53
- const { state, isMobile } = useSidebar();
54
-
55
- const { app, privateLayout, adminLayout } = config;
56
-
57
- // Admin layout: use adminLayout.menuSections converted to menuGroups, fallback to privateLayout
58
- const menuGroups = isAdmin && adminLayout
59
- ? adminLayout.menuSections.map((section, idx) => ({
60
- label: section.title,
61
- order: idx + 1,
62
- items: section.items.map(item => ({
63
- path: item.path,
64
- label: item.label,
65
- icon: item.icon || (() => null),
66
- badge: item.badge,
67
- })),
68
- }))
69
- : privateLayout.menuGroups;
70
-
71
- const homeHref = privateLayout.homeHref;
72
-
73
- const isActiveRoute = (path: string) => {
74
- // Only exact match - no prefix matching
75
- // This ensures /private/jobs ONLY matches /private/jobs
76
- // and NOT /private, /private/jobs/123, etc.
77
- return currentPath === path;
78
- };
79
-
80
- return (
81
- <Sidebar collapsible="icon">
82
- <SidebarHeader>
83
- <div
84
- className="flex items-center gap-3"
85
- style={state === "collapsed" ? {
86
- paddingLeft: '7px',
87
- paddingTop: '0.5rem',
88
- paddingBottom: '0.5rem',
89
- transition: 'padding 200ms ease-in-out'
90
- } : {
91
- padding: '0.5rem',
92
- transition: 'padding 200ms ease-in-out'
93
- }}
94
- >
95
- <Link href={homeHref}>
96
- <div className="flex items-center gap-3">
97
- {app.logoPath ? (
98
- <img
99
- src={app.logoPath}
100
- alt={app.name}
101
- className={isMobile ? "h-10 w-10 flex-shrink-0" : "h-8 w-8 flex-shrink-0"}
102
- />
103
- ) : (
104
- <div className={isMobile ? "h-10 w-10 bg-primary rounded-sm flex items-center justify-center flex-shrink-0" : "h-8 w-8 bg-primary rounded-sm flex items-center justify-center flex-shrink-0"}>
105
- <span className="text-primary-foreground font-bold text-sm">
106
- {app.name.charAt(0).toUpperCase()}
107
- </span>
108
- </div>
109
- )}
110
- {state !== "collapsed" && (
111
- <span className={isMobile ? "font-semibold text-foreground truncate text-base" : "font-semibold text-foreground truncate"} style={{ whiteSpace: 'nowrap' }}>
112
- {app.name}
113
- </span>
114
- )}
115
- </div>
116
- </Link>
117
- </div>
118
- </SidebarHeader>
119
-
120
- <SidebarContent>
121
- {menuGroups.map((group) => (
122
- <SidebarGroup key={group.label}>
123
- <SidebarGroupLabel>{group.label}</SidebarGroupLabel>
124
- <SidebarGroupContent>
125
- <SidebarMenu>
126
- {group.items.map((item) => {
127
- const isActive = isActiveRoute(item.path);
128
- const Icon = item.icon;
129
-
130
- return (
131
- <SidebarMenuItem key={item.path}>
132
- <SidebarMenuButton
133
- asChild
134
- isActive={isActive}
135
- tooltip={item.label}
136
- size={isMobile ? "lg" : "default"}
137
- >
138
- <Link href={item.path}>
139
- <Icon className={isMobile ? "h-5 w-5" : "h-4 w-4"} />
140
- <span className={isMobile ? "text-base" : ""}>{item.label}</span>
141
- {item.badge && (
142
- <SidebarMenuBadge>{item.badge}</SidebarMenuBadge>
143
- )}
144
- </Link>
145
- </SidebarMenuButton>
146
-
147
- {/* Submenu */}
148
- {item.subItems && item.subItems.length > 0 && (
149
- <SidebarMenuSub>
150
- {item.subItems.map((subItem) => {
151
- const isSubActive = isActiveRoute(subItem.path);
152
- const SubIcon = subItem.icon;
153
-
154
- return (
155
- <SidebarMenuSubItem key={subItem.path}>
156
- <SidebarMenuSubButton
157
- asChild
158
- isActive={isSubActive}
159
- size={isMobile ? "md" : "md"}
160
- >
161
- <Link href={subItem.path}>
162
- <SubIcon className={isMobile ? "h-5 w-5" : "h-4 w-4"} />
163
- <span className={isMobile ? "text-base" : ""}>{subItem.label}</span>
164
- </Link>
165
- </SidebarMenuSubButton>
166
- </SidebarMenuSubItem>
167
- );
168
- })}
169
- </SidebarMenuSub>
170
- )}
171
- </SidebarMenuItem>
172
- );
173
- })}
174
- </SidebarMenu>
175
- </SidebarGroupContent>
176
- </SidebarGroup>
177
- ))}
178
- </SidebarContent>
179
- </Sidebar>
180
- );
181
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * Private Layout Components
3
- */
4
-
5
- export { DashboardHeader } from './DashboardHeader';
6
- export type { DashboardHeaderProps } from './DashboardHeader';
7
- export { DashboardSidebar } from './DashboardSidebar';
8
- export type { DashboardSidebarProps } from './DashboardSidebar';
9
- export { DashboardContent } from './DashboardContent';
@@ -1,5 +0,0 @@
1
- /**
2
- * PrivateLayout Module
3
- */
4
-
5
- export * from './PrivateLayout';
@@ -1,44 +0,0 @@
1
- /**
2
- * Public Layout
3
- *
4
- * Layout for public pages (landing, docs, etc.)
5
- * All data accessed through context - no prop drilling
6
- */
7
-
8
- 'use client';
9
-
10
- import React, { ReactNode } from 'react';
11
- import { Navigation } from './components/Navigation';
12
- import { Footer } from './components/Footer';
13
-
14
- export interface PublicLayoutProps {
15
- children: ReactNode;
16
- }
17
-
18
- /**
19
- * Public Layout Component
20
- *
21
- * Features:
22
- * - Top navigation bar
23
- * - User menu integration
24
- * - Footer with links
25
- * - Mobile responsive
26
- *
27
- * All data from useAppContext() and useAuth() - no props needed!
28
- */
29
- export function PublicLayout({ children }: PublicLayoutProps) {
30
- return (
31
- <div className="min-h-screen flex flex-col">
32
- {/* Navigation - gets data from context */}
33
- <Navigation />
34
-
35
- {/* Main Content */}
36
- <main className="flex-1">
37
- {children}
38
- </main>
39
-
40
- {/* Footer - gets data from context */}
41
- <Footer />
42
- </div>
43
- );
44
- }