a7f6bcbe66
- useTenantDb composable + lib/supabase/tenantClient (tenantDb/tenantSchemaName)
- tenantStore: getters activeTenantSlug/activeTenantSchema; my_tenants() RPC
passa a devolver slug+nome (migration 07)
- codemod scripts/codemod-tenant-db.py: supabase.from('<84 tabelas + 6 views
tenant>') -> tenantDb().from(...) em 139 arquivos (777 chamadas), remove
.eq('tenant_id') das cadeias tenant (173)
- passada manual (4 agentes): remove tenant_id de payloads insert/upsert/update,
selects, .or/.is de defaults; onConflict ajustado pros uniques sem tenant_id
(singletons usam 'singleton'); realtime de tabelas tenant aponta pro schema
do tenant ativo; repos dropam tenant_id defensivamente de payloads externos
- agendaSelects: tenant_id fora do AGENDA_EVENT_SELECT (quebraria PostgREST)
- zero embeds cross-schema (todos FK embeds sao tenant->tenant ou global->global)
- build de producao passa; 67 .js checados
Pendente (fora do escopo F3, sao cross-tenant/anon -> F4/F6):
- AgendadorPublicoPage (anon, resolve tenant por link_slug)
- Saas{Feriados,NotificationTemplates,DocumentTemplates,Whatsapp}Page
(gerenciam defaults do sistema / views cross-tenant)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
120 lines
4.4 KiB
JavaScript
120 lines
4.4 KiB
JavaScript
/*
|
|
|--------------------------------------------------------------------------
|
|
| Agência PSI
|
|
|--------------------------------------------------------------------------
|
|
| Arquivo: src/composables/useSessionReminders.js
|
|
| Data: 2026-04-21
|
|
|
|
|
| Lembretes automáticos de sessão (CRM Grupo 2.4).
|
|
|--------------------------------------------------------------------------
|
|
*/
|
|
|
|
import { ref } from 'vue';
|
|
import { supabase } from '@/lib/supabase/client';
|
|
import { tenantDb } from '@/lib/supabase/tenantClient';
|
|
import { useTenantStore } from '@/stores/tenantStore';
|
|
|
|
const DEFAULTS = {
|
|
enabled: false,
|
|
send_24h: true,
|
|
send_2h: true,
|
|
template_24h: 'Oi {{nome_paciente}}! 👋 Lembrando da sua sessão amanhã, {{data_sessao}} às {{hora_sessao}}. Até lá!',
|
|
template_2h: 'Oi {{nome_paciente}}! Sua sessão começa em 2 horas, às {{hora_sessao}}. Te espero! 😊',
|
|
quiet_hours_enabled: true,
|
|
quiet_hours_start: '22:00',
|
|
quiet_hours_end: '08:00',
|
|
respect_opt_out: true
|
|
};
|
|
|
|
export function useSessionReminders() {
|
|
const tenantStore = useTenantStore();
|
|
const settings = ref({ ...DEFAULTS });
|
|
const recentLogs = ref([]);
|
|
const loading = ref(false);
|
|
const saving = ref(false);
|
|
|
|
async function load() {
|
|
const tenantId = tenantStore.activeTenantId;
|
|
if (!tenantId) return;
|
|
loading.value = true;
|
|
try {
|
|
const [settingsRes, logsRes] = await Promise.all([
|
|
tenantDb().from('session_reminder_settings')
|
|
.select('*')
|
|
.maybeSingle(),
|
|
tenantDb().from('session_reminder_logs')
|
|
.select('id, event_id, reminder_type, sent_at, provider, skip_reason, to_phone')
|
|
.order('sent_at', { ascending: false })
|
|
.limit(30)
|
|
]);
|
|
|
|
if (settingsRes.data) {
|
|
settings.value = {
|
|
enabled: !!settingsRes.data.enabled,
|
|
send_24h: !!settingsRes.data.send_24h,
|
|
send_2h: !!settingsRes.data.send_2h,
|
|
template_24h: settingsRes.data.template_24h || DEFAULTS.template_24h,
|
|
template_2h: settingsRes.data.template_2h || DEFAULTS.template_2h,
|
|
quiet_hours_enabled: !!settingsRes.data.quiet_hours_enabled,
|
|
quiet_hours_start: String(settingsRes.data.quiet_hours_start || DEFAULTS.quiet_hours_start).slice(0, 5),
|
|
quiet_hours_end: String(settingsRes.data.quiet_hours_end || DEFAULTS.quiet_hours_end).slice(0, 5),
|
|
respect_opt_out: !!settingsRes.data.respect_opt_out
|
|
};
|
|
} else {
|
|
settings.value = { ...DEFAULTS };
|
|
}
|
|
|
|
recentLogs.value = logsRes.data || [];
|
|
} catch (e) {
|
|
console.error('[useSessionReminders] load:', e?.message);
|
|
} finally {
|
|
loading.value = false;
|
|
}
|
|
}
|
|
|
|
async function save() {
|
|
const tenantId = tenantStore.activeTenantId;
|
|
if (!tenantId) return { ok: false, error: 'no_tenant' };
|
|
|
|
const payload = { ...settings.value };
|
|
payload.template_24h = String(payload.template_24h || '').trim().slice(0, 2000);
|
|
payload.template_2h = String(payload.template_2h || '').trim().slice(0, 2000);
|
|
if (!payload.template_24h || !payload.template_2h) {
|
|
return { ok: false, error: 'Templates não podem ficar vazios' };
|
|
}
|
|
|
|
saving.value = true;
|
|
try {
|
|
const { error } = await tenantDb().from('session_reminder_settings')
|
|
.upsert({ ...payload }, { onConflict: 'singleton' });
|
|
if (error) throw error;
|
|
return { ok: true };
|
|
} catch (e) {
|
|
return { ok: false, error: e?.message || 'Falha ao salvar' };
|
|
} finally {
|
|
saving.value = false;
|
|
}
|
|
}
|
|
|
|
// Dispara manualmente (pra teste ou pra catch-up de eventos perdidos)
|
|
async function runNow() {
|
|
try {
|
|
const { data, error } = await supabase.functions.invoke('send-session-reminders', { body: {} });
|
|
if (error) throw error;
|
|
return { ok: true, stats: data?.stats || null };
|
|
} catch (e) {
|
|
return { ok: false, error: e?.message || 'Falha ao executar' };
|
|
}
|
|
}
|
|
|
|
return {
|
|
settings,
|
|
recentLogs,
|
|
loading,
|
|
saving,
|
|
load,
|
|
save,
|
|
runNow
|
|
};
|
|
}
|