176 lines
5.8 KiB
Markdown
176 lines
5.8 KiB
Markdown
# TechXCar — Plan 3: Core Workshop Pages
|
|
|
|
**Date:** 2026-06-22
|
|
**Status:** Approved
|
|
|
|
## Scope
|
|
|
|
Build the 3 missing tenant-facing pages: Clients, Catalog, Work Orders. Backend has all 31 endpoints ready. Frontend needs hooks, pages, modals, and routing wired up.
|
|
|
|
---
|
|
|
|
## Architecture
|
|
|
|
Approach: **hooks-per-domain + pages**. Each domain has a dedicated hook file in `frontend/src/hooks/` that wraps TanStack Query. Pages consume hooks and stay presentational.
|
|
|
|
### New files
|
|
|
|
```
|
|
frontend/src/
|
|
├── hooks/
|
|
│ ├── useClients.ts
|
|
│ ├── useCatalog.ts
|
|
│ └── useWorkOrders.ts
|
|
├── components/
|
|
│ └── ui/
|
|
│ └── dialog.tsx ← shadcn Dialog (shared by all modals)
|
|
└── pages/app/
|
|
├── clients/
|
|
│ ├── ClientsPage.tsx
|
|
│ └── ClientDetailPage.tsx
|
|
├── catalog/
|
|
│ └── CatalogPage.tsx
|
|
└── work-orders/
|
|
├── WorkOrdersPage.tsx
|
|
└── WorkOrderDetailPage.tsx
|
|
```
|
|
|
|
### New routes (added to `App.tsx`)
|
|
|
|
```
|
|
/app/clients → ClientsPage
|
|
/app/clients/:id → ClientDetailPage
|
|
/app/catalog → CatalogPage
|
|
/app/work-orders → WorkOrdersPage
|
|
/app/work-orders/:id → WorkOrderDetailPage
|
|
```
|
|
|
|
---
|
|
|
|
## Domain: Clients
|
|
|
|
### `useClients.ts`
|
|
|
|
| Hook | Method | Endpoint |
|
|
|---|---|---|
|
|
| `useClients()` | GET | `/clients` |
|
|
| `useClient(id)` | GET | `/clients/:id` |
|
|
| `useCreateClient()` | POST | `/clients` |
|
|
| `useUpdateClient()` | PUT | `/clients/:id` |
|
|
| `useDeleteClient()` | DELETE | `/clients/:id` |
|
|
| `useClientVehicles(clientId)` | GET | `/clients/:id/vehicles` |
|
|
| `useCreateVehicle(clientId)` | POST | `/clients/:id/vehicles` |
|
|
| `useUpdateVehicle()` | PUT | `/vehicles/:id` |
|
|
|
|
All mutations call `queryClient.invalidateQueries` on success.
|
|
|
|
### `ClientsPage`
|
|
|
|
- Table columns: Nome, NIF, Telefone, Email, Data criação
|
|
- "Novo Cliente" button → modal (create)
|
|
- Edit icon per row → same modal prefilled
|
|
- Modal fields: nome (required), NIF, telefone, email, morada, notas
|
|
|
|
### `ClientDetailPage`
|
|
|
|
- Header: client name + fields (NIF, phone, email, address, notes) + Edit button
|
|
- "Veículos" section below: table with matrícula, marca, modelo, ano
|
|
- "Adicionar Veículo" → modal: matrícula (required), marca (required), modelo (required), ano, VIN, quilómetros, notas
|
|
- Edit icon per vehicle row → same modal prefilled
|
|
- Back link → `/app/clients`
|
|
|
|
---
|
|
|
|
## Domain: Catalog
|
|
|
|
### `useCatalog.ts`
|
|
|
|
| Hook | Method | Endpoint |
|
|
|---|---|---|
|
|
| `useCatalog()` | GET | `/catalog` |
|
|
| `useCreateCatalogItem()` | POST | `/catalog` |
|
|
| `useUpdateCatalogItem()` | PUT | `/catalog/:id` |
|
|
| `useDeleteCatalogItem()` | DELETE | `/catalog/:id` |
|
|
|
|
### `CatalogPage`
|
|
|
|
- Table columns: Código, Nome, Categoria, Unidade, Preço Base, Estado (badge)
|
|
- "Novo Item" button → modal (create)
|
|
- Edit icon per row → modal prefilled
|
|
- Delete: button toggles to "Confirmar?" before calling DELETE
|
|
- Modal fields: código, nome (required), categoria, unidade (select: un/hora/litro/kg), preço base (number), activo (checkbox)
|
|
|
|
---
|
|
|
|
## Domain: Work Orders
|
|
|
|
### `useWorkOrders.ts`
|
|
|
|
| Hook | Method | Endpoint |
|
|
|---|---|---|
|
|
| `useWorkOrders(status?)` | GET | `/work-orders?status=` |
|
|
| `useWorkOrder(id)` | GET | `/work-orders/:id` |
|
|
| `useCreateWorkOrder()` | POST | `/work-orders` |
|
|
| `useTransitionWorkOrder()` | POST | `/work-orders/:id/transition` |
|
|
| `useAddWOItem()` | POST | `/work-orders/:id/items` |
|
|
| `useRemoveWOItem()` | DELETE | `/work-orders/:id/items/:itemId` |
|
|
| `useAddStaffHours()` | POST | `/work-orders/:id/staff-hours` |
|
|
| `useRemoveStaffHours()` | DELETE | `/work-orders/:id/staff-hours/:shId` |
|
|
|
|
### `WorkOrdersPage`
|
|
|
|
- Table columns: Nº OT, Cliente, Veículo, Estado (badge colorido), Data
|
|
- Status filter: tabs (Todas / Abertas / Em Progresso / Concluídas / Faturadas / Canceladas)
|
|
- "Nova OT" → modal: cliente (searchable dropdown from `/clients`), veículo (filtered by client), notas internas, notas cliente
|
|
- Click row → navigate to `/app/work-orders/:id`
|
|
|
|
### `WorkOrderDetailPage`
|
|
|
|
Two-column layout:
|
|
|
|
**Left column — info + transitions:**
|
|
- OT number, status badge, client name, vehicle plate + brand/model
|
|
- Internal notes, client notes (editable inline via PUT)
|
|
- State stepper: `Aberta → Em Progresso → Concluída → Faturada`
|
|
- Transition buttons: advance to next state, or "Cancelar OT" (any state except invoiced)
|
|
- Back link → `/app/work-orders`
|
|
|
|
**Right column — items + hours:**
|
|
- "Peças / Serviços" table: descrição, qty, preço unitário, desconto %, total
|
|
- "Adicionar Item" → modal: select catalog item (searchable), qty, unit price (prefilled from catalog), discount %
|
|
- Remove icon per row (DELETE)
|
|
- "Horas de Técnico" table: técnico (staff_id), horas, custo/hora, total
|
|
- "Adicionar Horas" → modal: staff_id (text for now — staff module in Plan 4), horas, custo/hora
|
|
- Remove icon per row (DELETE)
|
|
- Totals row at bottom: subtotal peças + subtotal horas + total geral
|
|
|
|
### State badge colours
|
|
|
|
| Status | Colour |
|
|
|---|---|
|
|
| open | slate |
|
|
| in_progress | blue |
|
|
| completed | green |
|
|
| invoiced | purple |
|
|
| cancelled | red |
|
|
|
|
---
|
|
|
|
## Shared components
|
|
|
|
- **`dialog.tsx`** — shadcn Dialog, used by all create/edit modals. Install via shadcn CLI or copy pattern from existing ui components.
|
|
- All pages follow existing dark slate theme: `bg-slate-950` root, `bg-slate-900` panels, `border-slate-700/800`, white text.
|
|
- Loading states: `<p className="text-slate-400">A carregar...</p>`
|
|
- Empty states: `<p className="text-slate-500 text-sm">Nenhum registo.</p>`
|
|
- Error states: show `ApiError.message` in a red banner.
|
|
|
|
---
|
|
|
|
## Out of scope (Plan 4+)
|
|
|
|
- Staff management (staff_id is free text for now)
|
|
- PDF invoice generation
|
|
- Notifications (Telegram / email)
|
|
- Dashboard statistics
|
|
- Billing / invoicing module
|