diff --git a/README.md b/README.md index 58beeacc..db722ee4 100644 --- a/README.md +++ b/README.md @@ -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`). diff --git a/docs/ARCHITECTURE_PLAN.md b/docs/ARCHITECTURE_PLAN.md new file mode 100644 index 00000000..27c4651b --- /dev/null +++ b/docs/ARCHITECTURE_PLAN.md @@ -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// +├── 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//` 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`. diff --git a/docs/DESIGN_SYSTEM.md b/docs/DESIGN_SYSTEM.md new file mode 100644 index 00000000..51a2df76 --- /dev/null +++ b/docs/DESIGN_SYSTEM.md @@ -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 +
+
+
+``` + +### Compose classes with `cn()` + +```tsx +import { cn } from "@/lib/utils"; + +
; +``` + +### 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. diff --git a/docs/DESIGN_SYSTEM_MIGRATION.md b/docs/DESIGN_SYSTEM_MIGRATION.md new file mode 100644 index 00000000..3446239e --- /dev/null +++ b/docs/DESIGN_SYSTEM_MIGRATION.md @@ -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 diff --git a/docs/FEATURE_PRIORITIZATION.md b/docs/FEATURE_PRIORITIZATION.md new file mode 100644 index 00000000..32876efd --- /dev/null +++ b/docs/FEATURE_PRIORITIZATION.md @@ -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//`** — 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//` (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` diff --git a/UI_DOCUMENTATION.md b/docs/UI_DOCUMENTATION.md similarity index 90% rename from UI_DOCUMENTATION.md rename to docs/UI_DOCUMENTATION.md index ce310c2f..c7d7b7b2 100644 --- a/UI_DOCUMENTATION.md +++ b/docs/UI_DOCUMENTATION.md @@ -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';
``` **New Way** (CSS Modules): + ```javascript import styles from './Component.module.css';
// 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
Loading...
; if (error) return
Error: {error.message}
; - + return (
setSearch(e.target.value)} />
    - {data.items.map(item =>
  • {item.name}
  • )} + {data.items.map((item) => ( +
  • {item.name}
  • + ))}
); @@ -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 (
- Welcome {user.name}! - + Welcome {user.name}!
); } ``` ### 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() { {deleteModal.isOpen && ( - - Delete {deleteModal.data.name}? - + Delete {deleteModal.data.name}? )}
); @@ -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 ; ``` ### 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_