@djangocfg/layouts 1.0.1

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 (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +77 -0
  3. package/package.json +86 -0
  4. package/src/auth/README.md +962 -0
  5. package/src/auth/context/AuthContext.tsx +458 -0
  6. package/src/auth/context/index.ts +2 -0
  7. package/src/auth/context/types.ts +63 -0
  8. package/src/auth/hooks/index.ts +6 -0
  9. package/src/auth/hooks/useAuthForm.ts +329 -0
  10. package/src/auth/hooks/useAuthGuard.ts +23 -0
  11. package/src/auth/hooks/useAuthRedirect.ts +51 -0
  12. package/src/auth/hooks/useAutoAuth.ts +42 -0
  13. package/src/auth/hooks/useLocalStorage.ts +211 -0
  14. package/src/auth/hooks/useSessionStorage.ts +186 -0
  15. package/src/auth/index.ts +10 -0
  16. package/src/auth/middlewares/index.ts +1 -0
  17. package/src/auth/middlewares/proxy.ts +24 -0
  18. package/src/auth/server.ts +6 -0
  19. package/src/auth/utils/errors.ts +34 -0
  20. package/src/auth/utils/index.ts +2 -0
  21. package/src/auth/utils/validation.ts +14 -0
  22. package/src/index.ts +15 -0
  23. package/src/layouts/AppLayout/AppLayout.tsx +123 -0
  24. package/src/layouts/AppLayout/README.md +204 -0
  25. package/src/layouts/AppLayout/SUMMARY.md +240 -0
  26. package/src/layouts/AppLayout/USAGE.md +312 -0
  27. package/src/layouts/AppLayout/components/PageProgress.tsx +104 -0
  28. package/src/layouts/AppLayout/components/Seo.tsx +87 -0
  29. package/src/layouts/AppLayout/components/index.ts +6 -0
  30. package/src/layouts/AppLayout/context/AppContext.tsx +146 -0
  31. package/src/layouts/AppLayout/context/index.ts +5 -0
  32. package/src/layouts/AppLayout/hooks/index.ts +6 -0
  33. package/src/layouts/AppLayout/hooks/useLayoutMode.ts +26 -0
  34. package/src/layouts/AppLayout/hooks/useNavigation.ts +49 -0
  35. package/src/layouts/AppLayout/index.ts +31 -0
  36. package/src/layouts/AppLayout/layouts/AuthLayout/AuthContext.tsx +51 -0
  37. package/src/layouts/AppLayout/layouts/AuthLayout/AuthHelp.tsx +111 -0
  38. package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +40 -0
  39. package/src/layouts/AppLayout/layouts/AuthLayout/IdentifierForm.tsx +330 -0
  40. package/src/layouts/AppLayout/layouts/AuthLayout/OTPForm.tsx +158 -0
  41. package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +13 -0
  42. package/src/layouts/AppLayout/layouts/AuthLayout/types.ts +61 -0
  43. package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +92 -0
  44. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +60 -0
  45. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +170 -0
  46. package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +164 -0
  47. package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +7 -0
  48. package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +5 -0
  49. package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +44 -0
  50. package/src/layouts/AppLayout/layouts/PublicLayout/components/DesktopUserMenu.tsx +136 -0
  51. package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +262 -0
  52. package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileMenu.tsx +289 -0
  53. package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +159 -0
  54. package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +5 -0
  55. package/src/layouts/AppLayout/layouts/index.ts +7 -0
  56. package/src/layouts/AppLayout/providers/CoreProviders.tsx +47 -0
  57. package/src/layouts/AppLayout/providers/index.ts +5 -0
  58. package/src/layouts/AppLayout/types/config.ts +40 -0
  59. package/src/layouts/AppLayout/types/index.ts +10 -0
  60. package/src/layouts/AppLayout/types/layout.ts +47 -0
  61. package/src/layouts/AppLayout/types/navigation.ts +41 -0
  62. package/src/layouts/AppLayout/types/routes.ts +45 -0
  63. package/src/layouts/AppLayout/utils/index.ts +5 -0
  64. package/src/layouts/AppLayout/utils/routeDetection.ts +31 -0
  65. package/src/layouts/PaymentsLayout/PaymentsLayout.tsx +125 -0
  66. package/src/layouts/PaymentsLayout/README.md +133 -0
  67. package/src/layouts/PaymentsLayout/components/CreateApiKeyDialog.tsx +172 -0
  68. package/src/layouts/PaymentsLayout/components/CreatePaymentDialog.tsx +203 -0
  69. package/src/layouts/PaymentsLayout/components/DeleteApiKeyDialog.tsx +100 -0
  70. package/src/layouts/PaymentsLayout/components/index.ts +4 -0
  71. package/src/layouts/PaymentsLayout/events.ts +106 -0
  72. package/src/layouts/PaymentsLayout/index.ts +20 -0
  73. package/src/layouts/PaymentsLayout/types.ts +19 -0
  74. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeyMetrics.tsx +109 -0
  75. package/src/layouts/PaymentsLayout/views/apikeys/components/ApiKeysList.tsx +194 -0
  76. package/src/layouts/PaymentsLayout/views/apikeys/components/index.ts +3 -0
  77. package/src/layouts/PaymentsLayout/views/apikeys/index.tsx +19 -0
  78. package/src/layouts/PaymentsLayout/views/overview/components/BalanceCard.tsx +99 -0
  79. package/src/layouts/PaymentsLayout/views/overview/components/MetricsCards.tsx +103 -0
  80. package/src/layouts/PaymentsLayout/views/overview/components/RecentPayments.tsx +138 -0
  81. package/src/layouts/PaymentsLayout/views/overview/components/index.ts +4 -0
  82. package/src/layouts/PaymentsLayout/views/overview/index.tsx +23 -0
  83. package/src/layouts/PaymentsLayout/views/payments/components/PaymentsList.tsx +282 -0
  84. package/src/layouts/PaymentsLayout/views/payments/components/index.ts +2 -0
  85. package/src/layouts/PaymentsLayout/views/payments/index.tsx +18 -0
  86. package/src/layouts/PaymentsLayout/views/tariffs/index.tsx +29 -0
  87. package/src/layouts/PaymentsLayout/views/transactions/index.tsx +29 -0
  88. package/src/layouts/ProfileLayout/ProfileLayout.tsx +110 -0
  89. package/src/layouts/ProfileLayout/components/AvatarSection.tsx +146 -0
  90. package/src/layouts/ProfileLayout/components/ProfileForm.tsx +208 -0
  91. package/src/layouts/ProfileLayout/components/index.ts +3 -0
  92. package/src/layouts/ProfileLayout/index.ts +3 -0
  93. package/src/layouts/SupportLayout/README.md +91 -0
  94. package/src/layouts/SupportLayout/SupportLayout.tsx +178 -0
  95. package/src/layouts/SupportLayout/components/CreateTicketDialog.tsx +154 -0
  96. package/src/layouts/SupportLayout/components/MessageInput.tsx +92 -0
  97. package/src/layouts/SupportLayout/components/MessageList.tsx +312 -0
  98. package/src/layouts/SupportLayout/components/TicketCard.tsx +96 -0
  99. package/src/layouts/SupportLayout/components/TicketList.tsx +152 -0
  100. package/src/layouts/SupportLayout/components/index.ts +6 -0
  101. package/src/layouts/SupportLayout/context/SupportLayoutContext.tsx +260 -0
  102. package/src/layouts/SupportLayout/context/index.ts +2 -0
  103. package/src/layouts/SupportLayout/events.ts +31 -0
  104. package/src/layouts/SupportLayout/hooks/index.ts +2 -0
  105. package/src/layouts/SupportLayout/hooks/useInfiniteMessages.ts +118 -0
  106. package/src/layouts/SupportLayout/hooks/useInfiniteTickets.ts +91 -0
  107. package/src/layouts/SupportLayout/index.ts +6 -0
  108. package/src/layouts/SupportLayout/types.ts +23 -0
  109. package/src/layouts/index.ts +9 -0
  110. package/src/snippets/AuthDialog/AuthDialog.tsx +88 -0
  111. package/src/snippets/AuthDialog/events.ts +21 -0
  112. package/src/snippets/AuthDialog/index.ts +3 -0
  113. package/src/snippets/AuthDialog/useAuthDialog.ts +27 -0
  114. package/src/snippets/Breadcrumbs.tsx +80 -0
  115. package/src/snippets/Chat/ChatUIContext.tsx +110 -0
  116. package/src/snippets/Chat/ChatWidget.tsx +476 -0
  117. package/src/snippets/Chat/README.md +122 -0
  118. package/src/snippets/Chat/components/MessageInput.tsx +124 -0
  119. package/src/snippets/Chat/components/MessageList.tsx +168 -0
  120. package/src/snippets/Chat/components/SessionList.tsx +192 -0
  121. package/src/snippets/Chat/components/index.ts +9 -0
  122. package/src/snippets/Chat/hooks/index.ts +6 -0
  123. package/src/snippets/Chat/hooks/useInfiniteSessions.ts +83 -0
  124. package/src/snippets/Chat/index.tsx +44 -0
  125. package/src/snippets/Chat/types.ts +79 -0
  126. package/src/snippets/VideoPlayer/README.md +203 -0
  127. package/src/snippets/VideoPlayer/VideoControls.tsx +133 -0
  128. package/src/snippets/VideoPlayer/VideoPlayer.tsx +114 -0
  129. package/src/snippets/VideoPlayer/index.ts +8 -0
  130. package/src/snippets/VideoPlayer/types.ts +61 -0
  131. package/src/snippets/index.ts +10 -0
  132. package/src/styles/dashboard.css +41 -0
  133. package/src/styles/index.css +20 -0
  134. package/src/styles/sources.css +6 -0
  135. package/src/types/index.ts +1 -0
  136. package/src/types/pageConfig.ts +103 -0
  137. package/src/utils/index.ts +6 -0
  138. package/src/utils/logger.ts +57 -0
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Public Layout Navigation
3
+ *
4
+ * Full-featured navigation using @djangocfg/ui components
5
+ * Refactored from _old/MainLayout - uses context only!
6
+ */
7
+
8
+ 'use client';
9
+
10
+ import React from 'react';
11
+ import Link from 'next/link';
12
+ import { Menu } from 'lucide-react';
13
+ import {
14
+ NavigationMenu,
15
+ NavigationMenuContent,
16
+ NavigationMenuItem,
17
+ NavigationMenuLink,
18
+ NavigationMenuList,
19
+ NavigationMenuTrigger,
20
+ navigationMenuTriggerStyle,
21
+ } from '@djangocfg/ui/components';
22
+ import { ThemeToggle } from '@djangocfg/ui/theme';
23
+ import { cn } from '@djangocfg/ui/lib';
24
+ import { useIsMobile } from '@djangocfg/ui/hooks';
25
+ import { useAppContext } from '../../../context';
26
+ import { useAuth } from '../../../../../auth';
27
+ import { useNavigation } from '../../../hooks';
28
+ import { DesktopUserMenu } from './DesktopUserMenu';
29
+ import { MobileMenu } from './MobileMenu';
30
+
31
+ /**
32
+ * Navigation Component
33
+ *
34
+ * Features:
35
+ * - Logo and branding
36
+ * - NavigationMenu from @djangocfg/ui (Radix UI based)
37
+ * - Theme toggle
38
+ * - User menu (desktop)
39
+ * - Mobile menu button
40
+ *
41
+ * All data from context - zero prop drilling!
42
+ */
43
+ export function Navigation() {
44
+ const { config, toggleMobileMenu } = useAppContext();
45
+ const { user, isAuthenticated, logout } = useAuth();
46
+ const { isActive } = useNavigation();
47
+ const isMobile = useIsMobile();
48
+
49
+ const { app, publicLayout } = config;
50
+
51
+ return (
52
+ <nav className="sticky top-0 w-full border-b backdrop-blur-xl z-[9990] bg-surface-gradient border-border/30">
53
+ <div className="w-full px-4 sm:px-6 lg:px-8">
54
+ <div className="flex items-center justify-between h-16">
55
+ {/* Left side - Logo and Navigation Menu */}
56
+ <div className="flex items-center gap-6 flex-1">
57
+ {/* Logo */}
58
+ <Link
59
+ href={publicLayout.navigation.homePath}
60
+ className="flex items-center gap-3 group"
61
+ >
62
+ <img
63
+ src={app.logoPath}
64
+ alt={`${app.name} Logo`}
65
+ className="h-8 w-auto object-contain transition-transform duration-300 group-hover:scale-105"
66
+ />
67
+ <span className="text-xl font-bold transition-colors duration-300 text-foreground group-hover:text-primary">
68
+ {app.name}
69
+ </span>
70
+ </Link>
71
+
72
+ {/* Desktop Navigation Menu using @djangocfg/ui NavigationMenu */}
73
+ {!isMobile && (
74
+ <div>
75
+ <NavigationMenu>
76
+ <NavigationMenuList>
77
+ {publicLayout.navigation.menuSections.map((section) => {
78
+ // Single item section - render as direct link
79
+ if (section.items.length === 1) {
80
+ const item = section.items[0];
81
+ if (!item) return null;
82
+
83
+ return (
84
+ <NavigationMenuItem key={section.title}>
85
+ <NavigationMenuLink asChild>
86
+ <Link
87
+ href={item.path}
88
+ className={cn(
89
+ navigationMenuTriggerStyle(),
90
+ isActive(item.path) && 'bg-accent text-accent-foreground'
91
+ )}
92
+ >
93
+ {item.label}
94
+ </Link>
95
+ </NavigationMenuLink>
96
+ </NavigationMenuItem>
97
+ );
98
+ }
99
+
100
+ // Multiple items - render as dropdown menu
101
+ return (
102
+ <NavigationMenuItem key={section.title}>
103
+ <NavigationMenuTrigger>{section.title}</NavigationMenuTrigger>
104
+ <NavigationMenuContent>
105
+ <ul className="grid gap-3 p-4" style={{ width: '400px' }}>
106
+ {section.items.map((item) => (
107
+ <li key={item.path}>
108
+ <NavigationMenuLink asChild>
109
+ <Link
110
+ href={item.path}
111
+ className={cn(
112
+ 'block select-none rounded-md p-3 leading-none no-underline outline-none transition-colors whitespace-nowrap hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground',
113
+ isActive(item.path) && 'bg-accent/50'
114
+ )}
115
+ >
116
+ <div className="text-sm font-medium leading-none">
117
+ {item.label}
118
+ </div>
119
+ </Link>
120
+ </NavigationMenuLink>
121
+ </li>
122
+ ))}
123
+ </ul>
124
+ </NavigationMenuContent>
125
+ </NavigationMenuItem>
126
+ );
127
+ })}
128
+ </NavigationMenuList>
129
+ </NavigationMenu>
130
+ </div>
131
+ )}
132
+ </div>
133
+
134
+ {/* Right side - Theme Toggle & User Menu */}
135
+ {!isMobile && (
136
+ <div className="flex items-center gap-2">
137
+ <ThemeToggle />
138
+ <DesktopUserMenu />
139
+ </div>
140
+ )}
141
+
142
+ {/* Mobile Menu Button - Only visible on mobile */}
143
+ {isMobile && (
144
+ <button
145
+ onClick={toggleMobileMenu}
146
+ className="p-3 rounded-lg border shadow-sm transition-all duration-200 bg-card/50 hover:bg-card border-border/50 hover:border-primary/50 text-foreground hover:text-primary"
147
+ aria-label="Toggle mobile menu"
148
+ >
149
+ <Menu className="size-5" />
150
+ </button>
151
+ )}
152
+ </div>
153
+ </div>
154
+
155
+ {/* Mobile Menu Drawer */}
156
+ <MobileMenu />
157
+ </nav>
158
+ );
159
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * PublicLayout Module
3
+ */
4
+
5
+ export * from './PublicLayout';
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Layouts Module
3
+ */
4
+
5
+ export * from './PublicLayout';
6
+ export * from './PrivateLayout';
7
+ export * from './AuthLayout';
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Core Providers
3
+ *
4
+ * Wraps application with all necessary providers
5
+ */
6
+
7
+ 'use client';
8
+
9
+ import React, { ReactNode } from 'react';
10
+ import { ThemeProvider, Toaster } from '@djangocfg/ui';
11
+ import { AuthProvider } from '../../../auth';
12
+ import type { AppLayoutConfig } from '../types';
13
+
14
+ export interface CoreProvidersProps {
15
+ children: ReactNode;
16
+ config: AppLayoutConfig;
17
+ }
18
+
19
+ /**
20
+ * Core Providers Wrapper
21
+ *
22
+ * Provides:
23
+ * - ThemeProvider (dark/light mode)
24
+ * - AuthProvider (authentication)
25
+ * - Toaster (notifications)
26
+ */
27
+ export function CoreProviders({ children, config }: CoreProvidersProps) {
28
+ return (
29
+ <ThemeProvider>
30
+ <AuthProvider
31
+ config={{
32
+ apiUrl: config.api.baseUrl,
33
+ routes: {
34
+ auth: config.routes.auth,
35
+ defaultCallback: config.routes.defaultCallback,
36
+ defaultAuthCallback: config.routes.defaultAuthCallback || config.routes.defaultCallback,
37
+ },
38
+ }}
39
+ >
40
+ {children}
41
+ </AuthProvider>
42
+
43
+ {/* Global toast notifications */}
44
+ <Toaster />
45
+ </ThemeProvider>
46
+ );
47
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Providers Module
3
+ */
4
+
5
+ export * from './CoreProviders';
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Configuration Types
3
+ */
4
+
5
+ import type { ReactNode } from 'react';
6
+ import type { PublicLayoutConfig, PrivateLayoutConfig } from './layout';
7
+ import type { RouteConfig } from './routes';
8
+
9
+ /**
10
+ * Main AppLayout configuration
11
+ */
12
+ export interface AppLayoutConfig {
13
+ /** Application metadata */
14
+ app: {
15
+ name: string;
16
+ description?: string;
17
+ logoPath: string;
18
+ siteUrl?: string;
19
+ icons?: {
20
+ logo192?: string;
21
+ logo384?: string;
22
+ logo512?: string;
23
+ logoVector?: string;
24
+ };
25
+ };
26
+
27
+ /** API configuration */
28
+ api: {
29
+ baseUrl: string;
30
+ };
31
+
32
+ /** Route configuration */
33
+ routes: RouteConfig;
34
+
35
+ /** Public layout configuration */
36
+ publicLayout: PublicLayoutConfig;
37
+
38
+ /** Private layout configuration */
39
+ privateLayout: PrivateLayoutConfig;
40
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * AppLayout Type Definitions
3
+ *
4
+ * All types for the unified AppLayout system
5
+ */
6
+
7
+ export * from './config';
8
+ export * from './layout';
9
+ export * from './navigation';
10
+ export * from './routes';
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Layout Configuration Types
3
+ */
4
+
5
+ import type { ReactNode } from 'react';
6
+ import type { LucideIcon } from 'lucide-react';
7
+ import type { NavigationSection, DashboardMenuGroup } from './navigation';
8
+
9
+ /**
10
+ * Public layout configuration
11
+ */
12
+ export interface PublicLayoutConfig {
13
+ navigation: {
14
+ homePath: string;
15
+ menuSections: NavigationSection[];
16
+ };
17
+ userMenu: {
18
+ dashboardPath?: string;
19
+ profilePath: string;
20
+ };
21
+ footer: {
22
+ badge: {
23
+ icon: LucideIcon;
24
+ text: string;
25
+ };
26
+ links: {
27
+ docs?: string;
28
+ privacy?: string;
29
+ terms?: string;
30
+ security?: string;
31
+ cookies?: string;
32
+ };
33
+ menuSections: NavigationSection[];
34
+ };
35
+ }
36
+
37
+ /**
38
+ * Private/Dashboard layout configuration
39
+ */
40
+ export interface PrivateLayoutConfig {
41
+ homeHref: string;
42
+ profileHref: string;
43
+ showChat?: boolean;
44
+ menuGroups: DashboardMenuGroup[];
45
+ contentPadding?: 'default' | 'none';
46
+ headerActions?: ReactNode;
47
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Navigation Types
3
+ */
4
+
5
+ import type { LucideIcon } from 'lucide-react';
6
+
7
+ /**
8
+ * Navigation menu item
9
+ */
10
+ export interface NavigationItem {
11
+ label: string;
12
+ path: string;
13
+ }
14
+
15
+ /**
16
+ * Navigation menu section
17
+ */
18
+ export interface NavigationSection {
19
+ title: string;
20
+ items: NavigationItem[];
21
+ }
22
+
23
+ /**
24
+ * Dashboard menu item with icon
25
+ */
26
+ export interface DashboardMenuItem {
27
+ path: string;
28
+ label: string;
29
+ icon: LucideIcon;
30
+ badge?: string | number;
31
+ subItems?: DashboardMenuItem[]; // Support nested menu items
32
+ }
33
+
34
+ /**
35
+ * Dashboard menu group
36
+ */
37
+ export interface DashboardMenuGroup {
38
+ label: string;
39
+ order: number;
40
+ items: DashboardMenuItem[];
41
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Route Types
3
+ */
4
+
5
+ /**
6
+ * Route configuration
7
+ */
8
+ export interface RouteConfig {
9
+ /** Authentication route */
10
+ auth: string;
11
+
12
+ /** Default redirect after login */
13
+ defaultCallback: string;
14
+
15
+ /** Redirect for authenticated users on auth page */
16
+ defaultAuthCallback?: string;
17
+
18
+ /** Route detector functions */
19
+ detectors: RouteDetectors;
20
+ }
21
+
22
+ /**
23
+ * Route detection functions
24
+ */
25
+ export interface RouteDetectors {
26
+ /** Check if route is public */
27
+ isPublicRoute: (path: string) => boolean;
28
+
29
+ /** Check if route is private/protected */
30
+ isPrivateRoute: (path: string) => boolean;
31
+
32
+ /** Check if route is auth page */
33
+ isAuthRoute: (path: string) => boolean;
34
+
35
+ /** Get redirect URL for unauthenticated users */
36
+ getUnauthenticatedRedirect: (path: string) => string | null;
37
+
38
+ /** Get page title for route */
39
+ getPageTitle: (path: string) => string;
40
+ }
41
+
42
+ /**
43
+ * Layout mode based on route
44
+ */
45
+ export type LayoutMode = 'public' | 'private' | 'auth';
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Utilities Module
3
+ */
4
+
5
+ export * from './routeDetection';
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Route Detection Utilities
3
+ */
4
+
5
+ import type { LayoutMode, RouteDetectors } from '../types';
6
+
7
+ /**
8
+ * Determine layout mode from pathname
9
+ */
10
+ export function determineLayoutMode(
11
+ pathname: string,
12
+ detectors: RouteDetectors
13
+ ): LayoutMode {
14
+ if (detectors.isAuthRoute(pathname)) return 'auth';
15
+ if (detectors.isPrivateRoute(pathname)) return 'private';
16
+ return 'public';
17
+ }
18
+
19
+ /**
20
+ * Check if redirect is needed for unauthenticated user
21
+ */
22
+ export function getRedirectUrl(
23
+ pathname: string,
24
+ isAuthenticated: boolean,
25
+ detectors: RouteDetectors
26
+ ): string | null {
27
+ if (!isAuthenticated) {
28
+ return detectors.getUnauthenticatedRedirect(pathname);
29
+ }
30
+ return null;
31
+ }
@@ -0,0 +1,125 @@
1
+ /**
2
+ * Payments Layout
3
+ * Main layout with tabs for different payment-related views
4
+ */
5
+
6
+ 'use client';
7
+
8
+ import React, { useState } from 'react';
9
+ import {
10
+ PaymentsProvider,
11
+ BalancesProvider,
12
+ ApiKeysProvider,
13
+ OverviewProvider,
14
+ } from '@djangocfg/api/cfg/contexts';
15
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@djangocfg/ui';
16
+ import { Wallet, CreditCard, History, Key, Crown } from 'lucide-react';
17
+ import type { PaymentTab } from './types';
18
+ import { OverviewView } from './views/overview';
19
+ import { PaymentsView } from './views/payments';
20
+ import { TransactionsView } from './views/transactions';
21
+ import { ApiKeysView } from './views/apikeys';
22
+ import { TariffsView } from './views/tariffs';
23
+ import { CreateApiKeyDialog, DeleteApiKeyDialog } from './components';
24
+
25
+ // ─────────────────────────────────────────────────────────────────────────
26
+ // Payments Layout Content
27
+ // ─────────────────────────────────────────────────────────────────────────
28
+
29
+ const PaymentsLayoutContent: React.FC = () => {
30
+ const [activeTab, setActiveTab] = useState<PaymentTab>('overview');
31
+
32
+ return (
33
+ <div className="h-full p-6 space-y-6">
34
+ {/* Page Header */}
35
+ <div>
36
+ <h1 className="text-3xl font-bold tracking-tight">Payments & Billing</h1>
37
+ <p className="text-muted-foreground">
38
+ Manage your payments, subscriptions, API keys, and account balance
39
+ </p>
40
+ </div>
41
+
42
+ {/* Main Content with Tabs */}
43
+ <Tabs
44
+ value={activeTab}
45
+ onValueChange={(value) => setActiveTab(value as PaymentTab)}
46
+ className="space-y-6"
47
+ >
48
+ <TabsList className="inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground">
49
+ <TabsTrigger value="overview" className="inline-flex items-center gap-2 px-3 py-1.5">
50
+ <Wallet className="h-4 w-4" />
51
+ <span className="hidden sm:inline">Overview</span>
52
+ </TabsTrigger>
53
+ <TabsTrigger value="payments" className="inline-flex items-center gap-2 px-3 py-1.5">
54
+ <CreditCard className="h-4 w-4" />
55
+ <span className="hidden sm:inline">Payments</span>
56
+ </TabsTrigger>
57
+ <TabsTrigger value="transactions" className="inline-flex items-center gap-2 px-3 py-1.5">
58
+ <History className="h-4 w-4" />
59
+ <span className="hidden sm:inline">Transactions</span>
60
+ </TabsTrigger>
61
+ <TabsTrigger value="apikeys" className="inline-flex items-center gap-2 px-3 py-1.5">
62
+ <Key className="h-4 w-4" />
63
+ <span className="hidden sm:inline">API Keys</span>
64
+ </TabsTrigger>
65
+ <TabsTrigger value="tariffs" className="inline-flex items-center gap-2 px-3 py-1.5">
66
+ <Crown className="h-4 w-4" />
67
+ <span className="hidden sm:inline">Tariffs</span>
68
+ </TabsTrigger>
69
+ </TabsList>
70
+
71
+ {/* Overview Tab */}
72
+ <TabsContent value="overview" className="space-y-6">
73
+ <OverviewView />
74
+ </TabsContent>
75
+
76
+ {/* Payments Tab */}
77
+ <TabsContent value="payments" className="space-y-6">
78
+ <PaymentsView />
79
+ </TabsContent>
80
+
81
+ {/* Transactions Tab */}
82
+ <TabsContent value="transactions" className="space-y-6">
83
+ <TransactionsView />
84
+ </TabsContent>
85
+
86
+ {/* API Keys Tab */}
87
+ <TabsContent value="apikeys" className="space-y-6">
88
+ <ApiKeysView />
89
+ </TabsContent>
90
+
91
+ {/* Tariffs Tab */}
92
+ <TabsContent value="tariffs" className="space-y-6">
93
+ <TariffsView />
94
+ </TabsContent>
95
+ </Tabs>
96
+
97
+ {/* Global Dialogs */}
98
+ <CreateApiKeyDialog />
99
+ <DeleteApiKeyDialog />
100
+ </div>
101
+ );
102
+ };
103
+
104
+ // ─────────────────────────────────────────────────────────────────────────
105
+ // Payments Layout (with providers)
106
+ // ─────────────────────────────────────────────────────────────────────────
107
+
108
+ export interface PaymentsLayoutProps {
109
+ children?: React.ReactNode;
110
+ }
111
+
112
+ export const PaymentsLayout: React.FC<PaymentsLayoutProps> = () => {
113
+ return (
114
+ <OverviewProvider>
115
+ <PaymentsProvider>
116
+ <BalancesProvider>
117
+ <ApiKeysProvider>
118
+ <PaymentsLayoutContent />
119
+ </ApiKeysProvider>
120
+ </BalancesProvider>
121
+ </PaymentsProvider>
122
+ </OverviewProvider>
123
+ );
124
+ };
125
+