fix(frontend): Resolve login redirection and inject JWT token on requests

This commit is contained in:
VPS 4 Builder
2026-05-13 10:04:49 +02:00
parent e260eed28b
commit 526495db96
2 changed files with 73 additions and 88 deletions
+71 -87
View File
@@ -22,12 +22,13 @@ const enforceUppercase = <T extends object>(data: T): T => {
const API_URL = import.meta.env.VITE_API_URL || '/api';
// Returns headers always including the JWT token stored after login
const getAuthHeaders = (): HeadersInit => {
const apiFetch = async (url: string, options: RequestInit = {}) => {
const token = localStorage.getItem('SCOREODONTO_AUTH_TOKEN');
return {
'Content-Type': 'application/json',
const headers = {
...options.headers,
...(token ? { 'Authorization': `Bearer ${token}` } : {})
};
return fetch(url, { ...options, headers });
};
export const HybridBackend = {
@@ -45,7 +46,7 @@ export const HybridBackend = {
const cleanPass = pass.trim();
try {
const res = await fetch(`${API_URL}/login`, {
const res = await apiFetch(`${API_URL}/login`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email: cleanEmail, password: cleanPass })
@@ -144,7 +145,7 @@ export const HybridBackend = {
},
getDashboardStats: async () => {
const res = await fetch(`${API_URL}/dashboard/stats`);
const res = await apiFetch(`${API_URL}/dashboard/stats`);
return await res.json();
},
@@ -206,7 +207,7 @@ export const HybridBackend = {
},
getPacientes: async (termo: string = ''): Promise<Paciente[]> => {
const res = await fetch(`${API_URL}/pacientes`);
const res = await apiFetch(`${API_URL}/pacientes`);
const data = await res.json();
if (!termo) return data;
const t = termo.toUpperCase();
@@ -223,7 +224,7 @@ export const HybridBackend = {
savePaciente: async (paciente: Partial<Paciente>) => {
const data = enforceUppercase(paciente);
const body = { ...data, id: Math.random().toString() };
const res = await fetch(`${API_URL}/pacientes`, {
const res = await apiFetch(`${API_URL}/pacientes`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
@@ -233,7 +234,7 @@ export const HybridBackend = {
updatePaciente: async (paciente: Paciente) => {
const data = enforceUppercase(paciente);
const res = await fetch(`${API_URL}/pacientes/${paciente.id}`, {
const res = await apiFetch(`${API_URL}/pacientes/${paciente.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
@@ -243,18 +244,18 @@ export const HybridBackend = {
getDentistas: async (clinicaId?: string): Promise<Dentista[]> => {
const url = clinicaId ? `${API_URL}/dentistas?clinicaId=${clinicaId}` : `${API_URL}/dentistas`;
const res = await fetch(url);
const res = await apiFetch(url);
return await res.json();
},
getPlanos: async (): Promise<Plano[]> => {
const res = await fetch(`${API_URL}/planos`);
const res = await apiFetch(`${API_URL}/planos`);
return await res.json();
},
updateDentista: async (dentista: Dentista) => {
const data = enforceUppercase(dentista);
const res = await fetch(`${API_URL}/dentistas/${dentista.id}`, {
const res = await apiFetch(`${API_URL}/dentistas/${dentista.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
@@ -266,7 +267,7 @@ export const HybridBackend = {
const activeW = HybridBackend.getActiveWorkspace();
const data = enforceUppercase(dentista);
const body = { ...data, id: Math.random().toString(), clinica_id: activeW?.id };
const res = await fetch(`${API_URL}/dentistas`, {
const res = await apiFetch(`${API_URL}/dentistas`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
@@ -275,7 +276,7 @@ export const HybridBackend = {
},
reorderDentistas: async (orders: { id: string; ordem: number }[]) => {
const res = await fetch(`${API_URL}/dentistas/reorder`, {
const res = await apiFetch(`${API_URL}/dentistas/reorder`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ orders })
@@ -284,16 +285,16 @@ export const HybridBackend = {
},
deleteDentista: async (id: string) => {
await fetch(`${API_URL}/dentistas/${id}`, { method: 'DELETE' });
await apiFetch(`${API_URL}/dentistas/${id}`, { method: 'DELETE' });
},
getHorariosByDentista: async (dentistaId: string, clinicaId: string): Promise<DentistaHorario[]> => {
const res = await fetch(`${API_URL}/dentistas/${dentistaId}/horarios?clinicaId=${clinicaId}`);
const res = await apiFetch(`${API_URL}/dentistas/${dentistaId}/horarios?clinicaId=${clinicaId}`);
return await res.json();
},
saveHorario: async (horario: Partial<DentistaHorario>) => {
const res = await fetch(`${API_URL}/dentistas/horarios`, {
const res = await apiFetch(`${API_URL}/dentistas/horarios`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(horario)
@@ -302,17 +303,17 @@ export const HybridBackend = {
},
deleteHorario: async (id: string) => {
await fetch(`${API_URL}/dentistas/horarios/${id}`, { method: 'DELETE' });
await apiFetch(`${API_URL}/dentistas/horarios/${id}`, { method: 'DELETE' });
},
getProductivityReport: async (clinicaId: string, start?: string, end?: string) => {
const res = await fetch(`${API_URL}/reports/productivity?clinicaId=${clinicaId}&start=${start || ''}&end=${end || ''}`);
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 fetch(`${API_URL}/especialidades/${esp.id}`, {
const res = await apiFetch(`${API_URL}/especialidades/${esp.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
@@ -323,7 +324,7 @@ export const HybridBackend = {
saveEspecialidade: async (esp: Partial<Especialidade>) => {
const data = enforceUppercase(esp);
const body = { ...data, id: Math.random().toString() };
const res = await fetch(`${API_URL}/especialidades`, {
const res = await apiFetch(`${API_URL}/especialidades`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
@@ -332,7 +333,7 @@ export const HybridBackend = {
},
reorderEspecialidades: async (orders: { id: string; ordem: number }[]) => {
const res = await fetch(`${API_URL}/especialidades/reorder`, {
const res = await apiFetch(`${API_URL}/especialidades/reorder`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ orders })
@@ -341,12 +342,12 @@ export const HybridBackend = {
},
deleteEspecialidade: async (id: string) => {
await fetch(`${API_URL}/especialidades/${id}`, { method: 'DELETE' });
await apiFetch(`${API_URL}/especialidades/${id}`, { method: 'DELETE' });
},
updateProcedimento: async (proc: Procedimento) => {
const data = enforceUppercase(proc);
const res = await fetch(`${API_URL}/procedimentos/${proc.id}`, {
const res = await apiFetch(`${API_URL}/procedimentos/${proc.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
@@ -357,7 +358,7 @@ export const HybridBackend = {
saveProcedimento: async (proc: Partial<Procedimento>) => {
const data = enforceUppercase(proc);
const body = { ...data, id: Math.random().toString() };
const res = await fetch(`${API_URL}/procedimentos`, {
const res = await apiFetch(`${API_URL}/procedimentos`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
@@ -366,7 +367,7 @@ export const HybridBackend = {
},
reorderProcedimentos: async (orders: { id: string; ordem: number }[]) => {
const res = await fetch(`${API_URL}/procedimentos/reorder`, {
const res = await apiFetch(`${API_URL}/procedimentos/reorder`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ orders })
@@ -375,12 +376,12 @@ export const HybridBackend = {
},
deleteProcedimento: async (id: string) => {
await fetch(`${API_URL}/procedimentos/${id}`, { method: 'DELETE' });
await apiFetch(`${API_URL}/procedimentos/${id}`, { method: 'DELETE' });
},
updatePlano: async (plano: Plano) => {
const data = enforceUppercase(plano);
const res = await fetch(`${API_URL}/planos/${plano.id}`, {
const res = await apiFetch(`${API_URL}/planos/${plano.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
@@ -391,7 +392,7 @@ export const HybridBackend = {
savePlano: async (plano: Partial<Plano>) => {
const data = enforceUppercase(plano);
const body = { ...data, id: Math.random().toString() };
const res = await fetch(`${API_URL}/planos`, {
const res = await apiFetch(`${API_URL}/planos`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
@@ -400,57 +401,55 @@ export const HybridBackend = {
},
deletePlano: async (id: string) => {
await fetch(`${API_URL}/planos/${id}`, { method: 'DELETE' });
await apiFetch(`${API_URL}/planos/${id}`, { method: 'DELETE' });
},
getEspecialidades: async (): Promise<Especialidade[]> => {
const res = await fetch(`${API_URL}/especialidades`);
const res = await apiFetch(`${API_URL}/especialidades`);
return await res.json();
},
getProcedimentos: async (): Promise<Procedimento[]> => {
const res = await fetch(`${API_URL}/procedimentos`);
const res = await apiFetch(`${API_URL}/procedimentos`);
return await res.json();
},
getFinanceiro: async (): Promise<FinanceiroItem[]> => {
const res = await fetch(`${API_URL}/financeiro`);
const res = await apiFetch(`${API_URL}/financeiro`);
return await res.json();
},
saveFinanceiro: async (item: Partial<FinanceiroItem>) => {
const data = enforceUppercase(item);
const res = await fetch(`${API_URL}/financeiro`, {
const res = await apiFetch(`${API_URL}/financeiro`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
body: JSON.stringify(data)});
return await res.json();
},
updateFinanceiro: async (item: FinanceiroItem) => {
const data = enforceUppercase(item);
const res = await fetch(`${API_URL}/financeiro/${item.id}`, {
const res = await apiFetch(`${API_URL}/financeiro/${item.id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
body: JSON.stringify(data)});
return await res.json();
},
deleteFinanceiro: async (id: string) => {
await fetch(`${API_URL}/financeiro/${id}`, { method: 'DELETE' });
await apiFetch(`${API_URL}/financeiro/${id}`, { method: 'DELETE' });
},
getAgendamentos: async (): Promise<Agendamento[]> => {
const res = await fetch(`${API_URL}/agendamentos`, { headers: getAuthHeaders() });
const res = await apiFetch(`${API_URL}/agendamentos`, { });
return await res.json();
},
salvarAgendamento: async (ag: Partial<Agendamento>) => {
const res = await fetch(`${API_URL}/agendamentos`, {
const res = await apiFetch(`${API_URL}/agendamentos`, {
method: 'POST',
headers: getAuthHeaders(),
,
body: JSON.stringify(ag)
});
if (!res.ok) {
@@ -461,9 +460,9 @@ export const HybridBackend = {
},
atualizarAgendamento: async (id: string, data: Partial<Agendamento>) => {
const res = await fetch(`${API_URL}/agendamentos/${id}`, {
const res = await apiFetch(`${API_URL}/agendamentos/${id}`, {
method: 'PUT',
headers: getAuthHeaders(),
,
body: JSON.stringify(data)
});
if (!res.ok) {
@@ -474,7 +473,7 @@ export const HybridBackend = {
},
excluirAgendamento: async (id: string) => {
await fetch(`${API_URL}/agendamentos/${id}`, { method: 'DELETE', headers: getAuthHeaders() });
await apiFetch(`${API_URL}/agendamentos/${id}`, { method: 'DELETE'});
},
getOrtoTreatments: async (): Promise<TratamentoOrto[]> => {
@@ -492,15 +491,13 @@ export const HybridBackend = {
medicacao: 'NENHUMA',
alergias: 'NENHUMA',
respiracaoBucal: false,
habitos: 'BRUXISMO LEVE NOTURNO',
},
habitos: 'BRUXISMO LEVE NOTURNO'},
diagnostico: {
classeAngle: 'II',
perfil: 'Convexo',
apinhamento: 'Moderado',
mordida: 'Profunda',
linhaMedia: '1MM PARA ESQUERDA',
},
linhaMedia: '1MM PARA ESQUERDA'},
dataInicio: '2024-08-10',
tipoAparelho: 'AUTOLIGADO METÁLICO',
extracoes: 'NÃO',
@@ -528,8 +525,7 @@ export const HybridBackend = {
proximoRetorno: '2024-09-10',
fio: '0.12',
higiene: 8,
cooperacao: 'Boa',
},
cooperacao: 'Boa'},
{
id: 'ev2',
data: '2024-09-10',
@@ -538,8 +534,7 @@ export const HybridBackend = {
proximoRetorno: '2024-10-10',
fio: '0.14',
higiene: 7,
cooperacao: 'Boa',
},
cooperacao: 'Boa'},
{
id: 'ev3',
data: '2024-10-10',
@@ -549,8 +544,7 @@ export const HybridBackend = {
fio: '0.16',
elastico: 'CLASSE II',
higiene: 6,
cooperacao: 'Média',
},
cooperacao: 'Média'},
{
id: 'ev4',
data: '2025-11-10',
@@ -562,8 +556,7 @@ export const HybridBackend = {
procedimentos: ['Profilaxia'],
higiene: 5,
cooperacao: 'Média',
observacoes: 'HIGIENE ABAIXO DO IDEAL. ORIENTAÇÕES REFORÇADAS.',
},
observacoes: 'HIGIENE ABAIXO DO IDEAL. ORIENTAÇÕES REFORÇADAS.'},
{
id: 'ev5',
data: '2026-01-14',
@@ -573,8 +566,7 @@ export const HybridBackend = {
fio: '0.16',
elastico: 'CLASSE II',
higiene: 6,
cooperacao: 'Média',
},
cooperacao: 'Média'},
],
flags: [
{ tipo: 'warning', mensagem: 'HIGIENE ABAIXO DO IDEAL' },
@@ -583,8 +575,7 @@ export const HybridBackend = {
fotos: [
{ url: '', tipo: 'inicio', data: '2024-08-10' },
{ url: '', tipo: 'atual', data: '2026-01-14' },
],
},
]},
{
id: 't2',
pacienteId: '2',
@@ -601,15 +592,13 @@ export const HybridBackend = {
periodonto: 'RECESSÃO GENGIVAL LEVE EM 31-41',
implantes: 'NENHUM',
recessoes: '31 E 41 - CLASSE I MILLER',
mobilidade: 'NENHUMA',
},
mobilidade: 'NENHUMA'},
diagnostico: {
classeAngle: 'III',
perfil: 'Côncavo',
apinhamento: 'Severo',
mordida: 'Cruzada',
linhaMedia: '3MM PARA DIREITA',
},
linhaMedia: '3MM PARA DIREITA'},
dataInicio: '2024-03-05',
tipoAparelho: 'AUTOLIGADO CERÂMICO',
extracoes: '1º PRÉ-MOLARES SUPERIORES (14 E 24)',
@@ -637,8 +626,7 @@ export const HybridBackend = {
proximoRetorno: '2024-04-05',
fio: '0.14',
higiene: 9,
cooperacao: 'Boa',
},
cooperacao: 'Boa'},
{
id: 'ev7',
data: '2025-06-05',
@@ -648,8 +636,7 @@ export const HybridBackend = {
fio: '0.19x0.25',
procedimentos: ['IPR'],
higiene: 8,
cooperacao: 'Boa',
},
cooperacao: 'Boa'},
{
id: 'ev8',
data: '2026-02-05',
@@ -659,8 +646,7 @@ export const HybridBackend = {
fio: '0.19x0.25',
elastico: 'CLASSE III',
higiene: 9,
cooperacao: 'Boa',
},
cooperacao: 'Boa'},
],
flags: [
{ tipo: 'danger', mensagem: 'COMPLEXIDADE ALTA - APINHAMENTO SEVERO' },
@@ -669,8 +655,7 @@ export const HybridBackend = {
fotos: [
{ url: '', tipo: 'inicio', data: '2024-03-05' },
{ url: '', tipo: 'atual', data: '2026-02-05' },
],
},
]},
];
},
@@ -686,8 +671,7 @@ export const HybridBackend = {
procedimentos: evolucao.procedimentos,
higiene: evolucao.higiene,
cooperacao: evolucao.cooperacao,
observacoes: evolucao.observacoes,
};
observacoes: evolucao.observacoes};
console.log(`[Mock] Evolução salva no tratamento ${tratamentoId}:`, newEv);
return newEv;
},
@@ -703,7 +687,7 @@ export const HybridBackend = {
salvarAgendamento: async (agendamento: Partial<Agendamento>) => {
const data = enforceUppercase(agendamento);
const body = { id: Math.random().toString(), ...data };
const res = await fetch(`${API_URL}/agendamentos`, {
const res = await apiFetch(`${API_URL}/agendamentos`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(body)
@@ -713,7 +697,7 @@ export const HybridBackend = {
atualizarAgendamento: async (id: string, agendamento: Partial<Agendamento>) => {
const data = enforceUppercase(agendamento);
const res = await fetch(`${API_URL}/agendamentos/${id}`, {
const res = await apiFetch(`${API_URL}/agendamentos/${id}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
@@ -722,7 +706,7 @@ export const HybridBackend = {
},
excluirAgendamento: async (id: string) => {
await fetch(`${API_URL}/agendamentos/${id}`, { method: 'DELETE' });
await apiFetch(`${API_URL}/agendamentos/${id}`, { method: 'DELETE' });
},
// --- LEAD & NOTIFICATION METHODS ---
@@ -738,7 +722,7 @@ export const HybridBackend = {
dataCadastro: new Date().toISOString(),
status: 'Novo'
};
await fetch(`${API_URL}/leads`, {
await apiFetch(`${API_URL}/leads`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(newLead)
@@ -747,13 +731,13 @@ export const HybridBackend = {
},
getLeads: async (): Promise<Lead[]> => {
const res = await fetch(`${API_URL}/leads`);
const res = await apiFetch(`${API_URL}/leads`);
return await res.json();
},
getNotifications: async (): Promise<Notificacao[]> => {
try {
const res = await fetch(`${API_URL}/notificacoes`);
const res = await apiFetch(`${API_URL}/notificacoes`);
return await res.json();
} catch {
return [];
@@ -762,7 +746,7 @@ export const HybridBackend = {
markNotificationRead: async (id: string) => {
try {
await fetch(`${API_URL}/notificacoes/${id}/read`, { method: 'PUT' });
await apiFetch(`${API_URL}/notificacoes/${id}/read`, { method: 'PUT' });
} catch (err) {
console.error("Erro ao marcar como lida:", err);
}
@@ -770,7 +754,7 @@ export const HybridBackend = {
markAllNotificationsRead: async () => {
try {
await fetch(`${API_URL}/notificacoes/read-all`, { method: 'POST' });
await apiFetch(`${API_URL}/notificacoes/read-all`, { method: 'POST' });
} catch (err) {
console.error("Erro ao marcar todas como lidas:", err);
}
@@ -778,19 +762,19 @@ export const HybridBackend = {
deleteNotification: async (id: string) => {
try {
await fetch(`${API_URL}/notificacoes/${id}`, { method: 'DELETE' });
await apiFetch(`${API_URL}/notificacoes/${id}`, { method: 'DELETE' });
} catch (err) {
console.error("Erro ao excluir notificação:", err);
}
},
getSettings: async (): Promise<Settings> => {
const res = await fetch(`${API_URL}/settings`);
const res = await apiFetch(`${API_URL}/settings`);
return await res.json();
},
saveSettings: async (settings: Settings) => {
const res = await fetch(`${API_URL}/settings`, {
const res = await apiFetch(`${API_URL}/settings`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(settings)
@@ -800,7 +784,7 @@ export const HybridBackend = {
getGoogleEvents: async (): Promise<any[]> => {
try {
const res = await fetch(`${API_URL}/google/events`);
const res = await apiFetch(`${API_URL}/google/events`);
return await res.json();
} catch {
return [];