Skip to content

Commit

Permalink
Support for rescheduling appointment (#9956)
Browse files Browse the repository at this point in the history
  • Loading branch information
rithviknishad authored Jan 21, 2025
1 parent 7b11974 commit 317d9bd
Show file tree
Hide file tree
Showing 8 changed files with 388 additions and 238 deletions.
6 changes: 6 additions & 0 deletions public/locale/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -396,9 +396,12 @@
"app_settings": "App Settings",
"apply": "Apply",
"appointment_booking_success": "Your appointment has been successfully booked!",
"appointment_cancelled": "Appointment has been cancelled!",
"appointment_created_success": "Appointment created successfully",
"appointment_details": "Appointment Details",
"appointment_not_found": "Appointment not found",
"appointment_rescheduled": "Appointment has been rescheduled!",
"appointment_rescheduled_successfully": "Appointment rescheduled successfully",
"appointment_type": "Appointment Type",
"appointments": "Appointments",
"approve": "Approve",
Expand Down Expand Up @@ -1756,6 +1759,9 @@
"required": "Required",
"required_quantity": "Required Quantity",
"reschedule": "Reschedule",
"reschedule_appointment": "Reschedule Appointment",
"rescheduled": "Rescheduled",
"rescheduling": "Rescheduling...",
"resend_otp": "Resend OTP",
"reset": "Reset",
"reset_password": "Reset Password",
Expand Down
85 changes: 84 additions & 1 deletion src/pages/Appointments/AppointmentDetail.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
import { differenceInYears, format, isSameDay } from "date-fns";
import { BanIcon, PrinterIcon } from "lucide-react";
import { navigate } from "raviger";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";

Expand Down Expand Up @@ -43,6 +44,13 @@ import {
SelectValue,
} from "@/components/ui/select";
import { Separator } from "@/components/ui/separator";
import {
Sheet,
SheetContent,
SheetHeader,
SheetTitle,
SheetTrigger,
} from "@/components/ui/sheet";

import Loading from "@/components/Common/Loading";
import Page from "@/components/Common/Page";
Expand All @@ -63,11 +71,14 @@ import {
import { FacilityData } from "@/types/facility/facility";
import {
Appointment,
AppointmentFinalStatuses,
AppointmentStatuses,
AppointmentUpdateRequest,
} from "@/types/scheduling/schedule";
import scheduleApis from "@/types/scheduling/scheduleApis";

import { AppointmentSlotPicker } from "./components/AppointmentSlotPicker";

interface Props {
facilityId: string;
appointmentId: string;
Expand Down Expand Up @@ -230,6 +241,7 @@ const AppointmentDetails = ({
fulfilled: "primary",
entered_in_error: "destructive",
cancelled: "destructive",
rescheduled: "secondary",
noshow: "destructive",
} as Partial<
Record<Appointment["status"], BadgeProps["variant"]>
Expand Down Expand Up @@ -388,6 +400,8 @@ const AppointmentActions = ({
}: AppointmentActionsProps) => {
const { t } = useTranslation();
const queryClient = useQueryClient();
const [isRescheduleOpen, setIsRescheduleOpen] = useState(false);
const [selectedSlotId, setSelectedSlotId] = useState<string>();

const currentStatus = appointment.status;
const isToday = isSameDay(appointment.token_slot.start_datetime, new Date());
Expand All @@ -400,13 +414,35 @@ const AppointmentActions = ({
},
}),
onSuccess: () => {
toast.success(t("appointment_cancelled"));
queryClient.invalidateQueries({
queryKey: ["appointment", appointment.id],
});
},
});

if (["fulfilled", "cancelled", "entered_in_error"].includes(currentStatus)) {
const { mutate: rescheduleAppointment, isPending: isRescheduling } =
useMutation({
mutationFn: mutate(scheduleApis.appointments.reschedule, {
pathParams: {
facility_id: facilityId,
id: appointment.id,
},
}),
onSuccess: (newAppointment: Appointment) => {
toast.success(t("appointment_rescheduled"));
queryClient.invalidateQueries({
queryKey: ["appointment", appointment.id],
});
setIsRescheduleOpen(false);
setSelectedSlotId(undefined);
navigate(
`/facility/${facilityId}/patient/${appointment.patient.id}/appointments/${newAppointment.id}`,
);
},
});

if (AppointmentFinalStatuses.includes(currentStatus)) {
return null;
}

Expand Down Expand Up @@ -437,6 +473,53 @@ const AppointmentActions = ({
<PersonIcon className="size-4 mr-2" />
{t("view_patient")}
</Button>

<Sheet open={isRescheduleOpen} onOpenChange={setIsRescheduleOpen}>
<SheetTrigger asChild>
<Button variant="outline" size="lg">
<CalendarIcon className="size-4 mr-2" />
{t("reschedule")}
</Button>
</SheetTrigger>
<SheetContent className="w-full sm:max-w-xl overflow-y-auto">
<SheetHeader>
<SheetTitle>{t("reschedule_appointment")}</SheetTitle>
</SheetHeader>

<div className="mt-6">
<AppointmentSlotPicker
facilityId={facilityId}
resourceId={appointment.user?.id}
selectedSlotId={selectedSlotId}
onSlotSelect={setSelectedSlotId}
/>

<div className="flex justify-end gap-2 mt-6">
<Button
variant="outline"
onClick={() => {
setIsRescheduleOpen(false);
setSelectedSlotId(undefined);
}}
>
{t("cancel")}
</Button>
<Button
variant="default"
disabled={!selectedSlotId || isRescheduling}
onClick={() => {
if (selectedSlotId) {
rescheduleAppointment({ new_slot: selectedSlotId });
}
}}
>
{isRescheduling ? t("rescheduling") : t("reschedule")}
</Button>
</div>
</div>
</SheetContent>
</Sheet>

{currentStatus === "booked" && (
<>
<Button
Expand Down
Loading

0 comments on commit 317d9bd

Please sign in to comment.