Перейти к основному содержимому

Фронтенд (Next.js)

Стек

ТехнологияВерсияНазначение
Next.js16.1.6Фреймворк (App Router, SSR/ISR/CSR)
React19.2.0UI-библиотека
TypeScript5.2.2Типизация
Tailwind CSS3.3.3Стилизация
Redux ToolkitГлобальное состояние
AxiosHTTP-клиент для бэкенда
Next-Auth4.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 товаров)