+ Menu Hover no Layout Rail, Twilio, Sms, Email, Templates, LNovo Layout Configurações

This commit is contained in:
Leonardo
2026-03-25 08:39:45 -03:00
parent 53a4980396
commit 3f1786c9bf
59 changed files with 2553 additions and 1106 deletions
+9 -9
View File
@@ -741,10 +741,10 @@ function isRecent(row) {
<div class="absolute w-72 h-72 top-0 -left-16 rounded-full blur-[60px] bg-indigo-500/[0.09]" />
</div>
<div class="relative z-[1] flex items-center gap-3">
<div class="relative z-1 flex items-center gap-3">
<!-- Brand -->
<div class="flex items-center gap-2 flex-shrink-0">
<div class="grid place-items-center w-9 h-9 rounded-md flex-shrink-0 bg-indigo-500/10 text-indigo-500">
<div class="flex items-center gap-2 shrink-0">
<div class="grid place-items-center w-9 h-9 rounded-md shrink-0 bg-indigo-500/10 text-indigo-500">
<i class="pi pi-users text-base" />
</div>
<div class="min-w-0 hidden lg:block">
@@ -767,7 +767,7 @@ function isRecent(row) {
</div>
<!-- Ações desktop -->
<div class="hidden xl:flex items-center gap-1 flex-shrink-0">
<div class="hidden xl:flex items-center gap-1 shrink-0">
<Button icon="pi pi-refresh" severity="secondary" outlined class="h-9 w-9 rounded-full" :loading="loading" @click="fetchAll" />
<Button icon="pi pi-percentage" severity="secondary" outlined class="h-9 w-9 rounded-full" v-tooltip.top="'Descontos'" @click="router.push('/configuracoes/descontos')" />
<Button label="Novo" icon="pi pi-user-plus" class="rounded-full" @click="(e) => createPopoverRef?.toggle(e)" />
@@ -776,7 +776,7 @@ function isRecent(row) {
</div>
<!-- Mobile -->
<div class="flex xl:hidden items-center gap-1 flex-shrink-0 ml-auto">
<div class="flex xl:hidden items-center gap-1 shrink-0 ml-auto">
<Button icon="pi pi-search" severity="secondary" outlined class="h-9 w-9 rounded-full" @click="searchMobileDlg = true" />
<Button icon="pi pi-user-plus" class="h-9 w-9 rounded-full" @click="(e) => createPopoverRef?.toggle(e)" />
<Button label="Ações" icon="pi pi-ellipsis-v" severity="secondary" size="small" class="rounded-full" @click="(e) => patMobileMenuRef.toggle(e)" />
@@ -1225,14 +1225,14 @@ function isRecent(row) {
:style="{ borderBottomColor: `${grp.color || 'var(--surface-border)'}30` }"
@click="openGrpDialog(grp)"
>
<div class="w-9 h-9 rounded-lg flex items-center justify-center text-white font-bold text-sm flex-shrink-0 shadow-sm" :style="grpColorStyle(grp)">
<div class="w-9 h-9 rounded-lg flex items-center justify-center text-white font-bold text-sm shrink-0 shadow-sm" :style="grpColorStyle(grp)">
{{ (grp.name || '?')[0].toUpperCase() }}
</div>
<div class="flex-1 min-w-0">
<div class="font-semibold truncate text-sm" :style="{ color: grp.color || 'var(--text-color)' }">{{ grp.name }}</div>
<div class="text-[0.72rem] text-[var(--text-color-secondary)] opacity-70">{{ grp.patients.length }} paciente{{ grp.patients.length !== 1 ? 's' : '' }} · clique para ver</div>
</div>
<span class="inline-flex items-center justify-center min-w-[26px] h-6 px-1.5 rounded-full text-white text-xs font-bold flex-shrink-0" :style="grpColorStyle(grp)">{{ grp.patients.length }}</span>
<span class="inline-flex items-center justify-center min-w-[26px] h-6 px-1.5 rounded-full text-white text-xs font-bold shrink-0" :style="grpColorStyle(grp)">{{ grp.patients.length }}</span>
</button>
<!-- Chips de pacientes -->
@@ -1258,7 +1258,7 @@ function isRecent(row) {
}
"
>
<span class="w-5 h-5 rounded-full flex items-center justify-center text-[9px] font-bold flex-shrink-0 transition-colors group-hover:bg-white/20 group-hover:text-white" :style="grpChipAvatarStyle(grp)">
<span class="w-5 h-5 rounded-full flex items-center justify-center text-[9px] font-bold shrink-0 transition-colors group-hover:bg-white/20 group-hover:text-white" :style="grpChipAvatarStyle(grp)">
{{ (p.nome_completo || '?').charAt(0).toUpperCase() }}
</span>
<span class="max-w-[120px] truncate">{{ (p.nome_completo || '—').split(' ').slice(0, 2).join(' ') }}</span>
@@ -1293,7 +1293,7 @@ function isRecent(row) {
>
<template #header>
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center text-white font-bold text-base flex-shrink-0" :style="grpColorStyle(grpDialog.group)">
<div class="w-9 h-9 rounded-lg flex items-center justify-center text-white font-bold text-base shrink-0" :style="grpColorStyle(grpDialog.group)">
{{ (grpDialog.group?.name || '?')[0].toUpperCase() }}
</div>
<div>
@@ -564,11 +564,11 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
<div class="absolute w-72 h-72 top-0 -left-16 rounded-full blur-[60px] bg-emerald-400/[0.08]" />
</div>
<div class="relative z-[1] flex items-center gap-3">
<div class="relative z-1 flex items-center gap-3">
<!-- Brand -->
<div class="flex items-center gap-2 flex-shrink-0">
<div class="grid place-items-center w-9 h-9 rounded-md flex-shrink-0 bg-indigo-500/10 text-indigo-500">
<div class="flex items-center gap-2 shrink-0">
<div class="grid place-items-center w-9 h-9 rounded-md shrink-0 bg-indigo-500/10 text-indigo-500">
<i class="pi pi-user-plus text-base" />
</div>
<div class="min-w-0 hidden lg:block">
@@ -586,7 +586,7 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
<div class="flex-1" />
<!-- Ações (ocultas no modo dialog o Dialog tem seu próprio footer) -->
<div v-if="!dialogMode" class="flex items-center gap-1.5 flex-shrink-0">
<div v-if="!dialogMode" class="flex items-center gap-1.5 shrink-0">
<Button
v-if="canSee('testMODE')"
label="Preencher tudo"
@@ -634,7 +634,7 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
<!-- Avatar -->
<div class="flex items-center gap-3 pb-3.5 mb-3.5 border-b border-[var(--surface-border,#e2e8f0)] xl:flex-col xl:items-center xl:gap-2">
<!-- Foto -->
<div class="w-16 h-16 xl:w-20 xl:h-20 rounded-full overflow-hidden border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-ground,#f8fafc)] flex-shrink-0">
<div class="w-16 h-16 xl:w-20 xl:h-20 rounded-full overflow-hidden border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-ground,#f8fafc)] shrink-0">
<img
v-if="avatarPreviewUrl || form.avatar_url"
:src="avatarPreviewUrl || form.avatar_url"
@@ -676,7 +676,7 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
: 'border-transparent text-[var(--text-color)] hover:bg-[var(--surface-ground,#f8fafc)] font-medium'"
@click="openPanel(Number(item.value))"
>
<i :class="item.icon" class="text-[1rem] opacity-70 flex-shrink-0" />
<i :class="item.icon" class="text-[1rem] opacity-70 shrink-0" />
<span>{{ item.label }}</span>
</button>
</div>
@@ -705,7 +705,7 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
:class="activeValue === item.value ? 'bg-indigo-500/8 border-indigo-300/40 text-indigo-700 font-semibold' : 'text-[var(--text-color)] hover:bg-[var(--surface-ground,#f8fafc)] font-medium'"
@click="selectNav(item)"
>
<i :class="item.icon" class="text-[1rem] opacity-70 flex-shrink-0" />
<i :class="item.icon" class="text-[1rem] opacity-70 shrink-0" />
<span>{{ item.label }}</span>
</button>
</div>
@@ -802,7 +802,7 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
</FloatLabel>
<div class="mt-1 text-[0.72rem] text-[var(--text-color-secondary)] opacity-70">Usado para puxar um modelo de anamnese.</div>
</div>
<Button icon="pi pi-plus" severity="secondary" outlined class="flex-shrink-0" @click="openGroupDlg" />
<Button icon="pi pi-plus" severity="secondary" outlined class="shrink-0" @click="openGroupDlg" />
</div>
</div>
<!-- Tags -->
@@ -814,7 +814,7 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
<label for="f_tags">Tags</label>
</FloatLabel>
</div>
<Button icon="pi pi-plus" severity="secondary" outlined class="flex-shrink-0" @click="openTagDlg" />
<Button icon="pi pi-plus" severity="secondary" outlined class="shrink-0" @click="openTagDlg" />
</div>
</div>
<div>
@@ -928,11 +928,11 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
<div class="flex flex-col gap-4 pt-1">
<span class="text-[1rem] text-[var(--text-color-secondary)]">Crie um grupo para organizar seus pacientes.</span>
<div class="flex items-center gap-3">
<label for="group-name" class="w-20 text-[1rem] font-semibold flex-shrink-0">Nome</label>
<label for="group-name" class="w-20 text-[1rem] font-semibold shrink-0">Nome</label>
<InputText id="group-name" v-model="newGroup.name" class="flex-1" autocomplete="off" placeholder="Ex: Crianças" />
</div>
<div class="flex items-center gap-3">
<label class="w-20 text-[1rem] font-semibold flex-shrink-0">Cor</label>
<label class="w-20 text-[1rem] font-semibold shrink-0">Cor</label>
<div class="flex flex-1 items-center gap-2.5">
<input v-model="newGroup.color" type="color" class="h-9 w-12 cursor-pointer rounded-md border border-[var(--surface-border,#e2e8f0)] bg-transparent" />
<Chip :label="newGroup.color || '#—'" class="font-semibold" :style="{ backgroundColor: newGroup.color, color: '#fff' }" />
@@ -963,11 +963,11 @@ defineExpose({ fillRandomPatient, onSubmit, confirmDelete, saving, deleting, can
<div class="flex flex-col gap-4 pt-1">
<span class="text-[1rem] text-[var(--text-color-secondary)]">Crie uma tag para facilitar filtros e organização.</span>
<div class="flex items-center gap-3">
<label for="tag-name" class="w-20 text-[1rem] font-semibold flex-shrink-0">Nome</label>
<label for="tag-name" class="w-20 text-[1rem] font-semibold shrink-0">Nome</label>
<InputText id="tag-name" v-model="newTag.name" class="flex-1" autocomplete="off" placeholder="Ex: Ansiedade" />
</div>
<div class="flex items-center gap-3">
<label class="w-20 text-[1rem] font-semibold flex-shrink-0">Cor</label>
<label class="w-20 text-[1rem] font-semibold shrink-0">Cor</label>
<div class="flex flex-1 items-center gap-2.5">
<input v-model="newTag.color" type="color" class="h-9 w-12 cursor-pointer rounded-md border border-[var(--surface-border,#e2e8f0)] bg-transparent" />
<Chip :label="newTag.color || '#—'" class="font-semibold" :style="{ backgroundColor: newTag.color, color: '#fff' }" />
@@ -190,10 +190,10 @@ onBeforeUnmount(() => {
<div class="absolute w-72 h-72 top-0 -left-16 rounded-full blur-[60px] bg-emerald-400/[0.08]" />
</div>
<div class="relative z-[1] flex items-center gap-3">
<div class="relative z-1 flex items-center gap-3">
<!-- Brand -->
<div class="flex items-center gap-2 flex-shrink-0">
<div class="grid place-items-center w-9 h-9 rounded-md flex-shrink-0 bg-indigo-500/10 text-indigo-500">
<div class="flex items-center gap-2 shrink-0">
<div class="grid place-items-center w-9 h-9 rounded-md shrink-0 bg-indigo-500/10 text-indigo-500">
<i class="pi pi-link text-base" />
</div>
<div class="min-w-0 hidden lg:block">
@@ -206,7 +206,7 @@ onBeforeUnmount(() => {
<div class="hidden xl:flex flex-1 min-w-0 mx-2 items-center gap-3">
<!-- Badge de status -->
<span
class="inline-flex items-center gap-1.5 text-[0.75rem] px-2.5 py-1 rounded-full border flex-shrink-0 transition-colors"
class="inline-flex items-center gap-1.5 text-[0.75rem] px-2.5 py-1 rounded-full border shrink-0 transition-colors"
:class="inviteToken ? 'border-emerald-200 text-emerald-700 bg-emerald-50' : 'border-[var(--surface-border)] text-[var(--text-color-secondary)] bg-[var(--surface-ground)]'"
>
<span class="h-1.5 w-1.5 rounded-full" :class="inviteToken ? 'bg-emerald-500 animate-pulse' : 'bg-[var(--text-color-secondary)]'" />
@@ -224,12 +224,12 @@ onBeforeUnmount(() => {
</div>
<!-- Ações desktop -->
<div class="hidden xl:flex items-center gap-1 flex-shrink-0">
<div class="hidden xl:flex items-center gap-1 shrink-0">
<Button label="Gerar novo link" icon="pi pi-refresh" severity="secondary" outlined class="rounded-full" :loading="rotating" @click="rotateLink" />
</div>
<!-- Mobile -->
<div class="flex xl:hidden items-center gap-1 flex-shrink-0 ml-auto">
<div class="flex xl:hidden items-center gap-1 shrink-0 ml-auto">
<Button label="Ações" icon="pi pi-ellipsis-v" severity="secondary" size="small" class="rounded-full" @click="(e) => mobileMenuRef.toggle(e)" />
<Menu ref="mobileMenuRef" :model="mobileMenuItems" :popup="true" />
</div>
@@ -251,7 +251,7 @@ onBeforeUnmount(() => {
<div class="text-[1rem] text-[var(--text-color-secondary)] mt-0.5">Envie ao paciente por WhatsApp, e-mail ou mensagem direta</div>
</div>
<span
class="inline-flex items-center gap-1.5 text-[0.75rem] px-2.5 py-1 rounded-full border flex-shrink-0"
class="inline-flex items-center gap-1.5 text-[0.75rem] px-2.5 py-1 rounded-full border shrink-0"
:class="inviteToken ? 'border-emerald-200 text-emerald-700 bg-emerald-50' : 'border-[var(--surface-border)] text-[var(--text-color-secondary)] bg-[var(--surface-ground)]'"
>
<span class="h-1.5 w-1.5 rounded-full" :class="inviteToken ? 'bg-emerald-500 animate-pulse' : 'bg-[var(--text-color-secondary)]'" />
@@ -286,7 +286,7 @@ onBeforeUnmount(() => {
class="flex items-center gap-3 px-3.5 py-3 rounded-md border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-ground,#f8fafc)] cursor-pointer text-left transition-[background,box-shadow,transform] duration-150 hover:bg-[var(--surface-hover,#f1f5f9)] hover:shadow-[0_2px_12px_rgba(0,0,0,0.06)] hover:-translate-y-px active:translate-y-0"
@click="copyLink"
>
<div class="grid place-items-center w-9 h-9 rounded-md flex-shrink-0 bg-indigo-500/10 text-indigo-500">
<div class="grid place-items-center w-9 h-9 rounded-md shrink-0 bg-indigo-500/10 text-indigo-500">
<i class="pi pi-copy" />
</div>
<div class="min-w-0">
@@ -300,7 +300,7 @@ onBeforeUnmount(() => {
class="flex items-center gap-3 px-3.5 py-3 rounded-md border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-ground,#f8fafc)] cursor-pointer text-left transition-[background,box-shadow,transform] duration-150 hover:bg-[var(--surface-hover,#f1f5f9)] hover:shadow-[0_2px_12px_rgba(0,0,0,0.06)] hover:-translate-y-px active:translate-y-0"
@click="copyInviteMessage"
>
<div class="grid place-items-center w-9 h-9 rounded-md flex-shrink-0 bg-emerald-500/10 text-emerald-600">
<div class="grid place-items-center w-9 h-9 rounded-md shrink-0 bg-emerald-500/10 text-emerald-600">
<i class="pi pi-comment" />
</div>
<div class="min-w-0">
@@ -336,11 +336,11 @@ onBeforeUnmount(() => {
</div>
<!-- DIREITA: instruções -->
<div class="w-full lg:w-[272px] lg:flex-shrink-0 flex flex-col gap-3">
<div class="w-full lg:w-[272px] lg:shrink-0 flex flex-col gap-3">
<!-- Como funciona -->
<div class="rounded-md border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-card,#fff)] overflow-hidden">
<div class="flex items-center gap-2.5 px-3.5 pt-3 pb-2.5 border-b border-[var(--surface-border,#f1f5f9)]">
<div class="w-8 h-8 rounded-md flex items-center justify-center flex-shrink-0 bg-indigo-500/10 text-indigo-500">
<div class="w-8 h-8 rounded-md flex items-center justify-center shrink-0 bg-indigo-500/10 text-indigo-500">
<i class="pi pi-list-check text-[0.9rem]" />
</div>
<div class="min-w-0">
@@ -351,7 +351,7 @@ onBeforeUnmount(() => {
<ol class="flex flex-col divide-y divide-[var(--surface-border,#f1f5f9)]">
<li v-for="step in howItWorks" :key="step.n" class="flex items-start gap-3 px-3.5 py-3">
<div class="grid place-items-center w-7 h-7 rounded-md flex-shrink-0 bg-indigo-500/10 text-indigo-500 text-[0.75rem] font-bold mt-px">
<div class="grid place-items-center w-7 h-7 rounded-md shrink-0 bg-indigo-500/10 text-indigo-500 text-[0.75rem] font-bold mt-px">
{{ step.n }}
</div>
<div class="min-w-0">
@@ -365,7 +365,7 @@ onBeforeUnmount(() => {
<!-- Boas práticas -->
<div class="rounded-md border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-card,#fff)] overflow-hidden">
<div class="flex items-center gap-2.5 px-3.5 pt-3 pb-2.5 border-b border-[var(--surface-border,#f1f5f9)]">
<div class="w-8 h-8 rounded-md flex items-center justify-center flex-shrink-0 bg-emerald-500/10 text-emerald-600">
<div class="w-8 h-8 rounded-md flex items-center justify-center shrink-0 bg-emerald-500/10 text-emerald-600">
<i class="pi pi-shield text-[0.9rem]" />
</div>
<div class="min-w-0">
@@ -376,7 +376,7 @@ onBeforeUnmount(() => {
<ul class="flex flex-col divide-y divide-[var(--surface-border,#f1f5f9)]">
<li v-for="tip in goodPractices" :key="tip" class="flex items-start gap-2.5 px-3.5 py-2.5">
<i class="pi pi-check text-emerald-500 mt-0.5 flex-shrink-0 text-[1rem]" />
<i class="pi pi-check text-emerald-500 mt-0.5 shrink-0 text-[1rem]" />
<span class="text-[1rem] text-[var(--text-color-secondary)] leading-relaxed">{{ tip }}</span>
</li>
</ul>
@@ -495,10 +495,10 @@ onBeforeUnmount(() => {
<div class="absolute w-72 h-72 top-0 -left-16 rounded-full blur-[60px] bg-indigo-500/[0.09]" />
</div>
<div class="relative z-[1] flex items-center gap-3">
<div class="relative z-1 flex items-center gap-3">
<!-- Brand -->
<div class="flex items-center gap-2 flex-shrink-0">
<div class="grid place-items-center w-9 h-9 rounded-md flex-shrink-0 bg-indigo-500/10 text-indigo-500">
<div class="flex items-center gap-2 shrink-0">
<div class="grid place-items-center w-9 h-9 rounded-md shrink-0 bg-indigo-500/10 text-indigo-500">
<i class="pi pi-inbox text-base" />
</div>
<div class="min-w-0 hidden lg:block">
@@ -537,12 +537,12 @@ onBeforeUnmount(() => {
</div>
<!-- Ações desktop -->
<div class="hidden xl:flex items-center gap-1 flex-shrink-0">
<div class="hidden xl:flex items-center gap-1 shrink-0">
<Button icon="pi pi-refresh" severity="secondary" outlined class="h-9 w-9 rounded-full" :loading="loading" title="Atualizar" @click="fetchIntakes" />
</div>
<!-- Mobile -->
<div class="flex xl:hidden items-center gap-1 flex-shrink-0 ml-auto">
<div class="flex xl:hidden items-center gap-1 shrink-0 ml-auto">
<Button icon="pi pi-search" severity="secondary" outlined class="h-9 w-9 rounded-full" @click="recSearchDlgOpen = true" />
<Button label="Ações" icon="pi pi-ellipsis-v" severity="secondary" size="small" class="rounded-full" @click="(e) => recMobileMenuRef.toggle(e)" />
<Menu ref="recMobileMenuRef" :model="recMobileMenuItems" :popup="true" />
@@ -431,10 +431,10 @@ function isRecent(row) {
<div class="absolute w-72 h-72 top-0 -left-16 rounded-full blur-[60px] bg-indigo-500/[0.09]" />
</div>
<div class="relative z-[1] flex items-center gap-3">
<div class="relative z-1 flex items-center gap-3">
<!-- Brand -->
<div class="flex items-center gap-2 flex-shrink-0">
<div class="grid place-items-center w-9 h-9 rounded-md flex-shrink-0 bg-indigo-500/10 text-indigo-500">
<div class="flex items-center gap-2 shrink-0">
<div class="grid place-items-center w-9 h-9 rounded-md shrink-0 bg-indigo-500/10 text-indigo-500">
<i class="pi pi-sitemap text-base" />
</div>
<div class="min-w-0 hidden lg:block">
@@ -457,14 +457,14 @@ function isRecent(row) {
</div>
<!-- Ações desktop -->
<div class="hidden xl:flex items-center gap-1 flex-shrink-0">
<div class="hidden xl:flex items-center gap-1 shrink-0">
<Button v-if="selectedGroups?.length" label="Excluir selecionados" icon="pi pi-trash" severity="danger" outlined class="rounded-full" @click="confirmDeleteSelected" />
<Button label="Novo grupo" icon="pi pi-plus" class="rounded-full" @click="openCreate" />
<Button icon="pi pi-refresh" severity="secondary" outlined class="h-9 w-9 rounded-full" :loading="loading" @click="fetchAll" />
</div>
<!-- Mobile -->
<div class="flex xl:hidden items-center gap-1 flex-shrink-0 ml-auto">
<div class="flex xl:hidden items-center gap-1 shrink-0 ml-auto">
<Button icon="pi pi-search" severity="secondary" outlined class="h-9 w-9 rounded-full" @click="grpSearchDlgOpen = true" />
<Button icon="pi pi-plus" class="h-9 w-9 rounded-full" @click="openCreate" />
<Button label="Ações" icon="pi pi-ellipsis-v" severity="secondary" size="small" class="rounded-full" @click="(e) => grpMobileMenuRef.toggle(e)" />
@@ -564,7 +564,7 @@ function isRecent(row) {
<Column field="nome" header="Nome" sortable style="min-width: 16rem">
<template #body="{ data }">
<div class="flex items-center gap-2">
<span class="w-[3px] h-5 rounded-sm flex-shrink-0" :style="effectiveCor(data) ? colorStyle(effectiveCor(data)) : { background: 'var(--surface-border)' }" />
<span class="w-[3px] h-5 rounded-sm shrink-0" :style="effectiveCor(data) ? colorStyle(effectiveCor(data)) : { background: 'var(--surface-border)' }" />
<span class="font-medium">{{ data.nome }}</span>
</div>
</template>
@@ -616,18 +616,18 @@ function isRecent(row) {
</div>
<!-- PAINEL LATERAL: grupos com pacientes -->
<div class="w-full lg:w-[272px] lg:flex-shrink-0">
<div class="w-full lg:w-[272px] lg:shrink-0">
<div class="rounded-md border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-card,#fff)] overflow-hidden">
<!-- Header do painel -->
<div class="flex items-center gap-2.5 px-3.5 pt-3 pb-2.5 border-b border-[var(--surface-border,#f1f5f9)]">
<div class="w-8 h-8 rounded-md flex items-center justify-center flex-shrink-0 bg-indigo-500/10 text-indigo-500">
<div class="w-8 h-8 rounded-md flex items-center justify-center shrink-0 bg-indigo-500/10 text-indigo-500">
<i class="pi pi-users text-[0.9rem]" />
</div>
<div class="flex-1 min-w-0">
<span class="block text-[1rem] font-bold text-[var(--text-color)]">Pacientes por grupo</span>
<span class="block text-[0.72rem] text-[var(--text-color-secondary)]">Grupos com associações ativas</span>
</div>
<span v-if="cards.length" class="inline-flex items-center justify-center min-w-[20px] h-5 px-1 rounded-full bg-[var(--primary-color,#6366f1)] text-white text-[0.65rem] font-bold flex-shrink-0">{{ cards.length }}</span>
<span v-if="cards.length" class="inline-flex items-center justify-center min-w-[20px] h-5 px-1 rounded-full bg-[var(--primary-color,#6366f1)] text-white text-[0.65rem] font-bold shrink-0">{{ cards.length }}</span>
</div>
<!-- Skeleton -->
@@ -651,7 +651,7 @@ function isRecent(row) {
@click="openGroupPatientsModal(g)"
>
<!-- Dot cor -->
<div class="w-2.5 h-2.5 rounded-full flex-shrink-0" :style="effectiveCor(g) ? colorStyle(effectiveCor(g)) : { background: 'var(--surface-border)' }" />
<div class="w-2.5 h-2.5 rounded-full shrink-0" :style="effectiveCor(g) ? colorStyle(effectiveCor(g)) : { background: 'var(--surface-border)' }" />
<div class="flex-1 min-w-0">
<div class="font-semibold text-[0.8rem] truncate text-[var(--text-color)]">{{ g.nome }}</div>
<div class="text-[1rem] text-[var(--text-color-secondary)]">
@@ -659,10 +659,10 @@ function isRecent(row) {
</div>
</div>
<!-- Badge contagem -->
<span class="inline-flex items-center justify-center min-w-[22px] h-[22px] px-1 rounded-full font-bold text-[0.68rem] flex-shrink-0" :class="g.is_system ? 'bg-sky-500/10 text-sky-600' : 'bg-indigo-500/10 text-indigo-600'">{{
<span class="inline-flex items-center justify-center min-w-[22px] h-[22px] px-1 rounded-full font-bold text-[0.68rem] shrink-0" :class="g.is_system ? 'bg-sky-500/10 text-sky-600' : 'bg-indigo-500/10 text-indigo-600'">{{
Number(g.patients_count ?? g.patient_count ?? 0)
}}</span>
<i class="pi pi-chevron-right text-[0.6rem] text-[var(--text-color-secondary)] opacity-30 group-hover:opacity-100 group-hover:text-[var(--primary-color,#6366f1)] transition-all duration-150 flex-shrink-0" />
<i class="pi pi-chevron-right text-[0.6rem] text-[var(--text-color-secondary)] opacity-30 group-hover:opacity-100 group-hover:text-[var(--primary-color,#6366f1)] transition-all duration-150 shrink-0" />
</button>
</div>
@@ -791,7 +791,7 @@ function isRecent(row) {
>
<template #header>
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center text-white font-bold text-base flex-shrink-0" :style="{ background: patientsGroupHex }">
<div class="w-9 h-9 rounded-lg flex items-center justify-center text-white font-bold text-base shrink-0" :style="{ background: patientsGroupHex }">
{{ (patientsDialog.group?.nome || '?')[0].toUpperCase() }}
</div>
<div>
@@ -601,7 +601,7 @@ Tags: ${
<aside class="rounded-md border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-card,#fff)] p-3.5 xl:sticky xl:top-2 xl:self-start">
<!-- Avatar + info rápida -->
<div class="flex items-center gap-3 pb-3.5 mb-3.5 border-b border-[var(--surface-border,#e2e8f0)] xl:flex-col xl:items-center xl:gap-2 xl:text-center">
<div class="w-16 h-16 xl:w-20 xl:h-20 rounded-full overflow-hidden border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-ground,#f8fafc)] flex-shrink-0">
<div class="w-16 h-16 xl:w-20 xl:h-20 rounded-full overflow-hidden border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-ground,#f8fafc)] shrink-0">
<img :src="avatarUrl" alt="avatar" class="w-full h-full object-cover" />
</div>
<div class="flex-1 min-w-0">
@@ -636,7 +636,7 @@ Tags: ${
:class="activeValue === item.value ? 'bg-indigo-500/8 border-indigo-300/40 text-indigo-700 font-semibold' : 'border-transparent text-[var(--text-color)] hover:bg-[var(--surface-ground,#f8fafc)] font-medium'"
@click="openPanel(Number(item.value))"
>
<i :class="item.icon" class="text-sm opacity-70 flex-shrink-0" />
<i :class="item.icon" class="text-sm opacity-70 shrink-0" />
<span>{{ item.label }}</span>
</button>
</div>
@@ -657,7 +657,7 @@ Tags: ${
:class="activeValue === item.value ? 'bg-indigo-500/8 border-indigo-300/40 text-indigo-700 font-semibold' : 'text-[var(--text-color)] hover:bg-[var(--surface-ground,#f8fafc)] font-medium'"
@click="selectNav(item)"
>
<i :class="item.icon" class="text-sm opacity-70 flex-shrink-0" />
<i :class="item.icon" class="text-sm opacity-70 shrink-0" />
<span>{{ item.label }}</span>
</button>
</div>
+15 -15
View File
@@ -426,10 +426,10 @@ function isRecent(row) {
<div class="absolute w-72 h-72 top-0 -left-16 rounded-full blur-[60px] bg-indigo-500/[0.09]" />
</div>
<div class="relative z-[1] flex items-center gap-3">
<div class="relative z-1 flex items-center gap-3">
<!-- Brand -->
<div class="flex items-center gap-2 flex-shrink-0">
<div class="grid place-items-center w-9 h-9 rounded-md flex-shrink-0 bg-indigo-500/10 text-indigo-500">
<div class="flex items-center gap-2 shrink-0">
<div class="grid place-items-center w-9 h-9 rounded-md shrink-0 bg-indigo-500/10 text-indigo-500">
<i class="pi pi-tags text-base" />
</div>
<div class="min-w-0 hidden lg:block">
@@ -452,14 +452,14 @@ function isRecent(row) {
</div>
<!-- Ações desktop -->
<div class="hidden xl:flex items-center gap-1 flex-shrink-0">
<div class="hidden xl:flex items-center gap-1 shrink-0">
<Button v-if="etiquetasSelecionadas?.length" label="Excluir selecionados" icon="pi pi-trash" severity="danger" outlined class="rounded-full" @click="confirmarExclusaoSelecionadas" />
<Button label="Nova tag" icon="pi pi-plus" class="rounded-full" @click="abrirCriar" />
<Button icon="pi pi-refresh" severity="secondary" outlined class="h-9 w-9 rounded-full" :loading="carregando" @click="buscarEtiquetas" />
</div>
<!-- Mobile -->
<div class="flex xl:hidden items-center gap-1 flex-shrink-0 ml-auto">
<div class="flex xl:hidden items-center gap-1 shrink-0 ml-auto">
<Button icon="pi pi-search" severity="secondary" outlined class="h-9 w-9 rounded-full" @click="searchDlgOpen = true" />
<Button icon="pi pi-plus" class="h-9 w-9 rounded-full" @click="abrirCriar" />
<Button label="Ações" icon="pi pi-ellipsis-v" severity="secondary" size="small" class="rounded-full" @click="(e) => mobileMenuRef.toggle(e)" />
@@ -551,9 +551,9 @@ function isRecent(row) {
<template #body="{ data }">
<div class="flex items-center gap-2 min-w-0">
<!-- Barra lateral colorida -->
<span class="w-[3px] h-5 rounded-sm flex-shrink-0" :style="{ background: data.cor || '#94a3b8' }" />
<span class="w-[3px] h-5 rounded-sm shrink-0" :style="{ background: data.cor || '#94a3b8' }" />
<span class="font-medium truncate">{{ data.nome }}</span>
<span v-if="data.is_padrao" class="inline-flex items-center px-1.5 py-px rounded text-[0.65rem] font-semibold bg-[var(--surface-border,#e2e8f0)] text-[var(--text-color-secondary)] flex-shrink-0">padrão</span>
<span v-if="data.is_padrao" class="inline-flex items-center px-1.5 py-px rounded text-[0.65rem] font-semibold bg-[var(--surface-border,#e2e8f0)] text-[var(--text-color-secondary)] shrink-0">padrão</span>
</div>
</template>
</Column>
@@ -610,18 +610,18 @@ function isRecent(row) {
</div>
<!-- PAINEL LATERAL: tags mais usadas -->
<div class="w-full lg:w-[272px] lg:flex-shrink-0">
<div class="w-full lg:w-[272px] lg:shrink-0">
<div class="rounded-md border border-[var(--surface-border,#e2e8f0)] bg-[var(--surface-card,#fff)] overflow-hidden">
<!-- Header do painel -->
<div class="flex items-center gap-2.5 px-3.5 pt-3 pb-2.5 border-b border-[var(--surface-border,#f1f5f9)]">
<div class="w-8 h-8 rounded-md flex items-center justify-center flex-shrink-0 bg-pink-500/10 text-pink-500">
<div class="w-8 h-8 rounded-md flex items-center justify-center shrink-0 bg-pink-500/10 text-pink-500">
<i class="pi pi-tags text-[0.9rem]" />
</div>
<div class="flex-1 min-w-0">
<span class="block text-[1rem] font-bold text-[var(--text-color)]">Mais usadas</span>
<span class="block text-[0.72rem] text-[var(--text-color-secondary)]">Tags com pacientes associados</span>
</div>
<span v-if="cards.length" class="inline-flex items-center justify-center min-w-[20px] h-5 px-1 rounded-full bg-[var(--primary-color,#6366f1)] text-white text-[0.65rem] font-bold flex-shrink-0">{{ cards.length }}</span>
<span v-if="cards.length" class="inline-flex items-center justify-center min-w-[20px] h-5 px-1 rounded-full bg-[var(--primary-color,#6366f1)] text-white text-[0.65rem] font-bold shrink-0">{{ cards.length }}</span>
</div>
<!-- Skeleton -->
@@ -645,7 +645,7 @@ function isRecent(row) {
@click="abrirModalPacientesDaTag(t)"
>
<!-- Barra de cor -->
<div class="w-[3px] h-5 rounded-sm flex-shrink-0" :style="{ background: t.cor || '#94a3b8' }" />
<div class="w-[3px] h-5 rounded-sm shrink-0" :style="{ background: t.cor || '#94a3b8' }" />
<div class="flex-1 min-w-0">
<div class="font-semibold text-[0.8rem] truncate text-[var(--text-color)] flex items-center gap-1.5">
@@ -657,11 +657,11 @@ function isRecent(row) {
<!-- Badge contagem com cor da tag -->
<span
class="inline-flex items-center justify-center min-w-[22px] h-[22px] px-1 rounded-full font-bold text-[0.68rem] flex-shrink-0"
class="inline-flex items-center justify-center min-w-[22px] h-[22px] px-1 rounded-full font-bold text-[0.68rem] shrink-0"
:style="{ background: `${t.cor ? (t.cor.startsWith('#') ? t.cor : '#' + t.cor) : '#6366f1'}18`, color: t.cor ? (t.cor.startsWith('#') ? t.cor : '#' + t.cor) : '#6366f1' }"
>{{ Number(t.pacientes_count ?? 0) }}</span
>
<i class="pi pi-chevron-right text-[0.6rem] opacity-30 group-hover:opacity-100 transition-all duration-150 flex-shrink-0" :style="{ color: 'var(--text-color-secondary)' }" />
<i class="pi pi-chevron-right text-[0.6rem] opacity-30 group-hover:opacity-100 transition-all duration-150 shrink-0" :style="{ color: 'var(--text-color-secondary)' }" />
</button>
</div>
@@ -787,7 +787,7 @@ function isRecent(row) {
>
<template #header>
<div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-lg flex items-center justify-center text-white font-bold text-base flex-shrink-0" :style="{ background: modalTagHex }">
<div class="w-9 h-9 rounded-lg flex items-center justify-center text-white font-bold text-base shrink-0" :style="{ background: modalTagHex }">
{{ (modalPacientes.tag?.nome || '?')[0].toUpperCase() }}
</div>
<div>
@@ -806,7 +806,7 @@ function isRecent(row) {
</IconField>
<div class="flex items-center gap-2">
<span class="inline-flex items-center px-2.5 py-1 rounded-full text-xs font-semibold" :style="{ background: `${modalTagHex}18`, color: modalTagHex }">{{ modalPacientes.items.length }} paciente(s)</span>
<Button icon="pi pi-refresh" outlined class="h-8 w-8 rounded-full flex-shrink-0" :style="{ borderColor: modalTagHex, color: modalTagHex }" @click="recarregarModalPacientes" />
<Button icon="pi pi-refresh" outlined class="h-8 w-8 rounded-full shrink-0" :style="{ borderColor: modalTagHex, color: modalTagHex }" @click="recarregarModalPacientes" />
</div>
</div>