Melissa: hub Configuracoes + Embed + 9 Pages novas + dialog blueprint dark
Sprints 04-29 + 04-30 acumuladas. - MelissaConfiguracoes: hub 2-col com 6 grupos (Layout/Conta/Agenda/ Financeiro/WhatsApp/Sistema), tudo embedado via MelissaEmbed. - MelissaEmbed: wrapper generico que injeta layout-variant=melissa e remove cromos pra reaproveitar Pages tradicionais. - 9 Melissa Pages novas: CadastrosRecebidos, Compromissos, Configuracoes, Conversas, Embed, Grupos, Medicos, Recorrencias, Tags. - Dialog blueprint atualizado: bg-gray-100 (hardcoded light) -> bg-[var(--surface-ground)] (tema-aware). 22 dialogs migrados em 9 arquivos. Anti-pattern documentado. - PatientsCadastroPage: bug fix dropdown Grupo (optionLabel nome->name), toggle vertical/abas com persist localStorage, sticky margin-top. - Surface picker no popover do MelissaLayout (8 swatches). - useTopbarPlanMenu, useMelissaWhatsapp, useMelissaPacientesAside novos. - Migration: status agenda remarcado/confirmado. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -21,6 +21,7 @@ import { supabase } from '@/lib/supabase/client';
|
||||
import { useTenantStore } from '@/stores/tenantStore';
|
||||
import { useToast } from 'primevue/usetoast';
|
||||
import { useFeriados } from '@/composables/useFeriados';
|
||||
import { useLayout } from '@/layout/composables/layout';
|
||||
import DatePicker from 'primevue/datepicker';
|
||||
|
||||
defineOptions({ inheritAttrs: false });
|
||||
@@ -37,6 +38,14 @@ const emit = defineEmits(['bloqueado']);
|
||||
const router = useRouter();
|
||||
const tenantStore = useTenantStore();
|
||||
const toast = useToast();
|
||||
const { layoutConfig } = useLayout();
|
||||
|
||||
// Quando o layout ativo é Melissa, "Ver todos os feriados" leva pra rota
|
||||
// interna /melissa/bloqueios (abre MelissaConfiguracoes na seção embed
|
||||
// de Bloqueios). Caso contrário usa a rota tradicional de configurações.
|
||||
const verTodosFeriadosRoute = computed(() =>
|
||||
layoutConfig.variant === 'melissa' ? '/melissa/bloqueios' : '/configuracoes/bloqueios'
|
||||
);
|
||||
|
||||
const { nacionais, municipais, todos, loading, load, criar, remover, isDuplicata, doMes } = useFeriados();
|
||||
|
||||
@@ -236,7 +245,7 @@ function fmtDate(iso) {
|
||||
<i class="pi pi-star text-amber-500 text-sm" />
|
||||
<span class="font-semibold text-sm">Próximos feriados</span>
|
||||
</div>
|
||||
<span class="text-xs text-[var(--text-color-secondary)]">{{ nomeMes }}</span>
|
||||
<span class="pfc-month-badge">{{ nomeMes }}</span>
|
||||
</div>
|
||||
|
||||
<!-- Lista -->
|
||||
@@ -282,12 +291,12 @@ function fmtDate(iso) {
|
||||
<!-- Confirmação inline (expande abaixo do item) -->
|
||||
<Transition name="pfc-expand">
|
||||
<div v-if="confirmandoIso === f.data" class="pfc-confirm">
|
||||
<i class="pi pi-exclamation-triangle pfc-confirm__icon" />
|
||||
<div class="flex-1 min-w-0">
|
||||
<p class="text-xs font-semibold mb-0.5">Bloquear {{ f.nome }}?</p>
|
||||
<p class="text-xs opacity-70 leading-snug">O dia inteiro ficará indisponível. Sessões existentes serão marcadas para reagendamento.</p>
|
||||
</div>
|
||||
<div class="flex gap-1.5 shrink-0">
|
||||
<p class="text-xs font-semibold mb-0.5">
|
||||
<i class="pi pi-exclamation-triangle pfc-confirm__icon" />
|
||||
Bloquear {{ f.nome }}?
|
||||
</p>
|
||||
<p class="text-xs opacity-70 leading-snug">O dia inteiro ficará indisponível. Sessões existentes serão marcadas para reagendamento.</p>
|
||||
<div class="pfc-confirm__actions flex gap-1.5">
|
||||
<Button label="Não" size="small" severity="secondary" outlined class="rounded-full h-7 text-xs px-3" @click="cancelarConfirmacao" />
|
||||
<Button label="Bloquear" size="small" severity="danger" icon="pi pi-lock" class="rounded-full h-7 text-xs px-3" @click="confirmarBloqueio(f)" />
|
||||
</div>
|
||||
@@ -300,7 +309,7 @@ function fmtDate(iso) {
|
||||
<!-- Ações -->
|
||||
<div class="flex flex-col gap-1.5 px-4 pb-4">
|
||||
<Button icon="pi pi-plus" label="Cadastrar feriado municipal" severity="secondary" outlined size="small" class="w-full rounded-full" @click="abrirDialog" />
|
||||
<Button icon="pi pi-list" label="Ver todos os feriados" text size="small" class="w-full rounded-full" @click="router.push('/configuracoes/bloqueios')" />
|
||||
<Button icon="pi pi-list" label="Ver todos os feriados" text size="small" class="w-full rounded-full" @click="router.push(verTodosFeriadosRoute)" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -370,20 +379,38 @@ function fmtDate(iso) {
|
||||
|
||||
/* ── Confirmação inline ───────────────────────────────────── */
|
||||
.pfc-confirm {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.625rem;
|
||||
/* sem display:flex — texto flui em bloco; botões ganham margin-top
|
||||
pra distanciar do parágrafo. */
|
||||
padding: 0.625rem 0.75rem;
|
||||
border-radius: 0.875rem;
|
||||
background: color-mix(in srgb, var(--red-400, #f87171) 10%, var(--surface-card));
|
||||
border: 1px solid color-mix(in srgb, var(--red-400, #f87171) 30%, transparent);
|
||||
margin-left: 2.75rem; /* alinha com o nome, após a data */
|
||||
}
|
||||
.pfc-confirm__icon {
|
||||
color: var(--red-500, #ef4444);
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
margin-right: 0.375rem;
|
||||
font-size: 0.8rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
/* Espaço entre os botões "Não/Bloquear" e o texto acima. */
|
||||
.pfc-confirm__actions {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* ── Mês atual no header (badge primary) ─────────────────── */
|
||||
.pfc-month-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 2px 10px;
|
||||
border-radius: 999px;
|
||||
background: var(--p-primary-color);
|
||||
color: var(--p-primary-contrast-color, white);
|
||||
border: 1px solid var(--p-primary-color);
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.02em;
|
||||
text-transform: capitalize;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
/* ── Transição expand ─────────────────────────────────────── */
|
||||
|
||||
@@ -310,6 +310,16 @@ const navPopover = ref(null)
|
||||
const isCompact = ref(false)
|
||||
let mql = null, mqlCb = null
|
||||
|
||||
// View mode: 'vertical' (Accordion) | 'horizontal' (Tabs)
|
||||
const VIEW_MODE_KEY = 'pcd.viewMode.v1'
|
||||
const viewMode = ref('vertical')
|
||||
try {
|
||||
const saved = localStorage.getItem(VIEW_MODE_KEY)
|
||||
if (saved === 'vertical' || saved === 'horizontal') viewMode.value = saved
|
||||
} catch (_) {}
|
||||
watch(viewMode, (v) => { try { localStorage.setItem(VIEW_MODE_KEY, v) } catch (_) {} })
|
||||
function setViewMode (m) { if (m === 'vertical' || m === 'horizontal') viewMode.value = m }
|
||||
|
||||
function syncCompact () { isCompact.value = !!mql?.matches }
|
||||
function toggleNav (e) { navPopover.value?.toggle(e) }
|
||||
function selectNav (s) { openPanel(Number(s.value)); navPopover.value?.hide() }
|
||||
@@ -563,6 +573,19 @@ async function onCepBlur () {
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const deleting = ref(false)
|
||||
// `submitted` é true depois da primeira tentativa de salvar — usado pra
|
||||
// mostrar borda vermelha + msg "Campo obrigatório" embaixo dos inputs
|
||||
// sem incomodar o usuário antes da primeira interação.
|
||||
const submitted = ref(false)
|
||||
// Counts dos editores polimórficos (telefones/emails) — atualizados via
|
||||
// @change. Telefone e email são obrigatórios: pelo menos 1 cada.
|
||||
const phonesCount = ref(0)
|
||||
const emailsCount = ref(0)
|
||||
// Refs pros editores — usados pra chamar `flushPending` depois que o
|
||||
// paciente é criado (telefones/emails inseridos antes do save ficam
|
||||
// em modo pendente até a entidade existir no DB).
|
||||
const phonesEditorRef = ref(null)
|
||||
const emailsEditorRef = ref(null)
|
||||
|
||||
async function fetchAll () {
|
||||
loading.value = true
|
||||
@@ -600,6 +623,10 @@ watch(patientId, fetchAll, { immediate:true })
|
||||
// Submit
|
||||
// ─────────────────────────────────────────────────────────
|
||||
async function onSubmit () {
|
||||
// Marca pra que :invalid + mensagens de erro fiquem visíveis nos inputs
|
||||
// exigidos. Reseta no sucesso (logo abaixo) ou na próxima edição válida
|
||||
// (não reseta automaticamente — só atrapalharia o feedback visual).
|
||||
submitted.value = true
|
||||
saving.value = true
|
||||
try {
|
||||
const ownerId = await getOwnerId()
|
||||
@@ -609,6 +636,16 @@ async function onSubmit () {
|
||||
toast.add({ severity:'warn', summary:'Nome obrigatório', detail:'Preencha o nome completo.', life:3500 })
|
||||
await openPanel(0); return
|
||||
}
|
||||
// Telefone e email são obrigatórios: pelo menos 1 cada. Toast aponta
|
||||
// pro campo faltando + abre a seção Identidade (onde os editores ficam).
|
||||
if (phonesCount.value === 0) {
|
||||
toast.add({ severity:'warn', summary:'Telefone obrigatório', detail:'Adicione pelo menos um telefone.', life:3500 })
|
||||
await openPanel(0); return
|
||||
}
|
||||
if (emailsCount.value === 0) {
|
||||
toast.add({ severity:'warn', summary:'E-mail obrigatório', detail:'Adicione pelo menos um e-mail.', life:3500 })
|
||||
await openPanel(0); return
|
||||
}
|
||||
const payload = sanitizePayload(form.value, ownerId)
|
||||
payload.tenant_id = tenantId; payload.responsible_member_id = memberId
|
||||
if (isEdit.value) {
|
||||
@@ -618,15 +655,23 @@ async function onSubmit () {
|
||||
await replacePatientTags(patientId.value, tagIdsSelecionadas.value)
|
||||
await saveContatosSuporte(patientId.value, tenantId, ownerId)
|
||||
toast.add({ severity:'success', summary:'Salvo', detail:'Paciente atualizado.', life:2500 })
|
||||
submitted.value = false
|
||||
if (props.dialogMode) { emit('created', { id:patientId.value }); return }
|
||||
return
|
||||
}
|
||||
const created = await createPatient(payload)
|
||||
// Telefones/emails podem ter sido adicionados ANTES do paciente existir
|
||||
// (modo pendente — id 'pending_*' em memória). Agora que temos `created.id`,
|
||||
// gravamos tudo em lote no DB. Roda antes de avatar/grupos/tags pra que
|
||||
// qualquer falha aqui aborte o resto do fluxo.
|
||||
await phonesEditorRef.value?.flushPending('patient', created.id)
|
||||
await emailsEditorRef.value?.flushPending('patient', created.id)
|
||||
await maybeUploadAvatar(ownerId, created.id)
|
||||
await replacePatientGroups(created.id, grupoIdSelecionado.value)
|
||||
await replacePatientTags(created.id, tagIdsSelecionadas.value)
|
||||
await saveContatosSuporte(created.id, tenantId, ownerId)
|
||||
toast.add({ severity:'success', summary:'Salvo', detail:'Paciente cadastrado.', life:2500 })
|
||||
submitted.value = false
|
||||
if (props.dialogMode) { emit('created', created); return }
|
||||
form.value=resetForm(); grupoIdSelecionado.value=null; tagIdsSelecionadas.value=[]
|
||||
contatosSuporte.value=[]; avatarFile.value=null; revokePreview(); avatarPreviewUrl.value=''
|
||||
@@ -1014,7 +1059,7 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
<i class="pi pi-spin pi-spinner text-xl" /> Carregando…
|
||||
</div>
|
||||
|
||||
<div v-else class="grid grid-cols-1 gap-3 xl:grid-cols-[220px_1fr] max-w-[1040px] mx-auto">
|
||||
<div v-else class="grid grid-cols-1 gap-3 xl:grid-cols-[220px_1fr] xl:items-start max-w-[1040px] mx-auto">
|
||||
|
||||
<!-- ── SIDEBAR ────────────────────────────────────── -->
|
||||
<aside
|
||||
@@ -1044,8 +1089,32 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nav desktop (≥ xl) -->
|
||||
<div v-if="!isCompact" class="flex flex-col gap-0.5">
|
||||
<!-- Toggle layout vertical/horizontal -->
|
||||
<div class="flex items-center gap-1 mb-3 p-0.5 rounded-lg bg-[var(--surface-ground)] border border-[var(--surface-border)]">
|
||||
<button
|
||||
type="button"
|
||||
class="flex-1 flex items-center justify-center gap-1.5 px-2 py-1 rounded-md text-[0.68rem] font-medium transition-all duration-150"
|
||||
:class="viewMode === 'vertical' ? 'bg-[var(--surface-card)] text-[var(--text-color)] shadow-sm' : 'text-[var(--text-color-secondary)] hover:text-[var(--text-color)]'"
|
||||
title="Layout vertical (acordeão)"
|
||||
@click="setViewMode('vertical')"
|
||||
>
|
||||
<i class="pi pi-bars text-[0.68rem]" />
|
||||
<span>Vertical</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="flex-1 flex items-center justify-center gap-1.5 px-2 py-1 rounded-md text-[0.68rem] font-medium transition-all duration-150"
|
||||
:class="viewMode === 'horizontal' ? 'bg-[var(--surface-card)] text-[var(--text-color)] shadow-sm' : 'text-[var(--text-color-secondary)] hover:text-[var(--text-color)]'"
|
||||
title="Layout horizontal (abas)"
|
||||
@click="setViewMode('horizontal')"
|
||||
>
|
||||
<i class="pi pi-th-large text-[0.68rem] rotate-90" />
|
||||
<span>Abas</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Nav desktop (≥ xl) — só em vertical (em horizontal as tabs ficam acima do form) -->
|
||||
<div v-if="!isCompact && viewMode === 'vertical'" class="flex flex-col gap-0.5">
|
||||
<div class="text-[0.62rem] font-bold uppercase tracking-widest text-[var(--text-color-secondary)] opacity-40 px-2 mb-1">Seções</div>
|
||||
<button
|
||||
v-for="s in sections" :key="s.value" type="button"
|
||||
@@ -1080,8 +1149,8 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Nav compacto (< xl) -->
|
||||
<div v-if="isCompact">
|
||||
<!-- Nav compacto (< xl) — só em vertical -->
|
||||
<div v-if="isCompact && viewMode === 'vertical'">
|
||||
<Button
|
||||
type="button" class="w-full !rounded-full"
|
||||
icon="pi pi-list" iconPos="right"
|
||||
@@ -1155,7 +1224,39 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</aside>
|
||||
|
||||
<!-- ── MAIN ───────────────────────────────────────── -->
|
||||
<main class="rounded-xl border border-[var(--surface-border)] bg-[var(--surface-card)] overflow-hidden shadow-sm">
|
||||
<main
|
||||
class="rounded-xl border border-[var(--surface-border)] bg-[var(--surface-card)] overflow-hidden shadow-sm"
|
||||
:class="{ 'pcd-horizontal': viewMode === 'horizontal' }"
|
||||
>
|
||||
<!-- Tab list (só em horizontal) -->
|
||||
<div
|
||||
v-if="viewMode === 'horizontal'"
|
||||
class="flex gap-0.5 overflow-x-auto px-2 pt-2 border-b border-[var(--surface-border)] bg-[var(--surface-ground)]/40"
|
||||
role="tablist"
|
||||
>
|
||||
<button
|
||||
v-for="s in sections" :key="s.value"
|
||||
type="button" role="tab"
|
||||
:aria-selected="activeValue === s.value"
|
||||
class="flex items-center gap-1.5 px-3 py-2 rounded-t-lg text-[0.78rem] font-medium border-b-2 transition-all duration-150 shrink-0 whitespace-nowrap"
|
||||
:class="activeValue === s.value
|
||||
? `${pal[s.accent].activeBtn} !rounded-b-none`
|
||||
: 'text-[var(--text-color-secondary)] border-transparent hover:bg-[var(--surface-card)]/60 hover:text-[var(--text-color)]'"
|
||||
@click="activeValue = s.value"
|
||||
>
|
||||
<span class="flex items-center justify-center w-5 h-5 rounded-md text-[0.62rem] shrink-0" :class="pal[s.accent].iconBox">
|
||||
<i :class="s.icon"/>
|
||||
</span>
|
||||
<span>{{ s.label }}</span>
|
||||
<i v-if="p(s.value).filled === p(s.value).total"
|
||||
class="pi pi-check-circle text-emerald-500 text-[0.7rem] shrink-0" />
|
||||
<span v-else-if="p(s.value).filled > 0"
|
||||
class="text-[0.6rem] text-amber-600 font-bold shrink-0">
|
||||
{{ p(s.value).filled }}/{{ p(s.value).total }}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<Accordion :multiple="false" v-model:value="activeValue">
|
||||
|
||||
<!-- ╔═══════════════════════════════════════════╗
|
||||
@@ -1177,22 +1278,37 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</div>
|
||||
</AccordionHeader>
|
||||
<AccordionContent>
|
||||
<div class="p-4">
|
||||
<div class="p-5">
|
||||
|
||||
<!-- Nome & identidade -->
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<span class="text-[0.63rem] font-bold uppercase tracking-widest" :class="pal.indigo.divTxt">Nome & identidade</span>
|
||||
<div class="flex items-center gap-2 mb-5">
|
||||
<span class="text-[0.7rem] font-bold uppercase tracking-widest" :class="pal.indigo.divTxt">Nome & identidade</span>
|
||||
<div class="flex-1 h-px" :class="pal.indigo.divLine"/>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-3.5 xl:grid-cols-2 mb-6">
|
||||
<div class="grid grid-cols-1 gap-6 xl:grid-cols-2 mb-7">
|
||||
|
||||
<!-- Nome completo — full width -->
|
||||
<div class="xl:col-span-2">
|
||||
<FloatLabel variant="on">
|
||||
<IconField><InputIcon class="pi pi-user"/><InputText id="f_nome" v-model="form.nome_completo" class="w-full" variant="filled"/></IconField>
|
||||
<IconField>
|
||||
<InputIcon class="pi pi-user"/>
|
||||
<InputText
|
||||
id="f_nome"
|
||||
v-model="form.nome_completo"
|
||||
class="w-full"
|
||||
variant="filled"
|
||||
:invalid="submitted && !String(form.nome_completo || '').trim()"
|
||||
/>
|
||||
</IconField>
|
||||
<label for="f_nome">Nome completo *</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.indigo.hint">Exibido no header do perfil do paciente.</div>
|
||||
<small
|
||||
v-if="submitted && !String(form.nome_completo || '').trim()"
|
||||
class="mt-2 text-[0.85rem] text-red-500 flex items-center gap-1.5"
|
||||
>
|
||||
<i class="pi pi-exclamation-circle text-[0.78rem]"/>
|
||||
<span>Campo obrigatório.</span>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<!-- Nome social -->
|
||||
@@ -1201,7 +1317,6 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
<InputText id="f_nome_social" v-model="form.nome_social" class="w-full" variant="filled"/>
|
||||
<label for="f_nome_social">Nome social</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.indigo.hint">Card "Dados pessoais" → como prefere ser chamado(a).</div>
|
||||
</div>
|
||||
|
||||
<!-- Pronomes -->
|
||||
@@ -1210,16 +1325,18 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
<Select id="f_pronomes" v-model="form.pronomes" :options="pronounsOpts" optionLabel="label" optionValue="value" class="w-full" variant="filled"/>
|
||||
<label for="f_pronomes">Pronomes</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.indigo.hint">Header: <em>"32 anos · <strong>ela/dela</strong> · São Carlos, SP"</em></div>
|
||||
</div>
|
||||
|
||||
<!-- Data de nascimento -->
|
||||
<!-- Data de nascimento — InputGroup com idade calculada como addon à direita -->
|
||||
<div>
|
||||
<FloatLabel variant="on">
|
||||
<IconField><InputIcon class="pi pi-calendar"/><InputMask id="f_nasc" v-model="form.data_nascimento" mask="99-99-9999" :unmask="false" class="w-full" variant="filled"/></IconField>
|
||||
<InputGroup>
|
||||
<InputGroupAddon><i class="pi pi-calendar"/></InputGroupAddon>
|
||||
<InputMask id="f_nasc" v-model="form.data_nascimento" mask="99-99-9999" :unmask="false" variant="filled"/>
|
||||
<InputGroupAddon v-if="ageLabel !== '—'" class="font-semibold text-[var(--primary-color)]">{{ ageLabel }}</InputGroupAddon>
|
||||
</InputGroup>
|
||||
<label for="f_nasc">Data de nascimento</label>
|
||||
</FloatLabel>
|
||||
<div v-if="ageLabel!=='—'" class="mt-1 text-[0.63rem] text-indigo-600 font-semibold"><i class="pi pi-info-circle mr-1"/>{{ ageLabel }}</div>
|
||||
</div>
|
||||
|
||||
<!-- Gênero -->
|
||||
@@ -1244,7 +1361,6 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
<IconField><InputIcon class="pi pi-id-card"/><InputMask id="f_cpf" v-model="form.cpf" mask="999.999.999-99" :unmask="false" class="w-full" variant="filled"/></IconField>
|
||||
<label for="f_cpf">CPF</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.indigo.hint">Exibido mascarado: <em>••••456••••90</em></div>
|
||||
</div>
|
||||
|
||||
<!-- RG -->
|
||||
@@ -1261,7 +1377,6 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
<Select id="f_etnia" v-model="form.etnia" :options="etniaOpts" optionLabel="label" optionValue="value" class="w-full" variant="filled"/>
|
||||
<label for="f_etnia">Etnia / raça</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.indigo.hint">Card "Dados pessoais" → linha "Etnia".</div>
|
||||
</div>
|
||||
|
||||
<!-- Naturalidade -->
|
||||
@@ -1278,7 +1393,6 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
<IconField><InputIcon class="pi pi-briefcase"/><InputText id="f_prof" v-model="form.profissao" class="w-full" variant="filled"/></IconField>
|
||||
<label for="f_prof">Profissão</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.indigo.hint">Card "Dados pessoais" → "Desenvolvedora".</div>
|
||||
</div>
|
||||
|
||||
<!-- Escolaridade -->
|
||||
@@ -1287,41 +1401,91 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
<Select id="f_esc" v-model="form.escolaridade" :options="escolaridadeOpts" optionLabel="label" optionValue="value" class="w-full" variant="filled"/>
|
||||
<label for="f_esc">Escolaridade</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.indigo.hint">Card "Dados pessoais" → "Superior completo".</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Contato — alimenta card "Contato" -->
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<span class="text-[0.63rem] font-bold uppercase tracking-widest" :class="pal.indigo.divTxt">Contato</span>
|
||||
<div class="flex items-center gap-2 mb-5">
|
||||
<span class="text-[0.7rem] font-bold uppercase tracking-widest" :class="pal.indigo.divTxt">Contato</span>
|
||||
<div class="flex-1 h-px" :class="pal.indigo.divLine"/>
|
||||
<span class="text-[0.6rem]" :class="pal.indigo.hint">Card "Contato" no detalhe</span>
|
||||
</div>
|
||||
<!-- Telefones (polimórfico — tipo/número/principal/vinculado) -->
|
||||
<div class="col-span-full">
|
||||
<div class="text-xs font-semibold text-[var(--text-color-secondary)] mb-1.5 flex items-center gap-1.5">
|
||||
<i class="pi pi-phone text-[var(--primary-color)]" />
|
||||
Telefones
|
||||
<div class="col-span-full mb-7">
|
||||
<div
|
||||
class="flex items-start gap-3 p-4 mb-5 rounded-xl border transition-colors"
|
||||
:class="submitted && phonesCount === 0
|
||||
? 'border-red-300 bg-red-50/60 text-red-700'
|
||||
: pal.indigo.infoBox"
|
||||
>
|
||||
<span
|
||||
class="flex items-center justify-center w-9 h-9 rounded-lg shrink-0"
|
||||
:class="submitted && phonesCount === 0
|
||||
? 'bg-red-100 text-red-600'
|
||||
: pal.indigo.iconBox"
|
||||
>
|
||||
<i class="pi pi-phone text-[0.95rem]"/>
|
||||
</span>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="text-[0.95rem] font-semibold leading-tight">Telefones *</div>
|
||||
<div class="text-[0.85rem] mt-1 leading-snug opacity-90">
|
||||
Marque um telefone como <strong>principal</strong> — ele é usado pra cobranças, lembretes automáticos e contato padrão. Número vindo do CRM WhatsApp recebe a etiqueta <strong>"vinculado"</strong>.
|
||||
</div>
|
||||
<div
|
||||
v-if="submitted && phonesCount === 0"
|
||||
class="mt-2 text-[0.85rem] flex items-center gap-1.5 font-semibold"
|
||||
>
|
||||
<i class="pi pi-exclamation-circle text-[0.78rem]"/>
|
||||
<span>Adicione pelo menos um telefone.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ContactPhonesEditor
|
||||
ref="phonesEditorRef"
|
||||
entity-type="patient"
|
||||
:entity-id="patientId || null"
|
||||
@change="(arr) => phonesCount = (arr || []).length"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Emails (polimórfico — tipo/endereço/principal) -->
|
||||
<div class="col-span-full">
|
||||
<div class="text-xs font-semibold text-[var(--text-color-secondary)] mb-1.5 flex items-center gap-1.5">
|
||||
<i class="pi pi-envelope text-[var(--primary-color)]" />
|
||||
Emails
|
||||
<div class="col-span-full mb-7">
|
||||
<div
|
||||
class="flex items-start gap-3 p-4 mb-5 rounded-xl border transition-colors"
|
||||
:class="submitted && emailsCount === 0
|
||||
? 'border-red-300 bg-red-50/60 text-red-700'
|
||||
: pal.indigo.infoBox"
|
||||
>
|
||||
<span
|
||||
class="flex items-center justify-center w-9 h-9 rounded-lg shrink-0"
|
||||
:class="submitted && emailsCount === 0
|
||||
? 'bg-red-100 text-red-600'
|
||||
: pal.indigo.iconBox"
|
||||
>
|
||||
<i class="pi pi-envelope text-[0.95rem]"/>
|
||||
</span>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="text-[0.95rem] font-semibold leading-tight">E-mails *</div>
|
||||
<div class="text-[0.85rem] mt-1 leading-snug opacity-90">
|
||||
Marque um e-mail como <strong>principal</strong> — ele é usado pra envio de recibos, comprovantes e comunicações oficiais.
|
||||
</div>
|
||||
<div
|
||||
v-if="submitted && emailsCount === 0"
|
||||
class="mt-2 text-[0.85rem] flex items-center gap-1.5 font-semibold"
|
||||
>
|
||||
<i class="pi pi-exclamation-circle text-[0.78rem]"/>
|
||||
<span>Adicione pelo menos um e-mail.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<ContactEmailsEditor
|
||||
ref="emailsEditorRef"
|
||||
entity-type="patient"
|
||||
:entity-id="patientId || null"
|
||||
@change="(arr) => emailsCount = (arr || []).length"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 gap-3.5 xl:grid-cols-2">
|
||||
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2">
|
||||
|
||||
<!-- Canal preferido -->
|
||||
<div>
|
||||
@@ -1329,7 +1493,6 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
<Select id="f_canal" v-model="form.canal_preferido" :options="canalOpts" optionLabel="label" optionValue="value" class="w-full" variant="filled"/>
|
||||
<label for="f_canal">Canal preferido de contato</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.indigo.hint">Card Contato → "Canal preferido: <strong>WhatsApp</strong>".</div>
|
||||
</div>
|
||||
|
||||
<!-- Horário de contato -->
|
||||
@@ -1338,7 +1501,6 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
<IconField><InputIcon class="pi pi-clock"/><InputText id="f_horario" v-model="form.horario_contato" class="w-full" variant="filled" placeholder="Ex: 08h–18h"/></IconField>
|
||||
<label for="f_horario">Horário de contato</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.indigo.hint">Card Contato → "Horário: <strong>08h–18h</strong>".</div>
|
||||
</div>
|
||||
|
||||
<!-- Observações de endereço -->
|
||||
@@ -1347,7 +1509,10 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
<Textarea id="f_obs" v-model="form.observacoes" rows="2" class="w-full" variant="filled"/>
|
||||
<label for="f_obs">Observações de endereço</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.indigo.hint">Ex: Próximo ao posto, portão azul, sem interfone.</div>
|
||||
<div class="mt-2 text-[0.85rem] text-[var(--primary-color)] flex items-center gap-1.5">
|
||||
<i class="pi pi-info-circle text-[0.78rem]"/>
|
||||
<span>Ex: Próximo ao posto, portão azul, sem interfone.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1372,18 +1537,17 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</div>
|
||||
</AccordionHeader>
|
||||
<AccordionContent>
|
||||
<div class="p-4">
|
||||
<div class="p-5">
|
||||
<div :class="`flex items-start gap-2 mb-4 p-2.5 rounded-lg border text-[0.75rem] ${pal.teal.infoBox}`">
|
||||
<i class="pi pi-lightbulb mt-0.5 shrink-0"/>
|
||||
<span>Digite o CEP e cidade, estado, bairro e logradouro são preenchidos automaticamente via <strong>ViaCEP</strong>.</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-3.5 xl:grid-cols-2">
|
||||
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2">
|
||||
<div>
|
||||
<FloatLabel variant="on">
|
||||
<IconField><InputIcon class="pi pi-map-marker"/><InputText id="f_cep" v-model="form.cep" class="w-full" @blur="onCepBlur" variant="filled" placeholder="00000-000"/></IconField>
|
||||
<label for="f_cep">CEP</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.teal.hint">Card Contato → <em>"13560-000 · São Carlos"</em></div>
|
||||
</div>
|
||||
<div>
|
||||
<FloatLabel variant="on">
|
||||
@@ -1396,14 +1560,12 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
<IconField><InputIcon class="pi pi-building"/><InputText id="f_city" v-model="form.cidade" class="w-full" variant="filled"/></IconField>
|
||||
<label for="f_city">Cidade</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.teal.hint">Header → <em>"<strong>São Carlos</strong>, SP"</em></div>
|
||||
</div>
|
||||
<div>
|
||||
<FloatLabel variant="on">
|
||||
<IconField><InputIcon class="pi pi-compass"/><InputText id="f_uf" v-model="form.estado" class="w-full" variant="filled"/></IconField>
|
||||
<label for="f_uf">Estado (UF)</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.teal.hint">Header → "São Carlos, <strong>SP</strong>"</div>
|
||||
</div>
|
||||
<div class="xl:col-span-2">
|
||||
<FloatLabel variant="on">
|
||||
@@ -1453,7 +1615,7 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</div>
|
||||
</AccordionHeader>
|
||||
<AccordionContent>
|
||||
<div class="p-4">
|
||||
<div class="p-5">
|
||||
|
||||
<!-- Preview dos badges ao vivo -->
|
||||
<div v-if="form.status||convenioNome||form.patient_scope||tagIdsSelecionadas.length"
|
||||
@@ -1472,18 +1634,16 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</div>
|
||||
|
||||
<!-- Situação clínica -->
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<span class="text-[0.63rem] font-bold uppercase tracking-widest" :class="pal.violet.divTxt">Situação clínica</span>
|
||||
<div class="flex items-center gap-2 mb-5">
|
||||
<span class="text-[0.7rem] font-bold uppercase tracking-widest" :class="pal.violet.divTxt">Situação clínica</span>
|
||||
<div class="flex-1 h-px" :class="pal.violet.divLine"/>
|
||||
<span class="text-[0.6rem]" :class="pal.violet.hint">Badges no header do perfil</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-3.5 xl:grid-cols-3 mb-6">
|
||||
<div class="grid grid-cols-1 gap-4 xl:grid-cols-3 mb-7">
|
||||
<div>
|
||||
<FloatLabel variant="on">
|
||||
<Select id="f_status" v-model="form.status" :options="statusOpts" optionLabel="label" optionValue="value" class="w-full" variant="filled"/>
|
||||
<label for="f_status">Status</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.violet.hint">Badge <span class="font-bold text-green-600">verde</span> no header.</div>
|
||||
</div>
|
||||
<div>
|
||||
<!-- CONVÊNIO — seleciona de insurance_plans, máx 1 -->
|
||||
@@ -1517,33 +1677,33 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
@click="showConvenioDlg = true"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.violet.hint">Badge <span class="font-bold text-blue-500">azul</span> no header · máx 1 convênio.</div>
|
||||
</div>
|
||||
<div>
|
||||
<FloatLabel variant="on">
|
||||
<Select id="f_scope" v-model="form.patient_scope" :options="scopeOpts" optionLabel="label" optionValue="value" class="w-full" variant="filled"/>
|
||||
<label for="f_scope">Escopo de atendimento</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.violet.hint">Badge <span class="font-bold text-gray-500">cinza</span> no header.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Organização: grupo + tags -->
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<span class="text-[0.63rem] font-bold uppercase tracking-widest" :class="pal.violet.divTxt">Organização</span>
|
||||
<div class="flex items-center gap-2 mb-5">
|
||||
<span class="text-[0.7rem] font-bold uppercase tracking-widest" :class="pal.violet.divTxt">Organização</span>
|
||||
<div class="flex-1 h-px" :class="pal.violet.divLine"/>
|
||||
<span class="text-[0.6rem]" :class="pal.violet.hint">Chips coloridos no header</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-3.5 xl:grid-cols-2 mb-6">
|
||||
<div class="grid grid-cols-1 gap-6 xl:grid-cols-2 mb-7">
|
||||
<div class="flex gap-2">
|
||||
<div class="flex-1 min-w-0">
|
||||
<FloatLabel variant="on">
|
||||
<IconField><InputIcon class="pi pi-folder-open"/>
|
||||
<Select id="f_grupo" v-model="grupoIdSelecionado" :options="groups" optionLabel="nome" optionValue="id" class="w-full pl-[25px]" showClear filter variant="filled"/>
|
||||
<Select id="f_grupo" v-model="grupoIdSelecionado" :options="groups" optionLabel="name" optionValue="id" class="w-full pl-[25px]" showClear filter variant="filled"/>
|
||||
</IconField>
|
||||
<label for="f_grupo">Grupo</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.violet.hint">Define o modelo de anamnese.</div>
|
||||
<div class="mt-2 text-[0.85rem] text-[var(--primary-color)] flex items-center gap-1.5">
|
||||
<i class="pi pi-info-circle text-[0.78rem]"/>
|
||||
<span>Define o modelo de anamnese aplicado ao paciente.</span>
|
||||
</div>
|
||||
</div>
|
||||
<Button icon="pi pi-plus" severity="secondary" outlined class="shrink-0 h-[42px] mt-[1px]" title="Criar grupo" @click="openGroupDlg"/>
|
||||
</div>
|
||||
@@ -1555,25 +1715,22 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</IconField>
|
||||
<label for="f_tags">Tags clínicas</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.violet.hint">Aparecem como chips coloridos no header do perfil.</div>
|
||||
</div>
|
||||
<Button icon="pi pi-plus" severity="secondary" outlined class="shrink-0 h-[42px] mt-[1px]" title="Criar tag" @click="openTagDlg"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Origem — alimenta card "Origem" do detalhe -->
|
||||
<div class="flex items-center gap-2 mb-4">
|
||||
<span class="text-[0.63rem] font-bold uppercase tracking-widest" :class="pal.violet.divTxt">Origem</span>
|
||||
<div class="flex items-center gap-2 mb-5">
|
||||
<span class="text-[0.7rem] font-bold uppercase tracking-widest" :class="pal.violet.divTxt">Origem</span>
|
||||
<div class="flex-1 h-px" :class="pal.violet.divLine"/>
|
||||
<span class="text-[0.6rem]" :class="pal.violet.hint">Card "Origem" no perfil</span>
|
||||
</div>
|
||||
<div class="grid grid-cols-1 gap-3.5 xl:grid-cols-2">
|
||||
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2">
|
||||
<div>
|
||||
<FloatLabel variant="on">
|
||||
<IconField><InputIcon class="pi pi-megaphone"/><InputText id="f_lead" v-model="form.onde_nos_conheceu" class="w-full" variant="filled"/></IconField>
|
||||
<label for="f_lead">Como chegou até mim?</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.violet.hint">Origem → "Como chegou: <strong>Indicação</strong>".</div>
|
||||
</div>
|
||||
<div>
|
||||
<!-- ENCAMINHADO POR — múltiplos médicos -->
|
||||
@@ -1623,14 +1780,16 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
class="rounded-full w-full"
|
||||
@click="showMedicoDlg = true"
|
||||
/>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.violet.hint">Pode adicionar mais de um profissional de referência.</div>
|
||||
<div class="mt-2 text-[0.85rem] text-[var(--primary-color)] flex items-center gap-1.5">
|
||||
<i class="pi pi-info-circle text-[0.78rem]"/>
|
||||
<span>Você pode adicionar mais de um profissional de referência.</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<FloatLabel variant="on">
|
||||
<IconField><InputIcon class="pi pi-sign-out"/><InputText id="f_saida" v-model="form.motivo_saida" class="w-full" variant="filled" placeholder="Se aplicável"/></IconField>
|
||||
<label for="f_saida">Motivo de saída</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.violet.hint">Origem → "Motivo de saída" quando preenchido.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1659,7 +1818,7 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</div>
|
||||
</AccordionHeader>
|
||||
<AccordionContent>
|
||||
<div class="p-4">
|
||||
<div class="p-5">
|
||||
<div :class="`flex items-start gap-2 mb-4 p-2.5 rounded-lg border text-[0.75rem] ${pal.amber.infoBox}`">
|
||||
<i class="pi pi-info-circle mt-0.5 shrink-0"/>
|
||||
<span>Cada contato aqui aparece no card <strong>"Contatos & rede de suporte"</strong> do perfil. O marcado como <strong>emergência primária</strong> recebe badge vermelho.</span>
|
||||
@@ -1699,7 +1858,6 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
<InputText :id="`cr_${idx}`" v-model="c.relacao" class="w-full" variant="filled" placeholder="Ex: mãe, psiquiatra"/>
|
||||
<label :for="`cr_${idx}`">Relação / papel</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.amber.hint">Subtítulo no card: "Maria Lima · <strong>mãe</strong>".</div>
|
||||
</div>
|
||||
<div>
|
||||
<FloatLabel variant="on">
|
||||
@@ -1714,7 +1872,6 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</IconField>
|
||||
<label :for="`ctel_${idx}`">Telefone</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.amber.hint">Exibido abaixo do nome no card.</div>
|
||||
</div>
|
||||
<div>
|
||||
<FloatLabel variant="on">
|
||||
@@ -1723,7 +1880,6 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</IconField>
|
||||
<label :for="`cemail_${idx}`">E-mail</label>
|
||||
</FloatLabel>
|
||||
<div class="mt-1 text-[0.63rem]" :class="pal.amber.hint">Exibido ao lado do telefone.</div>
|
||||
</div>
|
||||
<!-- Emergência primária -->
|
||||
<div class="xl:col-span-2">
|
||||
@@ -1767,8 +1923,8 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</div>
|
||||
</AccordionHeader>
|
||||
<AccordionContent>
|
||||
<div class="p-4">
|
||||
<div class="grid grid-cols-1 gap-3.5 xl:grid-cols-2">
|
||||
<div class="p-5">
|
||||
<div class="grid grid-cols-1 gap-4 xl:grid-cols-2">
|
||||
<div class="xl:col-span-2">
|
||||
<FloatLabel variant="on">
|
||||
<IconField><InputIcon class="pi pi-user"/><InputText id="f_rn" v-model="form.nome_responsavel" class="w-full" variant="filled"/></IconField>
|
||||
@@ -1826,7 +1982,7 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</div>
|
||||
</AccordionHeader>
|
||||
<AccordionContent>
|
||||
<div class="p-4">
|
||||
<div class="p-5">
|
||||
<div :class="`flex items-start gap-2 mb-4 p-2.5 rounded-lg border text-[0.75rem] ${pal.rose.infoBox}`">
|
||||
<i class="pi pi-shield mt-0.5 shrink-0"/>
|
||||
<span>Campo interno: <strong>não aparece</strong> no cadastro externo nem é compartilhado com o paciente.</span>
|
||||
@@ -1861,9 +2017,9 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
class="dc-dialog w-[36rem]"
|
||||
: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' },
|
||||
header: { class: '!p-3 !rounded-t-[12px] border-b border-[var(--surface-border)] bg-[var(--surface-ground)]' },
|
||||
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' },
|
||||
footer: { class: '!p-0 !rounded-b-[12px] border-t border-[var(--surface-border)] bg-[var(--surface-ground)]' },
|
||||
pcCloseButton: { root: { class: '!rounded-md hover:!text-red-500' } },
|
||||
pcMaximizeButton: { root: { class: '!rounded-md hover:!text-primary' } },
|
||||
}"
|
||||
@@ -1918,9 +2074,9 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
class="dc-dialog w-[36rem]"
|
||||
: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' },
|
||||
header: { class: '!p-3 !rounded-t-[12px] border-b border-[var(--surface-border)] bg-[var(--surface-ground)]' },
|
||||
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' },
|
||||
footer: { class: '!p-0 !rounded-b-[12px] border-t border-[var(--surface-border)] bg-[var(--surface-ground)]' },
|
||||
pcCloseButton: { root: { class: '!rounded-md hover:!text-red-500' } },
|
||||
pcMaximizeButton: { root: { class: '!rounded-md hover:!text-primary' } },
|
||||
}"
|
||||
@@ -2015,3 +2171,24 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* Em modo horizontal, esconde os headers do Accordion (a navegação fica nas tabs em cima do main) */
|
||||
.pcd-horizontal :deep(.p-accordionheader) {
|
||||
display: none !important;
|
||||
}
|
||||
.pcd-horizontal :deep(.p-accordion-header) {
|
||||
display: none !important;
|
||||
}
|
||||
.pcd-horizontal :deep(.p-accordioncontent),
|
||||
.pcd-horizontal :deep(.p-accordion-content) {
|
||||
border: none !important;
|
||||
}
|
||||
|
||||
/* Tira o padding interno do wrapper do AccordionContent — o conteúdo já tem
|
||||
o próprio padding (.p-5) por seção, então o do PrimeVue duplicava o
|
||||
espaçamento e dava sensação de elementos descolados. */
|
||||
:deep(.p-accordioncontent-content) {
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -688,9 +688,9 @@ function isRecent(row) {
|
||||
maximizable
|
||||
class="w-[96vw] max-w-2xl"
|
||||
: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' },
|
||||
header: { class: '!p-3 !rounded-t-[12px] border-b border-[var(--surface-border)] bg-[var(--surface-ground)]' },
|
||||
content: { class: '!p-4' },
|
||||
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' },
|
||||
footer: { class: '!p-0 !rounded-b-[12px] border-t border-[var(--surface-border)] bg-[var(--surface-ground)]' },
|
||||
pcCloseButton: { root: { class: '!rounded-md hover:!text-red-500' } },
|
||||
pcMaximizeButton: { root: { class: '!rounded-md hover:!text-primary' } },
|
||||
}"
|
||||
|
||||
Reference in New Issue
Block a user