Web-портал (back-office) платформи Brigade — заміна AXLR8 для hospitality/event-staffing агентства. Портал відповідає за onboarding, scheduling, compliance, клієнтів та звітність (desktop-first). Детальний бриф — у docs/inbox/2026-07-08_brigade-brief.md.
Статус: проєкт ще не заскафолджено. Цей README фіксує узгоджену структуру папок/архітектуру до створення файлів, щоб розробка одразу йшла за єдиною конвенцією.
| Категорія | Вибір | Причина |
|---|---|---|
| Build tool | Vite | швидкий dev-server, нативний ESM, оптимізована prod-збірка |
| UI-бібліотека | React 18 + TypeScript | типобезпека критична для CRM-домену (RBAC, compliance, audit) |
| Лінтер | oxlint | Rust-based, на порядки швидший за ESLint; підключається як pre-commit / CI gate |
| Стилі | SCSS (CSS Modules) | ізоляція стилів по компонентах + дизайн-токени через SCSS-змінні |
| Роутинг | React Router | стандарт для SPA з великою кількістю розділів (staff/scheduling/compliance/clients) |
| Стан (сервер + клієнт) | Redux Toolkit + RTK Query | RTK Query кешує/рефетчить дані з REST API (staff, shifts, bookings) замість ручних fetch/useEffect; звичайні RTK-слайси — для суто клієнтського стану (auth-сесія, RBAC-права, UI-флаги) |
| Форми | React Hook Form + Zod | форм-білдери онбордингу, валідація на клієнті |
| Форматування | Prettier | узгоджено з oxlint (lint — правила коду, prettier — форматування) |
Остаточний вибір бібліотек стану/форм фіксується при скафолдингу — тут вони позначені як baseline-рекомендація.
staffmanager-frontend/
├── public/ # статичні файли (favicon, robots.txt)
├── docs/ # бриф, специфікації, копірайтинг (не код)
├── src/
│ ├── app/ # ініціалізація застосунку
│ │ ├── providers/ # Theme/Auth/QueryClient/Router провайдери
│ │ ├── router/ # опис маршрутів, guard-и (RBAC per route)
│ │ └── App.tsx
│ │
│ ├── pages/ # route-level контейнери (1 сторінка = 1 маршрут)
│ │ ├── dashboard/
│ │ ├── staff/
│ │ ├── scheduling/
│ │ ├── compliance/
│ │ ├── clients/
│ │ ├── reports/
│ │ └── auth/
│ │
│ ├── widgets/ # готові до вставки на сторінку композитні блоки
│ │ ├── AppSidebar/ # навігація + активна роль/RBAC-пункти меню
│ │ ├── StaffProfileCard/ # дані staff (entity) + compliance-бейдж (feature) + дії
│ │ └── ShiftCalendar/ # календар змін (scheduling) + drag-n-drop auto-assign
│ │
│ ├── features/ # самодостатні бізнес-фічі
│ │ ├── staff-onboarding/
│ │ ├── shift-scheduling/
│ │ ├── auto-assign/
│ │ ├── compliance-tracking/
│ │ ├── notifications/
│ │ └── <feature-name>/
│ │ ├── components/
│ │ ├── hooks/
│ │ ├── api/ # RTK Query endpoints: injectEndpoints(baseApi)
│ │ ├── model/ # RTK-слайс, якщо фічі потрібен власний UI-стан
│ │ ├── types.ts
│ │ └── index.ts # публічний API фічі (barrel export)
│ │
│ ├── entities/ # доменні моделі: типи + мінімальна логіка
│ │ ├── staff/
│ │ ├── shift/
│ │ ├── client/
│ │ └── booking/
│ │
│ ├── layouts/ # лейаути сторінок
│ │ ├── MainLayout/ # sidebar + header, для авторизованого Portal
│ │ ├── AuthLayout/ # login/forgot-password
│ │ └── EmptyLayout/
│ │
│ ├── components/ # перевикористовувані "тупі" UI-компоненти (design system)
│ │ ├── Button/
│ │ ├── Input/
│ │ ├── Modal/
│ │ ├── Table/
│ │ ├── Badge/
│ │ └── <Component>/
│ │ ├── Component.tsx
│ │ ├── Component.module.scss
│ │ └── index.ts
│ │
│ ├── store/ # configureStore, rootReducer, типізовані hooks
│ │ ├── slices/ # клієнтські RTK-слайси (auth, ui)
│ │ ├── hooks.ts # useAppDispatch / useAppSelector
│ │ └── index.ts
│ ├── hooks/ # спільні кастомні хуки (useDebounce, usePermissions...)
│ ├── api/ # base RTK Query api-слайс (createApi, baseQuery, tagTypes)
│ ├── lib/ # утиліти/хелпери (форматери дат, чисел, GDPR-маски)
│ ├── constants/ # константи, enum-и (ролі, статуси compliance)
│ ├── types/ # глобальні TS-типи, спільні для кількох фіч
│ ├── config/ # env-конфіг, feature flags
│ ├── assets/
│ │ ├── icons/
│ │ ├── images/
│ │ └── fonts/
│ │
│ └── styles/ # глобальні SCSS
│ ├── abstracts/
│ │ ├── _variables.scss # кольори, spacing, typography — дизайн-токени
│ │ ├── _mixins.scss
│ │ ├── _functions.scss
│ │ └── _index.scss # forward усіх abstracts (@use "abstracts" as *)
│ ├── base/
│ │ ├── _reset.scss
│ │ ├── _typography.scss
│ │ └── _index.scss
│ ├── themes/
│ │ ├── _light.scss
│ │ └── _dark.scss
│ └── main.scss # єдина точка входу, підключається в main.tsx
│
├── .oxlintrc.json
├── .env.example
├── vite.config.ts
├── tsconfig.json
└── package.json
app → pages → widgets → features → entities → shared (components/lib/hooks/api)
Шар справа не імпортує шар зліва (напр. entities нічого не знає про features). Це запобігає циклічним залежностям і дозволяє легко ізолювати/переносити фічі (важливо для full-ownership handover клієнту — код має читатись без контексту авторів).
widgets — це готові композитні блоки, які збирають докупи кілька entities/features/components, і потім просто вставляються на сторінку одним тегом. Різниця з сусідніми папками:
| Папка | Що там | Приклад | Чи знає про бізнес-логіку |
|---|---|---|---|
components/ |
"тупий" UI, без даних і бізнес-правил | Button, Badge, Table |
ні — тільки props |
entities/ |
дані/тип домену + мінімальне відображення (напр. StaffAvatar) |
staff, shift, client |
тільки про "себе" |
features/ |
одна дія користувача цілком (UI + API-запит + стан) | compliance-tracking (перевірка/оновлення статусу) |
так, про одну дію |
widgets/ |
збірка з кількох entities/features в один блок, що ставиться на сторінку без додаткової збірки | StaffProfileCard = аватар staff (entity) + compliance-бейдж (feature) + кнопки "Edit"/"Deactivate" |
так, але сам нічого не робить — лише компонує |
pages/ |
сторінка = layout + один або кілька widgets/features під конкретний route | pages/staff/StaffListPage.tsx рендерить список StaffProfileCard |
ні — просто розкладає widgets по layout |
Практичне правило: якщо блок використовується більш ніж на одній сторінці і складається з кількох дрібніших частин — це widgets. Якщо він логічно прив'язаний до одного місця (наприклад, форма фільтрів тільки на сторінці Scheduling) — це просто локальний компонент усередині pages/scheduling/, окрема папка не потрібна. Якщо для конкретного проєкту такий поділ здається зайвим ускладненням — шар можна прибрати і класти складені блоки прямо в pages/<page>/components/.
- Компоненти —
PascalCase, папка = компонент (Button/Button.tsx,Button/Button.module.scss,Button/index.ts). - Хуки/утиліти/файли-не-компоненти —
camelCase(useAuth.ts,formatDate.ts). - Стилі — CSS Modules (
*.module.scss) для локальних стилів компонента; BEM-подібні класи всередині модуля (.card,.card__title,.card--active) для читабельності DOM у devtools. - Дизайн-токени — тільки через SCSS-змінні з
styles/abstracts/_variables.scss(кольори, spacing, radius, шрифти). Хардкод hex/px у компонентних стилях — заборонений лінтом/рев'ю. - Аліаси шляхів —
@/*→src/*(налаштовується уvite.config.ts+tsconfig.json), щоб уникнути../../../../. - Публічний API фічі — імпорт ззовні тільки через
features/<feature>/index.ts, а не напряму у внутрішні файли. - Тести — колокейтяться поруч із кодом (
Component.test.tsx), без окремого__tests__/дерева.
Workflow .github/workflows/deploy-pages.yml білдить і публікує проєкт на GitHub Pages при пуші в main.
- Base-шлях (
/<repo-name>/) підставляється автоматично з назви репозиторію — окремо налаштовувати не треба. - Після білду
index.htmlкопіюється в404.html(SPA fallback), бо GitHub Pages не вміє в server-side rewrite, а без цього оновлення сторінки на будь-якому маршруті, крім/, дає 404 (React Router working onBrowserRouter). - Перед першим деплоєм: у налаштуваннях репозиторію → Settings → Pages → Source вибрати GitHub Actions.
- Використовується лише для прев'ю/демо — production-хостинг для клієнта визначається окремо.
npm create vite@latest . -- --template react-ts- Підключити
sass,oxlint,prettier,@reduxjs/toolkit+react-redux, налаштувати@/*alias - Створити базовий
styles/(reset + токени) іlayouts/MainLayout - Завести перші фічі відповідно до модулів SoW: onboarding, scheduling, compliance