# Blueprint — Melissa Table Page
Padrão de página Melissa que apresenta uma **coleção tabular** (intake
requests, médicos, recorrências, compromissos, etc.) com 2 modos de
visualização (lista/grade), filtros laterais coloridos, busca, e
DataTable com paginação + coluna de ação fixa.
Validado em `src/layout/melissa/MelissaCadastrosRecebidos.vue`
(referência canônica). Estende o
[`melissa-page-blueprint.md`](./melissa-page-blueprint.md) — leia aquele
primeiro pra entender a estrutura macro (`.xx-page` / `.xx-body` /
`.xx-side` / `.xx-main`, drawer mobile, header).
---
## 1. Princípio
Página de coleção = **sidebar de filtros + coluna principal com
toolbar + visualização tabular**. O user controla:
- **Busca** (texto livre — nome / email / telefone / etc.)
- **Filtro de status** (mutualmente exclusivo, com botão "Limpar")
- **Modo de visualização** (lista densa via DataTable ou grade de cards)
- **Paginação** (10/20/50/100 por página)
A linha tem 1 ação primária visível (botão pencil) que abre um Dialog
com detalhes + ações secundárias (rejeitar, converter, etc.).
---
## 2. Estrutura do template
Segue a macro do `melissa-page-blueprint.md` (drawer + backdrop + page
+ header + body com aside Teleportada). Sobre essa base, esta blueprint
adiciona um **subheader explicativo** (logo abaixo do header, antes do
body) e a estrutura tabular dentro da `.xx-main`:
```vue
…
Texto descritivo da página em 1-2 frases. Use
palavras-chave em negrito pra destacar as
ações disponíveis (autorize, recuse, converta, etc.).
…sidebar + main…
```
A diferença dentro da `.xx-main`:
```vue
…
```
E na sidebar (`.xx-side`), ao invés de Hoje/Pacientes/Mini-cal, tem:
```vue
```
---
## 3. Estado JS (script setup)
```js
// ── Filtros + busca ──
const busca = ref('');
const statusFilter = ref('');
function toggleStatusFilter(s) {
statusFilter.value = statusFilter.value === s ? '' : s;
}
// ── Computeds derivados ──
const stats = computed(() => {/* contadores por status */});
const filtered = computed(() => {/* aplica busca + statusFilter sobre rows */});
// ── Paginação compartilhada (DataTable + grid) ──
const PAGE_SIZE_OPTIONS = [10, 20, 50, 100];
const rowsXX = ref(10);
const firstXX = ref(0);
function onPage(event) {
firstXX.value = event.first;
rowsXX.value = event.rows;
}
watch([busca, statusFilter], () => { firstXX.value = 0; }); // reset à pg 1
// ── View mode persistido ──
const VIEW_MODE_KEY = 'xx.viewMode.v1';
const viewMode = ref('list');
try {
const saved = localStorage.getItem(VIEW_MODE_KEY);
if (saved === 'list' || saved === 'grid') viewMode.value = saved;
} catch (_) {}
function setViewMode(m) {
if (m !== 'list' && m !== 'grid') return;
viewMode.value = m;
try { localStorage.setItem(VIEW_MODE_KEY, m); } catch (_) {}
}
// ── Slice da grid (DataTable pagina internamente) ──
const pagedItems = computed(() =>
filtered.value.slice(firstXX.value, firstXX.value + rowsXX.value)
);
// ── Row click + ação ──
function onRowClick(event) { if (event?.data) openDetails(event.data); }
function rowStatusClass(data) { return statusClass(data?.status); }
```
---
## 4. DataTable (view Lista) — props canônicas
```vue
…avatar + nome + badge…
…email + tel…
…tempo relativo…
…empty state contextual…
…spinner inline…
```
### Props críticas explicadas
| Prop | Por quê |
|---|---|
| `:loading="loading"` | Overlay nativo do PrimeVue + slot `#loading` custom — substitui skeleton manual. |
| `paginator + :rows + :first + @page` | Paginator embutido controlado; `firstXX` permite resetar à página 1 quando filtros mudam. |
| `paginatorTemplate="RowsPerPageDropdown First… Last…"` | Ordem do exemplo PrimeVue 4: dropdown ANTES dos navegadores; CurrentPageReport no meio. |
| `currentPageReportTemplate="{first}–{last} de {totalRecords}"` | i18n PT-BR. |
| `:rowClass="rowStatusClass"` | Aplica `is-new` / `is-done` / `is-rejected` no `
` → border-left colorido via CSS deep. |
| `selectionMode="single"` | Marca visualmente a row selecionada; `@row-click` abre o dialog. |
| `scrollable + scrollHeight="flex"` | Tabela preenche o flex restante da `.xx-main` e scrolla internamente (vertical). |
| `tableStyle="min-width: 640px"` | Força scroll horizontal em mobile pra ativar a coluna frozen. |
| `dataKey="id"` | Identificação estável de rows pra seleção + reactive updates. |
### Coluna frozen — regras
- **Última ``** do template
- `frozen alignFrozen="right"` — fixa à direita
- `width: 60px, maxWidth: 60px, minWidth: 60px` — todas três pra evitar reflow durante scroll
- **`header=""`** vazio (icon do botão é auto-explicativo; tooltip cobre o resto)
- Botão interno usa **`@click.stop`** — sem isso, o row-click do DataTable também dispararia
---
## 5. View Grade (cards em CSS grid)
Quando `viewMode === 'grid'`, renderiza cards num grid responsivo com
Paginator standalone abaixo (compartilha state com a list view):
```vue