Correcao Sidebar Classico e Rail, Correcao Layout, Ajuste de Breakpoint para Tailwind, Ajuste AppTopbar, Ajuste Menu PopOver, Recriado Paleta de Cores, Inserido algumas animações leves, Reajuste Cor items NOVOS da tabela, Drawer Ajuda Corrigido no Logout, Whatsapp, sms, email, recursos extras
This commit is contained in:
159
blueprints/DialogConfirmation-blueprint.md
Normal file
159
blueprints/DialogConfirmation-blueprint.md
Normal file
@@ -0,0 +1,159 @@
|
||||
# DialogConfirmation — Padrão de Componente
|
||||
|
||||
> **Stack**: Vue 3 + PrimeVue 4 + Tailwind CSS
|
||||
|
||||
---
|
||||
|
||||
## Regras gerais
|
||||
|
||||
| Propriedade | Valor obrigatório |
|
||||
|---|---|
|
||||
| `group` | sempre `"headless"` — desacopla o template do trigger |
|
||||
| `ConfirmDialog` | declarado **uma única vez**, no componente pai (página) |
|
||||
| Filhos | disparam via `useConfirm()` com `group: 'headless'` — sem declarar `ConfirmDialog` próprio |
|
||||
| `icon` | passado em `confirm.require({ icon })` — classe PrimeIcons sem o prefixo `pi` (ex: `'pi-trash'`) |
|
||||
| `color` | passado em `confirm.require({ color })` — hex; define o fundo do círculo e a cor do botão Confirmar |
|
||||
| `ConfirmationService` | obrigatório em `main.js` — `app.use(ConfirmationService)` |
|
||||
|
||||
---
|
||||
|
||||
## Arquitetura pai / filho
|
||||
|
||||
```
|
||||
Pai (página)
|
||||
└── <ConfirmDialog group="headless" /> ← único, renderiza aqui
|
||||
├── Filho A (componente qualquer) → confirm.require({ group: 'headless', ... })
|
||||
└── Filho B (Dialog interno) → confirm.require({ group: 'headless', ... })
|
||||
```
|
||||
|
||||
> O `ConfirmDialog` **não** deve ser colocado dentro de um `<Dialog>` filho — isso causaria dois popups simultâneos. Sempre no pai.
|
||||
|
||||
---
|
||||
|
||||
## Setup obrigatório — `main.js`
|
||||
|
||||
```js
|
||||
import ConfirmationService from 'primevue/confirmationservice'
|
||||
|
||||
app.use(ConfirmationService) // sem isso, useConfirm() não funciona
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Template do `ConfirmDialog` (somente no pai)
|
||||
|
||||
```vue
|
||||
<!-- Declarado uma única vez, antes do conteúdo principal -->
|
||||
<ConfirmDialog group="headless">
|
||||
<template #container="{ message, acceptCallback, rejectCallback }">
|
||||
<div class="flex flex-col items-center p-8 bg-surface-0 dark:bg-surface-900 rounded-xl shadow-xl">
|
||||
|
||||
<!-- Círculo central: cor e ícone vindos de message -->
|
||||
<div
|
||||
class="rounded-full inline-flex justify-center items-center h-24 w-24 -mt-20"
|
||||
:style="{ background: message.color || 'var(--p-primary-color)', color: '#fff' }"
|
||||
>
|
||||
<i :class="`pi ${message.icon || 'pi-question'} !text-4xl`"></i>
|
||||
</div>
|
||||
|
||||
<span class="font-bold text-2xl block mb-2 mt-6">{{ message.header }}</span>
|
||||
<p class="mb-0 text-center text-[var(--text-color-secondary)]">{{ message.message }}</p>
|
||||
|
||||
<div class="flex items-center gap-2 mt-6">
|
||||
<!-- Confirmar: cor dinâmica via message.color -->
|
||||
<Button
|
||||
label="Confirmar"
|
||||
class="rounded-full"
|
||||
:style="{
|
||||
background: message.color || 'var(--p-primary-color)',
|
||||
borderColor: message.color || 'var(--p-primary-color)'
|
||||
}"
|
||||
@click="acceptCallback"
|
||||
/>
|
||||
<!-- Cancelar: sempre outlined, neutro -->
|
||||
<Button
|
||||
label="Cancelar"
|
||||
variant="outlined"
|
||||
class="rounded-full"
|
||||
@click="rejectCallback"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
</ConfirmDialog>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Uso nos componentes (pai ou filhos)
|
||||
|
||||
```vue
|
||||
<script setup>
|
||||
import { useConfirm } from 'primevue/useconfirm'
|
||||
const confirm = useConfirm()
|
||||
|
||||
function confirmDelete(item) {
|
||||
confirm.require({
|
||||
group: 'headless',
|
||||
header: 'Excluir item?',
|
||||
message: `"${item.name}" será removido permanentemente. Essa ação não pode ser desfeita.`,
|
||||
icon: 'pi-trash',
|
||||
color: '#ef4444',
|
||||
accept: () => onDelete(item)
|
||||
})
|
||||
}
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Paleta de ícones e cores por ação
|
||||
|
||||
| Ação | `icon` | `color` | Observação |
|
||||
|---|---|---|---|
|
||||
| Excluir / Remover | `pi-trash` | `#ef4444` | Vermelho — ação destrutiva |
|
||||
| Salvar / Confirmar | `pi-save` | `var(--p-primary-color)` | Cor primária do tema |
|
||||
| Editar / Atualizar | `pi-pencil` | `#f97316` | Laranja — mudança de estado |
|
||||
| Aviso / Atenção | `pi-exclamation-triangle` | `#eab308` | Amarelo — ação reversível |
|
||||
| Info / Neutro | `pi-info-circle` | `#3b82f6` | Azul — informativo |
|
||||
|
||||
---
|
||||
|
||||
## Referência completa de `confirm.require`
|
||||
|
||||
```js
|
||||
confirm.require({
|
||||
group: 'headless', // obrigatório — aponta para o ConfirmDialog correto
|
||||
header: 'Título do popup', // linha em negrito
|
||||
message: 'Descrição clara.', // linha secundária
|
||||
icon: 'pi-trash', // sufixo PrimeIcons sem o "pi " inicial
|
||||
color: '#ef4444', // hex — fundo do círculo + cor do botão Confirmar
|
||||
accept: () => { /* ação confirmada */ },
|
||||
reject: () => { /* opcional — ação cancelada */ }
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklist antes de usar
|
||||
|
||||
- [ ] `ConfirmationService` registrado no `main.js`
|
||||
- [ ] `<ConfirmDialog group="headless">` declarado **apenas no pai**, antes do conteúdo
|
||||
- [ ] Filhos usam `useConfirm()` com `group: 'headless'` — sem `ConfirmDialog` próprio
|
||||
- [ ] `icon` passado como sufixo PrimeIcons: `'pi-trash'`, não `'pi pi-trash'`
|
||||
- [ ] `color` em hex para ações com semântica de cor (delete = `#ef4444`)
|
||||
- [ ] `header` curto e direto | `message` com contexto suficiente para o usuário decidir
|
||||
- [ ] `accept` contém a ação real — `reject` é opcional
|
||||
|
||||
---
|
||||
|
||||
## Variações de confirmação
|
||||
|
||||
| Contexto | `header` | `icon` | `color` |
|
||||
|---|---|---|---|
|
||||
| Excluir registro | `'Excluir <entidade>?'` | `pi-trash` | `#ef4444` |
|
||||
| Remover item de lista | `'Remover campo?'` | `pi-trash` | `#ef4444` |
|
||||
| Salvar com impacto | `'Confirmar alterações?'` | `pi-save` | primária |
|
||||
| Atualizar com risco | `'Atualizar <entidade>?'` | `pi-pencil` | `#f97316` |
|
||||
| Ação irreversível genérica | `'Tem certeza?'` | `pi-exclamation-triangle` | `#eab308` |
|
||||
183
blueprints/dialog-blueprint.md
Normal file
183
blueprints/dialog-blueprint.md
Normal file
@@ -0,0 +1,183 @@
|
||||
# Dialog — Padrão de Componente
|
||||
|
||||
> **Stack**: Vue 3 + PrimeVue 4 + Tailwind CSS
|
||||
|
||||
---
|
||||
|
||||
## Regras gerais
|
||||
|
||||
| Propriedade | Valor obrigatório |
|
||||
|---|---|
|
||||
| `modal` | sempre `true` |
|
||||
| `maximizable` | sempre presente — botão nativo do PrimeVue, sem estado manual |
|
||||
| `:draggable` | sempre `false` |
|
||||
| `:closable` | `!saving` — desabilita o X durante operações assíncronas |
|
||||
| `:dismissableMask` | `!saving` — impede fechar clicando fora durante saving |
|
||||
| `pt:mask:class` | `backdrop-blur-xs` |
|
||||
| Largura | `w-[50rem]` (padrão); responsivo via `:breakpoints` |
|
||||
| Breakpoints | `{ '1199px': '90vw', '768px': '94vw' }` |
|
||||
|
||||
---
|
||||
|
||||
## Estrutura obrigatória
|
||||
|
||||
```
|
||||
<Dialog>
|
||||
├── #header ← dot de cor (se aplicável), título/subtítulo, btn Excluir
|
||||
├── Banner ← preview visual (opcional — apenas quando há cor/identidade visual)
|
||||
├── Corpo ← campos do formulário
|
||||
└── #footer ← Cancelar (flat) | Salvar (primary)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Configuração completa do `<Dialog>`
|
||||
|
||||
```vue
|
||||
<Dialog
|
||||
v-model:visible="visible"
|
||||
modal
|
||||
:draggable="false"
|
||||
:closable="!saving"
|
||||
:dismissableMask="!saving"
|
||||
maximizable
|
||||
class="dc-dialog w-[50rem]"
|
||||
:breakpoints="{ '1199px': '90vw', '768px': '94vw' }"
|
||||
:pt="{
|
||||
header: { class: '!p-3 !rounded-t-[12px] border-b border-[var(--surface-border)] shadow-[0_1px_0_0_rgba(255,255,255,0.06)] bg-gray-100' },
|
||||
content: { class: '!p-3' },
|
||||
footer: { class: '!p-0 !rounded-b-[12px] border-t border-[var(--surface-border)] shadow-[0_1px_0_0_rgba(255,255,255,0.06)] bg-gray-100' },
|
||||
pcCloseButton: { root: { class: '!rounded-md hover:!text-red-500' } },
|
||||
pcMaximizeButton: { root: { class: '!rounded-md hover:!text-primary' } },
|
||||
}"
|
||||
pt:mask:class="backdrop-blur-xs"
|
||||
>
|
||||
```
|
||||
|
||||
### Detalhes do `pt`
|
||||
|
||||
| Chave | O que faz |
|
||||
|---|---|
|
||||
| `header` | `!p-3` padding uniforme; `!rounded-t-[12px]` borda top arredondada; `border-b` + `shadow` separador com profundidade; `bg-gray-100` fundo levemente cinza |
|
||||
| `content` | `!p-3` padding interno do corpo |
|
||||
| `footer` | `!p-0` remove padding nativo (controlado pelo wrapper interno); `!rounded-b-[12px]` borda bottom arredondada; `border-t` + `shadow` separador; `bg-gray-100` fundo levemente cinza |
|
||||
| `pcCloseButton` | `!rounded-md` remove o círculo nativo; `hover:!text-red-500` feedback de danger no hover |
|
||||
| `pcMaximizeButton` | `!rounded-md` remove o círculo nativo; `hover:!text-primary` feedback de cor primária no hover |
|
||||
|
||||
> O `!` (important) é necessário porque o PrimeVue injeta estilos inline nos botões e no root do Dialog — sem ele o Tailwind perde a disputa de especificidade.
|
||||
|
||||
---
|
||||
|
||||
## Header — slot `#header`
|
||||
|
||||
```
|
||||
[dot-cor] [título / subtítulo] [btn-excluir] ← Close e Maximize nativos vêm após
|
||||
```
|
||||
|
||||
- O PrimeVue injeta **Maximize** e **Close** automaticamente à direita do slot `#header`.
|
||||
- O botão **Excluir** fica **sempre no header**, nunca no footer.
|
||||
- Excluir desabilitado quando o registro é nativo/padrão: `:disabled="saving || isNativeRecord"`.
|
||||
|
||||
```vue
|
||||
<template #header>
|
||||
<div class="flex w-full items-center justify-between gap-3 px-1">
|
||||
<div class="flex items-center gap-3 min-w-0">
|
||||
<!-- Dot de cor (omitir se não houver cor associada) -->
|
||||
<span
|
||||
class="shrink-0 w-3.5 h-3.5 rounded-full border-2 border-white/30
|
||||
shadow-[0_0_0_3px_rgba(0,0,0,0.08)] transition-colors duration-200"
|
||||
:style="{ backgroundColor: previewBgColor }"
|
||||
/>
|
||||
<div class="min-w-0">
|
||||
<div class="text-base font-semibold truncate">
|
||||
{{ form.name || (mode === 'create' ? 'Novo item' : 'Editar item') }}
|
||||
</div>
|
||||
<div class="text-xs opacity-50">
|
||||
{{ mode === 'create' ? 'Criando novo registro' : 'Editando registro' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-1 shrink-0">
|
||||
<!-- Excluir — visível apenas em edit, desabilitado se nativo -->
|
||||
<Button
|
||||
v-if="mode === 'edit' && canDelete !== undefined"
|
||||
icon="pi pi-trash"
|
||||
severity="danger"
|
||||
text
|
||||
rounded
|
||||
:disabled="saving || isNativeRecord"
|
||||
v-tooltip.top="'Excluir'"
|
||||
@click="emitDelete"
|
||||
/>
|
||||
<!-- Maximize e Close nativos do PrimeVue são injetados aqui automaticamente -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Footer — slot `#footer`
|
||||
|
||||
```vue
|
||||
<template #footer>
|
||||
<div class="flex items-center justify-end gap-2 px-3 py-3">
|
||||
<!-- Cancelar: sempre flat, hover vermelho suave -->
|
||||
<Button
|
||||
label="Cancelar"
|
||||
severity="secondary"
|
||||
text
|
||||
class="rounded-full hover:!text-red-500"
|
||||
:disabled="saving"
|
||||
@click="close"
|
||||
/>
|
||||
<!-- Salvar: sempre primary -->
|
||||
<Button
|
||||
label="Salvar"
|
||||
icon="pi pi-check"
|
||||
class="rounded-full"
|
||||
:loading="saving"
|
||||
:disabled="!canSubmit"
|
||||
@click="submit"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
> **Regra**: Cancelar = `severity="secondary" text` + `hover:!text-red-500`. Salvar = primary (sem severity, usa o padrão do tema). Padding controlado pelo `div` interno (`px-3 py-3`), não pelo `pt.footer`.
|
||||
|
||||
---
|
||||
|
||||
## Maximizar
|
||||
|
||||
Use a prop nativa `maximizable`. O PrimeVue injeta e gerencia o botão automaticamente — sem `ref`, sem `isMaximized`, sem `<Button>` manual.
|
||||
|
||||
```vue
|
||||
<Dialog maximizable ...>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklist antes de publicar um Dialog
|
||||
|
||||
- [ ] `modal`, `:draggable="false"`, `:closable="!saving"`, `:dismissableMask="!saving"` presentes
|
||||
- [ ] `maximizable` na prop (botão nativo, sem estado manual)
|
||||
- [ ] `class="dc-dialog w-[50rem]"` + `:breakpoints="{ '1199px': '90vw', '768px': '94vw' }"`
|
||||
- [ ] `pt` completo: header, content, footer, pcCloseButton, pcMaximizeButton
|
||||
- [ ] Header com `bg-gray-100`, `border-b`, shadow e `!rounded-t-[12px]`
|
||||
- [ ] Footer com `bg-gray-100`, `border-t`, shadow e `!rounded-b-[12px]`
|
||||
- [ ] Botão **Excluir** no header (nunca no footer), desabilitado se nativo
|
||||
- [ ] Cancelar = `text` + `hover:!text-red-500` | Salvar = primary
|
||||
- [ ] Padding do footer via `px-3 py-3` no `div` interno
|
||||
|
||||
---
|
||||
|
||||
## Variações de largura
|
||||
|
||||
| Uso | Classe |
|
||||
|---|---|
|
||||
| Formulário simples | `w-[36rem]` |
|
||||
| Formulário padrão | `w-[50rem]` ← **padrão** |
|
||||
| Formulário complexo | `w-[70rem]` |
|
||||
| Tela cheia | `maximizable` — usuário controla |
|
||||
Reference in New Issue
Block a user