Files
techxcar/docs/superpowers/specs/2026-06-22-plan3-core-pages-design.md
Luciano Milani 5de37bb512 Inicial
2026-07-02 12:47:55 +01:00

5.8 KiB

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