diff --git a/client/src/components/appointmentCard/AppointmentCard.tsx b/client/src/components/appointmentCard/AppointmentCard.tsx index 7a941eed..4f5d28e4 100644 --- a/client/src/components/appointmentCard/AppointmentCard.tsx +++ b/client/src/components/appointmentCard/AppointmentCard.tsx @@ -3,6 +3,7 @@ import { AppointmentStatusBar } from "./AppointmentStatusBar"; import { AppointmentAvatar } from "./AppointmentAvatar"; import { AppointmentInfo } from "./AppointmentInfo"; import { AppointmentJoinButton } from "./AppointmentJoinButton"; +import { RejectionReasonDisplay } from "./RejectionReasonDisplay"; import { getStatusStyles, isInternalVideoCallLink, @@ -69,6 +70,8 @@ export const AppointmentCard = ({ onDelete={isPast ? onDelete : undefined} /> + + ); }; diff --git a/client/src/components/appointmentCard/RejectAppointmentModal.tsx b/client/src/components/appointmentCard/RejectAppointmentModal.tsx new file mode 100644 index 00000000..cbdc21f8 --- /dev/null +++ b/client/src/components/appointmentCard/RejectAppointmentModal.tsx @@ -0,0 +1,132 @@ +import React, { useState } from "react"; +import { Appointment } from "../../types/appointments.types"; +import { useUpdateAppointmentMutation } from "../../features/appointments/mutations/useUpdateAppointmentMutation"; + +interface RejectAppointmentModalProps { + appointment: Appointment; + isOpen: boolean; + onClose: () => void; +} + +export const RejectAppointmentModal = ({ + appointment, + isOpen, + onClose, +}: RejectAppointmentModalProps) => { + const [rejectionReason, setRejectionReason] = useState(""); + const [error, setError] = useState(""); + const updateAppointmentMutation = useUpdateAppointmentMutation(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (rejectionReason.trim().length < 100) { + setError("Rejection reason must be at least 100 characters"); + return; + } + + if (rejectionReason.trim().length > 500) { + setError("Rejection reason must not exceed 500 characters"); + return; + } + + try { + await updateAppointmentMutation.mutateAsync({ + appointmentId: appointment.id, + status: "rejected", + rejectionReason: rejectionReason.trim(), + }); + onClose(); + setRejectionReason(""); + setError(""); + } catch { + setError("Failed to reject appointment. Please try again."); + } + }; + + const handleClose = () => { + onClose(); + setRejectionReason(""); + setError(""); + }; + + if (!isOpen) return null; + + return ( +
+
+

Reject Appointment

+ +
+

+ Student: {appointment.studentName} +

+

+ Lesson: {appointment.lesson} +

+

+ Date & Time: {appointment.date} at{" "} + {appointment.time} +

+
+ +
+
+ +