diff --git a/client/lib/api/appointment/index.ts b/client/lib/api/appointment/index.ts index a4596835..5ed4e734 100644 --- a/client/lib/api/appointment/index.ts +++ b/client/lib/api/appointment/index.ts @@ -1,88 +1,52 @@ -import IAppointment, { IPatient } from "@/types"; +import { withTempBaseUrl } from "@/lib/utils/withTempBaseUrl"; +import IAppointment, { AppointmentStatus } from "@/types"; import axios from "axios"; +import patientAxiosInstance from "../patient/authorizedRoutes"; +import doctorAxiosInstance from "../doctor/authorizedRoutes"; -const getAuthTokens = () => { - try { - return JSON.parse(localStorage.getItem("auth") || "{}"); - } catch (e) { - console.error("Failed to parse auth tokens", e); - return {}; - } -}; - -const setAuthTokens = (tokens: Record) => { - localStorage.setItem("auth", JSON.stringify(tokens)); -}; - -const axiosInstance = axios.create({ - baseURL: `${process.env.NEXT_PUBLIC_API_URL}/appointments`, - headers: { - "Content-Type": "application/json", - }, - withCredentials: true, -}); - -axiosInstance.interceptors.request.use( - (config) => { - const tokens = getAuthTokens(); - if (tokens.patientToken) { - config.headers.Authorization = `Bearer ${tokens.patientToken}`; - } - return config; - }, - (error) => Promise.reject(error) -); - -axiosInstance.interceptors.response.use( - (response) => response, - async (error: any) => { - const originalRequest = error.config; - const tokens = getAuthTokens(); - - if (error.response?.status === 403) { - setAuthTokens({ ...tokens, patientToken: "" }); - return Promise.reject(error); - } - if (error.response?.status === 401 && !originalRequest._retry) { - originalRequest._retry = true; - try { - const refreshResponse = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/patient/auth/refresh`, { - withCredentials: true, - }); - - const newAccessToken = refreshResponse.data.accessToken; - setAuthTokens({ ...tokens, patientToken: newAccessToken }); +export type verifyPaymentProps = { + paymentData: { razorpay_order_id: string; razorpay_payment_id: string; razorpay_signature: string }; + appointmentId: string +} - originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; - return axiosInstance(originalRequest); - } catch (refreshError: any) { - if (refreshError.response?.status === 401 || 403) { - setAuthTokens({ ...tokens, patientToken: "" }); - } - return Promise.reject(refreshError); - } - } - return Promise.reject(error); - } -); +const { doctorInstance, patientInstance, baseUrl } = { + doctorInstance: doctorAxiosInstance, + patientInstance: patientAxiosInstance, + baseUrl: `${process.env.NEXT_PUBLIC_API_URL}/appointments` +} export const getDoctorsList = async () => { const response = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/doctors`); return response.data; }; - export const createAppointment = async (appointment: IAppointment) => { - const response = await axiosInstance.post('/', { appointment }); + const response = await withTempBaseUrl(patientInstance, baseUrl, { + method: 'POST', + url: '/', + data: { + appointment + } + }); return response.data; -} +}; export const verifyPayment = async ({ appointmentId, paymentData }: verifyPaymentProps) => { - const response = await axiosInstance.post('/verify-payment', { paymentData, appointmentId }); + const response = await withTempBaseUrl(patientInstance, baseUrl, { + method: 'POST', + url: '/verify-payment', + data: { + paymentData, appointmentId + } + }); return response.data; -} +}; -export type verifyPaymentProps = { - paymentData: { razorpay_order_id: string; razorpay_payment_id: string; razorpay_signature: string }; - appointmentId: string + +export const getAppointmentsDoctor = async (status: AppointmentStatus) => { + const response = await withTempBaseUrl(doctorInstance, baseUrl, { + method: 'GET', + url: `/doctor?status=${status}`, + }) + return response.data; } \ No newline at end of file diff --git a/client/lib/api/doctor/authorizedRoutes.ts b/client/lib/api/doctor/authorizedRoutes.ts index 0bbc77d4..d27939b1 100644 --- a/client/lib/api/doctor/authorizedRoutes.ts +++ b/client/lib/api/doctor/authorizedRoutes.ts @@ -65,3 +65,7 @@ doctorAxiosInstance.interceptors.response.use( return Promise.reject(error); } ); + + + +export default doctorAxiosInstance \ No newline at end of file diff --git a/client/lib/api/patient/authorizedRoutes.ts b/client/lib/api/patient/authorizedRoutes.ts index d91ab637..3ed96275 100644 --- a/client/lib/api/patient/authorizedRoutes.ts +++ b/client/lib/api/patient/authorizedRoutes.ts @@ -1,5 +1,5 @@ import { IPatient } from "@/types"; -import axios from "axios"; +import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"; const getAuthTokens = () => { try { @@ -14,7 +14,7 @@ const setAuthTokens = (tokens: Record) => { localStorage.setItem("auth", JSON.stringify(tokens)); }; -const axiosInstance = axios.create({ +const patientAxiosInstance = axios.create({ baseURL: `${process.env.NEXT_PUBLIC_API_URL}/patient`, headers: { "Content-Type": "application/json", @@ -22,7 +22,7 @@ const axiosInstance = axios.create({ withCredentials: true, }); -axiosInstance.interceptors.request.use( +patientAxiosInstance.interceptors.request.use( (config) => { const tokens = getAuthTokens(); if (tokens.patientToken) { @@ -33,7 +33,7 @@ axiosInstance.interceptors.request.use( (error) => Promise.reject(error) ); -axiosInstance.interceptors.response.use( +patientAxiosInstance.interceptors.response.use( (response) => response, async (error: any) => { const originalRequest = error.config; @@ -54,7 +54,7 @@ axiosInstance.interceptors.response.use( setAuthTokens({ ...tokens, patientToken: newAccessToken }); originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; - return axiosInstance(originalRequest); + return patientAxiosInstance(originalRequest); } catch (refreshError: any) { if (refreshError.response?.status === 401 || 403) { setAuthTokens({ ...tokens, patientToken: "" }); @@ -68,21 +68,25 @@ axiosInstance.interceptors.response.use( ); export const getPatientProfile = async () => { - const response = await axiosInstance.get(`/profile`); + const response = await patientAxiosInstance.get(`/profile`); return response.data; }; export const updatePatientProfile = async (patient: IPatient) => { - const response = await axiosInstance.put("/profile", { patient }); + const response = await patientAxiosInstance.put("/profile", { patient }); return response.data; }; export const getUpdateProfileUrl = async () => { - const response = await axiosInstance.get("/profile/upload-url"); + const response = await patientAxiosInstance.get("/profile/upload-url"); return response.data; }; export const updateProfileImage = async (key: string) => { - const response = await axiosInstance.put("/profile/upload-url", { key }); + const response = await patientAxiosInstance.put("/profile/upload-url", { key }); return response.data; }; + + + +export default patientAxiosInstance \ No newline at end of file diff --git a/client/lib/api/slots/route.ts b/client/lib/api/slots/route.ts index 1db124a3..7ae7f564 100644 --- a/client/lib/api/slots/route.ts +++ b/client/lib/api/slots/route.ts @@ -1,116 +1,69 @@ import { ISlot, Days } from "@/types"; -import axios from "axios"; +import { withTempBaseUrl } from "@/lib/utils/withTempBaseUrl"; +import doctorAxiosInstance from "../doctor/authorizedRoutes"; -export const axiosInstance = axios.create({ - baseURL: `${process.env.NEXT_PUBLIC_API_URL}/slots`, - headers: { - "Content-Type": "application/json", - }, - withCredentials: true, -}); - -axiosInstance.interceptors.request.use( - (config) => { - const token = JSON.parse(localStorage.getItem("auth") || "{}"); - if (token.doctorToken) { - config.headers.Authorization = `Bearer ${token.doctorToken}`; - } - return config; - }, - (error) => { - return Promise.reject(error); - } -); - -axiosInstance.interceptors.response.use( - (response) => { - return response; - }, - async (error: any) => { - const originalRequest = error.config; - - if (error.response?.status === 401 && !originalRequest._retry) { - originalRequest._retry = true; - - try { - const tokens = JSON.parse(localStorage.getItem("auth") || "{}"); - const refreshResponse = await axios.get(`${process.env.NEXT_PUBLIC_API_URL}/doctor/auth/refresh`, { - withCredentials: true, - }); - - const newAccessToken = refreshResponse.data.accessToken; - - localStorage.setItem( - "auth", - JSON.stringify({ - ...tokens, - doctorToken: newAccessToken, - }) - ); - - originalRequest.headers.Authorization = `Bearer ${newAccessToken}`; - - return axiosInstance(originalRequest); - } catch (refreshError: any) { - if (refreshError.response.status === 401) { - const tokens = JSON.parse(localStorage.getItem("auth") || "{}"); - localStorage.setItem("auth", { - ...tokens, - doctorToken: "", - }); - } - return Promise.reject(refreshError); - } - } - - return Promise.reject(error); - } -); +const baseUrl = `${process.env.NEXT_PUBLIC_API_URL}/slots`; // =================================================================== // export const addSlotsDoctor = async (slots: ISlot[], day: Days) => { - const response = await axiosInstance.post('/day', { slots, day }); + const response = await withTempBaseUrl(doctorAxiosInstance, baseUrl, { + method: 'POST', + url: '/day', + data: { slots, day }, + }); return response.data; }; export const deleteManyByDayDoctor = async (slots: ISlot[], day: Days) => { - const response = await axiosInstance.delete('/day', { - data: { - slots, - day - } + const response = await withTempBaseUrl(doctorAxiosInstance, baseUrl, { + method: 'DELETE', + url: '/day', + data: { slots, day }, }); - return response.data -} + return response.data; +}; export const addSlotsAllDayDoctor = async (startTimes: string[]) => { - const response = await axiosInstance.post('/all-days', { startTimes }) + const response = await withTempBaseUrl(doctorAxiosInstance, baseUrl, { + method: 'POST', + url: '/all-days', + data: { startTimes }, + }); return response.data; -} +}; export const deleteSlotsAllDayDoctor = async (startTimes: string[]) => { - const response = await axiosInstance.delete('/all-days', { - data: { - startTimes - } - }) + const response = await withTempBaseUrl(doctorAxiosInstance, baseUrl, { + method: 'DELETE', + url: '/all-days', + data: { startTimes }, + }); return response.data; -} +}; export const getSlotsByDayDoctor = async (day: Days) => { - const response = await axiosInstance.get(`/doctor?day=${day}`); + const response = await withTempBaseUrl(doctorAxiosInstance, baseUrl, { + method: 'GET', + url: `/doctor?day=${day}`, + }); return response.data; -} +}; export const getAllSlotsDoctor = async () => { - const response = await axiosInstance.get('/doctor'); + const response = await withTempBaseUrl(doctorAxiosInstance, baseUrl, { + method: 'GET', + url: '/doctor', + }); return response.data; -} +}; + // =================================================================== // -export const getSlotsOfDoctor = async(doctorId:string,date:string)=>{ - - const response = await axiosInstance.get(`/${doctorId}?date=${date}`); +export const getSlotsOfDoctor = async (doctorId: string, date: string) => { + const response = await withTempBaseUrl(doctorAxiosInstance, baseUrl, { + method: 'GET', + url: `/${doctorId}?date=${date}`, + }); return response.data; -} \ No newline at end of file +}; diff --git a/client/lib/utils/withTempBaseUrl.ts b/client/lib/utils/withTempBaseUrl.ts new file mode 100644 index 00000000..6022193b --- /dev/null +++ b/client/lib/utils/withTempBaseUrl.ts @@ -0,0 +1,17 @@ +import { AxiosInstance, AxiosRequestConfig, AxiosResponse } from "axios"; + +export const withTempBaseUrl = async ( + instance: AxiosInstance, + newBaseUrl: string, + config: AxiosRequestConfig + ): Promise => { + const originalBaseUrl = instance.defaults.baseURL; + + try { + instance.defaults.baseURL = newBaseUrl; + const response = await instance(config); + return response; + } finally { + instance.defaults.baseURL = originalBaseUrl; + } + }; \ No newline at end of file diff --git a/server/src/domain/entities/IAppointment.ts b/server/src/domain/entities/IAppointment.ts index 949f8d93..5e1112bf 100644 --- a/server/src/domain/entities/IAppointment.ts +++ b/server/src/domain/entities/IAppointment.ts @@ -1,3 +1,6 @@ +import { IPatient } from "./IPatient"; +import ISlot from "./ISlot"; + export enum AppointmentStatus { PAYMENT_PENDING = 'payment-pending', PENDING = 'pending', @@ -23,3 +26,8 @@ export default interface IAppointment { readonly paymentId?: string; status?: AppointmentStatus; } + +export interface IExtendedAppointment extends IAppointment { + patient?: IPatient; + slot?: ISlot; +} \ No newline at end of file