Agenda, Agendador, Configurações
This commit is contained in:
@@ -467,9 +467,10 @@
|
||||
</template>
|
||||
</Column>
|
||||
|
||||
<Column :key="'col-acoes'" header="Ações" style="width: 16rem;" frozen alignFrozen="right">
|
||||
<Column :key="'col-acoes'" header="Ações" style="width: 20rem;" frozen alignFrozen="right">
|
||||
<template #body="{ data }">
|
||||
<div class="flex gap-2 justify-end">
|
||||
<Button label="Sessões" icon="pi pi-calendar" size="small" severity="info" outlined @click="abrirSessoes(data)" />
|
||||
<Button label="Prontuário" icon="pi pi-file" size="small" @click="openProntuario(data)" />
|
||||
<Button icon="pi pi-pencil" severity="secondary" outlined size="small" v-tooltip.top="'Editar'" @click="goEdit(data)" />
|
||||
<Button icon="pi pi-trash" severity="danger" outlined size="small" v-tooltip.top="'Excluir'" @click="confirmDeleteOne(data)" />
|
||||
@@ -521,7 +522,8 @@
|
||||
</div>
|
||||
|
||||
<!-- Ações -->
|
||||
<div class="mt-3 flex gap-2 justify-end">
|
||||
<div class="mt-3 flex gap-2 justify-end flex-wrap">
|
||||
<Button label="Sessões" icon="pi pi-calendar" size="small" severity="info" outlined @click="abrirSessoes(pat)" />
|
||||
<Button label="Prontuário" icon="pi pi-file" size="small" @click="openProntuario(pat)" />
|
||||
<Button icon="pi pi-pencil" severity="secondary" outlined size="small" @click="goEdit(pat)" />
|
||||
<Button icon="pi pi-trash" severity="danger" outlined size="small" @click="confirmDeleteOne(pat)" />
|
||||
@@ -585,6 +587,61 @@
|
||||
/>
|
||||
|
||||
<ConfirmDialog />
|
||||
|
||||
<!-- ── DIALOG SESSÕES DO PACIENTE ─────────────────────────── -->
|
||||
<Dialog
|
||||
v-model:visible="sessoesOpen"
|
||||
modal
|
||||
:draggable="false"
|
||||
:style="{ width: '700px', maxWidth: '96vw' }"
|
||||
:header="sessoesPaciente ? `Sessões — ${sessoesPaciente.nome_completo}` : 'Sessões'"
|
||||
>
|
||||
<div v-if="sessoesLoading" class="flex justify-center py-8">
|
||||
<ProgressSpinner />
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<!-- Recorrências ativas -->
|
||||
<div v-if="recorrencias.length" class="mb-5">
|
||||
<div class="text-sm font-semibold text-color-secondary mb-2 flex items-center gap-2">
|
||||
<i class="pi pi-sync" /> Recorrências
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div
|
||||
v-for="r in recorrencias"
|
||||
:key="r.id"
|
||||
class="sess-rec-card"
|
||||
>
|
||||
<Tag :value="r.status === 'ativo' ? 'Ativa' : 'Encerrada'" :severity="r.status === 'ativo' ? 'success' : 'secondary'" />
|
||||
<span class="text-sm">{{ fmtRecorrencia(r) }}</span>
|
||||
<span class="text-xs text-color-secondary ml-auto">
|
||||
{{ r.start_date }} {{ r.end_date ? `→ ${r.end_date}` : '(em aberto)' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Lista de sessões -->
|
||||
<div class="text-sm font-semibold text-color-secondary mb-2 flex items-center gap-2">
|
||||
<i class="pi pi-calendar" /> Sessões ({{ sessoesLista.length }})
|
||||
</div>
|
||||
|
||||
<div v-if="sessoesLista.length === 0" class="text-center py-6 text-color-secondary text-sm">
|
||||
Nenhuma sessão encontrada para este paciente.
|
||||
</div>
|
||||
|
||||
<div v-else class="sess-list">
|
||||
<div v-for="ev in sessoesLista" :key="ev.id" class="sess-item">
|
||||
<div class="flex items-center gap-3">
|
||||
<Tag :value="ev.status || 'agendado'" :severity="statusSessaoSev(ev.status)" />
|
||||
<span class="font-semibold text-sm">{{ fmtDataSessao(ev.inicio_em) }}</span>
|
||||
<Tag v-if="ev.modalidade" :value="ev.modalidade" severity="secondary" class="ml-auto" />
|
||||
</div>
|
||||
<div v-if="ev.titulo" class="text-xs text-color-secondary mt-1">{{ ev.titulo }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
@@ -602,6 +659,59 @@ import ProgressSpinner from 'primevue/progressspinner'
|
||||
import PatientProntuario from '@/features/patients/prontuario/PatientProntuario.vue'
|
||||
import ComponentCadastroRapido from '@/components/ComponentCadastroRapido.vue'
|
||||
|
||||
// ── Sessões do paciente ──────────────────────────────────────────
|
||||
const sessoesOpen = ref(false)
|
||||
const sessoesPaciente = ref(null) // { id, nome_completo }
|
||||
const sessoesLoading = ref(false)
|
||||
const sessoesLista = ref([])
|
||||
const recorrencias = ref([])
|
||||
|
||||
const MESES_BR = ['Jan','Fev','Mar','Abr','Mai','Jun','Jul','Ago','Set','Out','Nov','Dez']
|
||||
function fmtDataSessao (iso) {
|
||||
if (!iso) return '—'
|
||||
const d = new Date(iso)
|
||||
return `${String(d.getDate()).padStart(2,'0')} ${MESES_BR[d.getMonth()]} ${d.getFullYear()} ${String(d.getHours()).padStart(2,'0')}:${String(d.getMinutes()).padStart(2,'0')}`
|
||||
}
|
||||
function statusSessaoSev (st) {
|
||||
return { agendado: 'info', realizado: 'success', cancelado: 'danger', faltou: 'warn' }[st] || 'secondary'
|
||||
}
|
||||
|
||||
async function abrirSessoes (pat) {
|
||||
sessoesPaciente.value = pat
|
||||
sessoesOpen.value = true
|
||||
sessoesLoading.value = true
|
||||
sessoesLista.value = []
|
||||
recorrencias.value = []
|
||||
try {
|
||||
const [evts, recs] = await Promise.all([
|
||||
supabase
|
||||
.from('agenda_eventos')
|
||||
.select('id, titulo, tipo, status, inicio_em, fim_em, modalidade')
|
||||
.eq('patient_id', pat.id)
|
||||
.order('inicio_em', { ascending: false })
|
||||
.limit(100),
|
||||
supabase
|
||||
.from('recurrence_rules')
|
||||
.select('id, type, interval, weekdays, start_date, end_date, start_time, duration_min, status')
|
||||
.eq('patient_id', pat.id)
|
||||
.order('start_date', { ascending: false }),
|
||||
])
|
||||
sessoesLista.value = evts.data || []
|
||||
recorrencias.value = recs.data || []
|
||||
} catch (e) {
|
||||
console.error('Erro ao carregar sessões:', e)
|
||||
} finally {
|
||||
sessoesLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const DIAS_SEMANA = ['Dom','Seg','Ter','Qua','Qui','Sex','Sáb']
|
||||
function fmtRecorrencia (r) {
|
||||
const dias = (r.weekdays || []).map(d => DIAS_SEMANA[d]).join(', ')
|
||||
const freq = r.type === 'weekly' && r.interval === 2 ? 'Quinzenal' : r.type === 'weekly' ? 'Semanal' : 'Personalizado'
|
||||
return `${freq} · ${dias} · ${r.start_time?.slice(0,5) || '—'} · ${r.duration_min || 50}min`
|
||||
}
|
||||
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
@@ -1328,4 +1438,25 @@ function updateKpis() {
|
||||
/* Fade */
|
||||
.fade-enter-active, .fade-leave-active { transition: opacity 0.15s ease; }
|
||||
.fade-enter-from, .fade-leave-to { opacity: 0; }
|
||||
|
||||
/* ── Dialog Sessões ──────────────────────────────────────────── */
|
||||
.sess-list {
|
||||
display: flex; flex-direction: column; gap: .5rem;
|
||||
max-height: 55vh; overflow-y: auto;
|
||||
padding-right: .25rem;
|
||||
}
|
||||
.sess-item {
|
||||
padding: 10px 14px;
|
||||
border: 1px solid var(--surface-border);
|
||||
border-radius: .75rem;
|
||||
background: var(--surface-ground);
|
||||
}
|
||||
.sess-rec-card {
|
||||
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
|
||||
padding: 10px 14px;
|
||||
border: 1px solid var(--surface-border);
|
||||
border-radius: .75rem;
|
||||
background: var(--surface-ground);
|
||||
font-size: .85rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user