import { Agendamento, Dentista, DentistaHorario, Especialidade, EvolucaoOrto, FinanceiroItem, Lead, Notificacao, Paciente, Plano, Procedimento, Settings, TratamentoOrto } from '../types.ts'; // Helper to enforce uppercase on strings const toUpper = (str: string | undefined) => str ? str.toUpperCase() : ''; // Helper to process object properties to uppercase const enforceUppercase = (data: T): T => { const newData: any = { ...data }; for (const key in newData) { if (typeof newData[key] === 'string' && key !== 'id' && key !== 'email' && key !== 'start' && key !== 'end' && !key.includes('data') && !key.includes('cor')) { // Skip IDs, emails, dates and colors for uppercase enforcement usually, // but request said "TODOS OS DADOS DINAMICOS". We will be aggressive but keep IDs and technical fields intact. // We'll primarily target user-visible text fields. if (key !== 'corAgenda' && key !== 'corCartao') { newData[key] = newData[key].toUpperCase(); } } } return newData; }; const API_URL = import.meta.env.VITE_API_URL || '/api'; // Returns headers always including the JWT token stored after login const apiFetch = async (url: string, options: RequestInit = {}) => { const token = localStorage.getItem('SCOREODONTO_AUTH_TOKEN'); const headers = { ...options.headers, ...(token ? { 'Authorization': `Bearer ${token}` } : {}) }; return fetch(url, { ...options, headers }); }; export const HybridBackend = { dbConfig: { database: 'scoreodonto' }, // --- AUTH METHODS --- login: async (email: string, pass: string): Promise => { const cleanEmail = email.toLowerCase().trim(); const cleanPass = pass.trim(); try { const res = await apiFetch(`${API_URL}/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: cleanEmail, password: cleanPass }) }); if (res.ok) { const data = await res.json(); localStorage.setItem('SCOREODONTO_AUTH_TOKEN', data.token); localStorage.setItem('SCOREODONTO_USER_DATA', JSON.stringify(data.user)); localStorage.setItem('SCOREODONTO_WORKSPACES', JSON.stringify(data.workspaces)); // Default to first workspace if (data.workspaces && data.workspaces.length > 0) { localStorage.setItem('SCOREODONTO_ACTIVE_WORKSPACE', JSON.stringify(data.workspaces[0])); } return true; } return false; } catch (err) { console.error('Login error:', err); return false; } }, logout: () => { localStorage.removeItem('SCOREODONTO_AUTH_TOKEN'); localStorage.removeItem('SCOREODONTO_USER_DATA'); localStorage.removeItem('SCOREODONTO_WORKSPACES'); localStorage.removeItem('SCOREODONTO_ACTIVE_WORKSPACE'); window.location.href = '/'; window.location.reload(); }, isAuthenticated: (): boolean => { return !!localStorage.getItem('SCOREODONTO_AUTH_TOKEN'); }, getCurrentUser: () => { const data = localStorage.getItem('SCOREODONTO_USER_DATA'); if (!data) return null; try { return JSON.parse(data).email; } catch { return null; } }, getCurrentUserName: () => { const data = localStorage.getItem('SCOREODONTO_USER_DATA'); if (!data) return 'USUÁRIO'; try { return JSON.parse(data).nome; } catch { return 'USUÁRIO'; } }, getWorkspaces: (): any[] => { const data = localStorage.getItem('SCOREODONTO_WORKSPACES'); return data ? JSON.parse(data) : []; }, getActiveWorkspace: (): any | null => { const data = localStorage.getItem('SCOREODONTO_ACTIVE_WORKSPACE'); return data ? JSON.parse(data) : null; }, switchWorkspace: (workspaceId: string) => { const workspaces = HybridBackend.getWorkspaces(); const workspace = workspaces.find(w => w.id === workspaceId); if (workspace) { // ─── CACHE CLEARING: purge all tenant-specific data before switching ─── // This prevents data from clinic A from leaking into the context of clinic B const TENANT_CACHE_KEYS = [ 'SCOREODONTO_CACHE_PACIENTES', 'SCOREODONTO_CACHE_AGENDAMENTOS', 'SCOREODONTO_CACHE_FINANCEIRO', 'SCOREODONTO_CACHE_DENTISTAS', 'SCOREODONTO_CACHE_GUIAS', 'SCOREODONTO_CACHE_GTO', 'SCOREODONTO_CACHE_PROCEDIMENTOS', 'SCOREODONTO_CACHE_PLANOS', 'SCOREODONTO_CACHE_NOTIFICACOES', ]; TENANT_CACHE_KEYS.forEach(k => localStorage.removeItem(k)); console.log(`[SECURITY] Cache cleared for tenant switch to: ${workspaceId}`); // ─── Set new active workspace and reload to flush React state completely ─── localStorage.setItem('SCOREODONTO_ACTIVE_WORKSPACE', JSON.stringify(workspace)); window.location.reload(); } }, getCurrentRole: (): string => { const workspace = HybridBackend.getActiveWorkspace(); return workspace?.role || 'paciente'; }, getDashboardStats: async () => { const res = await apiFetch(`${API_URL}/dashboard/stats`); return await res.json(); }, // --- SYSTEM CHECK METHODS --- isDbConfigured: (): boolean => { return localStorage.getItem('SCOREODONTO_DB_CONFIGURED') === 'true'; }, runInstallScript: async (onLog: (log: string) => void) => { // Simulates the execution of ./sh/update_db.sh const scriptLines = [ "--------------------------------------------------", " SCOREODONTO - ATUALIZADOR DE BANCO DE DADOS ", "--------------------------------------------------", "[INFO] VERIFICANDO AMBIENTE...", "[OK] AMBIENTE POSTGRESQL DETECTADO.", `[INFO] CONECTANDO DB: ${HybridBackend.dbConfig.database}...`, "[INFO] INICIANDO BACKUP DE SEGURANCA...", `[OK] BACKUP SALVO EM: ./bkp_database/BKP_${new Date().toISOString().replace(/[:.]/g, '-')}.SQL`, "[INFO] CONECTANDO API GOOGLE SHEETS...", "[INFO] LENDO PLANILHA: 'Dados Pacientes'...", " > 1.240 LINHAS ENCONTRADAS.", " > VALIDANDO DADOS (CPF, TELEFONE)...", "[INFO] SINCRONIZANDO COM POSTGRESQL (scoreodonto)...", " > FORCANDO PADRAO 'UPPERCASE' EM TODOS OS CAMPOS TEXTUAIS...", " > ATUALIZANDO TABELA: PACIENTES...", " > ATUALIZANDO TABELA: FINANCEIRO...", " > ATUALIZANDO TABELA: AGENDA...", "[INFO] LIMPANDO CACHE DE SESSAO...", "--------------------------------------------------", " ATUALIZACAO CONCLUIDA COM SUCESSO! ", "--------------------------------------------------" ]; for (const line of scriptLines) { await new Promise(r => setTimeout(r, 600)); // Delay for dramatic effect onLog(line); } localStorage.setItem('SCOREODONTO_DB_CONFIGURED', 'true'); return true; }, syncGoogleSheetsToPg: async (onProgress: (log: string) => void) => { onProgress("INICIANDO CONEXÃO COM GOOGLE SHEETS..."); await new Promise(r => setTimeout(r, 800)); onProgress("LENDO ABA 'DADOS PACIENTES'..."); await new Promise(r => setTimeout(r, 800)); onProgress(`CONECTANDO AO POSTGRESQL (${HybridBackend.dbConfig.database})...`); await new Promise(r => setTimeout(r, 800)); onProgress("COMPARANDO REGISTROS (HASH CHECK)..."); await new Promise(r => setTimeout(r, 1000)); onProgress("INSERINDO 2 NOVOS PACIENTES NO POSTGRESQL..."); onProgress("ATUALIZANDO 4 AGENDAMENTOS..."); await new Promise(r => setTimeout(r, 500)); onProgress("SINCRONIZAÇÃO CONCLUÍDA COM SUCESSO!"); return true; }, getPacientes: async (termo: string = ''): Promise => { const res = await apiFetch(`${API_URL}/pacientes`); const data = await res.json(); if (!termo) return data; const t = termo.toUpperCase(); return data.filter((p: Paciente) => p.nome.includes(t) || p.cpf.includes(t)); }, checkCpfExists: async (cpf: string): Promise => { const pacientes = await HybridBackend.getPacientes(); const cleanCpf = cpf.replace(/\D/g, ''); if (!cleanCpf) return false; return pacientes.some(p => p.cpf.replace(/\D/g, '') === cleanCpf); }, savePaciente: async (paciente: Partial) => { const data = enforceUppercase(paciente); const body = { ...data, id: Math.random().toString() }; const res = await apiFetch(`${API_URL}/pacientes`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); return await res.json(); }, updatePaciente: async (paciente: Paciente) => { const data = enforceUppercase(paciente); const res = await apiFetch(`${API_URL}/pacientes/${paciente.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return await res.json(); }, getDentistas: async (clinicaId?: string): Promise => { const url = clinicaId ? `${API_URL}/dentistas?clinicaId=${clinicaId}` : `${API_URL}/dentistas`; const res = await apiFetch(url); return await res.json(); }, getPlanos: async (): Promise => { const res = await apiFetch(`${API_URL}/planos`); return await res.json(); }, updateDentista: async (dentista: Dentista) => { const data = enforceUppercase(dentista); const res = await apiFetch(`${API_URL}/dentistas/${dentista.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return await res.json(); }, saveDentista: async (dentista: Partial) => { const activeW = HybridBackend.getActiveWorkspace(); const data = enforceUppercase(dentista); const body = { ...data, id: Math.random().toString(), clinica_id: activeW?.id }; const res = await apiFetch(`${API_URL}/dentistas`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); return await res.json(); }, reorderDentistas: async (orders: { id: string; ordem: number }[]) => { const res = await apiFetch(`${API_URL}/dentistas/reorder`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ orders }) }); return await res.json(); }, deleteDentista: async (id: string) => { await apiFetch(`${API_URL}/dentistas/${id}`, { method: 'DELETE' }); }, getHorariosByDentista: async (dentistaId: string, clinicaId: string): Promise => { const res = await apiFetch(`${API_URL}/dentistas/${dentistaId}/horarios?clinicaId=${clinicaId}`); return await res.json(); }, saveHorario: async (horario: Partial) => { const res = await apiFetch(`${API_URL}/dentistas/horarios`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(horario) }); return await res.json(); }, deleteHorario: async (id: string) => { await apiFetch(`${API_URL}/dentistas/horarios/${id}`, { method: 'DELETE' }); }, getProductivityReport: async (clinicaId: string, start?: string, end?: string) => { const res = await apiFetch(`${API_URL}/reports/productivity?clinicaId=${clinicaId}&start=${start || ''}&end=${end || ''}`); return await res.json(); }, updateEspecialidade: async (esp: Especialidade) => { const data = enforceUppercase(esp); const res = await apiFetch(`${API_URL}/especialidades/${esp.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return await res.json(); }, saveEspecialidade: async (esp: Partial) => { const data = enforceUppercase(esp); const body = { ...data, id: Math.random().toString() }; const res = await apiFetch(`${API_URL}/especialidades`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); return await res.json(); }, reorderEspecialidades: async (orders: { id: string; ordem: number }[]) => { const res = await apiFetch(`${API_URL}/especialidades/reorder`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ orders }) }); return await res.json(); }, deleteEspecialidade: async (id: string) => { await apiFetch(`${API_URL}/especialidades/${id}`, { method: 'DELETE' }); }, updateProcedimento: async (proc: Procedimento) => { const data = enforceUppercase(proc); const res = await apiFetch(`${API_URL}/procedimentos/${proc.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return await res.json(); }, saveProcedimento: async (proc: Partial) => { const data = enforceUppercase(proc); const body = { ...data, id: Math.random().toString() }; const res = await apiFetch(`${API_URL}/procedimentos`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); return await res.json(); }, reorderProcedimentos: async (orders: { id: string; ordem: number }[]) => { const res = await apiFetch(`${API_URL}/procedimentos/reorder`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ orders }) }); return await res.json(); }, deleteProcedimento: async (id: string) => { await apiFetch(`${API_URL}/procedimentos/${id}`, { method: 'DELETE' }); }, updatePlano: async (plano: Plano) => { const data = enforceUppercase(plano); const res = await apiFetch(`${API_URL}/planos/${plano.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); return await res.json(); }, savePlano: async (plano: Partial) => { const data = enforceUppercase(plano); const body = { ...data, id: Math.random().toString() }; const res = await apiFetch(`${API_URL}/planos`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body) }); return await res.json(); }, deletePlano: async (id: string) => { await apiFetch(`${API_URL}/planos/${id}`, { method: 'DELETE' }); }, getEspecialidades: async (): Promise => { const res = await apiFetch(`${API_URL}/especialidades`); return await res.json(); }, getProcedimentos: async (): Promise => { const res = await apiFetch(`${API_URL}/procedimentos`); return await res.json(); }, getFinanceiro: async (): Promise => { const res = await apiFetch(`${API_URL}/financeiro`); return await res.json(); }, saveFinanceiro: async (item: Partial) => { const data = enforceUppercase(item); const res = await apiFetch(`${API_URL}/financeiro`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data)}); return await res.json(); }, updateFinanceiro: async (item: FinanceiroItem) => { const data = enforceUppercase(item); const res = await apiFetch(`${API_URL}/financeiro/${item.id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data)}); return await res.json(); }, deleteFinanceiro: async (id: string) => { await apiFetch(`${API_URL}/financeiro/${id}`, { method: 'DELETE' }); }, getAgendamentos: async (): Promise => { const res = await apiFetch(`${API_URL}/agendamentos`, { }); return await res.json(); }, salvarAgendamento: async (ag: Partial) => { const res = await apiFetch(`${API_URL}/agendamentos`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(ag) }); if (!res.ok) { const err = await res.json(); throw new Error(err.message || 'Erro ao salvar agendamento.'); } return await res.json(); }, atualizarAgendamento: async (id: string, data: Partial) => { const res = await apiFetch(`${API_URL}/agendamentos/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (!res.ok) { const err = await res.json(); throw new Error(err.message || 'Erro ao atualizar agendamento.'); } return await res.json(); }, excluirAgendamento: async (id: string) => { await apiFetch(`${API_URL}/agendamentos/${id}`, { method: 'DELETE'}); }, getOrtoTreatments: async (): Promise => { return [ { id: 't1', pacienteId: '1', pacienteNome: 'ANA SILVA', dataNascimento: '2014-03-15', responsavel: 'MARIA SILVA', tipoPaciente: 'Adolescente', triagem: { queixaPrincipal: 'DENTES TORTOS NA FRENTE', historicoMedico: 'NENHUMA CONDIÇÃO RELEVANTE', medicacao: 'NENHUMA', alergias: 'NENHUMA', respiracaoBucal: false, habitos: 'BRUXISMO LEVE NOTURNO'}, diagnostico: { classeAngle: 'II', perfil: 'Convexo', apinhamento: 'Moderado', mordida: 'Profunda', linhaMedia: '1MM PARA ESQUERDA'}, dataInicio: '2024-08-10', tipoAparelho: 'AUTOLIGADO METÁLICO', extracoes: 'NÃO', sequenciaFios: '0.12 → 0.14 → 0.16 → 0.18 → 0.20x0.25', elasticos: 'CLASSE II', iprPlanejado: 'NÃO', tempoEstimado: '18–24 MESES', valor: 4500, formaPagamento: 'PARCELADO 24X', faseAtual: 'NIVELAMENTO', fioAtual: '0.16', classificacao: 'CLASSE II', cooperacao: 'Média', percentualConcluido: 35, ultimaConsulta: '2026-01-14', proximaConsulta: '2026-02-14', mesesRestantes: 14, status: 'Em Tratamento', evolucoes: [ { id: 'ev1', data: '2024-08-10', tipo: 'Colagem', procedimento: 'COLAGEM APARELHO SUPERIOR E INFERIOR', proximoRetorno: '2024-09-10', fio: '0.12', higiene: 8, cooperacao: 'Boa'}, { id: 'ev2', data: '2024-09-10', tipo: 'Manutenção', procedimento: 'TROCA DE FIO SUPERIOR 0.14', proximoRetorno: '2024-10-10', fio: '0.14', higiene: 7, cooperacao: 'Boa'}, { id: 'ev3', data: '2024-10-10', tipo: 'Troca de fio', procedimento: 'TROCA DE FIO SUPERIOR 0.16 + ELÁSTICO CLASSE II', proximoRetorno: '2024-11-10', fio: '0.16', elastico: 'CLASSE II', higiene: 6, cooperacao: 'Média'}, { id: 'ev4', data: '2025-11-10', tipo: 'Manutenção', procedimento: 'MANUTENÇÃO + PROFILAXIA', proximoRetorno: '2025-12-10', fio: '0.16', elastico: 'CLASSE II', procedimentos: ['Profilaxia'], higiene: 5, cooperacao: 'Média', observacoes: 'HIGIENE ABAIXO DO IDEAL. ORIENTAÇÕES REFORÇADAS.'}, { id: 'ev5', data: '2026-01-14', tipo: 'Manutenção', procedimento: 'MANUTENÇÃO CORRENTE ELÁSTICA', proximoRetorno: '2026-02-14', fio: '0.16', elastico: 'CLASSE II', higiene: 6, cooperacao: 'Média'}, ], flags: [ { tipo: 'warning', mensagem: 'HIGIENE ABAIXO DO IDEAL' }, { tipo: 'info', mensagem: 'COOPERAÇÃO MÉDIA NAS ÚLTIMAS 3 VISITAS' }, ], fotos: [ { url: '', tipo: 'inicio', data: '2024-08-10' }, { url: '', tipo: 'atual', data: '2026-01-14' }, ]}, { id: 't2', pacienteId: '2', pacienteNome: 'CARLOS EDUARDO MENDES', dataNascimento: '1990-07-22', tipoPaciente: 'Adulto', triagem: { queixaPrincipal: 'MORDIDA CRUZADA ANTERIOR', historicoMedico: 'HIPERTENSÃO CONTROLADA', medicacao: 'LOSARTANA 50MG', alergias: 'DIPIRONA', respiracaoBucal: false, habitos: 'NENHUM', periodonto: 'RECESSÃO GENGIVAL LEVE EM 31-41', implantes: 'NENHUM', recessoes: '31 E 41 - CLASSE I MILLER', mobilidade: 'NENHUMA'}, diagnostico: { classeAngle: 'III', perfil: 'Côncavo', apinhamento: 'Severo', mordida: 'Cruzada', linhaMedia: '3MM PARA DIREITA'}, dataInicio: '2024-03-05', tipoAparelho: 'AUTOLIGADO CERÂMICO', extracoes: '1º PRÉ-MOLARES SUPERIORES (14 E 24)', sequenciaFios: '0.14 → 0.16 → 0.18 → 0.19x0.25 → 0.21x0.25', elasticos: 'CLASSE III', iprPlanejado: 'SIM - INFERIOR ANTERIOR', tempoEstimado: '24–30 MESES', valor: 7200, formaPagamento: 'PARCELADO 30X', faseAtual: 'FECHAMENTO DE ESPAÇOS', fioAtual: '0.19x0.25', classificacao: 'CLASSE III', cooperacao: 'Boa', percentualConcluido: 55, ultimaConsulta: '2026-02-05', proximaConsulta: '2026-03-05', mesesRestantes: 10, status: 'Em Tratamento', evolucoes: [ { id: 'ev6', data: '2024-03-05', tipo: 'Colagem', procedimento: 'COLAGEM APARELHO CERÂMICO SUP/INF + EXTRAÇÕES 14 E 24', proximoRetorno: '2024-04-05', fio: '0.14', higiene: 9, cooperacao: 'Boa'}, { id: 'ev7', data: '2025-06-05', tipo: 'Manutenção', procedimento: 'IPR INFERIOR ANTERIOR + MOLA FECHAMENTO', proximoRetorno: '2025-07-05', fio: '0.19x0.25', procedimentos: ['IPR'], higiene: 8, cooperacao: 'Boa'}, { id: 'ev8', data: '2026-02-05', tipo: 'Manutenção', procedimento: 'ATIVAÇÃO MOLA + ELÁSTICO CLASSE III', proximoRetorno: '2026-03-05', fio: '0.19x0.25', elastico: 'CLASSE III', higiene: 9, cooperacao: 'Boa'}, ], flags: [ { tipo: 'danger', mensagem: 'COMPLEXIDADE ALTA - APINHAMENTO SEVERO' }, { tipo: 'warning', mensagem: 'RISCO PERIODONTAL - RECESSÃO CLASSE I' }, ], fotos: [ { url: '', tipo: 'inicio', data: '2024-03-05' }, { url: '', tipo: 'atual', data: '2026-02-05' }, ]}, ]; }, saveEvolucaoOrto: async (tratamentoId: string, evolucao: Partial): Promise => { const newEv: EvolucaoOrto = { id: 'ev_' + Math.random().toString(36).substring(2, 8), data: new Date().toISOString().split('T')[0], tipo: evolucao.tipo || 'Manutenção', procedimento: evolucao.procedimento || '', proximoRetorno: evolucao.proximoRetorno || '', fio: evolucao.fio, elastico: evolucao.elastico, procedimentos: evolucao.procedimentos, higiene: evolucao.higiene, cooperacao: evolucao.cooperacao, observacoes: evolucao.observacoes}; console.log(`[Mock] Evolução salva no tratamento ${tratamentoId}:`, newEv); return newEv; }, updateTratamentoOrto: async (tratamento: Partial): Promise => { console.log('[Mock] Tratamento atualizado:', tratamento); }, finalizarTratamentoOrto: async (tratamentoId: string, contencao: { tipo: string; superior: boolean; inferior: boolean; tempoUso: string }): Promise => { console.log(`[Mock] Tratamento ${tratamentoId} finalizado com contenção:`, contencao); }, // --- LEAD & NOTIFICATION METHODS --- saveLead: async (lead: Partial) => { const newLead: Lead = { id: Math.random().toString(), nome: toUpper(lead.nome), sobrenome: toUpper(lead.sobrenome), cpf: lead.cpf || '', whatsapp: lead.whatsapp || '', tipoInteresse: lead.tipoInteresse || 'Particular', dataCadastro: new Date().toISOString(), status: 'Novo' }; await apiFetch(`${API_URL}/leads`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(newLead) }); return newLead; }, getLeads: async (): Promise => { const res = await apiFetch(`${API_URL}/leads`); return await res.json(); }, getNotifications: async (): Promise => { try { const res = await apiFetch(`${API_URL}/notificacoes`); return await res.json(); } catch { return []; } }, markNotificationRead: async (id: string) => { try { await apiFetch(`${API_URL}/notificacoes/${id}/read`, { method: 'PUT' }); } catch (err) { console.error("Erro ao marcar como lida:", err); } }, markAllNotificationsRead: async () => { try { await apiFetch(`${API_URL}/notificacoes/read-all`, { method: 'POST' }); } catch (err) { console.error("Erro ao marcar todas como lidas:", err); } }, deleteNotification: async (id: string) => { try { await apiFetch(`${API_URL}/notificacoes/${id}`, { method: 'DELETE' }); } catch (err) { console.error("Erro ao excluir notificação:", err); } }, getSettings: async (): Promise => { const res = await apiFetch(`${API_URL}/settings`); return await res.json(); }, saveSettings: async (settings: Settings) => { const res = await apiFetch(`${API_URL}/settings`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(settings) }); return await res.json(); }, getGoogleEvents: async (): Promise => { try { const res = await apiFetch(`${API_URL}/google/events`); return await res.json(); } catch { return []; } } };