fix-delete-appoiment
This commit is contained in:
parent
dc83db3e7c
commit
2cc3687628
@ -55,7 +55,7 @@ import {
|
|||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
import { mockProfessionals } from "@/lib/mocks/appointment-mocks";
|
import { mockProfessionals } from "@/lib/mocks/appointment-mocks";
|
||||||
import { listarAgendamentos, buscarPacientesPorIds, buscarMedicosPorIds, atualizarAgendamento, buscarAgendamentoPorId, deletarAgendamento } from "@/lib/api";
|
import { listarAgendamentos, buscarPacientesPorIds, buscarMedicosPorIds, atualizarAgendamento, buscarAgendamentoPorId, deletarAgendamento, addDeletedAppointmentId } from "@/lib/api";
|
||||||
import { CalendarRegistrationForm } from "@/components/features/forms/calendar-registration-form";
|
import { CalendarRegistrationForm } from "@/components/features/forms/calendar-registration-form";
|
||||||
|
|
||||||
const formatDate = (date: string | Date) => {
|
const formatDate = (date: string | Date) => {
|
||||||
@ -140,6 +140,8 @@ export default function ConsultasPage() {
|
|||||||
try {
|
try {
|
||||||
// call server DELETE
|
// call server DELETE
|
||||||
await deletarAgendamento(appointmentId);
|
await deletarAgendamento(appointmentId);
|
||||||
|
// Mark as deleted in cache so it won't appear again
|
||||||
|
addDeletedAppointmentId(appointmentId);
|
||||||
// remove from UI
|
// remove from UI
|
||||||
setAppointments((prev) => prev.filter((a) => a.id !== appointmentId));
|
setAppointments((prev) => prev.filter((a) => a.id !== appointmentId));
|
||||||
// also update originalAppointments cache
|
// also update originalAppointments cache
|
||||||
|
|||||||
@ -18,7 +18,7 @@ import Link from 'next/link'
|
|||||||
import ProtectedRoute from '@/components/shared/ProtectedRoute'
|
import ProtectedRoute from '@/components/shared/ProtectedRoute'
|
||||||
import { useAuth } from '@/hooks/useAuth'
|
import { useAuth } from '@/hooks/useAuth'
|
||||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||||
import { buscarPacientes, buscarPacientePorUserId, getUserInfo, listarAgendamentos, buscarMedicosPorIds, buscarMedicos, atualizarPaciente, buscarPacientePorId, getDoctorById, atualizarAgendamento, deletarAgendamento } from '@/lib/api'
|
import { buscarPacientes, buscarPacientePorUserId, getUserInfo, listarAgendamentos, buscarMedicosPorIds, buscarMedicos, atualizarPaciente, buscarPacientePorId, getDoctorById, atualizarAgendamento, deletarAgendamento, addDeletedAppointmentId } from '@/lib/api'
|
||||||
import { CalendarRegistrationForm } from '@/components/features/forms/calendar-registration-form'
|
import { CalendarRegistrationForm } from '@/components/features/forms/calendar-registration-form'
|
||||||
import { buscarRelatorioPorId, listarRelatoriosPorMedico } from '@/lib/reports'
|
import { buscarRelatorioPorId, listarRelatoriosPorMedico } from '@/lib/reports'
|
||||||
import { ENV_CONFIG } from '@/lib/env-config'
|
import { ENV_CONFIG } from '@/lib/env-config'
|
||||||
@ -610,6 +610,19 @@ export default function PacientePage() {
|
|||||||
hora: sched ? sched.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }) : '',
|
hora: sched ? sched.toLocaleTimeString('pt-BR', { hour: '2-digit', minute: '2-digit' }) : '',
|
||||||
status: a.status ? String(a.status) : 'Pendente',
|
status: a.status ? String(a.status) : 'Pendente',
|
||||||
}
|
}
|
||||||
|
}).filter((consulta: any) => {
|
||||||
|
// Filter out cancelled appointments (those with cancelled_at set OR status='cancelled')
|
||||||
|
const raw = rows.find((r: any) => String(r.id) === String(consulta.id));
|
||||||
|
if (!raw) return false;
|
||||||
|
|
||||||
|
// Check cancelled_at field
|
||||||
|
const cancelled = raw.cancelled_at;
|
||||||
|
if (cancelled && cancelled !== '' && cancelled !== 'null') return false;
|
||||||
|
|
||||||
|
// Check status field
|
||||||
|
if (raw.status && String(raw.status).toLowerCase() === 'cancelled') return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
})
|
})
|
||||||
|
|
||||||
setDoctorsMap(doctorsMap)
|
setDoctorsMap(doctorsMap)
|
||||||
@ -856,6 +869,8 @@ export default function PacientePage() {
|
|||||||
if (!ok) return
|
if (!ok) return
|
||||||
// call API to delete
|
// call API to delete
|
||||||
await deletarAgendamento(consulta.id)
|
await deletarAgendamento(consulta.id)
|
||||||
|
// Mark as deleted in cache so it won't appear again
|
||||||
|
addDeletedAppointmentId(consulta.id)
|
||||||
// remove from local list
|
// remove from local list
|
||||||
setAppointments((prev) => {
|
setAppointments((prev) => {
|
||||||
if (!prev) return prev
|
if (!prev) return prev
|
||||||
|
|||||||
@ -1293,7 +1293,23 @@ export async function listarAgendamentos(query?: string): Promise<Appointment[]>
|
|||||||
if (!res.ok && res.status === 401) {
|
if (!res.ok && res.status === 401) {
|
||||||
throw new Error('Não autenticado. Token ausente ou expirado. Faça login novamente.');
|
throw new Error('Não autenticado. Token ausente ou expirado. Faça login novamente.');
|
||||||
}
|
}
|
||||||
return await parse<Appointment[]>(res);
|
const appointments = await parse<Appointment[]>(res);
|
||||||
|
// Filter out soft-deleted appointments (those with cancelled_at set OR status='cancelled' OR in deleted cache)
|
||||||
|
return appointments.filter((a) => {
|
||||||
|
const id = String(a.id);
|
||||||
|
|
||||||
|
// Check if in deleted cache
|
||||||
|
if (deletedAppointmentIds.has(id)) return false;
|
||||||
|
|
||||||
|
// Check cancelled_at field
|
||||||
|
const cancelled = a.cancelled_at;
|
||||||
|
if (cancelled && cancelled !== '' && cancelled !== 'null') return false;
|
||||||
|
|
||||||
|
// Check status field
|
||||||
|
if (a.status && String(a.status).toLowerCase() === 'cancelled') return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1309,13 +1325,68 @@ export async function buscarAgendamentoPorId(id: string | number, select: string
|
|||||||
const url = `${REST}/appointments?id=eq.${encodeURIComponent(sId)}&${params.toString()}`;
|
const url = `${REST}/appointments?id=eq.${encodeURIComponent(sId)}&${params.toString()}`;
|
||||||
const headers = baseHeaders();
|
const headers = baseHeaders();
|
||||||
const arr = await fetchWithFallback<Appointment[]>(url, headers);
|
const arr = await fetchWithFallback<Appointment[]>(url, headers);
|
||||||
if (arr && arr.length) return arr[0];
|
// Filter out soft-deleted appointments (those with cancelled_at set OR status='cancelled' OR in deleted cache)
|
||||||
|
const active = arr?.filter((a) => {
|
||||||
|
const id = String(a.id);
|
||||||
|
|
||||||
|
// Check if in deleted cache
|
||||||
|
if (deletedAppointmentIds.has(id)) return false;
|
||||||
|
|
||||||
|
// Check cancelled_at field
|
||||||
|
const cancelled = a.cancelled_at;
|
||||||
|
if (cancelled && cancelled !== '' && cancelled !== 'null') return false;
|
||||||
|
|
||||||
|
// Check status field
|
||||||
|
if (a.status && String(a.status).toLowerCase() === 'cancelled') return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
if (active && active.length) return active[0];
|
||||||
throw new Error('404: Agendamento não encontrado');
|
throw new Error('404: Agendamento não encontrado');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deleta um agendamento por ID (DELETE /rest/v1/appointments?id=eq.<id>)
|
* Deleta um agendamento por ID (DELETE /rest/v1/appointments?id=eq.<id>)
|
||||||
*/
|
*/
|
||||||
|
// Track deleted appointment IDs in localStorage to persist across page reloads
|
||||||
|
const DELETED_APPOINTMENTS_KEY = 'deleted_appointment_ids';
|
||||||
|
|
||||||
|
function getDeletedAppointmentIds(): Set<string> {
|
||||||
|
try {
|
||||||
|
if (typeof window === 'undefined') return new Set();
|
||||||
|
const stored = localStorage.getItem(DELETED_APPOINTMENTS_KEY);
|
||||||
|
if (stored) {
|
||||||
|
const ids = JSON.parse(stored);
|
||||||
|
return new Set(Array.isArray(ids) ? ids : []);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[API] Erro ao ler deleted appointments do localStorage', e);
|
||||||
|
}
|
||||||
|
return new Set();
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveDeletedAppointmentIds(ids: Set<string>) {
|
||||||
|
try {
|
||||||
|
if (typeof window === 'undefined') return;
|
||||||
|
localStorage.setItem(DELETED_APPOINTMENTS_KEY, JSON.stringify(Array.from(ids)));
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('[API] Erro ao salvar deleted appointments no localStorage', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deletedAppointmentIds = getDeletedAppointmentIds();
|
||||||
|
|
||||||
|
export function addDeletedAppointmentId(id: string | number) {
|
||||||
|
const idStr = String(id);
|
||||||
|
deletedAppointmentIds.add(idStr);
|
||||||
|
saveDeletedAppointmentIds(deletedAppointmentIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function clearDeletedAppointments() {
|
||||||
|
deletedAppointmentIds.clear();
|
||||||
|
saveDeletedAppointmentIds(deletedAppointmentIds);
|
||||||
|
}
|
||||||
|
|
||||||
export async function deletarAgendamento(id: string | number): Promise<void> {
|
export async function deletarAgendamento(id: string | number): Promise<void> {
|
||||||
if (!id) throw new Error('ID do agendamento é obrigatório');
|
if (!id) throw new Error('ID do agendamento é obrigatório');
|
||||||
const url = `${REST}/appointments?id=eq.${encodeURIComponent(String(id))}`;
|
const url = `${REST}/appointments?id=eq.${encodeURIComponent(String(id))}`;
|
||||||
@ -1325,9 +1396,11 @@ export async function deletarAgendamento(id: string | number): Promise<void> {
|
|||||||
headers: withPrefer({ ...baseHeaders() }, 'return=minimal'),
|
headers: withPrefer({ ...baseHeaders() }, 'return=minimal'),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (res.status === 204) return;
|
if (res.status === 204 || res.status === 200) {
|
||||||
// Some deployments may return 200 with a representation — accept that too
|
// Mark as deleted locally AND persist in localStorage
|
||||||
if (res.status === 200) return;
|
addDeletedAppointmentId(id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Otherwise surface a friendly error using parse()
|
// Otherwise surface a friendly error using parse()
|
||||||
await parse(res as Response);
|
await parse(res as Response);
|
||||||
}
|
}
|
||||||
@ -3018,7 +3091,8 @@ export async function countAppointmentsToday(): Promise<number> {
|
|||||||
const today = new Date().toISOString().split('T')[0];
|
const today = new Date().toISOString().split('T')[0];
|
||||||
const tomorrow = new Date(Date.now() + 86400000).toISOString().split('T')[0];
|
const tomorrow = new Date(Date.now() + 86400000).toISOString().split('T')[0];
|
||||||
|
|
||||||
const url = `${REST}/appointments?scheduled_at=gte.${today}T00:00:00&scheduled_at=lt.${tomorrow}T00:00:00&select=id&limit=1`;
|
// Filter out soft-deleted appointments: cancelled_at is null
|
||||||
|
const url = `${REST}/appointments?scheduled_at=gte.${today}T00:00:00&scheduled_at=lt.${tomorrow}T00:00:00&cancelled_at=is.null&select=id&limit=1`;
|
||||||
const res = await fetch(url, {
|
const res = await fetch(url, {
|
||||||
headers: {
|
headers: {
|
||||||
...baseHeaders(),
|
...baseHeaders(),
|
||||||
@ -3045,9 +3119,25 @@ export async function getUpcomingAppointments(limit: number = 10): Promise<any[]
|
|||||||
const today = new Date().toISOString();
|
const today = new Date().toISOString();
|
||||||
const nextWeek = new Date(Date.now() + 7 * 86400000).toISOString();
|
const nextWeek = new Date(Date.now() + 7 * 86400000).toISOString();
|
||||||
|
|
||||||
const url = `${REST}/appointments?scheduled_at=gte.${today}&scheduled_at=lt.${nextWeek}&order=scheduled_at.asc&limit=${limit}&select=id,scheduled_at,status,doctor_id,patient_id`;
|
const url = `${REST}/appointments?scheduled_at=gte.${today}&scheduled_at=lt.${nextWeek}&order=scheduled_at.asc&limit=${limit}&select=id,scheduled_at,status,doctor_id,patient_id,cancelled_at`;
|
||||||
const res = await fetch(url, { headers: baseHeaders() });
|
const res = await fetch(url, { headers: baseHeaders() });
|
||||||
return await parse<any[]>(res);
|
const appointments = await parse<any[]>(res);
|
||||||
|
// Filter out soft-deleted appointments (those with cancelled_at set OR status='cancelled' OR in deleted cache)
|
||||||
|
return appointments.filter((a) => {
|
||||||
|
const id = String(a.id);
|
||||||
|
|
||||||
|
// Check if in deleted cache
|
||||||
|
if (deletedAppointmentIds.has(id)) return false;
|
||||||
|
|
||||||
|
// Check cancelled_at field
|
||||||
|
const cancelled = a.cancelled_at;
|
||||||
|
if (cancelled && cancelled !== '' && cancelled !== 'null') return false;
|
||||||
|
|
||||||
|
// Check status field
|
||||||
|
if (a.status && String(a.status).toLowerCase() === 'cancelled') return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[getUpcomingAppointments] Erro:', err);
|
console.error('[getUpcomingAppointments] Erro:', err);
|
||||||
return [];
|
return [];
|
||||||
@ -3063,9 +3153,25 @@ export async function getAppointmentsByDateRange(days: number = 14): Promise<any
|
|||||||
startDate.setDate(startDate.getDate() - days);
|
startDate.setDate(startDate.getDate() - days);
|
||||||
const endDate = new Date().toISOString();
|
const endDate = new Date().toISOString();
|
||||||
|
|
||||||
const url = `${REST}/appointments?scheduled_at=gte.${startDate.toISOString()}&scheduled_at=lt.${endDate}&select=scheduled_at,status&order=scheduled_at.asc`;
|
const url = `${REST}/appointments?scheduled_at=gte.${startDate.toISOString()}&scheduled_at=lt.${endDate}&select=scheduled_at,status,cancelled_at,id&order=scheduled_at.asc`;
|
||||||
const res = await fetch(url, { headers: baseHeaders() });
|
const res = await fetch(url, { headers: baseHeaders() });
|
||||||
return await parse<any[]>(res);
|
const appointments = await parse<any[]>(res);
|
||||||
|
// Filter out soft-deleted appointments (those with cancelled_at set OR status='cancelled' OR in deleted cache)
|
||||||
|
return appointments.filter((a) => {
|
||||||
|
const id = String(a.id);
|
||||||
|
|
||||||
|
// Check if in deleted cache
|
||||||
|
if (deletedAppointmentIds.has(id)) return false;
|
||||||
|
|
||||||
|
// Check cancelled_at field
|
||||||
|
const cancelled = a.cancelled_at;
|
||||||
|
if (cancelled && cancelled !== '' && cancelled !== 'null') return false;
|
||||||
|
|
||||||
|
// Check status field
|
||||||
|
if (a.status && String(a.status).toLowerCase() === 'cancelled') return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('[getAppointmentsByDateRange] Erro:', err);
|
console.error('[getAppointmentsByDateRange] Erro:', err);
|
||||||
return [];
|
return [];
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user