Фронтенд (Next.js)
Стек
| Технология | Версия | Назначение |
|---|---|---|
| Next.js | 16.1.6 | Фреймворк (App Router, SSR/ISR/CSR) |
| React | 19.2.0 | UI-библиотека |
| TypeScript | 5.2.2 | Типизация |
| Tailwind CSS | 3.3.3 | Стилизация |
| Redux Toolkit | — | Глобальное состояние |
| Axios | — | HTTP-клиент для бэкенда |
| Next-Auth | 4.24.13 | Сессии (аутентификация) |
| Swiper | — | Слайдеры/карусели |
| Hot Toast | — | Уведомления |
Структура директорий
front/src/
├── app/
│ ├── (site)/ # Публичная витрина
│ │ ├── layout.tsx # Общий layout (header, footer, contexts)
│ │ ├── page.tsx # Главная страница
│ │ └── (pages)/
│ │ ├── catalog/[categoryId]/ # Каталог по категории
│ │ ├── products/[id]/ # Карточка товара
│ │ ├── cart/ # Корзина
│ │ ├── checkout/ # Оформление заказа
│ │ ├── my-account/ # Личный кабинет
│ │ ├── signin/ signup/ # Авторизация
│ │ └── contact/ # Форма обратной связи
│ ├── admin/ # Административная панель
│ │ ├── layout.tsx # Layout с боковым меню
│ │ ├── page.tsx # Дашборд
│ │ ├── products/ [id]/ # Управление товарами
│ │ ├── orders/ [id]/ # Управление заказами
│ │ ├── categories/ [id]/ # Управление категориями
│ │ ├── users/ [id]/ # Управление пользователями
│ │ ├── reviews/ # Управление отзывами
│ │ ├── banners/ # Управление баннерами
│ │ ├── features/ # Управление фичами
│ │ ├── contacts/ # Обращения клиентов
│ │ ├── settings/ # Настройки сайта (app_config)
│ │ └── media/ # Библиотека изображений
│ ├── context/ # React Context
│ │ ├── AppConfigContext.tsx
│ │ ├── BackendStatusContext.tsx
│ │ ├── CartSidebarModalContext.tsx
│ │ ├── PreviewSliderContext.tsx
│ │ └── QuickViewModalContext.tsx
│ └── api/
│ └── images/[id]/route.ts # Next.js Image Proxy
├── components/ # Переиспользуемые компоненты
│ ├── Admin/
│ │ └── ImageUpload.tsx # Загрузчик изображений
│ ├── Home/ # Блоки главной страницы
│ ├── Catalog/ # Компоненты каталога
│ ├── ProductDetail/ # Карточка товара
│ └── ...
├── lib/ # Утилиты
│ ├── api.ts # Axios-инстанс
│ ├── auth.ts # Login/register вызовы
│ ├── imageService.ts # URL изображений
│ ├── fetchConfig.ts # ISR-загрузка app_config
│ ├── stockImages.ts # Запасные аватары
│ └── recentlyViewed.ts # Недавно просмотренные
├── redux/ # Redux store
│ ├── store.ts
│ ├── cartSlice.ts
│ ├── wishlistSlice.ts
│ └── ...
└── types/ # TypeScript-типы
├── product.ts
├── category.ts
├── order.ts
├── review.ts
├── banner.ts
├── config.ts # PublicConfigMap
└── auth.ts # AuthResponse, AuthUser
API-клиент (src/lib/api.ts)
Единый Axios-инстанс для всех запросов к бэкенду:
const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_BASE_URL ?? 'http://localhost:8080',
timeout: 5000,
});
// Request interceptor: прикрепляет JWT токен
api.interceptors.request.use(config => {
const token = localStorage.getItem('dm_shop_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
Все операции в Admin-панели используют этот инстанс напрямую. В публичной части для server-side рендеринга используется fetch() с ISR.
Стратегии получения данных
| Страница | Стратегия | Причина |
|---|---|---|
| Главная (баннеры, фичи, отзывы) | ISR (revalidate: 60 с) | Данные меняются редко |
| Настройки сайта (app_config) | ISR (revalidate: 60 с) | Редкие изменения |
| Каталог категорий | ISR | Иерархия меняется редко |
| Карточка товара | SSR или ISR | Цены, остатки актуальны |
| Корзина, заказы | CSR (Axios) | Персональные данные |
| Вся Admin-панель | CSR (Axios) | Реального времени, CRUD |
Redux Store
Redux Toolkit управляет только клиентским состоянием:
| Slice | Назначение |
|---|---|
cartSlice | Содержимое корзины |
wishlistSlice | Список желаемого |
productDetailsSlice | Состояние модального окна товара |
Используйте useAppSelector из src/redux/store.ts (типизированная обёртка над useSelector).
Контексты (React Context)
| Контекст | Назначение |
|---|---|
AppConfigContext | Публичные настройки сайта из /api/v1/config/public |
BackendStatusContext | Статус соединения с бэкендом |
CartSidebarModalContext | Открыт/закрыт сайдбар корзины |
QuickViewModalContext | Модальное окно быстрого просмотра товара |
PreviewSliderContext | Слайдер изображений |
Изображения
Прокси Next.js
src/app/api/images/[id]/route.ts — проксирует GET /api/v1/images/{id} через Next.js. Это позволяет использовать next/image для оптимизации изображений без добавления внешнего домена в next.config.js.
Утилиты (src/lib/imageService.ts)
getImageUrl(id: number): string
// → `/api/images/${id}` — относительный URL через прокси
getFirstImageUrl(ids: number[]): string | null
// → первое изображение из массива или null
resolveApiUrl(url: string): string
// → конвертирует /api/... в абсолютный URL (для SSR)
Компонент ImageUpload (src/components/Admin/ImageUpload.tsx)
Готовый загрузчик для форм в Admin-панели:
<ImageUpload
previewUrl={resolvedUrl} // текущее изображение для предпросмотра
shape="circle" // "circle" | "square"
size={80} // px
hint="JPG, PNG · 400×400"
onUploaded={(id, url) => { // вызывается после успешной загрузки
setImageId(id);
setPreviewUrl(url);
}}
onClear={() => { // вызывается при удалении
setImageId(null);
setPreviewUrl(null);
}}
/>
Внутри: POST /api/v1/images (multipart/form-data), индикатор загрузки, отображение ошибок.
Аутентификация на фронтенде
// src/lib/auth.ts
login(email, password) → POST /api/v1/auth/login
register(name, email, pass) → POST /api/v1/auth/register
JWT-токен хранится в localStorage под ключом dm_shop_token. Данные пользователя — в localStorage под ключом dm_shop_user.
Конфигурация сайта (ISR)
// src/lib/fetchConfig.ts
fetchPublicConfig(): Promise<PublicConfigMap>
// → fetch('/api/v1/config/public', { next: { revalidate: 60 } })
// Используется в Server Components для получения настроек
Недавно просмотренные
// src/lib/recentlyViewed.ts
addToRecentlyViewed(productId: number): void
getRecentlyViewed(): number[]
// Хранится в localStorage (не более 10 товаров)