# Design — Busca de cliente type-ahead (AutoComplete, prefixo no nome)

> Data: **2026-07-14**. Branch: `feat/loan-product-required-cosif-accounts`.
> Status: **aprovado no brainstorming**. Área: crédito (`SimulationDialog.vue` etapa 1 + endpoint
> `/cooperative/members`).

## Problema

A busca de cliente na tela de criação de proposta está "muito superficial": só dispara com **3+
caracteres** (`InputText` + botão "Buscar" + lista manual), e o backend faz **substring** (`%termo%`)
em nome/CPF/nº — então "BR" acha "Ga**br**iela"/"So**br**al", e "B" sozinho não busca nada. O dono quer
type-ahead intuitivo: conforme digita o nome, mostrar quem **começa** com o texto, desde o 1º caractere.

## Decisões (dono)
- **Match do nome:** começa com (**prefixo** estrito no nome). "BR" → só nomes que iniciam com BR.
- **UI:** **PrimeVue AutoComplete** (dropdown de sugestões conforme digita; sem botão "Buscar").

## Comportamento
- Campo vira `AutoComplete`: dropdown com clientes cujo **nome começa** com o texto, a partir do **1º
  caractere**. Seleção por clique/teclado.
- Continua "CPF ou Nome": dígitos casam por prefixo em CPF/nº; texto por prefixo no nome (numérico não bate
  em nome e vice-versa → funciona natural).
- Debounce ~300ms (nativo do AutoComplete). Lista ordenada por nome (já é). Mostra até 20; se `total` >
  retornados, hint **"digite mais para afinar"**.
- Ao selecionar → **mesmo card de cliente encontrado de hoje** (bens, renegociação, KYC desatualizado, etc.).
  Limpar → volta a buscar.

## Backend (`/cooperative/members`)
- Novo parâmetro **opt-in `match=prefix`**. Quando presente, o filtro `:search` casa por **prefixo**
  (`termo%`) em vez de substring (`%termo%`) nos campos nome/cpf_cnpj/member_number. **Default inalterado**
  (substring) → não afeta a tela de Clientes nem outros consumidores.
- `members_controller.build_member_filters` repassa `params["match"]`; `Members.list_members` monta o
  pattern com base no `match` (extraído antes do reduce dos filtros, pra estar disponível na cláusula
  `:search`). Escape de `% _ \\` preservado.
- A tela de empréstimo passa `match=prefix` (+ `has_active_account=true`, `unmask=true`, `page_size=20`
  como hoje).

## Frontend (`SimulationDialog.vue`)
- Troca `InputText` + botão + lista manual pelo `AutoComplete`:
  - `v-model` = query; `:suggestions` = `memberResults`; `@complete` → `searchMember(query)`;
    `@item-select` → `selectMember(value.id)`; slot `#option` = nome + CPF + tag PJ.
  - `:minLength="1"`, `:delay="300"`. Remove o `watch` + `setTimeout` manual (o AutoComplete debouncia).
  - `searchMember` passa `match: 'prefix'` e lê `data.total` pro hint.
- Mantém o padrão atual de exibição do card de cliente encontrado + `clearMember`.

## Testes
- **Backend** (`members` context/controller): `match=prefix` → "BR" casa "BRUNO", **não** "Gabriela";
  **sem** `match` → substring atual intacto; resultado ordenado por nome; escape de curinga preservado.
- **Front:** type-check.

## Fora de escopo (YAGNI)
- Normalização de CPF com máscara **nesse** endpoint (limitação pré-existente; a tela de Clientes já tem a
  versão normalizada via `SearchTerm`).
- Índice de nome pra `ilike 'termo%'` (perf ok em ~1.500 clientes — seq scan rápido; follow-up se crescer).

## Arquivos afetados (previsão)
| Arquivo | Mudança |
|---|---|
| `use_cases/cooperative/members.ex` | `list_members` search casa prefixo quando `match=prefix` |
| `controllers/cooperative/members_controller.ex` | `build_member_filters` repassa `match` |
| `SimulationDialog.vue` | `AutoComplete` na etapa 1 (+ `match=prefix`, hint de "afinar") |
| testes de `members` | prefixo vs substring + ordenação + escape |
