docs: add architecture plan and design system documentation

This commit is contained in:
Arthur Bassi 2026-06-12 11:41:07 -03:00
parent 0451788764
commit d3076f6d88
6 changed files with 602 additions and 94 deletions

View file

@ -1,70 +1,55 @@
# Getting Started with Create React App
# SeaHaven Admin
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
Vite + React SPA for SeaHaven facility management (work orders, vendor portal, uplifts, and related admin features).
## Available Scripts
## Requirements
In the project directory, you can run:
- Node.js 20+
- npm 10+
### `npm start`
## Setup
Runs the app in the development mode.\
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
```bash
npm ci
cp .env.example .env
```
The page will reload when you make changes.\
You may also see any lint errors in the console.
### Environment variables
### `npm test`
| Variable | Description | Default |
| ----------------- | ------------------------------------------------------ | ----------------------- |
| `VITE_API_URL` | Backend API base URL (baked into the production build) | `/api` |
| `VITE_API_TARGET` | Dev proxy target for `/api` (Vite only) | `http://localhost:5141` |
Launches the test runner in the interactive watch mode.\
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
## Scripts
### `npm run build`
| Command | Description |
| ---------------------- | ------------------------------------------ |
| `npm run dev` | Start Vite dev server on port 3000 |
| `npm run build` | Type-check and production build to `dist/` |
| `npm run preview` | Preview production build locally |
| `npm test` | Run Vitest unit tests |
| `npm run test:watch` | Run Vitest in watch mode |
| `npm run lint` | ESLint |
| `npm run lint:fix` | ESLint with auto-fix |
| `npm run format` | Prettier write |
| `npm run format:check` | Prettier check (used in CI) |
Builds the app for production to the `build` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.
## Architecture
The build is minified and the filenames include the hashes.\
Your app is ready to be deployed!
New code lives under `src/domain/`, `src/app/`, and `src/api/` following the IrisLoan conventions documented in `docs/ARCHITECTURE_PLAN.md`.
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
## CI
### `npm run eject`
GitHub Actions workflow (`.github/workflows/ci.yml`) runs on push and pull requests:
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
1. `npm run format:check`
2. `npm run lint`
3. `npm run build`
4. `npm test`
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
Local pre-commit hooks (Husky + lint-staged) run ESLint and Prettier on staged files.
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
## Development proxy
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
## Learn More
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
To learn React, check out the [React documentation](https://reactjs.org/).
### Code Splitting
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
### Analyzing the Bundle Size
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
### Making a Progressive Web App
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
### Advanced Configuration
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
### Deployment
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
### `npm run build` fails to minify
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
During `npm run dev`, requests to `/api` are proxied to `VITE_API_TARGET` (see `vite.config.ts`).

102
docs/ARCHITECTURE_PLAN.md Normal file
View file

@ -0,0 +1,102 @@
# Plano de Arquitetura — SeaHaven (Rebuild Seletivo)
> Alinhamento com IrisLoan.Admin · SPA Vite + React Router · **sem Next.js** · Tailwind + MUI
Documento espelho do plano Cursor. Estratégia: **reaproveitar conhecimento, descartar código legado** — não normalizar/refatorar o projeto atual.
---
## Estratégia em uma frase
Montar `api/` + `domain/` + `app/` do zero; usar `pages/` apenas como **referência de endpoints e regras**; **deletar** cada pasta legada quando a feature nova estiver pronta.
---
## Matriz: Manter vs Descartar
### Salvar (extrair lógica → código novo)
| Legado | Salvar | Destino |
| ------------------------------------- | --------------------------------- | ------------------------------------------------ |
| `lib/api/services.js` | Paths ASP.NET + parse de resposta | `api/api-paths.ts`, `api/handle-api-response.ts` |
| `pages/*/api.js` | Endpoints reais | `api/api-paths.ts` |
| `constants/queryKeys.js` | Entidades | `infra/query-key/query-key.ts` |
| `hooks/useDebounce.js`, `useModal.js` | Hooks | `hooks/*.ts` |
| `hooks/api/usePMSchedules.js` | Padrão RQ | Template `domain/*/use-cases/` |
| `lib/queryClient.js` | Defaults cache | `lib/query/query-client.ts` |
| `App.js`, `Sidebar.js` | Rotas e menu | `app/routes.tsx`, `config/menu.ts` |
| `pages/workorders/` (list) | Fluxo de negócio | Referência para POC |
### Descartar (apagar sem refatorar)
- `apiUtil.js`, `services/api.js`, `lib/api/client.js`
- `tokenUtility.js`, `authService.js`, Redux (`app/store.js`, slices)
- `usePaginatedList.js`, toda pasta `pages/` (após substituição)
- `Site.Layout.js`, `setupProxy.js`, `site.css`, CSS por feature, Font Awesome
- Forms `*FormPage.js` (reescrever com RHF + Zod)
- `SharedTable.js`, `ActionBar.js` (recriar se adaptar for caro)
### Adiar (later)
- `pages/vendor-portal/` → `domain/vendor-portal/`
- `pages/calendar/` → wrapper FullCalendar novo
---
## Prioridade de features
Matriz completa (waves, dependências, LOC, critérios de done): **[`FEATURE_PRIORITIZATION.md`](FEATURE_PRIORITIZATION.md)** · config TS: [`src/config/feature-priorities.ts`](../src/config/feature-priorities.ts).
| Wave | Escopo |
| ----------------- | --------------------------------------------------------------------------- |
| **0 POC** | auth → work-orders (list) |
| **1 must-have** | work-orders (full), dashboard, settings/dropdowns |
| **2** | accounts, locations, employees |
| **3 should-have** | vendors, vendor-pos, uplifts, follow-ups, contacts, settings/task-templates |
| **4 later** | calendar, vendor-portal, pm-schedules, assets, reports/documents |
Decisão stakeholder: **auth + work-orders primeiro**; POC = auth + work-orders list (não pm-schedules).
---
## Estrutura alvo
```
src/
├── api/
├── domain/<feature>/
├── infra/query-key/
├── lib/
├── components/ui/ + common/
├── app/
├── config/menu.ts
├── hooks/
├── providers/
└── main.tsx
```
---
## Fases
| Fase | Objetivo |
| ----- | --------------------------------------------------------------------------------------- |
| **0** | Completar fundação (Ky, routes, layout, AuthProvider, tooling) — Vite/TS/Tailwind já ok |
| **1** | Ky + auth cookies httpOnly; deletar stacks HTTP legadas |
| **2** | Reescrever features must-have; delete `pages/<feature>/` por entrega |
| **3** | Shell UI + forms RHF+Zod |
| **4** | Apagar `pages/`, Redux, CSS legado; Docker + CI + Vitest |
---
## Próximos passos
1. Completar fundação (`api/api.ts`, `routes.tsx`, AuthProvider, layout shell)
2. `api/api-paths.ts` (extrair de `lib/api/services.js`)
3. POC: `domain/auth` + login → delete `pages/auth/`
4. POC: `domain/work-orders` (list) → delete trecho list de `pages/workorders/`
5. Contrato cookies com backend
---
Ver plano completo com diagramas em `.cursor/plans/migração_arquitetura_irisloan_cf39e1a6.plan.md`.

137
docs/DESIGN_SYSTEM.md Normal file
View file

@ -0,0 +1,137 @@
# SeaHaven Design System
Visual foundation for **seaheven.front**, aligned with [seahaven.desing](https://github.com) tokens and IrisLoan.Admin integration patterns (centralized CSS, `@theme inline`, `cn()`).
## Stack
- **MUI-only** — no Radix/shadcn; MUI is the sole component library
- **Tailwind v4** — semantic utility classes from CSS tokens
- **Fonts** — Montserrat (headings), DM Sans (body), JetBrains Mono (code) via `@fontsource`
## Token source of truth
| File | Purpose |
| --------------------------- | ------------------------------------------------------------ |
| `src/styles/theme.css` | All `:root` CSS variables + `@theme inline` Tailwind aliases |
| `src/styles/fonts.css` | `@fontsource` imports |
| `src/styles/typography.css` | Base `html`, `body`, `h1–h6`, `.text-caption`, `.text-label` |
| `src/styles/motion.css` | `--duration-fast`, `--duration-normal`, `--ease-default` |
| `src/styles/globals.css` | Entry: imports all above + `@custom-variant dark` |
## Key tokens
### Brand & surfaces
| Token | Value | Usage |
| -------------------- | --------- | -------------------------- |
| `--primary` | `#1c75bc` | Buttons, links, focus ring |
| `--primary-hover` | `#155a92` | Hover states |
| `--foreground` | `#262262` | Primary text |
| `--background` | `#ffffff` | Cards, inputs |
| `--color-bg-page` | `#f9fafb` | Page background |
| `--border` | `#dfe3ea` | Dividers, outlines |
| `--muted-foreground` | `#58595b` | Secondary text |
### Layout
| Token | Value |
| ----------------------------- | ------- |
| `--spacing-sidebar` | `220px` |
| `--spacing-sidebar-collapsed` | `56px` |
| `--spacing-topbar` | `64px` |
### Typography scale
| Token | Size |
| ---------------- | ---- |
| `--text-xs` | 11px |
| `--text-sm` | 12px |
| `--text-base-sm` | 13px |
| `--text-base` | 14px |
| `--text-md` | 16px |
| `--text-lg` | 18px |
| `--text-xl` | 22px |
| `--text-2xl` | 28px |
### Elevation
| Token | Usage |
| ------------- | ------------------- |
| `--shadow-sm` | Cards, subtle depth |
| `--shadow-md` | Dropdowns, panels |
| `--shadow-lg` | Dialogs, modals |
### Work order badges (immutable)
Type, status, and completion-doc tokens (`--type-*`, `--status-*`, `--doc-*`) must not be changed — see `src/components/domain/`.
## Typography hierarchy
| Element | Font | Weight | Size |
| --------------- | ---------- | ------ | -------------- |
| `h1` | Montserrat | 800 | 28px |
| `h2` | Montserrat | 700 | 22px |
| `h3` | Montserrat | 600 | 18px |
| `h4–h6` | Montserrat | 600 | 16–13px |
| Body | DM Sans | 400 | 14px |
| `.text-caption` | DM Sans | 400 | 12px |
| `.text-label` | DM Sans | 600 | 11px uppercase |
MUI typography variants in `src/lib/theme/mui-theme.ts` mirror this hierarchy.
## MUI + Tailwind usage
### Prefer themed MUI in forms and data tables
MUI components inherit Seahaven styling via `mui-theme.ts` overrides (`Button`, `TextField`, `Table`, `Chip`, `Paper`, `Dialog`, `Alert`).
### Prefer semantic Tailwind in layout shells
```tsx
<div className="bg-background text-foreground border-border" />
<div className="text-muted-foreground" />
<div className="bg-primary text-primary-foreground" />
```
### Compose classes with `cn()`
```tsx
import { cn } from "@/lib/utils";
<div className={cn("flex gap-2", isActive && "bg-sidebar-accent")} />;
```
### Shared UI wrappers
| Component | Path | Pattern |
| ------------ | ------------------------------- | ---------------------------- |
| `Button` | `components/ui/button.tsx` | MUI Button + variant presets |
| `Badge` | `components/ui/badge.tsx` | MUI Chip |
| `PageHeader` | `components/ui/page-header.tsx` | PATTERN_001 |
| `EmptyState` | `components/ui/empty-state.tsx` | PATTERN_003 |
| `FormField` | `components/ui/form-field.tsx` | PATTERN_004 |
### Domain badges
| Component | Path |
| -------------------------------------- | ------------------------------------ |
| `TypeBadge` | `components/domain/type-badge.tsx` |
| `StatusBadge` / `WorkOrderStatusBadge` | `components/domain/status-badge.tsx` |
| `DocBadge` | `components/domain/doc-badge.tsx` |
## Legacy → Seahaven mapping
| Legacy | Seahaven | Consumption |
| ------------------------- | ------------------------------ | -------------------------------- |
| `#0c4f6f` / `--blue-main` | `--primary` (`#1c75bc`) | MUI `primary.main`, `bg-primary` |
| `#083a52` / `--blue-dark` | `--primary-hover` | Button hover |
| `#f4f6f7` / `--bg` | `--color-bg-page` | Body, page shell |
| `#d9dde0` / `--border` | `--border` | Dividers, inputs |
| `#fbfbfb` sidebar | `--color-sidebar-bg` | App sidebar |
| Segoe UI 13px | DM Sans 14px | Body typography |
| Bootstrap calendar colors | `--primary`, `--success`, etc. | `calendar-utils.ts` |
| `#1a3a5c` login header | `--color-header-bg-start` | Login branding strip |
## Dark mode
`@custom-variant dark (&:is(.dark *))` is configured in `globals.css`. Full dark token set is a future enhancement; light mode is the current default.

View file

@ -0,0 +1,88 @@
# Design System Migration Log
Migration from legacy CSS (`index.css`, `#0c4f6f` palette) to Seahaven DS tokens. Strategy: **MUI-only**.
## Phase 1 — Foundation
| File | Action | Reason |
| --------------------------- | ---------- | --------------------------------------------------------------------- |
| `src/styles/theme.css` | Created | SoT: 131+ Seahaven tokens + layout/typography/elevation extensions |
| `src/styles/fonts.css` | Created | Montserrat, DM Sans, JetBrains Mono via `@fontsource` |
| `src/styles/typography.css` | Created | Base html/body/heading/caption/label styles |
| `src/styles/motion.css` | Created | Duration and easing tokens |
| `src/styles/globals.css` | Refactored | Import chain + `@custom-variant dark`; removed legacy `@theme` tokens |
| `src/lib/utils.ts` | Created | `cn()` with clsx + tailwind-merge |
| `src/lib/theme/css-vars.ts` | Created | `getCssVar()` helper for MUI bridge |
| `package.json` | Modified | Added `@fontsource/*`, `clsx`, `tailwind-merge` |
## Phase 2 — MUI Bridge
| File | Action | Reason |
| ---------------------------- | -------- | --------------------------------------------------------------- |
| `src/lib/theme/mui-theme.ts` | Expanded | Full palette, typography, shape, shadows, 8 component overrides |
## Phase 3 — App Shell
| File | Action | Reason |
| ----------------------------------------- | -------- | -------------------------------------------------- |
| `src/components/layout/app-sidebar.tsx` | Created | MUI Drawer + sidebar tokens; replaces `Sidebar.js` |
| `src/components/layout/app-topbar.tsx` | Created | MUI AppBar + header gradient; replaces `Topbar.js` |
| `src/app/(protected)/_layout.tsx` | Modified | Uses new layout components |
| `src/main.tsx` | Modified | Removed `index.css` import |
| `src/app/v/_components/vendor-portal.css` | Modified | Re-tokenized with Seahaven CSS vars |
| `src/index.css` | Deleted | ~487 lines migrated to tokens + TSX shell |
| `src/components/Sidebar.js` | Deleted | Replaced by `app-sidebar.tsx` |
| `src/components/Topbar.js` | Deleted | Replaced by `app-topbar.tsx` |
| `src/styles/FormPage.css` | Deleted | Unused (no imports in codebase) |
## Phase 4 — Shared UI
| File | Action | Reason |
| ------------------------------------------ | -------- | -------------------------------------------- |
| `src/components/ui/button.tsx` | Created | MUI Button wrapper |
| `src/components/ui/badge.tsx` | Created | MUI Chip wrapper |
| `src/components/ui/page-header.tsx` | Created | PATTERN_001 |
| `src/components/ui/empty-state.tsx` | Created | PATTERN_003 |
| `src/components/ui/form-field.tsx` | Created | PATTERN_004 |
| `src/components/domain/type-badge.tsx` | Created | WO type tokens |
| `src/components/domain/status-badge.tsx` | Created | WO status tokens + API aliases |
| `src/components/domain/doc-badge.tsx` | Created | Completion doc tokens (MUI icons) |
| `src/app/(protected)/workorders/index.tsx` | Modified | `WorkOrderStatusBadge` replaces generic Chip |
## Phase 5 — Page refactor
| File | Action | Reason |
| ---------------------------------------------------------------------- | -------- | ------------------------------------------ |
| `src/app/(auth)/login.tsx` | Modified | `#1a3a5c` → `var(--color-header-bg-start)` |
| `src/app/(protected)/dashboard.tsx` | Modified | `border-gray-*` → `border-border` |
| `src/app/(protected)/index.tsx` | Modified | Same as dashboard |
| `src/app/(protected)/settings/dropdowns.tsx` | Modified | Semantic sidebar + active states |
| `src/app/(protected)/settings/task-templates.tsx` | Modified | Semantic borders/text |
| `src/components/common/settings-nav.tsx` | Modified | `text-primary`, `border-border` |
| `src/components/common/calendar/calendar-utils.ts` | Modified | Bootstrap hex → Seahaven CSS vars |
| `src/components/common/calendar/calendar-action-bar.tsx` | Modified | Semantic surface classes |
| `src/components/common/calendar/event-calendar.tsx` | Modified | Semantic surface classes |
| `src/components/common/signature-capture.tsx` | Modified | Canvas colors from CSS vars |
| `src/app/(protected)/workorders/_components/dispatch-create-modal.tsx` | Modified | Semantic borders/hover |
| `src/app/(protected)/workorders/_components/dispatch-detail-modal.tsx` | Modified | Semantic borders |
| `src/app/(protected)/workorders/[id].tsx` | Modified | Semantic borders |
| `src/app/(protected)/vendor-pos/[id].tsx` | Modified | Semantic borders |
## Phase 6 — Docs & validation
| File | Action | Reason |
| --------------------------------- | ------- | ------------------------------- |
| `docs/DESIGN_SYSTEM.md` | Created | Token reference and usage guide |
| `docs/DESIGN_SYSTEM_MIGRATION.md` | Created | This file |
## Visual impact summary
- Primary color: `#0c4f6f` → `#1c75bc` (expected, documented)
- Body font: Segoe UI 13px → DM Sans 14px
- Sidebar: `#fbfbfb` → `#f6f8fb` with Seahaven active/hover states
- Header: flat blue gradient → Seahaven multi-stop gradient
## Not migrated (intentional)
- CRUD list pages using default MUI theming — inherit via `mui-theme.ts` without JSX changes
- `src/app/v/_components/signature-pad.tsx` — vendor portal isolated; minor `#111` stroke remains

View file

@ -0,0 +1,160 @@
# Matriz de Priorização de Features — SeaHaven
> Fonte de verdade para ordem de migração legado → `domain/` + `app/`.
> Config machine-readable: [`src/config/feature-priorities.ts`](../src/config/feature-priorities.ts).
---
## Decisões de stakeholders
| Decisão | Detalhe |
| ------------------- | -------------------------------------------------------------------------------------- |
| Prioridade absoluta | **`auth`** e **`work-orders`** |
| POC end-to-end | **`auth` + work-orders (list)** — validar Ky, React Query, layout shell e AuthProvider |
| Demais features | Ordem flexível, guiada por dependências técnicas (waves abaixo) |
| POC **não** inclui | `pm-schedules` (adiado para Wave 4) |
---
## Matriz completa
| Ordem | Feature | Tier | Wave | Pasta legado | Rotas | LOC ~ | Dependências |
| ----- | ------------------------- | ----------- | ---- | ----------------------------------- | ----------------------------------------------------------------------------- | ------ | ---------------------------------------------------------------------------- |
| 1 | auth | POC | 0 | `pages/auth/` | `/login` | 513 | — |
| 2 | work-orders (list) | POC | 0 | `pages/workorders/` (list) | `/workorders` | 4.3k\* | auth |
| 3 | work-orders (form + view) | must-have | 1 | `pages/workorders/` | `/workorders/new`, `/workorders/:id`, `/workorders/edit/:id` | 4.3k\* | auth, work-orders (list), settings/dropdowns, accounts, locations, employees |
| 4 | dashboard | must-have | 1 | `pages/Dashboard.js` | `/`, `/dashboard` | 253 | auth |
| 5 | settings/dropdowns | must-have | 1 | `pages/settings/` (DropdownOptions) | `/settings/dropdowns` | 866\* | auth |
| 6 | accounts | must-have | 2 | `pages/accounts/` | `/accounts`, `/accounts/new`, `/accounts/edit/:id` | 745 | auth |
| 7 | locations | must-have | 2 | `pages/locations/` | `/locations`, `/locations/new`, `/locations/edit/:id` | 1.1k | auth, accounts |
| 8 | employees | must-have | 2 | `pages/employees/` | `/employees`, `/employees/new`, `/employees/edit/:id` | 1.9k | auth |
| 9 | vendors | should-have | 3 | `pages/vendors/` | `/vendors`, `/vendors/new`, `/vendors/edit/:id` | 610 | auth, work-orders (full) |
| 10 | vendor-pos | should-have | 3 | `pages/vendor-pos/` | `/vendor-pos`, `/vendor-pos/:id` | 1k | auth, work-orders (full) |
| 11 | uplifts | should-have | 3 | `pages/uplifts/` | `/uplifts` | 317 | auth, work-orders (full) |
| 12 | follow-ups | should-have | 3 | `pages/followup/` | `/followups`, `/followups/new`, `/followups/edit/:id` | 1.3k | auth, employees, accounts, locations |
| 13 | contacts | should-have | 3 | `pages/contacts/` | `/contacts`, `/contacts/new`, `/contacts/edit/:id` | 1.1k | auth |
| 14 | settings/task-templates | should-have | 3 | `pages/settings/` (TaskTemplates) | `/settings/task-templates` | 866\* | auth, work-orders (full) |
| 15 | calendar | later | 4 | `pages/calendar/` | `/calendar`, `/calendar/new`, `/calendar/edit/:id` | 1.9k | auth |
| 16 | vendor-portal | later | 4 | `pages/vendor-portal/` | `/v/:token`, `/v/:token/dashboard`, `/v/:token/pos`, `/v/:token/dispatch/:id` | 1.4k | auth, vendors |
| 17 | pm-schedules | later | 4 | `pages/PmSchedule/` | `/pmschedules`, `/pmschedules/new`, `/pmschedules/edit/:id` | 1.1k | auth |
| 18 | assets | later | 4 | `pages/assets/` | `/assets`, `/assets/new`, `/assets/edit/:id` | 638 | auth, accounts |
| 19 | reports / documents | later | 4 | — (sem implementação) | `/reports`, `/documents` (menu apenas) | — | auth |
\* LOC da pasta inteira; escopo POC usa somente o submódulo de listagem.
### Complexidade legada (referência)
| Feature | Complexidade | Observação |
| ------------- | ------------ | ----------------------------------------------------------- |
| auth | Baixa | Substituir Redux/`tokenUtility` por AuthProvider + cookies |
| dashboard | Média | KPIs reais via `GET /Dashboard/Stats` |
| work-orders | **Alta** | View monolítica; dispatch, checklist, signoff |
| contacts | Alta | Form legado **stub** (save TODO) — reescrever com RHF + Zod |
| employees | Alta | Form grande |
| vendor-portal | Alta | Auth por token URL + assinatura |
| calendar | Alta | Wrapper FullCalendar |
| pm-schedules | Média | Bugs API legados (`PmSchedule/Create` vs `Save`) |
---
## Grafo de dependências
```mermaid
flowchart TD
subgraph wave0 [Wave0_POC]
auth[auth]
woList[work_orders_list]
end
subgraph wave1 [Wave1_MustHave]
woFull[work_orders_full]
dash[dashboard]
dropdowns[settings_dropdowns]
end
subgraph wave2 [Wave2_Dependencies]
accounts[accounts]
locations[locations]
employees[employees]
end
subgraph wave3 [Wave3_ShouldHave]
vendors[vendors]
vendorPos[vendor_pos]
uplifts[uplifts]
followups[follow_ups]
contacts[contacts]
settingsTpl[task_templates]
end
subgraph wave4 [Wave4_Later]
calendar[calendar]
vendorPortal[vendor_portal]
pmSched[pm_schedules]
assets[assets]
reports[reports_documents]
end
auth --> woList
woList --> woFull
dropdowns --> woFull
accounts --> woFull
locations --> woFull
employees --> woFull
woFull --> vendors
vendors --> vendorPortal
woFull --> vendorPos
woFull --> uplifts
```
### Waves (resumo)
| Wave | Tier | Features |
| ----- | ----------- | --------------------------------------------------------------------------- |
| **0** | POC | auth, work-orders (list) |
| **1** | must-have | work-orders (full), dashboard, settings/dropdowns |
| **2** | must-have | accounts, locations, employees |
| **3** | should-have | vendors, vendor-pos, uplifts, follow-ups, contacts, settings/task-templates |
| **4** | later | calendar, vendor-portal, pm-schedules, assets, reports/documents |
> `accounts` e `locations` podem avançar em paralelo à view de WO se a list POC usar dados já existentes no backend; a ordem acima é a sequência mínima para o form completo de WO.
---
## Gaps e cleanup do legado
| Item | Local | Ação na migração |
| ------------------------ | --------------------------------------------------------- | ------------------------------------------------------------------------------------------- |
| Dashboard duplicado | `pages/dashboard/` (protótipo mock) | **Não roteado** — ignorar; usar apenas `pages/Dashboard.js` como referência |
| Login órfão | `pages/user/api.js` | Deletar na Fase 1 (Ky + auth); duplica fluxo de `pages/auth/` |
| Menu fantasma | `Sidebar.js` → Reports `/reports`, Documents `/documents` | Sem rotas nem páginas — implementar do zero (Wave 4) ou remover do menu em `config/menu.ts` |
| Contacts form incompleto | `pages/contacts/form/` | Não portar stub; reescrever com RHF + Zod na Wave 3 |
| PM schedules API | `pages/PmSchedule/` | Documentar inconsistência Create vs Save; corrigir na reescrita |
---
## Critérios de "done" por feature
Uma feature está **done** quando todos os itens abaixo forem atendidos:
1. **`domain/<feature>/`** — schemas Zod, hooks/use-cases, tipos; sem imports de `pages/`, Redux, axios ou `apiUtil`
2. **`app/`** — views finas (list, form, view conforme escopo) registradas em `app/routes.tsx`
3. **`api/api-paths.ts`** — endpoints da feature centralizados; respostas via `handle-api-response.ts`
4. **`infra/query-key/query-key.ts`** — query keys da feature
5. **Rota protegida** — navegação via `config/menu.ts` (quando aplicável à wave)
6. **Delete legado** — pasta `pages/<feature>/` (ou arquivos equivalentes) removida após validação
7. **Sem regressão** — fluxo principal testado manualmente ou com Vitest (quando existir)
### Done por escopo especial
| Escopo | Critério adicional |
| ------------------------ | ---------------------------------------------------------------------------------- |
| **POC auth** | Login/logout com cookies httpOnly; `AuthProvider` substitui Redux + `tokenUtility` |
| **POC work-orders list** | Listagem paginada/filtrada com Ky + RQ; sem form/view nesta entrega |
| **work-orders full** | Form + view (dispatch, checklist, signoff) funcionais |
| **settings/dropdowns** | Dropdowns Problem/Trade/SubTrade disponíveis para WO form |
| **reports/documents** | Decisão explícita: implementar feature ou remover links do menu |
---
## Referências
- Plano de arquitetura: [`ARCHITECTURE_PLAN.md`](ARCHITECTURE_PLAN.md)
- Convenções IrisLoan: `.cursor/rules/seahaven-admin.mdc`
- Rotas legado: `src/App.js`
- Menu legado: `src/components/Sidebar.js`

View file

@ -112,16 +112,19 @@ src/
### 1. State Management: Redux vs React Query
**Use Redux for:**
- ✅ User authentication (login/logout)
- ✅ App-wide settings (theme, sidebar state)
- ✅ Data that needs to be shared everywhere
**Use React Query for:**
- ✅ API data (lists, details)
- ✅ Any server data
- ✅ Automatic caching and refetching
**Use useState for:**
- ✅ Local component state
- ✅ Form inputs
- ✅ UI toggles (dropdowns, tabs)
@ -129,18 +132,21 @@ src/
### 2. CSS Modules (Built-in - No Dependencies!)
**Old Way** (Global CSS):
```javascript
import './Component.css';
<div className="container">
```
**New Way** (CSS Modules):
```javascript
import styles from './Component.module.css';
<div className={styles.container}> // Automatically scoped!
```
**Benefits:**
- No naming conflicts
- Better tree-shaking
- No extra build tools needed
@ -150,13 +156,13 @@ import styles from './Component.module.css';
```javascript
// ❌ Bad
queryKey: ['pmschedules']
setTimeout(fn, 600)
queryKey: ["pmschedules"];
setTimeout(fn, 600);
// ✅ Good
import { PM_SCHEDULES_LIST, DEBOUNCE_SEARCH } from '../constants';
queryKey: [PM_SCHEDULES_LIST]
setTimeout(fn, DEBOUNCE_SEARCH)
import { PM_SCHEDULES_LIST, DEBOUNCE_SEARCH } from "../constants";
queryKey: [PM_SCHEDULES_LIST];
setTimeout(fn, DEBOUNCE_SEARCH);
```
---
@ -164,27 +170,30 @@ setTimeout(fn, DEBOUNCE_SEARCH)
## 💻 Code Examples
### Example 1: Loading Data with React Query
```javascript
import { usePMSchedules } from '../hooks/api/usePMSchedules';
import { useDebounce, DEBOUNCE_SEARCH } from '../constants';
import { usePMSchedules } from "../hooks/api/usePMSchedules";
import { useDebounce, DEBOUNCE_SEARCH } from "../constants";
function PMScheduleList() {
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);
const { data, isLoading, error } = usePMSchedules({
search: debouncedSearch,
page: 1
const { data, isLoading, error } = usePMSchedules({
search: debouncedSearch,
page: 1,
});
if (isLoading) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>;
return (
<div>
<input value={search} onChange={(e) => setSearch(e.target.value)} />
<ul>
{data.items.map(item => <li key={item.id}>{item.name}</li>)}
{data.items.map((item) => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
@ -192,8 +201,9 @@ function PMScheduleList() {
```
### Example 2: Using Redux Auth
```javascript
import { useAuth } from '../hooks/useAuth';
import { useAuth } from "../hooks/useAuth";
function MyComponent() {
const { user, isAuthenticated, login, logout } = useAuth();
@ -204,16 +214,16 @@ function MyComponent() {
return (
<div>
Welcome {user.name}!
<button onClick={logout}>Logout</button>
Welcome {user.name}!<button onClick={logout}>Logout</button>
</div>
);
}
```
### Example 3: Using Modals
```javascript
import { useModal } from '../hooks/useModal';
import { useModal } from "../hooks/useModal";
function MyComponent() {
const deleteModal = useModal();
@ -223,9 +233,7 @@ function MyComponent() {
<button onClick={() => deleteModal.open(item)}>Delete</button>
{deleteModal.isOpen && (
<Modal onClose={deleteModal.close}>
Delete {deleteModal.data.name}?
</Modal>
<Modal onClose={deleteModal.close}>Delete {deleteModal.data.name}?</Modal>
)}
</div>
);
@ -233,8 +241,9 @@ function MyComponent() {
```
### Example 4: CSS Modules
```javascript
import styles from './LoginPage.module.css';
import styles from "./LoginPage.module.css";
function LoginPage() {
return (
@ -260,6 +269,7 @@ function LoginPage() {
## 🔧 Common Patterns
### Pattern 1: Fetch and Display Data
```javascript
const { data, isLoading, error } = useDataHook(params);
@ -270,6 +280,7 @@ return <Display data={data} />;
```
### Pattern 2: Delete with Confirmation
```javascript
const deleteMutation = useDeleteHook();
const deleteModal = useModal();
@ -286,6 +297,7 @@ const handleDelete = async () => {
```
### Pattern 3: Search with Debounce
```javascript
const [search, setSearch] = useState("");
const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);
@ -300,42 +312,48 @@ const { data } = useDataHook({ search: debouncedSearch });
## 📚 Constants Reference
### API Constants
```javascript
API_URL // Backend URL
API_ERROR_MESSAGE // Default error message
API_SUCCESS_MESSAGE // Success message
API_URL; // Backend URL
API_ERROR_MESSAGE; // Default error message
API_SUCCESS_MESSAGE; // Success message
```
### Timing Constants
```javascript
DEBOUNCE_SEARCH // 600ms
CACHE_TIME_MEDIUM // 5 minutes
STALE_TIME_MEDIUM // 5 minutes
DEBOUNCE_SEARCH; // 600ms
CACHE_TIME_MEDIUM; // 5 minutes
STALE_TIME_MEDIUM; // 5 minutes
```
### View Modes
```javascript
VIEW_MODE_LIST // 'list'
VIEW_MODE_CARD // 'card'
VIEW_MODE_LIST; // 'list'
VIEW_MODE_CARD; // 'card'
```
### Status Types
```javascript
STATUS_OPEN // 'Open'
STATUS_COMPLETED // 'Completed'
STATUS_OPEN; // 'Open'
STATUS_COMPLETED; // 'Completed'
```
### Query Keys
```javascript
PM_SCHEDULES_LIST // 'pmSchedulesList'
WORK_ORDERS_LIST // 'workOrdersList'
EMPLOYEES_DROPDOWN // 'employeesDropdown'
PM_SCHEDULES_LIST; // 'pmSchedulesList'
WORK_ORDERS_LIST; // 'workOrdersList'
EMPLOYEES_DROPDOWN; // 'employeesDropdown'
```
### Storage Keys
```javascript
STORAGE_KEY_TOKEN // 'token'
STORAGE_KEY_THEME // 'theme'
STORAGE_KEY_TOKEN; // 'token'
STORAGE_KEY_THEME; // 'theme'
```
---
@ -343,16 +361,19 @@ STORAGE_KEY_THEME // 'theme'
## 🚀 Getting Started
### 1. Run Development Server
```bash
npm start
```
### 2. Build for Production
```bash
npm run build
```
### 3. Deploy to S3
```bash
aws s3 sync build/ s3://shoc-ui-app --acl public-read
```
@ -364,13 +385,16 @@ aws s3 sync build/ s3://shoc-ui-app --acl public-read
### Converting a Page to New Architecture
**Step 1: Use React Query Hook**
```javascript
// Old
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(() => {
setLoading(true);
fetchData().then(setData).finally(() => setLoading(false));
fetchData()
.then(setData)
.finally(() => setLoading(false));
}, []);
// New
@ -378,6 +402,7 @@ const { data, isLoading } = usePMSchedules({ page: 1 });
```
**Step 2: Use Constants**
```javascript
// Old
const [search, setSearch] = useState("");
@ -387,11 +412,12 @@ useEffect(() => {
}, [search]);
// New
import { DEBOUNCE_SEARCH } from '../constants';
import { DEBOUNCE_SEARCH } from "../constants";
const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);
```
**Step 3: Convert CSS to Modules**
```javascript
// 1. Rename: Component.css → Component.module.css
// 2. Update import: import styles from './Component.module.css';
@ -403,14 +429,17 @@ const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);
## ✅ What's Been Refactored
### Pages
- ✅ **LoginPage** - Uses Redux for auth
- ✅ **PM Schedules List** - Uses React Query, constants, CSS Modules
### Components
- ✅ **Topbar** - Uses Redux for user state
- ✅ **DeleteModal** - Reusable component
### Hooks
- ✅ **useAuth** - Redux integration for login/logout
- ✅ **useModal** - Reusable modal state
- ✅ **useDebounce** - Search debouncing
@ -433,6 +462,7 @@ const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);
## 📖 Quick Reference
### File to Check for Examples
- `src/pages/PmSchedule/list/List.js` - Fully refactored list page
- `src/pages/auth/LoginPage.js` - Redux auth + CSS Modules
- `src/hooks/api/usePMSchedules.js` - React Query hook pattern
@ -463,15 +493,19 @@ const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);
## 🛠️ Troubleshooting
### Issue: Constants not found
**Solution:** Make sure you're importing from `'../constants'` (folder) not `'../constants.js'` (old file)
### Issue: CSS not scoped
**Solution:** File must be named `.module.css` and imported as `import styles from`
### Issue: Redux state not updating
**Solution:** Make sure you're using the hooks (`useAuth`, `useSelector`) not direct access
### Issue: React Query not caching
**Solution:** Check that query keys use constants and are consistent
---
@ -479,6 +513,7 @@ const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);
## 🎉 Summary
### What We Built:
- ✅ Modern React architecture
- ✅ Redux Toolkit for global state
- ✅ React Query for server state
@ -487,6 +522,7 @@ const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);
- ✅ Custom hooks for reusable logic
### Benefits:
- 📉 70% less boilerplate code
- 🚀 Automatic caching and refetching
- 🎨 No CSS naming conflicts
@ -495,6 +531,7 @@ const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);
- ⚡ Better performance
### Next Steps:
1. Refactor remaining pages using PM Schedules as template
2. Convert more CSS to CSS Modules
3. Add more API hooks as needed
@ -504,5 +541,4 @@ const debouncedSearch = useDebounce(search, DEBOUNCE_SEARCH);
**Welcome to the modern SeaHaven UI! 🎊**
*Last updated: 2026*
_Last updated: 2026_