diff --git a/blueprints/melissa-table-page-blueprint.md b/blueprints/melissa-table-page-blueprint.md new file mode 100644 index 0000000..1ea606a --- /dev/null +++ b/blueprints/melissa-table-page-blueprint.md @@ -0,0 +1,812 @@ +# 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 + + + + + + + + + + + + + + + + + + + +``` + +### 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 +
+
+
+
+
+
+ +
+ + +
+
+
+
+
+
+
+ +
+``` + +### Por que `
` em vez de `