feat: завершить полную миграцию V1→V2 с модульной архитектурой и документацией

АРХИТЕКТУРНЫЕ ИЗМЕНЕНИЯ:
- Полная миграция на URL структуру /{role}/{domain}/{section}/{view}
- Удаление всех старых директорий (/fulfillment-supplies/, /fulfillment-warehouse/, etc.)
- Модульная архитектура seller warehouse с URL-based routing
- Система rollback через комментарии для безопасных изменений

НОВЫЕ КОМПОНЕНТЫ И СТРАНИЦЫ:
- Создание всех недостающих страниц для FULFILLMENT, SELLER ролей
- Модульный layout для seller warehouse с 3 табами
- Извлечение переиспользуемого хука useWBWarehouseData

ИСПРАВЛЕНИЯ БЕЗОПАСНОСТИ:
- Добавление 'use client' директив во все WHOLESALE и LOGISTICS страницы
- Исправление отсутствующих security guards (useRoleGuard + AuthGuard)
- Обновление navigation конфигураций для всех ролей

ДОКУМЕНТАЦИЯ:
- Создание MIGRATION_GUIDE_V1_TO_V2.md: 8-этапное руководство по миграции
- Создание NEXTJS_BEST_PRACTICES.md: паттерны для Next.js 13+ в SFERA
- Обновление URL_ROUTING_RULES.md с seller warehouse и rollback системой
- Обновление SIDEBAR_ARCHITECTURE_IMPLEMENTATION.md с новыми метриками
- Обновление INDEX.md с новыми документами Development раздела

ИСПРАВЛЕНИЯ ESLINT:
- Удаление неиспользуемых импортов и переменных
- Исправление import/order ошибок в модульных компонентах
- Исправление react/no-unescaped-entities
- Перенос длинных строк для соответствия max-len

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-30 22:37:15 +03:00
parent b40ac083ab
commit 3f0cc933fc
76 changed files with 2060 additions and 609 deletions

View File

@ -0,0 +1,381 @@
# ⚛️ NEXT.JS 13+ BEST PRACTICES ДЛЯ SFERA
> **Статус**: ✅ **ДЕЙСТВУЮЩИЙ**
> **Применимо к**: Next.js 15 + TypeScript
> **Дата создания**: 30.08.2025
---
## 🎯 КЛЮЧЕВЫЕ КОНЦЕПЦИИ
### 1. 'use client' ДИРЕКТИВА
#### ОБЯЗАТЕЛЬНО ИСПОЛЬЗОВАТЬ КОГДА:
- Используются React hooks (`useState`, `useEffect`, `useRouter`)
- Используются custom hooks (`useRoleGuard`, `useAuth`)
- Используются event handlers (`onClick`, `onSubmit`)
- Используются browser APIs (`localStorage`, `window`)
#### ПАТТЕРН ДЛЯ SFERA СТРАНИЦ:
```typescript
'use client'
import { AuthGuard } from '@/components/auth-guard'
import { ComponentDashboard } from '@/components/path/component-dashboard'
import { useRoleGuard } from '@/hooks/useRoleGuard'
export default function RolePage() {
useRoleGuard('ROLE_NAME')
return (
<AuthGuard>
<ComponentDashboard />
</AuthGuard>
)
}
```
### 2. APP ROUTER СТРУКТУРА
#### ФАЙЛОВАЯ СИСТЕМА:
```
src/app/
├── {role}/ # Динамический сегмент роли
│ ├── page.tsx # Главная роли (редирект)
│ ├── layout.tsx # Layout для роли (опционально)
│ └── {domain}/
│ ├── page.tsx # Главная домена
│ ├── layout.tsx # Layout с табами
│ └── {section}/
│ ├── page.tsx # Основная страница секции
│ └── {view}/
│ └── page.tsx # Конкретное представление
```
#### ПРАВИЛА ИМЕНОВАНИЯ:
- **Роли**: `seller`, `fulfillment`, `wholesale`, `logistics`
- **Домены**: `supplies`, `warehouse`, `orders`, `statistics`
- **Секции**: `goods`, `consumables`, `marketplace`
- **Представления**: `cards`, `suppliers`, `new`, `receiving`
---
## 🛡️ SECURITY PATTERNS
### ЗАЩИТА СТРАНИЦ:
#### Базовый паттерн:
```typescript
'use client'
import { AuthGuard } from '@/components/auth-guard'
import { useRoleGuard } from '@/hooks/useRoleGuard'
export default function SecurePage() {
useRoleGuard('REQUIRED_ROLE') // Проверка роли
return (
<AuthGuard> {/* Проверка авторизации */}
<PageContent />
</AuthGuard>
)
}
```
#### Доступные роли:
- `'SELLER'` - селлеры
- `'FULFILLMENT'` - фулфилмент
- `'WHOLESALE'` - поставщики
- `'LOGIST'` - логистика
### MIDDLEWARE ЗАЩИТА:
```typescript
// middleware.ts - автоматическая проверка URL соответствия роли
export function middleware(request: NextRequest) {
const url = request.nextUrl.pathname
const userRole = getUserRole(request) // из JWT токена
// Проверяем соответствие роли и URL
if (url.startsWith('/seller/') && userRole !== 'SELLER') {
return NextResponse.redirect('/unauthorized')
}
// аналогично для других ролей
}
```
---
## 🎨 LAYOUT PATTERNS
### LAYOUT С ТАБАМИ:
#### Структура файлов:
```
warehouse/
├── layout.tsx # Tabs UI + активный таб по URL
├── fulfillment/page.tsx # /warehouse/fulfillment
├── wildberries/page.tsx # /warehouse/wildberries
└── storage/page.tsx # /warehouse/storage
```
#### Реализация layout.tsx:
```typescript
'use client'
import { usePathname } from 'next/navigation'
import Link from 'next/link'
export default function WarehouseLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname()
// Автоматическое определение активного таба по URL
const getActiveTab = () => {
if (pathname.includes('/fulfillment')) return 'fulfillment'
if (pathname.includes('/wildberries')) return 'wildberries'
if (pathname.includes('/storage')) return 'storage'
return 'fulfillment' // default
}
const activeTab = getActiveTab()
return (
<div className="space-y-6">
{/* Tabs Header */}
<div className="flex space-x-1 bg-gray-900 p-1 rounded-lg">
<Link
href="/seller/warehouse/fulfillment"
className={`flex-1 py-2 px-4 text-center rounded-md transition-all whitespace-nowrap ${
activeTab === 'fulfillment'
? 'bg-blue-600 text-white shadow-lg'
: 'text-white/60 hover:bg-white/10'
}`}
>
Склад фулфилмент
</Link>
{/* другие табы */}
</div>
{/* Tab Content */}
{children}
</div>
)
}
```
### ПЕРЕИСПОЛЬЗУЕМЫЕ ХУКИ:
#### Паттерн извлечения данных:
```typescript
// hooks/useWBWarehouseData.ts
export function useWBWarehouseData() {
const { data, loading, error } = useQuery(GET_WB_WAREHOUSE_DATA, {
fetchPolicy: 'cache-first',
errorPolicy: 'ignore',
})
return {
data: data?.getWBWarehouseData || [],
loading,
error,
refetch: () => {
/* логика обновления */
},
}
}
// Использование в компонентах:
const { data, loading } = useWBWarehouseData()
```
---
## 🔄 ROUTING PATTERNS
### ПРОГРАММНАЯ НАВИГАЦИЯ:
```typescript
import { useRouter } from 'next/navigation'
function NavigationComponent() {
const router = useRouter()
// ✅ Правильно: используем новые пути
const handleNavigate = () => {
router.push('/seller/warehouse/fulfillment')
}
// ❌ Неправильно: старые пути
// router.push('/wb-warehouse')
}
```
### ОПРЕДЕЛЕНИЕ АКТИВНЫХ СОСТОЯНИЙ:
```typescript
import { usePathname } from 'next/navigation'
function ActiveStateComponent() {
const pathname = usePathname()
// ✅ Правильно: проверяем новые пути
const isActive = pathname.startsWith('/seller/warehouse')
// ✅ Конкретная секция:
const isFulfillmentActive = pathname.includes('/warehouse/fulfillment')
}
```
---
## 📦 COMPONENT PATTERNS
### МОДУЛЬНАЯ АРХИТЕКТУРА:
#### Структура папки компонента:
```
ComponentName/
├── index.tsx # Основной компонент
├── ComponentName.types.ts # TypeScript типы
├── ComponentName.hooks.ts # Custom hooks
├── ComponentName.utils.ts # Утилиты
└── components/ # Подкомпоненты
├── Header.tsx
├── Content.tsx
└── Footer.tsx
```
#### Основной компонент:
```typescript
'use client'
import { ComponentNameProvider } from './ComponentName.context'
import { useComponentName } from './ComponentName.hooks'
import { Header } from './components/Header'
import { Content } from './components/Content'
export function ComponentName() {
return (
<ComponentNameProvider>
<div className="space-y-6">
<Header />
<Content />
</div>
</ComponentNameProvider>
)
}
```
---
## 🚨 ОШИБКИ И РЕШЕНИЯ
### ПРОБЛЕМА 1: "useRoleGuard from server"
**Ошибка:**
```
Error: Attempted to call useRoleGuard() from the server but useRoleGuard is on the client
```
**Причина**: Отсутствует 'use client' в page.tsx
**Решение:**
```typescript
'use client' // Добавить в начало файла
import { useRoleGuard } from '@/hooks/useRoleGuard'
```
### ПРОБЛЕМА 2: Redirect loops
**Ошибка**: Бесконечные редиректы между путями
**Причина**: Одновременно существуют старые и новые пути
**Решение**: Удалить старые директории:
```bash
rm -rf src/app/fulfillment-supplies/
rm -rf src/app/fulfillment-warehouse/
```
### ПРОБЛЕМА 3: Потеря активных состояний
**Ошибка**: Табы не показывают активное состояние
**Причина**: Проверка pathname на старые пути
**Решение**: Обновить логику проверки:
```typescript
// ✅ До:
const isActive = pathname.startsWith('/fulfillment-supplies')
// ✅ После:
const isActive = pathname.startsWith('/fulfillment/supplies')
```
---
## 📋 CHECKLIST ДЛЯ НОВЫХ КОМПОНЕНТОВ
### ПЕРЕД СОЗДАНИЕМ:
- [ ] Прочитал MODULAR_ARCHITECTURE_PATTERN.md
- [ ] Определил нужность модульной архитектуры
- [ ] Выбрал правильный URL путь по формуле `/{role}/{domain}/{section}/{view}`
- [ ] Добавил 'use client' если используются hooks
### ПРИ СОЗДАНИИ:
- [ ] Добавил useRoleGuard с правильной ролью
- [ ] Обернул в AuthGuard для проверки авторизации
- [ ] Использовал существующие компоненты и хуки
- [ ] Следовал naming conventions
### ПОСЛЕ СОЗДАНИЯ:
- [ ] Проверил `npm run typecheck`
- [ ] Проверил `npm run lint`
- [ ] Протестировал в браузере
- [ ] Убедился что navigation работает
---
## 🎯 ЗАКЛЮЧЕНИЕ
**NEXT.JS 13+ В SFERA: СТАБИЛЬНАЯ PRODUCTION-READY СИСТЕМА**
🎯 **ПРИНЦИПЫ:**
- 'use client' для всех interactive компонентов
- URL-based routing вместо внутренних состояний
- Модульная архитектура для сложных компонентов
- Систематическая структура путей по ролям
🚀 **РЕЗУЛЬТАТ:**
- 100% совместимость с Next.js 15
- Отсутствие server/client конфликтов
- SEO-оптимизированные URL
- Простая навигация и maintenance
**Данные практики обеспечивают стабильность и масштабируемость SFERA на Next.js 13+.**
---
_Создано: 30.08.2025_
_На основе реального опыта разработки SFERA_