shoc-frontend-new/docs/work-orders/board-search-api.md

210 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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`.
## Contrato (board semanal vs search)
| Superfície | Endpoint | O que volta |
| ------------------------------ | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Week + Day (AAP) | `GET /board?weekStart&weekEnd` | Só WOs com data (ou `ScheduleWeekOnly` + `TargetWeek`) **nessa semana**. `unscheduled: []`. Sem `page`/`pageSize`. |
| Contagens do board | `counts.total` / `counts.returned` | Scheduled da semana (antes/depois do search da barra). Nunca `0` se há rows. |
| Lista flat | `GET /board/search` | Envelope paginado. `page` 0-based. `pageSize` default 100, **max 200**. |
| Fila sem data / sem dispatcher | `GET /board/search` | Filtros (`dispatchers=__unassigned__`, statuses, janela Custom). **Não** entra no `/board`. |
O AAP não tem pin Unassigned na tabela. Dated sem assignee entram no dia. Undated só em Advanced Filters / filtro Dispatcher.
## Quando usar qual endpoint
| Modo FE | Endpoint | Quando |
| --------------------------------------- | -------------------------- | ------------------------------------------------ |
| Barra principal (`advApplied === null`) | `GET /board` | Semana agendada + dispatchers + tipo + 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 `200` |
| `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 |
| `severities` | `severities` | Integers `1`–`5` (Emergency/Reactive) |
| `rescheduled` | `rescheduled` | `true` = reschedule count ≥ 2 |
| `carriedOver` | `carriedOver` | `true` = carried-over count ≥ 2 |
| `addOn` | `addOn` | `true` = `isAddOn` indicator |
| `flagColors` | `flagColors` | Multi `#RRGGBB` (board flag palette) |
| `internalOnly` | `internalOnly` | `true` = WO# starts with `SH` |
| `hasUplift` | `hasUplift` | `true` = WO has standing uplift |
| `upliftStatuses` | `upliftStatuses` | With `hasUplift`; see below |
### `severities`
Multi-select SEV 1–5. Applies to Emergency/Reactive work orders with a transcribed APM severity.
### Indicators (`rescheduled`, `carriedOver`, `addOn`)
Boolean flags matching board badge thresholds (counters ≥ 2 for reschedule/carried-over). `addOn` filters the `isAddOn` indicator (not a WO type).
### `flagColors`
Repeated `#RRGGBB` values from the fixed board flag palette (`WorkOrderFlagColors`).
### `internalOnly`
When `true`, only work orders whose number starts with `SH` (internal placeholder).
### Uplift filters
| FE state | API param | Regra |
| ---------------- | ---------------- | ------------------------------------------------ |
| `hasUplift` | `hasUplift` | `true` = WO has non-cancelled/non-revoked uplift |
| `upliftStatuses` | `upliftStatuses` | Optional refinement when `hasUplift=true` |
Allowed `upliftStatuses`: `pending`, `approved`, `auto_approved`, `rejected` (excludes cancelled/revoked).
Exemplo: pending uplifts only — `hasUplift=true&upliftStatuses=pending`.
## Backend producer (`shoc-backend` `dev`)
These facet keys are handled on `GET /board/search` in `shoc-backend` `origin/dev` ([#45](https://github.com/Sea-Haven-Industries/shoc-backend/pull/45), [#65](https://github.com/Sea-Haven-Industries/shoc-backend/pull/65)). There is no frontend deployment fence.
- `IsAddOn` is owned by migration `20260730150000_WoIsAddOn` (legacy type-7 backfill).
- `20260813193000_SH121_BoardSearchFacets` is a **no-op** so both PRs can land without a duplicate column.
- Pagination matches this contract: `page` is 0-based; `pageSize` default is 100, max is 200.
- `GET /board` returns only scheduled-in-week rows. `unscheduled` is always `[]`. Undated / global Unassigned live on `GET /board/search`.
- `counts.total` / `counts.returned` are scheduled-in-week (before/after the bar search).
- Aveta (`avetaOnly`) remains omitted until `avetaRequired` is confirmed.
### `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`.
## Add-On indicator
See [addon-indicator.md](./addon-indicator.md) (SH-184): backend recalculates
`isAddOn` on schedule move/clear with audit; FE only reflects the API value.
## 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.