Ajuste usuarios - Inicio agenda

This commit is contained in:
Leonardo
2026-02-23 18:57:40 -03:00
parent 89b4ecaba1
commit b1c0cb47c0
11 changed files with 2244 additions and 576 deletions

View File

@@ -1,12 +1,28 @@
// src/features/agenda/services/agendaRepository.js
import { supabase } from '@/lib/supabase/client'
import { useTenantStore } from '@/stores/tenantStore'
export async function getMyAgendaSettings () {
function assertValidTenantId (tenantId) {
if (!tenantId || tenantId === 'null' || tenantId === 'undefined') {
throw new Error('Tenant ativo inválido. Selecione a clínica/tenant antes de carregar a agenda.')
}
}
async function getUid () {
const { data: userRes, error: userErr } = await supabase.auth.getUser()
if (userErr) throw userErr
const uid = userRes?.user?.id
if (!uid) throw new Error('Usuário não autenticado.')
return uid
}
/**
* Configurações da agenda (por owner)
* Se você decidir que configurações são por tenant também, adicionamos tenant_id aqui.
*/
export async function getMyAgendaSettings () {
const uid = await getUid()
const { data, error } = await supabase
.from('agenda_configuracoes')
@@ -18,16 +34,23 @@ export async function getMyAgendaSettings () {
return data
}
export async function listMyAgendaEvents ({ startISO, endISO }) {
const { data: userRes, error: userErr } = await supabase.auth.getUser()
if (userErr) throw userErr
/**
* Lista agenda do terapeuta (somente do owner logado) dentro do tenant ativo.
* Isso impede misturar eventos caso o terapeuta atue em múltiplas clínicas.
*/
export async function listMyAgendaEvents ({ startISO, endISO, tenantId: tenantIdArg } = {}) {
const uid = await getUid()
const uid = userRes?.user?.id
if (!uid) throw new Error('Usuário não autenticado.')
const tenantStore = useTenantStore()
const tenantId = tenantIdArg || tenantStore.activeTenantId
assertValidTenantId(tenantId)
if (!startISO || !endISO) throw new Error('Intervalo inválido (startISO/endISO).')
const { data, error } = await supabase
.from('agenda_eventos')
.select('*')
.eq('tenant_id', tenantId)
.eq('owner_id', uid)
.gte('inicio_em', startISO)
.lt('inicio_em', endISO)
@@ -37,13 +60,26 @@ export async function listMyAgendaEvents ({ startISO, endISO }) {
return data || []
}
export async function listClinicEvents ({ ownerIds, startISO, endISO }) {
/**
* Lista eventos para mosaico da clínica (admin/secretaria) dentro de um tenant específico.
* IMPORTANTE: SEM tenant_id aqui vira vazamento multi-tenant.
*/
export async function listClinicEvents ({ tenantId, ownerIds, startISO, endISO }) {
assertValidTenantId(tenantId)
if (!ownerIds?.length) return []
if (!startISO || !endISO) throw new Error('Intervalo inválido (startISO/endISO).')
// Sanitiza ownerIds
const safeOwnerIds = ownerIds
.filter(id => typeof id === 'string' && id && id !== 'null' && id !== 'undefined')
if (!safeOwnerIds.length) return []
const { data, error } = await supabase
.from('agenda_eventos')
.select('*')
.in('owner_id', ownerIds)
.eq('tenant_id', tenantId)
.in('owner_id', safeOwnerIds)
.gte('inicio_em', startISO)
.lt('inicio_em', endISO)
.order('inicio_em', { ascending: true })
@@ -53,7 +89,7 @@ export async function listClinicEvents ({ ownerIds, startISO, endISO }) {
}
export async function listTenantStaff (tenantId) {
if (!tenantId || tenantId === 'null' || tenantId === 'undefined') return []
assertValidTenantId(tenantId)
const { data, error } = await supabase
.from('v_tenant_staff')
@@ -64,10 +100,33 @@ export async function listTenantStaff (tenantId) {
return data || []
}
/**
* Criação segura:
* - injeta tenant_id do tenantStore
* - injeta owner_id do usuário logado (ignora owner_id vindo de fora)
*
* Observação:
* - Para admin/secretária criar para outros owners, o ideal é ter uma função separada
* (ex.: createClinicAgendaEvento) que permita owner_id explicitamente.
* Por enquanto, deixo esta função como "safe default" para terapeuta.
*/
export async function createAgendaEvento (payload) {
const uid = await getUid()
const tenantStore = useTenantStore()
const tenantId = tenantStore.activeTenantId
assertValidTenantId(tenantId)
if (!payload) throw new Error('Payload vazio.')
const insertPayload = {
...payload,
tenant_id: tenantId,
owner_id: uid
}
const { data, error } = await supabase
.from('agenda_eventos')
.insert(payload)
.insert(insertPayload)
.select('*')
.single()
@@ -75,11 +134,24 @@ export async function createAgendaEvento (payload) {
return data
}
export async function updateAgendaEvento (id, patch) {
/**
* Atualização segura:
* - filtra por id + tenant_id (evita update cruzado por acidente)
* RLS deve reforçar isso no banco.
*/
export async function updateAgendaEvento (id, patch, { tenantId: tenantIdArg } = {}) {
if (!id) throw new Error('ID inválido.')
if (!patch) throw new Error('Patch vazio.')
const tenantStore = useTenantStore()
const tenantId = tenantIdArg || tenantStore.activeTenantId
assertValidTenantId(tenantId)
const { data, error } = await supabase
.from('agenda_eventos')
.update(patch)
.eq('id', id)
.eq('tenant_id', tenantId)
.select('*')
.single()
@@ -87,12 +159,61 @@ export async function updateAgendaEvento (id, patch) {
return data
}
export async function deleteAgendaEvento (id) {
/**
* Delete seguro:
* - filtra por id + tenant_id
*/
export async function deleteAgendaEvento (id, { tenantId: tenantIdArg } = {}) {
if (!id) throw new Error('ID inválido.')
const tenantStore = useTenantStore()
const tenantId = tenantIdArg || tenantStore.activeTenantId
assertValidTenantId(tenantId)
const { error } = await supabase
.from('agenda_eventos')
.delete()
.eq('id', id)
.eq('tenant_id', tenantId)
if (error) throw error
return true
}
// Adicione no mesmo arquivo: src/features/agenda/services/agendaRepository.js
/**
* Criação para a área da clínica (admin/secretária):
* - exige tenantId explícito (ou cai no tenantStore)
* - permite definir owner_id (terapeuta dono do compromisso)
*
* Segurança real deve ser garantida por RLS:
* - clinic_admin/tenant_admin pode criar para qualquer owner dentro do tenant
* - therapist não deve conseguir passar daqui (guard + RLS)
*/
export async function createClinicAgendaEvento (payload, { tenantId: tenantIdArg } = {}) {
const tenantStore = useTenantStore()
const tenantId = tenantIdArg || tenantStore.activeTenantId
assertValidTenantId(tenantId)
if (!payload) throw new Error('Payload vazio.')
const ownerId = payload.owner_id
if (!ownerId || ownerId === 'null' || ownerId === 'undefined') {
throw new Error('owner_id é obrigatório para criação pela clínica.')
}
const insertPayload = {
...payload,
tenant_id: tenantId
}
const { data, error } = await supabase
.from('agenda_eventos')
.insert(insertPayload)
.select('*')
.single()
if (error) throw error
return data
}