Files
sfera-new/docs/development/NEXTJS_BEST_PRACTICES.md
Veronika Smirnova 3f0cc933fc 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>
2025-08-30 22:37:15 +03:00

382 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# ⚛️ 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_