@djangocfg/layouts 1.4.30 → 2.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.
- package/README.md +277 -18
- package/package.json +15 -24
- package/src/auth/context/AuthContext.tsx +5 -5
- package/src/auth/hooks/useAuthGuard.ts +1 -1
- package/src/auth/hooks/useAutoAuth.ts +8 -7
- package/src/components/ErrorBoundary.tsx +78 -0
- package/src/components/JsonLd.tsx +31 -0
- package/src/components/LucideIcon.tsx +91 -0
- package/src/components/PageProgress.tsx +127 -0
- package/src/components/Suspense.tsx +29 -0
- package/src/{layouts/AppLayout/components → components}/UpdateNotifier/UpdateNotifier.tsx +56 -49
- package/src/components/index.ts +10 -0
- package/src/index.ts +25 -7
- package/src/layouts/AdminLayout/AdminLayout.tsx +46 -0
- package/src/layouts/AdminLayout/index.ts +7 -0
- package/src/layouts/AppLayout/AppLayout.tsx +278 -326
- package/src/layouts/AppLayout/index.ts +2 -39
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthContext.tsx +3 -2
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/AuthHelp.tsx +1 -0
- package/src/layouts/AuthLayout/AuthLayout.tsx +61 -0
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/IdentifierForm.tsx +47 -34
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/OTPForm.tsx +2 -3
- package/src/layouts/AuthLayout/index.ts +24 -0
- package/src/layouts/{AppLayout/layouts/AuthLayout → AuthLayout}/types.ts +1 -0
- package/src/layouts/PrivateLayout/PrivateLayout.tsx +144 -0
- package/src/layouts/PrivateLayout/components/PrivateContent.tsx +32 -0
- package/src/layouts/PrivateLayout/components/PrivateHeader.tsx +57 -0
- package/src/layouts/PrivateLayout/components/PrivateSidebar.tsx +141 -0
- package/src/layouts/PrivateLayout/components/index.ts +8 -0
- package/src/layouts/PrivateLayout/index.ts +7 -0
- package/src/layouts/ProfileLayout/ProfileLayout.tsx +15 -7
- package/src/layouts/PublicLayout/PublicLayout.tsx +121 -0
- package/src/layouts/PublicLayout/components/PublicFooter.tsx +190 -0
- package/src/layouts/PublicLayout/components/PublicMobileDrawer.tsx +117 -0
- package/src/layouts/PublicLayout/components/PublicNavigation.tsx +101 -0
- package/src/layouts/PublicLayout/components/index.ts +8 -0
- package/src/layouts/PublicLayout/index.ts +7 -0
- package/src/layouts/_components/UserMenu.tsx +160 -0
- package/src/layouts/_components/index.ts +7 -0
- package/src/layouts/index.ts +15 -8
- package/src/snippets/Analytics/AnalyticsProvider.tsx +8 -4
- package/src/snippets/Analytics/useAnalytics.ts +11 -21
- package/src/snippets/Chat/ChatWidget.tsx +4 -4
- package/src/snippets/ContactForm/ContactFormProvider.tsx +32 -19
- package/src/snippets/ContactForm/ContactPage.tsx +2 -4
- package/src/snippets/ContactForm/types.ts +3 -2
- package/src/snippets/index.ts +0 -1
- package/src/layouts/AppLayout/README.md +0 -204
- package/src/layouts/AppLayout/SUMMARY.md +0 -240
- package/src/layouts/AppLayout/USAGE.md +0 -312
- package/src/layouts/AppLayout/components/ErrorBoundary.tsx +0 -112
- package/src/layouts/AppLayout/components/PageProgress.tsx +0 -123
- package/src/layouts/AppLayout/components/Seo.tsx +0 -171
- package/src/layouts/AppLayout/components/UserMenu.tsx +0 -385
- package/src/layouts/AppLayout/components/index.ts +0 -11
- package/src/layouts/AppLayout/context/AppContext.tsx +0 -151
- package/src/layouts/AppLayout/context/index.ts +0 -5
- package/src/layouts/AppLayout/hooks/index.ts +0 -8
- package/src/layouts/AppLayout/hooks/useLayoutMode.ts +0 -26
- package/src/layouts/AppLayout/hooks/useNavigation.ts +0 -51
- package/src/layouts/AppLayout/layouts/AdminLayout/AdminLayout.tsx +0 -224
- package/src/layouts/AppLayout/layouts/AdminLayout/README.md +0 -409
- package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.example.tsx +0 -98
- package/src/layouts/AppLayout/layouts/AdminLayout/components/PagePreloader.tsx +0 -149
- package/src/layouts/AppLayout/layouts/AdminLayout/components/ParentSync.tsx +0 -146
- package/src/layouts/AppLayout/layouts/AdminLayout/components/index.ts +0 -3
- package/src/layouts/AppLayout/layouts/AdminLayout/context/CfgAppContext.tsx +0 -48
- package/src/layouts/AppLayout/layouts/AdminLayout/context/index.ts +0 -2
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/index.ts +0 -6
- package/src/layouts/AppLayout/layouts/AdminLayout/hooks/useApp.ts +0 -279
- package/src/layouts/AppLayout/layouts/AdminLayout/index.ts +0 -24
- package/src/layouts/AppLayout/layouts/AdminLayout/lottie/energizing.json +0 -1
- package/src/layouts/AppLayout/layouts/AdminLayout/types/index.ts +0 -45
- package/src/layouts/AppLayout/layouts/AuthLayout/AuthLayout.tsx +0 -41
- package/src/layouts/AppLayout/layouts/AuthLayout/index.ts +0 -15
- package/src/layouts/AppLayout/layouts/PrivateLayout/PrivateLayout.tsx +0 -82
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardContent.tsx +0 -62
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardHeader.tsx +0 -89
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/DashboardSidebar.tsx +0 -181
- package/src/layouts/AppLayout/layouts/PrivateLayout/components/index.ts +0 -9
- package/src/layouts/AppLayout/layouts/PrivateLayout/index.ts +0 -5
- package/src/layouts/AppLayout/layouts/PublicLayout/PublicLayout.tsx +0 -44
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Footer.tsx +0 -242
- package/src/layouts/AppLayout/layouts/PublicLayout/components/MobileDrawer.tsx +0 -150
- package/src/layouts/AppLayout/layouts/PublicLayout/components/Navigation.tsx +0 -169
- package/src/layouts/AppLayout/layouts/PublicLayout/index.ts +0 -5
- package/src/layouts/AppLayout/layouts/index.ts +0 -7
- package/src/layouts/AppLayout/providers/CoreProviders.tsx +0 -80
- package/src/layouts/AppLayout/providers/index.ts +0 -5
- package/src/layouts/AppLayout/types/config.ts +0 -79
- package/src/layouts/AppLayout/types/index.ts +0 -11
- package/src/layouts/AppLayout/types/layout.ts +0 -54
- package/src/layouts/AppLayout/types/navigation.ts +0 -43
- package/src/layouts/AppLayout/types/page.ts +0 -80
- package/src/layouts/AppLayout/types/routes.ts +0 -43
- package/src/layouts/AppLayout/utils/index.ts +0 -5
- package/src/layouts/AppLayout/utils/routeDetection.ts +0 -31
- package/src/layouts/ErrorLayout/ErrorLayout.tsx +0 -173
- package/src/layouts/ErrorLayout/errorConfig.tsx +0 -152
- package/src/layouts/ErrorLayout/index.ts +0 -8
- package/src/layouts/SimpleLayout/SimpleLayout.tsx +0 -72
- package/src/layouts/SimpleLayout/index.ts +0 -3
- package/src/snippets/VideoPlayer/README.md +0 -238
- package/src/snippets/VideoPlayer/VideoControls.tsx +0 -137
- package/src/snippets/VideoPlayer/VideoPlayer.tsx +0 -248
- package/src/snippets/VideoPlayer/index.ts +0 -8
- package/src/snippets/VideoPlayer/types.ts +0 -61
- package/src/types/index.ts +0 -2
- package/src/types/pageConfig.ts +0 -100
- /package/src/{validation → components/ErrorsTracker}/README.md +0 -0
- /package/src/{validation → components/ErrorsTracker}/components/ErrorButtons.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/components/ErrorToast.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/hooks.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/index.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/providers/ErrorTrackingProvider.tsx +0 -0
- /package/src/{validation → components/ErrorsTracker}/types.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/utils/curl-generator.ts +0 -0
- /package/src/{validation → components/ErrorsTracker}/utils/formatters.ts +0 -0
- /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
|
-
}
|