diff --git a/docs/work-orders/board-search-api.md b/docs/work-orders/board-search-api.md new file mode 100644 index 00000000..fde9438b --- /dev/null +++ b/docs/work-orders/board-search-api.md @@ -0,0 +1,148 @@ +# Work Orders Board & Search API + +Frontend integration contract for weekly board and advanced search filters. + +## Base + +| Item | Valor | +| ------------ | ------------------------------------------- | +| Prefixo | `/api/workorders` (alias: `/api/WorkOrder`) | +| Auth | `Authorization: Bearer {token}` (JWT) | +| Content-Type | Não necessário (GET) | + +Paths no FE: `API_PATHS.workOrder.board`, `boardSearch`, `lookupsDispatchers`. + +## Quando usar qual endpoint + +| Modo FE | Endpoint | Quando | +| --------------------------------------- | -------------------------- | ------------------------------------------------ | +| Barra principal (`advApplied === null`) | `GET /board` | Semana + dispatchers + tipo segment + search | +| Filtros avançados aplicados | `GET /board/search` | Todos os filtros avançados + paginação/ordenação | +| Lookup dispatchers | `GET /lookups/dispatchers` | Popular multi-select de dispatchers | + +**Não misture parâmetros:** `weekStart`/`weekEnd` só no `/board`; `datePreset` só no `/search`. + +## Query builders (FE) + +- [`board-query-params.ts`](../../src/domain/work-orders/utils/board-query-params.ts) — `toBoardQueryParams`, `toBoardSearchQueryParams` +- Arrays na query: repetição simples (`types=2&types=6`), via `toUrlSearchParams` em `work-orders-api.ts` + +## Barra → `GET /board` + +| FE state | API param | Regra | +| --------------- | ---------------------- | ----------------------------------------------------------- | +| `search` | `search` | Enviar só se `length >= 2`; com 1 char, filtrar client-side | +| `weekMonday` | `weekStart` | ISO `YYYY-MM-DD` (segunda) | +| — | `weekEnd` | `weekStart + 4` (sexta) | +| `dispatcherIds` | `dispatchers` | `__unassigned` → `__unassigned__` | +| "My WOs" | `dispatchers={userId}` | Equivalente a `myWorkOrders=true` | +| `typeFilter` | `types` / `overdue` | Tipos reais em `types`; past-due via `overdue=true` (OR) | + +## Advanced → `GET /board/search` + +| FE state | API param | Regra | +| ---------------- | --------------------- | ------------------------------------------- | +| `page` | `page` | **0-based** (primeira página = `0`) | +| `pageSize` | `pageSize` | Default `100`, max `100` | +| `sortBy` | `sortBy` | `scheduledDate` \| `woNumber` \| `dueDate` | +| `sortDir` | `sortDir` | `asc` \| `desc` | +| `search` | `search` | `>= 2` chars | +| `dateRange` | `datePreset` | PascalCase (ver abaixo) | +| `customFrom/To` | `dateFrom` / `dateTo` | Obrigatórios se `datePreset=Custom` | +| `sites` (IDs UI) | `sites` | **Site codes**, não location ID | +| `types` | `types` | Integers de `WorkOrderType` reais | +| overdue (UI) | `overdue` | `true` = past-due (`isPastDue`); ver abaixo | +| `dispatchers` | `dispatchers` | `__unassigned` → `__unassigned__` | +| `statuses` | `statuses` | Integers 1–10 | +| `pmTypes` | `pmTypes` | Strings (labels Problem dropdown) | +| `vendorTechs` | `vendorIds` | Integers (resolvidos por companyName) | +| `docs` | `docStatuses` | No=2, Yes=1, NN=3 | + +### `datePreset` + +| FE key | API value | +| --------------- | ------------- | +| `this-week` | `ThisWeek` | +| `last-week` | `LastWeek` | +| `this-month` | `ThisMonth` | +| `last-3-months` | `Last3Months` | +| `next-week` | `NextWeek` | +| `next-month` | `NextMonth` | +| `custom` | `Custom` | + +### `types` — WorkOrderType + +Somente tipos reais de work order. **Não** use sentinel `99` para overdue. + +| Label FE | API | +| --------- | -------------------------------------------- | +| PM | `2` | +| Emergency | `3` | +| Reactive | `6` | +| Add-On | `7` | +| Other | tipo real do enum (não é filtro de past-due) | + +### `overdue` — past-due + +| FE / UI | API param | Regra | +| ------- | -------------- | ----------------------------------------- | +| Overdue | `overdue=true` | Filtra work orders past-due (`isPastDue`) | + +`types` e `overdue` combinam com **OR**: uma WO entra no resultado se corresponder a algum `types` **ou** estiver overdue (quando `overdue=true`). + +Exemplos: + +- Só PM e Emergency: `types=2&types=3` +- Só past-due: `overdue=true` +- PM **ou** past-due: `types=2&overdue=true` + +### `statuses` — LifecycleStatus + +Incomplete=1, Pending=2, Scheduled=3, En Route=4, On Site=5, In Progress=6, Completed=7, Rescheduled=8, Canceled=9, Pending Quote=10. + +### `docStatuses` + +Pending (`No`)=2, Uploaded (`Yes`)=1, N/N (`NN`)=3. + +## Respostas + +### `/board` + +```json +{ + "weekStart": "2026-07-14", + "weekEnd": "2026-07-18", + "counts": { "total": 42, "returned": 10 }, + "scheduled": [], + "unscheduled": [] +} +``` + +### `/board/search` + +```json +{ + "items": [], + "totalCount": 150, + "page": 0, + "pageSize": 100, + "totalPages": 2, + "hasPrevious": false, + "hasNext": true +} +``` + +## Erros HTTP + +| Situação | Status | +| --------------------------------------------------- | ------ | +| `weekEnd < weekStart` ou janela > 7 dias | 400 | +| `datePreset=Custom` sem datas / `dateTo < dateFrom` | 400 | +| `sortBy` inválido | 400 | +| Sem token / expirado | 401 | + +Body típico: `{ "status": "Error", "message": "..." }` — o FE exibe `message` via `ApiError`. + +## Validação UI + +O side sheet de filtros avançados valida Custom (From/To obrigatórios e `dateTo >= dateFrom`) antes de aplicar, para evitar 400 desnecessário.