# GenericCrudService — Diretrizes de Uso em Campos Dinâmicos

> IMPORTANTE: Não remova o GenericCrudService do `providers` de componentes de campos dinâmicos que carregam dados remotos (selects, radio, lists, etc.). Cada componente deve manter sua própria instância do serviço.

## Por que uma instância por componente?

- Isolamento de estado: `GenericCrudService.configure(resourcePath, endpointKey?)` altera o estado interno (baseApiUrl, resourcePath, endpoints). Dois selects com recursos distintos em uma mesma página precisam de estados independentes.
- Concorrência segura: com instância compartilhada (global), dois selects que configuram recursos diferentes em momentos próximos podem disputar o estado e causar requisições para o recurso “errado”.
- Ciclo de vida previsível: cada select controla quando configurar o recurso e quando recarregar opções, sem efeitos colaterais em outros selects.

## Evitando logs/duplicidades de configuração

- Short‑circuit no serviço: `GenericCrudService.configure` possui guarda para ignorar reconfigurações quando `baseApiUrl`, `resourcePath` e `endpointKey` não mudam.
- Guarda no componente: componentes como `MaterialAsyncSelectComponent` e `MaterialSearchableSelectComponent` mantêm `lastConfiguredPath` e só chamam `configureCrudService(path)` quando o `resourcePath` realmente mudar.
- Autoload controlado: para evitar requisições remotas duplicadas no ciclo de montagem, os componentes aplicam `super.setMetadata()` (base input) e gerenciam o carregamento remoto explicitamente (ex.: apenas quando o select abre/ao digitar), em vez de delegar ao autoload do base para selects.

## Padrões adotados nos componentes

- `MaterialAsyncSelectComponent` e `MaterialSearchableSelectComponent`:
  - Providers incluem `GenericCrudService` local (não remover).
  - Implementam `setSelectMetadata()` sem acionar o autoload do base.
  - Usam guarda `lastConfiguredPath` para evitar reconfiguração redundante.
  - Disparam carregamentos apenas em interações (abrir, digitar, retry), via `filterOptions` (POST /options/filter) e correlatos.
- Outros selects remotos (ex.: `material-select`, `material-radio-group`, `material-selection-list`) também mantêm `GenericCrudService` no `providers` e devem seguir o mesmo padrão quando necessário.

## Sintomas comuns ao remover o provider local

- Requisições com `resourcePath` incorreto quando existem múltiplos selects na mesma view.
- Logs “configure” intercalados para recursos diferentes.
- Comportamentos intermitentes em dev (re-renderizações) por competição de estado do serviço.

## Resumo das boas práticas

- Mantenha `GenericCrudService` no `providers` dos componentes que dependem de dados remotos.
- Adote guarda de reconfiguração (`lastConfiguredPath`) nos componentes.
- Centralize autoload em pontos previsíveis (ao abrir select, ao digitar), evitando autoload do base para selects.
- Confie no short‑circuit do serviço para evitar logs/estado redundante.

---

## Endpoints suportados para selects (Options vs Filter)

- Options (OptionDTO) — recomendados para selects assíncronos
  - `POST /{resource}/options/filter` → `filterOptions(criteria, pageable, opts)`
    - Query params: `page`, `size`, `sort`, `includeIds` (reexibir rótulos já selecionados na primeira página)
    - Corpo: `criteria` (ex.: `{ [optionLabelKey]: termo, ...filterCriteria }`)
    - Cabeçalho: `X-Data-Version` quando `observeVersionHeader` estiver habilitado
    - Retorna: `Page<OptionDTO<ID>>` (e opcionalmente `dataVersion`)
  - `GET /{resource}/options/by-ids` → `getOptionsByIds(ids)`
    - Query params: `ids=1&ids=2` ...
    - Retorna: `OptionDTO<ID>[]`
  - Cursor (quando disponível) → `POST /{resource}/filter/cursor` retornando `OptionDTO` em `CursorPage`

- Filter (entidade)
  - `POST /{resource}/filter` → `filter(criteria, pageable)`
    - Exige mapear no front: `optionLabelKey` e `optionValueKey` do item retornado para `{ label, value }`.

Notas
- Termo de busca: os componentes aplicam o termo em `criteria` usando a chave `optionLabelKey()`.
- `filterCriteria`: sempre compõe o corpo do POST (Options e Filter). Informe chaves conforme o backend espera.
- `OptionDTO`: `{ id: ID, label: string }` — formato esperado pelos selects assíncronos.
- Se o backend não expõe `/options`, prefira `filter` + `optionLabelKey/optionValueKey` em selects não‑assíncronos.

---

## Exemplos de metadata (Options vs Filter)

Exemplo — Options (async-select, backend com `/options`)

```ts
const fields: FieldMetadata[] = [
  {
    name: 'rotaId',
    label: 'Rota',
    controlType: 'async-select',
    resourcePath: 'requests/routes',
    // async-select usa OptionDTO do backend; não exige optionLabelKey/optionValueKey
    loadOn: 'open', // 'init' para carregar já na inicialização se desejado
    searchable: true,
    // filtros adicionais enviados no corpo do POST /options/filter
    filterCriteria: { active: true },
  },
];
```

Exemplo — Filter genérico (select, backend só com `/filter`)

```ts
const fields: FieldMetadata[] = [
  {
    name: 'destinoReqId',
    label: 'Destino',
    controlType: 'select',
    resourcePath: 'requests/destinations',
    // necessárias para mapear entidade → {label,value}
    optionLabelKey: 'nome',
    optionValueKey: 'id',
    searchable: true, // habilita busca local; termo remoto é aplicado via optionLabelKey
    filterCriteria: {},
  },
];
```

Exemplo — Cursor (lista grande; OptionDTO por cursor)

```ts
const fields: FieldMetadata[] = [
  {
    name: 'propriedadeReqId',
    label: 'Propriedade',
    controlType: 'async-select',
    resourcePath: 'requests/destination-properties',
    useCursor: true,   // ativa POST /filter/cursor
    searchable: true,
  },
];
```

---

## Diagrama de fluxo (simplificado)

Options (async-select)

```
Componente (async-select)
   │  configura GenericCrudService(resourcePath)
   ▼
GenericCrudService
   │  POST /{resource}/options/filter  (criteria + pageable + includeIds)
   │  GET  /{resource}/options/by-ids  (ids) [pré-carregar rótulos]
   ▼
Backend (OptionDTO)
   │  { id, label } + paginação/cursor
   ▼
Componente mapeia OptionDTO → opções visíveis
```

Filter genérico (select)

```
Componente (select)
   │  configura GenericCrudService(resourcePath)
   ▼
GenericCrudService
   │  POST /{resource}/filter  (criteria + pageable)
   ▼
Backend (Entidade)
   │  [{ ... entidade ... }]
   ▼
Componente mapeia entidade usando optionLabelKey/optionValueKey → {label,value}
```
