diff --git a/src/layout/melissa/MelissaRelatorios.vue b/src/layout/melissa/MelissaRelatorios.vue
index 13ec3c6..b137e3f 100644
--- a/src/layout/melissa/MelissaRelatorios.vue
+++ b/src/layout/melissa/MelissaRelatorios.vue
@@ -12,12 +12,15 @@
* isoWeek/isoMonth + Chart.js).
*/
import { ref, computed, watch, onMounted, onBeforeUnmount } from 'vue';
+import { useToast } from 'primevue/usetoast';
import { supabase } from '@/lib/supabase/client';
import { useTenantStore } from '@/stores/tenantStore';
+import { exportSessionsToPDF, exportSessionsToXLSX, exportSessionsToCSV } from '@/services/reportExport.service';
// Chart/DataTable/Column/Tag/Skeleton: auto via PrimeVueResolver
const emit = defineEmits(['close']);
const tenantStore = useTenantStore();
+const toast = useToast();
// ── Breakpoints + drawer mobile ────────────────────────
const drawerOpen = ref(false);
@@ -251,6 +254,66 @@ function patientName(s) {
return s.patients?.nome_completo || '—';
}
+// ── Export PDF / Excel / CSV ──────────────────────────
+const exportingPdf = ref(false);
+const exportingXlsx = ref(false);
+
+function buildExportParams() {
+ const period = PERIOD_OPTIONS.find(p => p.key === selectedPeriod.value)?.label || '';
+ const normalized = sessionsFiltradas.value.map(s => ({
+ ...s,
+ paciente_nome: s.patients?.nome_completo || '—'
+ }));
+ return {
+ title: 'Relatório de Sessões',
+ subtitle: period,
+ sessions: normalized,
+ kpis: [
+ { label: 'Total', value: total.value },
+ { label: 'Realizadas', value: realizadas.value },
+ { label: 'Faltas', value: faltas.value },
+ { label: 'Canceladas', value: canceladas.value }
+ ],
+ tenantName: tenantStore.activeTenantName || tenantStore.tenant?.name || '',
+ terapeutaNome: tenantStore.user?.full_name || tenantStore.user?.email || ''
+ };
+}
+
+async function exportPdf() {
+ if (exportingPdf.value) return;
+ exportingPdf.value = true;
+ try {
+ const file = await exportSessionsToPDF(buildExportParams());
+ toast.add({ severity: 'success', summary: 'PDF gerado', detail: file, life: 2500 });
+ } catch (e) {
+ toast.add({ severity: 'error', summary: 'Erro ao gerar PDF', detail: e?.message || '', life: 4500 });
+ } finally {
+ exportingPdf.value = false;
+ }
+}
+
+async function exportXlsx() {
+ if (exportingXlsx.value) return;
+ exportingXlsx.value = true;
+ try {
+ const file = await exportSessionsToXLSX(buildExportParams());
+ toast.add({ severity: 'success', summary: 'Excel gerado', detail: file, life: 2500 });
+ } catch (e) {
+ toast.add({ severity: 'error', summary: 'Erro ao gerar Excel', detail: e?.message || '', life: 4500 });
+ } finally {
+ exportingXlsx.value = false;
+ }
+}
+
+function exportCsv() {
+ try {
+ const file = exportSessionsToCSV(buildExportParams());
+ toast.add({ severity: 'success', summary: 'CSV gerado', detail: file, life: 2500 });
+ } catch (e) {
+ toast.add({ severity: 'error', summary: 'Erro ao gerar CSV', detail: e?.message || '', life: 4500 });
+ }
+}
+
watch(selectedPeriod, () => {
statusFilter.value = null;
loadSessions();
@@ -308,6 +371,30 @@ onBeforeUnmount(() => {
{{ periodLabel }}
+
+
+