@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,240 +0,0 @@
1
- # AppLayout - Implementation Summary
2
-
3
- ## ✅ Что создано
4
-
5
- ### 📁 Структура (идеально декомпозированная)
6
-
7
- ```
8
- AppLayout/
9
- ├── types/ # ✅ Все типы
10
- │ ├── config.ts # AppLayoutConfig
11
- │ ├── layout.ts # PublicLayoutConfig, PrivateLayoutConfig
12
- │ ├── navigation.ts # NavigationItem, DashboardMenuItem
13
- │ ├── routes.ts # RouteConfig, LayoutMode
14
- │ └── index.ts
15
-
16
- ├── context/ # ✅ Unified context
17
- │ ├── AppContext.tsx # Главный контекст со всем состоянием
18
- │ └── index.ts
19
-
20
- ├── hooks/ # ✅ Custom hooks
21
- │ ├── useLayoutMode.ts # Текущий режим layout'а
22
- │ ├── useNavigation.ts # Навигационные утилиты
23
- │ └── index.ts
24
-
25
- ├── providers/ # ✅ Provider wrappers
26
- │ ├── CoreProviders.tsx # Theme + Auth + Toaster
27
- │ └── index.ts
28
-
29
- ├── layouts/ # ✅ Layout components
30
- │ ├── PublicLayout/ # Публичный layout (refactored MainLayout)
31
- │ │ ├── PublicLayout.tsx
32
- │ │ ├── components/
33
- │ │ │ ├── Navigation.tsx
34
- │ │ │ └── Footer.tsx
35
- │ │ └── index.ts
36
- │ │
37
- │ ├── PrivateLayout/ # Приватный layout (refactored DashboardLayout)
38
- │ │ ├── PrivateLayout.tsx
39
- │ │ └── index.ts
40
- │ │
41
- │ ├── AuthLayout/ # Auth layout (перенесен)
42
- │ └── index.ts
43
-
44
- ├── components/ # ✅ UI components
45
- │ ├── Seo.tsx # SEO meta tags
46
- │ ├── PageProgress.tsx # Loading progress bar
47
- │ └── index.ts
48
-
49
- ├── utils/ # ✅ Utilities
50
- │ ├── routeDetection.ts # Определение типа route
51
- │ └── index.ts
52
-
53
- ├── AppLayout.tsx # ✅ Main component
54
- ├── index.ts # ✅ Public exports
55
- ├── README.md # ✅ Architecture docs
56
- ├── USAGE.md # ✅ Usage guide
57
- └── SUMMARY.md # ✅ This file
58
- ```
59
-
60
- ## 🎯 Ключевые особенности
61
-
62
- ### 1. Zero Prop Drilling
63
- ```typescript
64
- // ❌ Старый подход
65
- <Layout
66
- config={config}
67
- user={user}
68
- isAuthenticated={isAuthenticated}
69
- onLogout={logout}
70
- menuOpen={menuOpen}
71
- onToggleMenu={toggleMenu}
72
- >
73
-
74
- // ✅ Новый подход
75
- <AppLayout config={config}>
76
- {/* Всё доступно через контекст! */}
77
- </AppLayout>
78
- ```
79
-
80
- ### 2. Unified Context
81
- ```typescript
82
- const {
83
- config, // Вся конфигурация
84
- layoutMode, // Текущий режим
85
- mobileMenuOpen, // UI состояние
86
- toggleMobileMenu, // Actions
87
- sidebarCollapsed,
88
- toggleSidebar,
89
- } = useAppContext();
90
- ```
91
-
92
- ### 3. Smart Layout Detection
93
- ```typescript
94
- // Автоматически определяет layout на основе route:
95
- / → PublicLayout
96
- /private/* → PrivateLayout
97
- /auth/* → AuthLayout
98
- ```
99
-
100
- ### 4. Single Entry Point
101
- ```typescript
102
- // _app.tsx - единственное место подключения
103
- <AppLayout config={appLayoutConfig}>
104
- <Component {...pageProps} />
105
- </AppLayout>
106
- ```
107
-
108
- ## 📝 Что сделано в cloud app
109
-
110
- ### 1. Создан appLayoutConfig.ts
111
- ```typescript
112
- // apps/cloud/src/core/appLayoutConfig.ts
113
- export const appLayoutConfig: AppLayoutConfig = {
114
- app: { /* ... */ },
115
- api: { /* ... */ },
116
- routes: { /* ... */ },
117
- publicLayout: { /* ... */ },
118
- privateLayout: { /* ... */ },
119
- };
120
- ```
121
-
122
- ### 2. Обновлен _app.tsx
123
- ```typescript
124
- // Было: ~50 строк с SmartLayout и логикой
125
- // Стало: 15 строк с AppLayout
126
- <AppLayout config={appLayoutConfig}>
127
- <Component {...pageProps} />
128
- </AppLayout>
129
- ```
130
-
131
- ### 3. Обновлен core/index.ts
132
- ```typescript
133
- export { appLayoutConfig } from './appLayoutConfig';
134
- ```
135
-
136
- ## 🎨 Как использовать
137
-
138
- ### В любом компоненте:
139
- ```typescript
140
- import { useAppContext } from '@djangocfg/layouts';
141
-
142
- function MyComponent() {
143
- const { config, layoutMode } = useAppContext();
144
- // Всё доступно!
145
- }
146
- ```
147
-
148
- ### С хуками:
149
- ```typescript
150
- import { useLayoutMode, useNavigation } from '@djangocfg/layouts';
151
-
152
- function MyComponent() {
153
- const mode = useLayoutMode();
154
- const { isActive } = useNavigation();
155
- }
156
- ```
157
-
158
- ## 🏗️ Архитектурные принципы
159
-
160
- ### ✅ Separation of Concerns
161
- Каждая папка отвечает за свою область:
162
- - `types/` - типы
163
- - `context/` - состояние
164
- - `hooks/` - логика
165
- - `layouts/` - рендеринг
166
- - `components/` - UI
167
-
168
- ### ✅ Single Source of Truth
169
- Один `AppLayoutConfig` для всего
170
-
171
- ### ✅ Context over Props
172
- Никакого prop drilling
173
-
174
- ### ✅ Composability
175
- Легко расширять и модифицировать
176
-
177
- ### ✅ Type Safety
178
- TypeScript везде
179
-
180
- ## 📊 Миграция
181
-
182
- ### Старые компоненты → Новые
183
-
184
- ```
185
- MainLayout → AppLayout (PublicLayout)
186
- DashboardLayout → AppLayout (PrivateLayout)
187
- AuthLayout → AppLayout (AuthLayout)
188
-
189
- layoutConfig → appLayoutConfig.publicLayout
190
- dashboardConfig → appLayoutConfig.privateLayout
191
- smartLayoutConfig → appLayoutConfig (unified)
192
- ```
193
-
194
- ### Старые файлы
195
- ```
196
- _old/MainLayout/ → Reference only
197
- _old/DashboardLayout/ → Reference only
198
- _old/AppLayout/ → Reference only
199
- ```
200
-
201
- ## 🚀 Next Steps
202
-
203
- ### Immediate:
204
- 1. ✅ Refactor Navigation component from _old/MainLayout
205
- 2. ✅ Refactor Footer component from _old/MainLayout
206
- 3. ✅ Refactor PrivateLayout components from _old/DashboardLayout
207
- 4. ✅ Test all routes and layouts
208
- 5. ✅ Remove _old/ folder
209
-
210
- ### Future:
211
- - Add tests
212
- - Add Storybook stories
213
- - Add accessibility improvements
214
- - Add analytics integration
215
-
216
- ## 💡 Benefits
217
-
218
- ### For Developers:
219
- - **Простота**: Один компонент для всего
220
- - **Понятность**: Чистая структура папок
221
- - **Удобство**: Хуки для всего
222
- - **Скорость**: Нет prop drilling
223
-
224
- ### For Architecture:
225
- - **Масштабируемость**: Легко добавлять функционал
226
- - **Тестируемость**: Всё изолировано
227
- - **Поддерживаемость**: Чистый код
228
- - **Производительность**: Context оптимизирован
229
-
230
- ## 🎉 Result
231
-
232
- **Одна строка подключения. Бесконечные возможности.**
233
-
234
- ```typescript
235
- <AppLayout config={appLayoutConfig}>
236
- <YourApp />
237
- </AppLayout>
238
- ```
239
-
240
- Вот и всё! 🚀
@@ -1,312 +0,0 @@
1
- # AppLayout - Usage Guide
2
-
3
- ## 🎯 Философия
4
-
5
- **Одна точка входа. Ноль prop drilling. Максимум умности.**
6
-
7
- AppLayout - это единственный компонент, который нужен для управления всеми layout'ами в приложении.
8
-
9
- ## 🚀 Quick Start
10
-
11
- ### 1. Создай конфиг (один раз)
12
-
13
- ```typescript
14
- // apps/cloud/src/core/appLayoutConfig.ts
15
- import type { AppLayoutConfig } from '@djangocfg/layouts';
16
-
17
- export const appLayoutConfig: AppLayoutConfig = {
18
- app: {
19
- name: 'My App',
20
- logoPath: '/logo.svg',
21
- },
22
- api: {
23
- baseUrl: process.env.NEXT_PUBLIC_API_URL!,
24
- },
25
- routes: {
26
- auth: '/auth',
27
- defaultCallback: '/dashboard',
28
- detectors: {
29
- isPublicRoute: (path) => !path.startsWith('/private'),
30
- isPrivateRoute: (path) => path.startsWith('/private'),
31
- isAuthRoute: (path) => path.startsWith('/auth'),
32
- getUnauthenticatedRedirect: (path) =>
33
- path.startsWith('/private') ? '/auth' : null,
34
- getPageTitle: (path) => 'My App',
35
- },
36
- },
37
- publicLayout: {
38
- navigation: {
39
- homePath: '/',
40
- menuSections: [
41
- {
42
- title: 'Main',
43
- items: [
44
- { label: 'Home', path: '/' },
45
- { label: 'Docs', path: '/docs' },
46
- ],
47
- },
48
- ],
49
- },
50
- userMenu: {
51
- profilePath: '/profile',
52
- },
53
- footer: {
54
- badge: { icon: MyIcon, text: 'My App' },
55
- links: { privacy: '/privacy', terms: '/terms' },
56
- menuSections: [],
57
- },
58
- },
59
- privateLayout: {
60
- homeHref: '/',
61
- profileHref: '/profile',
62
- showChat: true,
63
- menuGroups: [
64
- {
65
- label: 'Main',
66
- order: 1,
67
- items: [
68
- { path: '/dashboard', label: 'Dashboard', icon: DashboardIcon },
69
- ],
70
- },
71
- ],
72
- contentPadding: 'default',
73
- },
74
- };
75
- ```
76
-
77
- ### 2. Используй в _app.tsx (один раз)
78
-
79
- ```typescript
80
- // pages/_app.tsx
81
- import { AppLayout } from '@djangocfg/layouts';
82
- import { appLayoutConfig } from '@/core';
83
-
84
- export default function App({ Component, pageProps }: AppProps) {
85
- return (
86
- <AppLayout config={appLayoutConfig}>
87
- <Component {...pageProps} />
88
- </AppLayout>
89
- );
90
- }
91
- ```
92
-
93
- ### 3. Всё! 🎉
94
-
95
- Теперь:
96
- - ✅ Public страницы автоматически получат MainLayout
97
- - ✅ Private страницы (`/private/*`) автоматически получат DashboardLayout
98
- - ✅ Auth страницы (`/auth/*`) получат минимальный layout
99
- - ✅ Все компоненты имеют доступ к конфигу через `useAppContext()`
100
-
101
- ## 🎨 Использование в компонентах
102
-
103
- ### Доступ к конфигу и состоянию
104
-
105
- ```typescript
106
- import { useAppContext } from '@djangocfg/layouts';
107
-
108
- function MyComponent() {
109
- const {
110
- config, // Весь конфиг
111
- layoutMode, // 'public' | 'private' | 'auth'
112
- currentPath, // Текущий путь
113
- mobileMenuOpen, // Состояние мобильного меню
114
- toggleMobileMenu, // Функция для переключения
115
- sidebarCollapsed, // Sidebar в dashboard
116
- toggleSidebar, // Функция для sidebar
117
- } = useAppContext();
118
-
119
- return (
120
- <button onClick={toggleMobileMenu}>
121
- Menu
122
- </button>
123
- );
124
- }
125
- ```
126
-
127
- ### Использование хуков
128
-
129
- ```typescript
130
- import { useLayoutMode, useNavigation } from '@djangocfg/layouts';
131
-
132
- function MyComponent() {
133
- const mode = useLayoutMode();
134
- const { isActive, getPageTitle } = useNavigation();
135
-
136
- if (mode === 'private') {
137
- return <DashboardContent />;
138
- }
139
-
140
- return <PublicContent />;
141
- }
142
- ```
143
-
144
- ### Доступ к auth
145
-
146
- ```typescript
147
- import { useAuth } from '@djangocfg/layouts';
148
-
149
- function UserProfile() {
150
- const { user, isAuthenticated, logout } = useAuth();
151
-
152
- if (!isAuthenticated) {
153
- return <div>Not authenticated</div>;
154
- }
155
-
156
- return (
157
- <div>
158
- <p>Welcome, {user.email}</p>
159
- <button onClick={logout}>Logout</button>
160
- </div>
161
- );
162
- }
163
- ```
164
-
165
- ## 🔧 Настройка layout'ов
166
-
167
- ### Public Layout
168
-
169
- Управляется через `config.publicLayout`:
170
-
171
- ```typescript
172
- publicLayout: {
173
- navigation: {
174
- homePath: '/',
175
- menuSections: [/* меню для navbar */],
176
- },
177
- userMenu: {
178
- dashboardPath: '/dashboard', // Опционально
179
- profilePath: '/profile',
180
- },
181
- footer: {
182
- badge: { icon: MyIcon, text: 'App Name' },
183
- links: { privacy: '/privacy', terms: '/terms' },
184
- menuSections: [/* меню для footer */],
185
- },
186
- }
187
- ```
188
-
189
- ### Private Layout
190
-
191
- Управляется через `config.privateLayout`:
192
-
193
- ```typescript
194
- privateLayout: {
195
- homeHref: '/',
196
- profileHref: '/profile',
197
- showChat: true, // Показывать чат
198
- contentPadding: 'default', // или 'none'
199
- menuGroups: [
200
- {
201
- label: 'Main',
202
- order: 1,
203
- items: [
204
- { path: '/dashboard', label: 'Dashboard', icon: Icon },
205
- ],
206
- },
207
- ],
208
- headerActions: <CustomActions />, // Опционально
209
- }
210
- ```
211
-
212
- ## 📊 Структура проекта
213
-
214
- ```
215
- apps/cloud/
216
- ├── src/
217
- │ ├── core/
218
- │ │ ├── appLayoutConfig.ts ← Единый конфиг
219
- │ │ ├── routes.ts ← Определение маршрутов
220
- │ │ └── settings.ts ← Настройки приложения
221
- │ │
222
- │ └── pages/
223
- │ ├── _app.tsx ← AppLayout подключается здесь
224
- │ ├── index.tsx ← Публичная страница (auto)
225
- │ ├── auth/
226
- │ │ └── index.tsx ← Auth layout (auto)
227
- │ └── private/
228
- │ └── dashboard.tsx ← Dashboard layout (auto)
229
- ```
230
-
231
- ## 🎯 Преимущества
232
-
233
- ### ✅ Нет prop drilling
234
- Всё через контекст - никаких пропсов вниз по дереву
235
-
236
- ### ✅ Автоматический роутинг
237
- Layout определяется автоматически на основе пути
238
-
239
- ### ✅ Единый конфиг
240
- Один объект конфигурации для всего приложения
241
-
242
- ### ✅ Типобезопасность
243
- TypeScript везде
244
-
245
- ### ✅ Легкое тестирование
246
- Весь функционал доступен через хуки
247
-
248
- ### ✅ Простота использования
249
- Подключил один раз в `_app.tsx` - и всё работает
250
-
251
- ## 🔍 Как это работает
252
-
253
- ```
254
- _app.tsx
255
-
256
- AppLayout (config)
257
-
258
- CoreProviders (Theme, Auth, Toaster)
259
-
260
- AppContextProvider (State management)
261
-
262
- LayoutRouter (Auto-detect route)
263
-
264
- ├─ PublicLayout (/)
265
- ├─ PrivateLayout (/private/*)
266
- └─ AuthLayout (/auth/*)
267
-
268
- Page Component
269
- ```
270
-
271
- ## 💡 Best Practices
272
-
273
- ### 1. Один конфиг
274
- Храни всю конфигурацию в одном файле `appLayoutConfig.ts`
275
-
276
- ### 2. Используй хуки
277
- Вместо прямого доступа к контексту, используй специализированные хуки
278
-
279
- ### 3. Не гоняй пропсы
280
- Если данные доступны в контексте - берите их там, не передавайте пропсами
281
-
282
- ### 4. Кастомные layout'ы
283
- Если нужен кастомный layout для страницы - используй `getLayout`:
284
-
285
- ```typescript
286
- // pages/special.tsx
287
- import { CustomLayout } from '@/layouts';
288
-
289
- function SpecialPage() {
290
- return <div>Special content</div>;
291
- }
292
-
293
- SpecialPage.getLayout = (page) => {
294
- return <CustomLayout>{page}</CustomLayout>;
295
- };
296
-
297
- export default SpecialPage;
298
- ```
299
-
300
- ## 🐛 Troubleshooting
301
-
302
- ### "useAppContext must be used within AppContextProvider"
303
-
304
- Убедись что компонент рендерится внутри `<AppLayout>`
305
-
306
- ### Layout не меняется
307
-
308
- Проверь `routes.detectors` в конфиге - они определяют тип layout'а
309
-
310
- ### Sidebar не работает
311
-
312
- Используй `useAppContext()` для доступа к `sidebarCollapsed` и `toggleSidebar`
@@ -1,112 +0,0 @@
1
- /**
2
- * ErrorBoundary - Automatic Error Handling
3
- *
4
- * Built-in React Error Boundary for AppLayout
5
- * Catches all runtime errors and displays ErrorLayout
6
- * No manual setup required - works automatically!
7
- */
8
-
9
- 'use client';
10
-
11
- import React, { Component, ReactNode } from 'react';
12
- import { ErrorLayout } from '../../ErrorLayout';
13
- import { Bug } from 'lucide-react';
14
- import logger from '../../../utils/logger';
15
- import { Analytics, AnalyticsEvent, AnalyticsCategory } from '../../../snippets/Analytics';
16
-
17
- interface ErrorBoundaryProps {
18
- children: ReactNode;
19
- /** Callback when error occurs */
20
- onError?: (error: Error, errorInfo: React.ErrorInfo) => void;
21
- /** Custom fallback UI */
22
- fallback?: (error: Error, reset: () => void) => ReactNode;
23
- /** Support email for error pages */
24
- supportEmail?: string;
25
- }
26
-
27
- interface ErrorBoundaryState {
28
- hasError: boolean;
29
- error?: Error;
30
- errorInfo?: React.ErrorInfo;
31
- }
32
-
33
- /**
34
- * ErrorBoundary Component
35
- *
36
- * Automatically wraps all AppLayout children
37
- * Catches React errors and shows ErrorLayout
38
- */
39
- export class ErrorBoundary extends Component<ErrorBoundaryProps, ErrorBoundaryState> {
40
- declare state: ErrorBoundaryState;
41
- declare props: Readonly<ErrorBoundaryProps>;
42
-
43
- constructor(props: ErrorBoundaryProps) {
44
- super(props);
45
- this.state = { hasError: false };
46
- }
47
-
48
- static getDerivedStateFromError(error: Error): ErrorBoundaryState {
49
- return { hasError: true, error };
50
- }
51
-
52
- componentDidCatch(error: Error, errorInfo: React.ErrorInfo) {
53
- // Log error
54
- logger.error('ErrorBoundary caught error:', error);
55
- logger.error('Error info:', errorInfo);
56
-
57
- // Track error in analytics
58
- Analytics.event(AnalyticsEvent.ERROR_BOUNDARY, {
59
- category: AnalyticsCategory.ERROR,
60
- label: error.message,
61
- error_name: error.name,
62
- component_stack: errorInfo.componentStack?.slice(0, 500), // Limit size
63
- });
64
-
65
- // Call optional callback
66
- this.props.onError?.(error, errorInfo);
67
-
68
- // Store error info in state using base class method
69
- Component.prototype.setState.call(this, { errorInfo });
70
- }
71
-
72
- resetError = () => {
73
- Component.prototype.setState.call(this, { hasError: false, error: undefined, errorInfo: undefined });
74
- };
75
-
76
- render() {
77
- if (this.state.hasError && this.state.error) {
78
- // Use custom fallback if provided
79
- if (this.props.fallback) {
80
- return this.props.fallback(this.state.error, this.resetError);
81
- }
82
-
83
- // Default error UI using ErrorLayout
84
- return (
85
- <ErrorLayout
86
- title="Application Error"
87
- description="Something went wrong while rendering the page. Try refreshing or going back."
88
- illustration={<Bug className="w-24 h-24 text-destructive/50" strokeWidth={1.5} />}
89
- supportEmail={this.props.supportEmail}
90
- actions={
91
- <div className="flex gap-4">
92
- <button
93
- onClick={() => window.location.reload()}
94
- className="px-6 py-3 bg-primary text-primary-foreground rounded-lg hover:bg-primary/90 transition-colors font-medium"
95
- >
96
- Refresh Page
97
- </button>
98
- <button
99
- onClick={() => window.history.back()}
100
- className="px-6 py-3 bg-secondary text-secondary-foreground rounded-lg hover:bg-secondary/90 transition-colors font-medium"
101
- >
102
- Go Back
103
- </button>
104
- </div>
105
- }
106
- />
107
- );
108
- }
109
-
110
- return this.props.children;
111
- }
112
- }