carousel, agenda arquivados, agenda cor, agenda arquivados, grupos pacientes, pacientes arquivados - desativados, sessoes verificadas, ajuste notificações, Prontuario, Agenda Animation, Menu Profile, bagdes Profile, Offline
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
// src/composables/useMenuBadges.js
|
||||
// Singleton — contadores para badges do menu (agenda hoje, cadastros e agendamentos recebidos)
|
||||
|
||||
import { ref } from 'vue'
|
||||
import { supabase } from '@/lib/supabase/client'
|
||||
import { useTenantStore } from '@/stores/tenantStore'
|
||||
|
||||
// ─── estado compartilhado ──────────────────────────────────
|
||||
const agendaHoje = ref(0)
|
||||
const cadastrosRecebidos = ref(0)
|
||||
const agendamentosRecebidos = ref(0)
|
||||
|
||||
let _timer = null
|
||||
let _started = false
|
||||
|
||||
async function _refresh () {
|
||||
try {
|
||||
const tenantStore = useTenantStore()
|
||||
const role = tenantStore.role
|
||||
const tenantId = tenantStore.activeTenantId || tenantStore.tenantId || tenantStore.tenant?.id || null
|
||||
|
||||
const { data: authData } = await supabase.auth.getUser()
|
||||
const ownerId = authData?.user?.id
|
||||
if (!ownerId) return
|
||||
|
||||
const isClinic = role === 'clinic_admin' || role === 'tenant_admin' || role === 'clinic'
|
||||
|
||||
const now = new Date()
|
||||
const y = now.getFullYear(), mo = now.getMonth(), d = now.getDate()
|
||||
const startDay = new Date(y, mo, d).toISOString()
|
||||
const endDay = new Date(y, mo, d + 1).toISOString()
|
||||
|
||||
// 1. Agenda hoje
|
||||
{
|
||||
let q = supabase
|
||||
.from('agenda_eventos')
|
||||
.select('id', { count: 'exact', head: true })
|
||||
.gte('inicio_em', startDay)
|
||||
.lt('inicio_em', endDay)
|
||||
if (isClinic && tenantId) q = q.eq('tenant_id', tenantId)
|
||||
else q = q.eq('owner_id', ownerId)
|
||||
const { count } = await q
|
||||
agendaHoje.value = count || 0
|
||||
}
|
||||
|
||||
// 2. Cadastros recebidos (status = 'new') — RLS filtra pelo owner
|
||||
{
|
||||
const { count } = await supabase
|
||||
.from('patient_intake_requests')
|
||||
.select('id', { count: 'exact', head: true })
|
||||
.eq('status', 'new')
|
||||
cadastrosRecebidos.value = count || 0
|
||||
}
|
||||
|
||||
// 3. Agendamentos recebidos (status = 'pendente')
|
||||
{
|
||||
let q = supabase
|
||||
.from('agendador_solicitacoes')
|
||||
.select('id', { count: 'exact', head: true })
|
||||
.eq('status', 'pendente')
|
||||
if (isClinic && tenantId) q = q.eq('tenant_id', tenantId)
|
||||
else q = q.eq('owner_id', ownerId)
|
||||
const { count } = await q
|
||||
agendamentosRecebidos.value = count || 0
|
||||
}
|
||||
} catch {
|
||||
// badge falhar não deve quebrar a navegação
|
||||
}
|
||||
}
|
||||
|
||||
// ─── API pública ───────────────────────────────────────────
|
||||
export function useMenuBadges () {
|
||||
if (!_started) {
|
||||
_started = true
|
||||
_refresh()
|
||||
_timer = setInterval(_refresh, 5 * 60 * 1000) // atualiza a cada 5 min
|
||||
}
|
||||
|
||||
return {
|
||||
agendaHoje,
|
||||
cadastrosRecebidos,
|
||||
agendamentosRecebidos,
|
||||
refresh: _refresh
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
// src/composables/usePatientLifecycle.js
|
||||
import { supabase } from '@/lib/supabase/client'
|
||||
|
||||
export function usePatientLifecycle () {
|
||||
|
||||
async function canDelete (patientId) {
|
||||
const { data, error } = await supabase.rpc('can_delete_patient', { p_patient_id: patientId })
|
||||
if (error) return false
|
||||
return !!data
|
||||
}
|
||||
|
||||
async function deletePatient (patientId) {
|
||||
const { data, error } = await supabase.rpc('safe_delete_patient', { p_patient_id: patientId })
|
||||
if (error) return { ok: false, error: 'rpc_error', message: error.message }
|
||||
return data // { ok, error?, message? }
|
||||
}
|
||||
|
||||
async function checkActiveSchedule (patientId) {
|
||||
const now = new Date().toISOString()
|
||||
const [evts, recs] = await Promise.all([
|
||||
supabase
|
||||
.from('agenda_eventos')
|
||||
.select('id', { count: 'exact', head: true })
|
||||
.eq('patient_id', patientId)
|
||||
.eq('status', 'agendado')
|
||||
.gt('inicio_em', now),
|
||||
supabase
|
||||
.from('recurrence_rules')
|
||||
.select('id', { count: 'exact', head: true })
|
||||
.eq('patient_id', patientId)
|
||||
.eq('status', 'ativo')
|
||||
])
|
||||
return {
|
||||
hasFutureSessions: (evts.count ?? 0) > 0,
|
||||
hasActiveRecurrence: (recs.count ?? 0) > 0
|
||||
}
|
||||
}
|
||||
|
||||
async function deactivatePatient (patientId) {
|
||||
const { error } = await supabase
|
||||
.from('patients')
|
||||
.update({ status: 'Inativo', updated_at: new Date().toISOString() })
|
||||
.eq('id', patientId)
|
||||
return error ? { ok: false, error } : { ok: true }
|
||||
}
|
||||
|
||||
async function archivePatient (patientId) {
|
||||
const { error } = await supabase
|
||||
.from('patients')
|
||||
.update({ status: 'Arquivado', updated_at: new Date().toISOString() })
|
||||
.eq('id', patientId)
|
||||
return error ? { ok: false, error } : { ok: true }
|
||||
}
|
||||
|
||||
async function reactivatePatient (patientId) {
|
||||
const { error } = await supabase
|
||||
.from('patients')
|
||||
.update({ status: 'Ativo', updated_at: new Date().toISOString() })
|
||||
.eq('id', patientId)
|
||||
return error ? { ok: false, error } : { ok: true }
|
||||
}
|
||||
|
||||
return { canDelete, deletePatient, checkActiveSchedule, deactivatePatient, archivePatient, reactivatePatient }
|
||||
}
|
||||
|
||||
// ─── Helper puro — não precisa de instância do composable ───────────────────
|
||||
export function getPatientAgendaPermissions (status) {
|
||||
return {
|
||||
canCreateSession: !['Inativo', 'Arquivado'].includes(status),
|
||||
canReschedule: !['Inativo'].includes(status),
|
||||
canEditPastSession: !['Arquivado'].includes(status),
|
||||
canCreateRecurrence: !['Inativo', 'Arquivado'].includes(status),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user