Frontend DOP — Документація імплементації¶
Локація: c:\eswf\frontend\erp\
Домен: erp.eswf.dev
Дата початку: 2026-02-17
1. Планові етапи імплементації¶
| Етап | Назва | Статус |
|---|---|---|
| 1 | Архітектура & Setup | ✅ Завершено |
| 2 | Auth (Login / JWT refresh) | ✅ Завершено |
| 3 | MasterData — список + форма | ✅ Завершено |
| 4 | TransactionData — список + форма | ✅ Завершено |
| 5 | Journals & Ledgers — перегляд | ✅ Завершено |
| 6 | Reports — placeholder + charts | ✅ Завершено |
| 7 | Applications — App Store сторінка | ✅ Завершено |
| 8 | Tabbed navigation (відкриті вкладки) | ✅ Завершено |
| 9 | i18n — повний переклад всіх рядків | ✅ Завершено |
| 10 | Полірування & Production build | ⏳ Заплановано |
2. Етап 1 — Архітектура & Setup (завершено)¶
Що реалізовано і навіщо¶
2.1 Scaffold Vite + React 19 + TypeScript¶
Навіщо: Vite забезпечує миттєвий HMR і швидкий production-build. React 19 — актуальна версія, сумісна з Mantine v7 та React Router v7. TypeScript обов'язковий для config-driven архітектури (типізовані секції/групи/items).
Видалено boilerplate: App.css, index.css, assets/react.svg.
2.2 Встановлення залежностей¶
Runtime:
@mantine/core @mantine/hooks @mantine/notifications @mantine/charts
@tabler/icons-react
zustand
@tanstack/react-query
react-router-dom
axios
i18next react-i18next
recharts
Dev:
Навіщо кожна:
- @mantine/* — UI-компоненти (AppShell, NavLink, Card, etc.), повністю підходить для enterprise DOP-інтерфейсу
- @tabler/icons-react — використовується у всіх config-файлах (icon: "CarOutlined" → IconCar)
- zustand — легкий state manager для auth, UI-налаштувань, відкритих вкладок (не потребує Provider-обгортки)
- @tanstack/react-query — кешування запитів до API, фонові refetch-и, error/loading стани
- react-router-dom — routing між секціями/групами/items DOP
- axios — HTTP-клієнт з interceptors для JWT
- i18next — інтернаціоналізація EN/UA з persistence в localStorage
- recharts — графіки для Mantine Charts (бекенд пакету)
- postcss-preset-mantine — обов'язковий для CSS-змінних Mantine (breakpoints, кольори)
2.3 Конфігурація Vite (vite.config.ts)¶
server: { port: 5173 } // відокремлено від News (5174)
resolve.alias: { '@': './src' } // абсолютні імпорти @/components/...
css.postcss: './postcss.config.cjs' // Mantine CSS-змінні
proxy: { '/api': 'http://localhost:8000' } // проксі на Django backend
Навіщо: Єдиний порт для всього DOP (5173). Проксі дозволяє не налаштовувати CORS у dev-режимі — всі запити /api/* прозоро йдуть до Django.
tsconfig.app.json доповнено:
- baseUrl: "." + paths: { "@/*": ["src/*"] } — підтримка аліасу @/ у TypeScript
- Видалено erasableSyntaxOnly: true — забороняло enum, який використовується в config-файлах
2.4 PostCSS (postcss.config.cjs)¶
plugins: {
'postcss-preset-mantine': {},
'postcss-simple-vars': { variables: { 'mantine-breakpoint-xs': '36em', ... } }
}
Навіщо: Mantine v7 вимагає PostCSS-плагіни для обробки CSS-змінних breakpoints. Без цього стилі AppShell (collapse sidebar) не працюватимуть коректно.
2.5 Config: src/config/itemTypes.ts¶
export enum ItemType {
MASTERDATA = 'MASTERDATA',
TRANSACTIONDATA = 'TRANSACTIONDATA',
JOURNAL = 'JOURNAL',
LEDGER = 'LEDGER',
REPORT = 'REPORT',
PROCESS = 'PROCESS',
APPLICATION = 'APPLICATION',
}
Навіщо: Центральний enum для класифікації всіх елементів DOP-навігації. Визначає який React-компонент рендерити для кожного item (MasterDataList, TransactionList, JournalViewer, etc.).
2.6 Config: src/config/types.ts¶
TypeScript-інтерфейси для всієї ієрархії конфігурації:
Основні типи:
- Section — модуль DOP (Essentials, Fleet, Logistic, Registers, Applications)
- Group — група документів всередині модуля (Master Data, Transactions, Journals, Reports)
- Subgroup — підгрупа (Goods Accounting, Cash Accounting, etc.)
- SectionItem — конкретний елемент (Items, Vehicles, Waybills, etc.) з полями componentPath, componentProps
- Subtable — вбудована таблиця в формі (OdometerReadings, RouteWaypoints, etc.)
- Props-інтерфейси: MasterDataListProps, TransactionListProps, JournalViewerProps, LedgerViewerProps, ReportViewProps
Навіщо: Без типізації неможливо безпечно ітерувати конфіг і рендерити динамічні компоненти. Типи забезпечують autocomplete при розробці нових секцій.
2.7 Config-файли (src/config/essentials.ts і т.д.)¶
Скопійовано з docs/ у src/config/:
- essentials.ts — 22+ items (Items, Units, Warehouses, Invoices, etc.)
- fleet.ts — 13+ items (Vehicles, Drivers, Routes, Waybills, etc.)
- logistic.ts — Logistic Orders, Route Sheets, Plan vs Fact
- registers.ts — Cash/Inventory Ledgers & Journals
- applications.ts — App Store з 16 додатками
Файли вже були розроблені з коментарем // frontend/src/config/... зверху — вони одразу призначались для цієї локації.
src/config/index.ts — barrel export:
export const sections: Section[] = [
essentialsSection, fleetSection, logisticSection, registersSection, applicationsSection
];
Навіщо: Єдина точка імпорту всіх секцій. Sidebar і Dashboard ітерують саме цей масив.
2.8 Zustand Stores (src/store/)¶
authStore.ts
persist middleware → зберігається в localStorage (eswf-auth)
- login() — зберігає user + tokens, ставить isAuthenticated: true
- logout() — очищає все
uiStore.ts
persist middleware → зберігає тему і мову між сесіями
- colorScheme → передається в MantineProvider
tabsStore.ts
openTab() → якщо tab вже відкрита — просто активує її (не дублює)
Навіщо Zustand замість Redux/Context: Мінімум boilerplate, підтримка persist з коробки, доступ до стану поза React (useAuthStore.getState() в axios interceptor).
2.9 API Client (src/api/client.ts)¶
const apiClient = axios.create({ baseURL: '/api/v1' });
// Request: додає Authorization: Bearer <token>
apiClient.interceptors.request.use(config => {
const token = useAuthStore.getState().accessToken;
if (token) config.headers.Authorization = `Bearer ${token}`;
return config;
});
// Response: на 401 → logout + redirect /login
apiClient.interceptors.response.use(ok => ok, err => {
if (err.response?.status === 401) {
useAuthStore.getState().logout();
window.location.href = '/login';
}
return Promise.reject(err);
});
Навіщо: Централізована обробка JWT. Всі компоненти імпортують apiClient замість голого axios — не потрібно в кожному місці додавати header вручну. Автоматичний logout на expired token.
2.10 i18n (src/i18n/)¶
locales/en.json — англійські рядки
locales/ua.json — українські рядки
index.ts — ініціалізація i18next з читанням мови з localStorage
Ключі: auth.*, common.*, ui.*, nav.*
Навіщо: Інтерфейс двомовний (EN/UA) згідно з вимогами проєкту. Мова зберігається в uiStore (persist) і синхронізується з i18next при перемиканні в Header.
Примітка: Назви секцій/items беруться напряму з config (name / name_ua) — не через i18n. i18n покриває лише системні рядки UI (кнопки, лейбли, повідомлення).
2.11 Layout Components (src/components/Layout/)¶
AppLayout.tsx
- Обгортає весь DOP в Mantine AppShell
- Header: висота 56px
- Navbar: ширина 260px, collapsible на mobile і desktop
- <Outlet /> — React Router рендерить поточну сторінку
Header.tsx
- Burger (toggle sidebar)
- Логотип "ESWF DOP"
- Кнопка EN/UA (перемикає i18n + uiStore)
- Кнопка dark/light theme (перемикає MantineProvider + uiStore)
- User menu (ім'я, Logout)
Sidebar.tsx
- Ітерує sections з config
- Показує лише showInSidebar: true секції та групи
- Активний стан визначається за location.pathname
- 3-рівнева навігація: Section → Group → Item
SectionDashboardPage.tsx
- Огляд секції: заголовок + опис + картки всіх items
- Кожна картка: badge з типом (MASTERDATA, REPORT, etc.), назва, опис
- Адаптивна сітка: 1/2/3 колонки
Навіщо окремі файли: Мantine AppShell вимагає явного розділення Header/Navbar/Main. Компоненти будуть рости (Header отримає Search, Notifications; Sidebar — drag-and-drop для вкладок).
2.12 Pages (src/pages/)¶
LoginPage.tsx — форма входу (username/password), POST /api/v1/token/, зберігає токени через authStore.login()
DashboardPage.tsx — головна сторінка: картки всіх активних секцій з кількістю items
SectionPage.tsx — знаходить секцію за sectionCode з URL параметра → рендерить SectionDashboardPage
NotFoundPage.tsx — 404 з кнопкою повернення
2.13 Routing (App.tsx)¶
/login → LoginPage
/ → redirect → /essentials
/:sectionCode → SectionPage (essentials, fleet, logistic...)
/:sectionCode/:groupCode → SectionPage (той самий, але для навігації)
/:sectionCode/:groupCode/:itemCode → SectionPage (placeholder до реального CRUD)
/dashboard → DashboardPage
* → NotFoundPage
Всі DOP-маршрути захищені ProtectedRoute (redirect → /login якщо не авторизовано).
MantineProvider + QueryClientProvider + BrowserRouter — всі на верхньому рівні App.tsx.
2.14 launcher.js — додано DOP¶
{
name: "DOP",
cwd: path.join(ROOT, "frontend", "erp"),
command: "npm.cmd",
args: ["run", "dev"],
url: "http://localhost:5173",
color: "\x1b[34m", // blue
}
Навіщо: DOP тепер стартує разом з усіма сервісами командою node launcher.js з кореня проєкту.
3. Дальніші плановані кроки¶
Етап 2 — Auth (повна авторизація)¶
Що потрібно зробити:
- [ ] POST
/api/v1/token/— login з username/password (вже частково є в LoginPage) - [ ] GET
/api/v1/auth/me/— завантаженняCurrentUserпісля логіну - [ ] POST
/api/v1/token/refresh/— автоматичний refresh access-токена (axios interceptor на 401) - [ ] POST
/api/v1/auth/logout/— blacklist refresh-токена на бекенді - [ ]
useCurrentUserhook (React Query) — завантаження профілю - [ ] Сторінка профілю (Settings)
Файли для зміни: src/api/client.ts, src/store/authStore.ts, src/pages/LoginPage.tsx
Етап 3 — MasterData (список + форма) ✅ Завершено¶
Реалізовано:
- [x] src/api/entities.ts — entitiesApi (list/detail/fields/create/update/remove)
- [x] src/hooks/useEntityList.ts — React Query, keepPreviousData
- [x] src/hooks/useEntityDetail.ts — для форми редагування
- [x] src/hooks/useEntityFields.ts — OPTIONS → field definitions (тип, label, required, choices)
- [x] src/components/MasterData/MasterDataList.tsx — таблиця + пошук + пагінація + delete confirm
- [x] src/components/MasterData/MasterDataForm.tsx — @mantine/form, dynamic fields від OPTIONS
- [x] src/components/MasterData/MasterDataPage.tsx — router: list vs form за :recordId
- [x] src/pages/SectionPage.tsx — знаходить item у конфізі, рендерить MasterDataPage для MASTERDATA
- [x] src/App.tsx — маршрут /:sectionCode/:groupCode/:itemCode/:recordId
- [x] i18n — додано ключі masterdata.* та common.refresh
- [x] @mantine/form встановлено
Маршрутизація:
/:sectionCode/:groupCode/:itemCode → MasterDataList (список)
/:sectionCode/:groupCode/:itemCode/new → MasterDataForm (новий запис)
/:sectionCode/:groupCode/:itemCode/:id → MasterDataForm (редагування)
Динамічна форма:
- Поля отримуються через OPTIONS /api/v1/{module}/{entityCode}/ → actions.POST
- Рендеринг: TextInput / Textarea / NumberInput / Switch / Select по типу поля
- Булеві поля відображаються окремим рядком знизу
Що не реалізовано (для наступних етапів):
- Підтримка hierarchy: true — деревоподібний список (TableTree)
- Subtables у формі — вкладені таблиці
- Інтеграція з tabsStore (Етап 8)
Етап 4 — TransactionData (список + форма) ✅ Завершено¶
Компоненти:
src/components/TransactionData/
├── TransactionList.tsx ← таблиця з фільтром по статусу
├── TransactionForm.tsx ← форма документа (з header + lines)
└── TransactionPage.tsx
Реалізовано:
- [x] src/api/entities.ts — action() метод + гнучкий ListParams (підтримка status та інших фільтрів)
- [x] TransactionList.tsx — таблиця з пошуком, фільтром по статусу (Select), пагінацією, підтвердженням видалення
- [x] TransactionForm.tsx — динамічна форма (header fields від OPTIONS), статус-badge + workflow кнопки
- [x] Status workflow: draft → approved → posted → cancelled (через PATCH)
- [x] Кнопки дій: "Узгодити" (approved), "Провести" (posted), "Скасувати" (cancelled)
- [x] "Копіювати" — клонує документ зі статусом draft
- [x] "Друк" — placeholder (показує notification)
- [x] Subtable lines — для invoices → invoicelines (перегляд, додавання, видалення рядків)
- [x] TransactionPage.tsx — роутер list vs form за :recordId
- [x] SectionPage.tsx — TRANSACTIONDATA кейс
- [x] i18n — ключі transaction.* в en.json та ua.json
Маршрутизація:
/:sectionCode/:groupCode/:itemCode → TransactionList (список)
/:sectionCode/:groupCode/:itemCode/new → TransactionForm (новий)
/:sectionCode/:groupCode/:itemCode/:id → TransactionForm (редагування)
Subtable логіка:
- LINES_CONFIG map: invoices → { linesCode: 'invoicelines', parentField: 'invoice' }
- Лінії завантажуються з record.lines (nested у відповіді API)
- Додавання рядка: POST /essentials/invoicelines/ з {invoice: id, ...fields}
- Видалення рядка: DELETE /essentials/invoicelines/{lineId}/
- Поля для форми рядка: від OPTIONS /essentials/invoicelines/
Що не реалізовано (для наступних етапів):
- Реальні друковані форми (PDF)
- Inline редагування рядків (зараз тільки add/delete)
- Subtable для Fleet (Waybill lines, Route waypoints)
- Інтеграція з tabsStore (Етап 8)
Пріоритетні: Invoices ✅, IncomingPayments ✅, DocumentOperations ✅
Етап 5 — Journals & Ledgers ✅ Завершено¶
Компоненти:
src/components/Accounting/
├── JournalViewer.tsx ← хронологічний журнал (тільки читання)
└── LedgerViewer.tsx ← регістр з фільтрами по датах/рахунках
Реалізовано:
- [x] JournalViewer.tsx — читання журналу (CashJournal, InventoryJournal)
- Пошук по тексту
- Фільтрація по датах (date__gte / date__lte) через два TextInput type="date"
- Badge для operation_type (income=green / expense=red)
- Динамічні колонки з пріоритетом (date, number, operation_type, cashbox, client, amount...)
- Пагінація (30 записів на сторінку)
- Експорт CSV (BOM UTF-8 для Excel) — поточна сторінка
- Read-only (без Edit/Delete)
- [x] LedgerViewer.tsx — перегляд леджера (CashLedger, InventoryLedger)
- Аналогічна фільтрація по датах та пошук
- Числові колонки (debit, credit, balance, quantity, amount) — right-align + monospace
- Totals summary cards — сума по кожній числовій колонці на поточній сторінці
- Динамічні колонки з пріоритетом (date, cashbox, item, warehouse, debit, credit, balance...)
- Пагінація та Експорт CSV
- [x] src/api/entities.ts — доданий listByPath(apiPath, params) для нестандартних URL
- [x] src/config/types.ts — оновлено JournalViewerProps і LedgerViewerProps (+ поле apiPath)
- [x] src/config/registers.ts — додано componentProps до всіх 4 items:
- cashJournal → /registers/journals/cash/
- inventoryJournal → /registers/journals/inventory/
- cashLedger → /registers/ledgers/cash/
- inventoryLedger → /registers/ledgers/inventory/
- [x] src/pages/SectionPage.tsx — додано кейси ItemType.JOURNAL та ItemType.LEDGER
- [x] src/i18n/locales/en.json та ua.json — додано ключі journal.* та ledger.*
- [x] backend/registers/views.py — оновлено filterset_fields до dict-формату з підтримкою date__gte / date__lte (django-filter)
Маршрутизація (успадкована від Етап 1-4):
/registers/ledgers/cashLedger → LedgerViewer (Cash Ledger)
/registers/ledgers/inventoryLedger → LedgerViewer (Inventory Ledger)
/registers/journals/cashJournal → JournalViewer (Cash Journal)
/registers/journals/inventoryJournal → JournalViewer (Inventory Journal)
Backend API endpoints:
GET /api/v1/registers/ledgers/cash/?date__gte=2024-01-01&date__lte=2024-12-31
GET /api/v1/registers/ledgers/inventory/?date__gte=...
GET /api/v1/registers/journals/cash/?date__gte=...&operation_type=income
GET /api/v1/registers/journals/inventory/?date__gte=...
Що не реалізовано (для наступних етапів): - Фільтрація по конкретному cashbox/account/item (потребує Select з lazy-load) - Export всього набору (зараз тільки поточна сторінка) - Freeze header при скролі великих таблиць
Етап 6 — Reports ✅ Завершено¶
Компоненти:
Реалізовано:
- [x] ReportViewer.tsx — єдиний компонент для всіх звітів, перемикається по reportCode
- [x] Фільтр дат (dateFrom / dateTo) + кнопка "Сформувати"
- [x] 4 Summary cards (ключові метрики кожного звіту)
- [x] Mantine Charts інтеграція: BarChart, LineChart, AreaChart, DonutChart
- [x] Badge "DEMO" — позначає mock-дані
- [x] Mock-дані — детерміновані wave() функцією (sin/cos), реалістично виглядають
- [x] Повна підтримка EN/UA (i18n.language)
- [x] SectionPage.tsx — доданий кейс ItemType.REPORT
- [x] i18n — ключі report.* в en.json та ua.json
Звіти:
| reportCode | Модуль | Тип графіка | Опис |
|---|---|---|---|
| profitAndLoss | Essentials | BarChart | Виручка / Витрати / Прибуток по місяцях |
| cashFlow | Essentials | AreaChart | Надходження / Виплати / Чистий потік |
| balanceSheet | Essentials | DonutChart × 2 | Структура активів + Структура капіталу |
| fuelMovement | Fleet | BarChart | Отримано / Витрачено / Залишок по ТЗ |
| transportStatistics | Fleet | LineChart | Рейси / Пробіг / Завантаженість по місяцях |
| logisticsPlanFact | Logistic | BarChart | Замовлення план vs факт по місяцях |
Маршрутизація (успадкована):
/essentials/essentialsreports/profitAndLoss → ReportViewer (P&L)
/essentials/essentialsreports/cashFlow → ReportViewer (Cash Flow)
/essentials/essentialsreports/balanceSheet → ReportViewer (Balance Sheet)
/fleet/.../fuelMovement → ReportViewer (Fuel Movement)
/fleet/.../transportStatistics → ReportViewer (Transport Statistics)
/logistic/.../logisticsPlanFact → ReportViewer (Plan vs Fact)
Що не реалізовано (для наступних етапів): - Реальні API-ендпоінти для звітів на бекенді (зараз mock-дані) - Експорт звіту в PDF / Excel - Додаткові фільтри (по підрозділу, організації, ТЗ тощо)
Етап 7 — Applications (App Store) ✅ Завершено¶
Компоненти:
Реалізовано:
- [x] ApplicationsPage.tsx — повноцінна App Store сторінка
- [x] SegmentedControl фільтр: Всі / Встановлені (N) / Доступні (N)
- [x] Лічильник у підзаголовку: "X встановлено · Y доступно"
- [x] SimpleGrid карток (1/2/3 колонки адаптивно)
- [x] Кожна картка: ThemeIcon + назва, опис (lineClamp=2), badge статусу, badge ціни
- [x] Статус: installed (зелений) / available (синій)
- [x] Ціна: free (сірий) / paid (помаранчевий)
- [x] Кнопки дій: "Встановити" (install) / "Видалити" (uninstall outline red) / "Налаштування" (light)
- [x] Локальний стан встановлення (useState, ініціалізується з config status)
- [x] Модальне вікно: клік на назву → Modal з ThemeIcon + повний опис + badges + кнопки
- [x] notifications.show на Install / Uninstall / Settings
- [x] Іконки: маппінг string → Tabler icon (IconCar, IconPackage, IconCalculator, etc.)
- [x] Повна EN/UA локалізація (ключі app.*)
- [x] SectionPage.tsx — доданий кейс ItemType.APPLICATION → <ApplicationsPage />
- [x] i18n/locales/en.json та ua.json — додано секцію app.*
Маршрутизація:
Джерело даних: applicationsSection з src/config/applications.ts — 16 додатків (виключений сам "appstore" item).
Що не реалізовано (для наступних етапів): - Backend API для збереження встановлених додатків (зараз локальний стан) - Реальне завантаження/видалення модулів - Фільтр за категорією (Business, Integration, Mobile, etc.)
Етап 8 — Tabbed Navigation ✅ Завершено¶
Концепція: відкривати кожен item у своїй вкладці (як у 1С або SAP)
Реалізовано:
- [x] src/store/tabsStore.ts — оновлено: MAX_TABS=10 (закривати найстарішу), updateTabTitle()
- [x] src/components/Tabs/TabBar.tsx — горизонтальна панель вкладок з ScrollArea
- Кожна вкладка: назва (EN/UA) + × кнопка закриття
- Активна вкладка підсвічена синім кольором
- Горизонтальний скрол при переповненні
- Клік → navigate + setActiveTab
- Закриття → closeTab + автоматичний перехід на сусідню вкладку
- [x] src/components/Layout/AppLayout.tsx — динамічна висота Header (56px без вкладок / 92px з вкладками)
- [x] src/pages/SectionPage.tsx — useEffect auto-реєструє вкладку при відкритті будь-якого item
- Tab ID = location.pathname (унікальний ключ)
- Заголовок: Item Name / Item Name — New / Item Name #ID
- EN/UA назви зберігаються в Tab-об'єкті
- [x] i18n — ключ tabs.close в en.json та ua.json
Архітектурні рішення:
- Tab ID = URL pathname → кожна URL = одна вкладка, без дублікатів
- openTab() при існуючому ID → просто активує (не дублює)
- Вкладки скидаються при перезавантаженні (без persist) — очікувана поведінка
- Стан форми при перемиканні вкладок не зберігається (обмеження: форма перемонтується)
Що не реалізовано: - Drag-and-drop для перетягування вкладок - Збереження стану форми між вкладками (потребує окремого store per-tab)
Етап 9 — i18n (повний переклад) ✅ Завершено¶
Реалізовано:
- [x] Додано нові ключі у locales/en.json та ua.json:
- common.id, common.active, common.inactive, common.items
- tabs.suffixNew — суфікс вкладки для нових записів
- ui.sectionNotFound, ui.itemNotFound, ui.typeNotImplemented
- report.months — масив назв місяців (EN: Jan–Dec / UA: Січ–Гру)
- report.* — всі рядки звітів: назви, підписи карток, серії графіків (37 нових ключів)
- [x] MasterDataList.tsx — t('common.id'), t('common.active') / t('common.inactive') для is_active
- [x] TransactionList.tsx — t('common.id') замість hardcoded "ID"
- [x] JournalViewer.tsx — t('common.id') замість hardcoded "ID"
- [x] LedgerViewer.tsx — t('common.id') замість hardcoded "ID"
- [x] DashboardPage.tsx — t('ui.dashboard') та t('common.items')
- [x] SectionPage.tsx — t('ui.sectionNotFound'), t('ui.itemNotFound'), t('ui.typeNotImplemented') з параметрами; суфікси вкладок через i18n.getFixedT('en'/'ua')('tabs.suffixNew')
- [x] ReportViewer.tsx — повний рефакторинг: всі isUA ? 'UA' : 'EN' замінені на t('report.*'); buildReport отримує t: TFunction; місяці через t('report.months', { returnObjects: true }); t('report.demo') для DEMO-бейджа
Що залишено без змін (відповідно до архітектурного рішення):
- Назви секцій/груп/items — беруться з config (name / name_ua)
- Назви вкладок — зберігають обидва переклади з config
- Числовий формат uk-UA — стандарт для DOP в Україні
Етап 10 — Полірування & Production¶
- [ ] Code splitting (dynamic
import()для важких сторінок) - [ ] Error Boundary (глобальний + per-page)
- [ ] Loading skeleton замість спінерів
- [ ]
vite.config.ts—build.rollupOptions.output.manualChunksдля vendor splitting - [ ]
index.html— favicon ESWF,<title>ESWF DOP</title> - [ ] Environment variables:
VITE_API_URL,VITE_APP_ENV - [ ] Nginx config для
erp.eswf.dev(static files + proxy/api)
Структура файлів (поточна)¶
frontend/erp/
├── src/
│ ├── api/
│ │ └── client.ts
│ ├── auth/
│ │ └── ProtectedRoute.tsx
│ ├── api/
│ │ ├── client.ts
│ │ ├── auth.ts
│ │ └── entities.ts ← NEW (Етап 3)
│ ├── hooks/
│ │ ├── useCurrentUser.ts
│ │ ├── useEntityList.ts ← NEW (Етап 3)
│ │ ├── useEntityDetail.ts ← NEW (Етап 3)
│ │ └── useEntityFields.ts ← NEW (Етап 3)
│ ├── components/
│ │ ├── Layout/
│ │ │ ├── AppLayout.tsx
│ │ │ ├── Header.tsx
│ │ │ ├── Sidebar.tsx
│ │ │ └── SectionDashboardPage.tsx
│ │ ├── MasterData/ ← NEW (Етап 3)
│ │ │ ├── MasterDataList.tsx
│ │ │ ├── MasterDataForm.tsx
│ │ │ └── MasterDataPage.tsx
│ │ └── Reports/ ← NEW (Етап 6)
│ │ └── ReportViewer.tsx
│ ├── config/
│ │ ├── itemTypes.ts
│ │ ├── types.ts
│ │ ├── index.ts
│ │ ├── essentials.ts
│ │ ├── fleet.ts
│ │ ├── logistic.ts
│ │ ├── registers.ts
│ │ └── applications.ts
│ ├── i18n/
│ │ ├── index.ts
│ │ └── locales/
│ │ ├── en.json
│ │ └── ua.json
│ ├── pages/
│ │ ├── LoginPage.tsx
│ │ ├── DashboardPage.tsx
│ │ ├── SectionPage.tsx ← UPDATED (Етап 3)
│ │ ├── ProfilePage.tsx
│ │ └── NotFoundPage.tsx
│ ├── store/
│ │ ├── authStore.ts
│ │ ├── uiStore.ts
│ │ └── tabsStore.ts
│ ├── App.tsx
│ └── main.tsx
├── postcss.config.cjs
├── vite.config.ts
├── tsconfig.app.json
└── package.json
Технічний стек (підсумок)¶
| Категорія | Технологія | Версія |
|---|---|---|
| Build | Vite | 7.x |
| UI Framework | React | 19.x |
| Мова | TypeScript | ~5.9 |
| UI Components | Mantine | 7.x |
| State | Zustand | latest |
| Data Fetching | TanStack React Query | v5 |
| Routing | React Router | v7 |
| HTTP | Axios | latest |
| i18n | i18next + react-i18next | latest |
| Charts | Mantine Charts (recharts) | latest |
| Icons | Tabler Icons | latest |
| Forms (план) | React Hook Form + Zod | — |
| Dev port | — | 5173 |
| Prod domain | — | erp.eswf.dev |