import { API_ENDPOINTS } from "@/lib/api_urls"; import { getStoredTokens } from "./auth"; import type { CreateAppointmentInput, ScheduleAppointmentInput, RejectAppointmentInput, UpdateAvailabilityInput, } from "@/lib/schema/appointments"; import type { Appointment, AppointmentResponse, AppointmentsListResponse, AvailableDatesResponse, AdminAvailability, AppointmentStats, JitsiMeetingInfo, ApiError, } from "@/lib/models/appointments"; // Helper function to extract error message from API response function extractErrorMessage(error: ApiError): string { if (error.detail) { if (Array.isArray(error.detail)) { return error.detail.join(", "); } return String(error.detail); } if (error.message) { if (Array.isArray(error.message)) { return error.message.join(", "); } return String(error.message); } if (typeof error === "string") { return error; } return "An error occurred while creating the appointment"; } // Create appointment export async function createAppointment( input: CreateAppointmentInput ): Promise { const tokens = getStoredTokens(); if (!tokens.access) { throw new Error("Authentication required. Please log in to book an appointment."); } const response = await fetch(API_ENDPOINTS.meetings.createAppointment, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${tokens.access}`, }, body: JSON.stringify(input), }); const data: AppointmentResponse = await response.json(); if (!response.ok) { const errorMessage = extractErrorMessage(data as unknown as ApiError); throw new Error(errorMessage); } // Handle different response formats if (data.appointment) { return data.appointment; } if ((data as any).data) { return (data as any).data; } // If appointment is returned directly return data as unknown as Appointment; } // Get available dates export async function getAvailableDates(): Promise { const response = await fetch(API_ENDPOINTS.meetings.availableDates, { method: "GET", headers: { "Content-Type": "application/json", }, }); const data: AvailableDatesResponse | string[] = await response.json(); if (!response.ok) { const errorMessage = extractErrorMessage(data as unknown as ApiError); throw new Error(errorMessage); } // API returns array of dates in YYYY-MM-DD format if (Array.isArray(data)) { return data; } return (data as AvailableDatesResponse).dates || []; } // List appointments (Admin sees all, users see their own) export async function listAppointments(email?: string): Promise { const tokens = getStoredTokens(); if (!tokens.access) { throw new Error("Authentication required."); } const url = email ? `${API_ENDPOINTS.meetings.listAppointments}?email=${encodeURIComponent(email)}` : API_ENDPOINTS.meetings.listAppointments; const response = await fetch(url, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${tokens.access}`, }, }); const data = await response.json(); if (!response.ok) { const errorMessage = extractErrorMessage(data as unknown as ApiError); throw new Error(errorMessage); } // Handle different response formats // API might return array directly or wrapped in an object if (Array.isArray(data)) { return data; } if (data.appointments && Array.isArray(data.appointments)) { return data.appointments; } if (data.results && Array.isArray(data.results)) { return data.results; } return []; } // Get user appointments export async function getUserAppointments(): Promise { const tokens = getStoredTokens(); if (!tokens.access) { throw new Error("Authentication required."); } const response = await fetch(API_ENDPOINTS.meetings.userAppointments, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${tokens.access}`, }, }); const data = await response.json(); if (!response.ok) { const errorMessage = extractErrorMessage(data as unknown as ApiError); throw new Error(errorMessage); } // Handle different response formats // API might return array directly or wrapped in an object if (Array.isArray(data)) { return data; } if (data.appointments && Array.isArray(data.appointments)) { return data.appointments; } if (data.results && Array.isArray(data.results)) { return data.results; } return []; } // Get appointment detail export async function getAppointmentDetail(id: string): Promise { const tokens = getStoredTokens(); if (!tokens.access) { throw new Error("Authentication required."); } const response = await fetch(`${API_ENDPOINTS.meetings.listAppointments}${id}/`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${tokens.access}`, }, }); const data: AppointmentResponse = await response.json(); if (!response.ok) { const errorMessage = extractErrorMessage(data as unknown as ApiError); throw new Error(errorMessage); } if (data.appointment) { return data.appointment; } return data as unknown as Appointment; } // Schedule appointment (Admin only) export async function scheduleAppointment( id: string, input: ScheduleAppointmentInput ): Promise { const tokens = getStoredTokens(); if (!tokens.access) { throw new Error("Authentication required."); } const response = await fetch(`${API_ENDPOINTS.meetings.listAppointments}${id}/schedule/`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${tokens.access}`, }, body: JSON.stringify(input), }); const data: AppointmentResponse = await response.json(); if (!response.ok) { const errorMessage = extractErrorMessage(data as unknown as ApiError); throw new Error(errorMessage); } if (data.appointment) { return data.appointment; } return data as unknown as Appointment; } // Reject appointment (Admin only) export async function rejectAppointment( id: string, input: RejectAppointmentInput ): Promise { const tokens = getStoredTokens(); if (!tokens.access) { throw new Error("Authentication required."); } const response = await fetch(`${API_ENDPOINTS.meetings.listAppointments}${id}/reject/`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${tokens.access}`, }, body: JSON.stringify(input), }); const data: AppointmentResponse = await response.json(); if (!response.ok) { const errorMessage = extractErrorMessage(data as unknown as ApiError); throw new Error(errorMessage); } if (data.appointment) { return data.appointment; } return data as unknown as Appointment; } // Get admin availability export async function getAdminAvailability(): Promise { const tokens = getStoredTokens(); if (!tokens.access) { throw new Error("Authentication required."); } const response = await fetch(API_ENDPOINTS.meetings.adminAvailability, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${tokens.access}`, }, }); const data: any = await response.json(); if (!response.ok) { const errorMessage = extractErrorMessage(data as unknown as ApiError); throw new Error(errorMessage); } // Handle both string and array formats for available_days let availableDays: number[] = []; if (typeof data.available_days === 'string') { try { availableDays = JSON.parse(data.available_days); } catch { // If parsing fails, try splitting by comma availableDays = data.available_days.split(',').map((d: string) => parseInt(d.trim())).filter((d: number) => !isNaN(d)); } } else if (Array.isArray(data.available_days)) { availableDays = data.available_days; } return { available_days: availableDays, available_days_display: data.available_days_display || [], } as AdminAvailability; } // Update admin availability export async function updateAdminAvailability( input: UpdateAvailabilityInput ): Promise { const tokens = getStoredTokens(); if (!tokens.access) { throw new Error("Authentication required."); } const response = await fetch(API_ENDPOINTS.meetings.adminAvailability, { method: "PUT", headers: { "Content-Type": "application/json", Authorization: `Bearer ${tokens.access}`, }, body: JSON.stringify(input), }); const data: AdminAvailability = await response.json(); if (!response.ok) { const errorMessage = extractErrorMessage(data as unknown as ApiError); throw new Error(errorMessage); } return data; } // Get appointment stats (Admin only) export async function getAppointmentStats(): Promise { const tokens = getStoredTokens(); if (!tokens.access) { throw new Error("Authentication required."); } const response = await fetch(`${API_ENDPOINTS.meetings.listAppointments}stats/`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${tokens.access}`, }, }); const data: AppointmentStats = await response.json(); if (!response.ok) { const errorMessage = extractErrorMessage(data as unknown as ApiError); throw new Error(errorMessage); } return data; } // Get Jitsi meeting info export async function getJitsiMeetingInfo(id: string): Promise { const tokens = getStoredTokens(); if (!tokens.access) { throw new Error("Authentication required."); } const response = await fetch(`${API_ENDPOINTS.meetings.listAppointments}${id}/jitsi-meeting/`, { method: "GET", headers: { "Content-Type": "application/json", Authorization: `Bearer ${tokens.access}`, }, }); const data: JitsiMeetingInfo = await response.json(); if (!response.ok) { const errorMessage = extractErrorMessage(data as unknown as ApiError); throw new Error(errorMessage); } return data; }