Content ITV PRO
This is Itvedant Content department
Please create an account to use this template.
Business Scenario
In this lab, we will enhance the Medicare application by implementing the appointment booking workflow for patients.
Pre-Lab Preparation
Module:
1) React Basics & Backend Integration (Spring Boot)
Phase 12E — Appointment Booking
Find Doctor ↓ Doctor Card ↓ Book Appointment ↓ Select Date
↓ Select Slot ↓ POST /api/appointments ↓ PENDING + UNPAID ↓ Booking Confirmation
Create appointmentService.js
1
src/services/appointmentService.js
import api, {getAuthConfig} from "../api/api";
const createAppointment = async (appointmentData) => {
const response = await api.post("/appointments",appointmentData,
getAuthConfig());
return response.data;
};
const appointmentService = {createAppointment};
export default appointmentService;getMyAppointments() cancelAppointment() getDoctorAppointments() completeAppointment()
Update DoctorCard
2
import {Link} from "react-router";<button className="btn btn-primary w-100" disabled>
Book Appointment
</button><Link to={`/patient/doctors/${doctor.id}/book`} className="btn btn-primary w-100">
Book Appointment
</Link>import { Link } from "react-router";
function DoctorCard({ doctor }) {
return (
<div className="card h-100 shadow-sm">
{doctor.photoUrl ? (
<img
src={doctor.photoUrl}
className="card-img-top"
alt={doctor.name}
style={{ height: "220px", objectFit: "cover" }}
/> ) : (
<div
className="bg-light d-flex align-items-center j
ustify-content-center text-secondary"
style={{ height: "220px" }}>
No Photo
</div>
)}
<div className="card-body d-flex flex-column">
<h5 className="card-title">{doctor.name}</h5>
<p className="text-primary fw-semibold mb-2">
{doctor.specialization}
</p>
<p className="card-text mb-2">
<strong>Experience:</strong> {doctor.experienceYears} years
</p>
<p className="card-text mb-4">
<strong>Consultation Fee:</strong> ₹{doctor.consultationFee}
</p>
<Link
to={`/patient/doctors/${doctor.id}/book`}
className="btn btn-primary w-100 mt-auto"
>
Book Appointment
</Link> </div>
</div>
);
}
export default DoctorCard;Create Booking Page
3
src/pages/patient/BookAppointment.jsx
import { useEffect, useState } from "react";
import { Link, useParams } from "react-router";
import doctorService from "../../services/doctorService";
import appointmentService from "../../services/appointmentService";
function BookAppointment() {
const { doctorId } = useParams();
const [doctor, setDoctor] = useState(null);
const [appointmentDate, setAppointmentDate] = useState("");
const [timeSlot, setTimeSlot] = useState("");
const [loadingDoctor, setLoadingDoctor] = useState(true);
const [booking, setBooking] = useState(false);
const [error, setError] = useState("");
const [success, setSuccess] = useState(null);
useEffect(() => {
loadDoctor();
}, [doctorId]);
const loadDoctor = async () => {
try {
setLoadingDoctor(true);
setError("");
const data = await doctorService.getDoctorById(doctorId);
setDoctor(data);
} catch (error) {
setError(error.response?.data?.message ||
"Unable to load doctor");
} finally {
setLoadingDoctor(false);
}
};
const getToday = () => {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, "0");
const day = String(today.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
const handleSubmit = async (event) => {
event.preventDefault();
setError("");
setSuccess(null);
if (!appointmentDate) {
setError("Please select an appointment date");
return;
}
if (!timeSlot) {
setError("Please select a time slot");
return;
}
try {
setBooking(true);
const appointmentData = {
doctorId: Number(doctorId),
appointmentDate,
timeSlot
};
const response =
await appointmentService.createAppointment(appointmentData);
setSuccess(response);
setAppointmentDate("");
setTimeSlot("");
} catch (error) {
setError(error.response?.data?.message ||
"Unable to book appointment");
} finally {
setBooking(false);
}
};
if (loadingDoctor) {
return (
<div className="container py-5 text-center">
<div className="spinner-border text-primary"></div>
<p className="text-secondary mt-3">Loading doctor...</p>
</div>
);
}
if (!doctor) {
return (
<div className="container py-5">
<div className="alert alert-danger">
Doctor information could not be loaded.
</div>
<Link to="/patient/doctors" className="btn btn-primary">
Back to Doctors
</Link>
</div>
);
}
return (
<div className="container py-5">
<div className="row justify-content-center">
<div className="col-lg-8">
{/* PAGE HEADER */}
<div className="mb-4">
<h2>Book Appointment</h2>
<p className="text-secondary">
Select your preferred appointment date and time.
</p>
</div>
{/* DOCTOR INFORMATION */}
<div className="card shadow-sm mb-4">
<div className="card-body">
<div className="row align-items-center">
{doctor.photoUrl && (
<div className="col-md-3 mb-3 mb-md-0">
<img src={doctor.photoUrl} alt={doctor.name}
className="img-fluid rounded" />
</div>
)}
<div className={doctor.photoUrl ? "col-md-9" : "col-12"}>
<h4 className="mb-1">{doctor.name}</h4>
<p className="text-primary fw-semibold mb-2">
{doctor.specialization}
</p>
<p className="mb-1">
<strong>Experience:</strong>
{doctor.experienceYears} years
</p>
<p className="mb-0">
<strong>Consultation Fee:</strong>
₹{doctor.consultationFee}
</p>
</div>
</div>
</div>
</div>
{/* ERROR */}
{error && (
<div className="alert alert-danger">{error}</div>
)}
{/* SUCCESS */}
{success && (
<div className="alert alert-success">
<h5>Appointment Booked</h5>
<p className="mb-1">
Appointment ID: <strong>{success.id}</strong>
</p>
<p className="mb-1">
Doctor: <strong>{success.doctorName}</strong>
</p>
<p className="mb-1">
Date: <strong>{success.appointmentDate}</strong>
</p>
<p className="mb-1">
Slot: <strong>{success.timeSlot}</strong>
</p>
<p className="mb-1">
Status: <strong>{success.appointmentStatus}</strong>
</p>
<p className="mb-0">
Payment: <strong>{success.paymentStatus}</strong>
</p>
</div>
)}
{/* BOOKING FORM */}
<div className="card shadow-sm">
<div className="card-body p-4">
<form onSubmit={handleSubmit}>
{/* DATE */}
<div className="mb-4">
<label className="form-label">
Appointment Date</label>
<input
type="date"
className="form-control"
min={getToday()}
value={appointmentDate}
onChange={(event) =>
setAppointmentDate(event.target.value)}
required
/>
</div>
{/* TIME SLOT */}
<div className="mb-4">
<label className="form-label">
Select Time Slot</label>
<div className="row g-3">
{/* 10 AM */}
<div className="col-md-4">
<input
type="radio"
className="btn-check"
name="timeSlot"
id="slotMorning"
value="MORNING_10_AM"
checked={timeSlot === "MORNING_10_AM"}
onChange={(event) =>
setTimeSlot(event.target.value)}
/>
<label className="btn btn-outline-primary w-100"
htmlFor="slotMorning">
10:00 AM
</label>
</div>
{/* 2 PM */}
<div className="col-md-4">
<input
type="radio"
className="btn-check"
name="timeSlot"
id="slotAfternoon"
value="AFTERNOON_2_PM"
checked={timeSlot === "AFTERNOON_2_PM"}
onChange={(event) =>
setTimeSlot(event.target.value)}
/>
<label className="btn btn-outline-primary
w-100" htmlFor="slotAfternoon">
2:00 PM
</label>
</div>
{/* 5 PM */}
<div className="col-md-4">
<input
type="radio"
className="btn-check"
name="timeSlot"
id="slotEvening"
value="EVENING_5_PM"
checked={timeSlot === "EVENING_5_PM"}
onChange={(event) =>
setTimeSlot(event.target.value)}
/>
<label className="btn btn-outline-primary
w-100" htmlFor="slotEvening">
5:00 PM
</label>
</div>
</div>
</div>
{/* FEE */}
<div className="alert alert-light border">
Consultation Fee: <strong>
₹{doctor.consultationFee}</strong>
</div>
<div className="d-flex gap-2">
<Link to="/patient/doctors"
className="btn btn-outline-secondary">
Back
</Link>
<button
type="submit"
className="btn btn-primary flex-grow-1"
disabled={booking}
>
{booking ? "Booking..." : "Book Appointment"}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
);
}
export default BookAppointment;
function BookAppointment() {
const { doctorId } = useParams();
const [doctor, setDoctor] = useState(null);
const [appointmentDate, setAppointmentDate] = useState("");
const [timeSlot, setTimeSlot] = useState("");
const [loadingDoctor, setLoadingDoctor] = useState(true);
const [booking, setBooking] = useState(false);
const [error, setError] = useState("");
const [success, setSuccess] = useState(null);
useEffect(() => {
loadDoctor();
}, [doctorId]);
const loadDoctor = async () => {
try {
setLoadingDoctor(true);
setError("");
const data = await doctorService.getDoctorById(doctorId);
setDoctor(data);
} catch (error) {
setError(error.response?.data?.message ||
"Unable to load doctor");
} finally {
setLoadingDoctor(false);
}
};
const getToday = () => {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, "0");
const day = String(today.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
const handleSubmit = async (event) => {
event.preventDefault();
setError("");
setSuccess(null);
if (!appointmentDate) {
setError("Please select an appointment date");
return;
}
if (!timeSlot) {
setError("Please select a time slot");
return;
}
try {
setBooking(true);
const appointmentData = {
doctorId: Number(doctorId),
appointmentDate,
timeSlot
};
const response =
await appointmentService.createAppointment(appointmentData);
setSuccess(response);
setAppointmentDate("");
setTimeSlot("");
} catch (error) {
setError(error.response?.data?.message ||
"Unable to book appointment");
} finally {
setBooking(false);
}
};
if (loadingDoctor) {
return (
<div className="container py-5 text-center">
<div className="spinner-border text-primary"></div>
<p className="text-secondary mt-3">Loading doctor...</p>
</div>
);
}
if (!doctor) {
return (
<div className="container py-5">
<div className="alert alert-danger">
Doctor information could not be loaded.
</div>
<Link to="/patient/doctors" className="btn btn-primary">
Back to Doctors
</Link>
</div>
);
}
return (
<div className="container py-5">
<div className="row justify-content-center">
<div className="col-lg-8">
{/* PAGE HEADER */}
<div className="mb-4">
<h2>Book Appointment</h2>
<p className="text-secondary">
Select your preferred appointment date and time.
</p>
</div>
{/* DOCTOR INFORMATION */}
<div className="card shadow-sm mb-4">
<div className="card-body">
<div className="row align-items-center">
{doctor.photoUrl && (
<div className="col-md-3 mb-3 mb-md-0">
<img src={doctor.photoUrl} alt={doctor.name}
className="img-fluid rounded" />
</div>
)}
<div className={doctor.photoUrl ? "col-md-9" : "col-12"}>
<h4 className="mb-1">{doctor.name}</h4>
<p className="text-primary fw-semibold mb-2">
{doctor.specialization}
</p>
<p className="mb-1">
<strong>Experience:</strong>
{doctor.experienceYears} years
</p>
<p className="mb-0">
<strong>Consultation Fee:</strong>
₹{doctor.consultationFee}
</p>
</div>
</div>
</div>
</div>
{/* ERROR */}
{error && (
<div className="alert alert-danger">{error}</div>
)}
{/* SUCCESS */}
{success && (
<div className="alert alert-success">
<h5>Appointment Booked</h5>
<p className="mb-1">
Appointment ID: <strong>{success.id}</strong>
</p>
<p className="mb-1">
Doctor: <strong>{success.doctorName}</strong>
</p>
<p className="mb-1">
Date: <strong>{success.appointmentDate}</strong>
</p>
<p className="mb-1">
Slot: <strong>{success.timeSlot}</strong>
</p>
<p className="mb-1">
Status: <strong>{success.appointmentStatus}</strong>
</p>
<p className="mb-0">
Payment: <strong>{success.paymentStatus}</strong>
</p>
</div>
)}
{/* BOOKING FORM */}
<div className="card shadow-sm">
<div className="card-body p-4">
<form onSubmit={handleSubmit}>
{/* DATE */}
<div className="mb-4">
<label className="form-label">
Appointment Date</label>
<input
type="date"
className="form-control"
min={getToday()}
value={appointmentDate}
onChange={(event) =>
setAppointmentDate(event.target.value)}
required
/>
</div>
{/* TIME SLOT */}
<div className="mb-4">
<label className="form-label">
Select Time Slot</label>
<div className="row g-3">
{/* 10 AM */}
<div className="col-md-4">
<input
type="radio"
className="btn-check"
name="timeSlot"
id="slotMorning"
value="MORNING_10_AM"
checked={timeSlot === "MORNING_10_AM"}
onChange={(event) =>
setTimeSlot(event.target.value)}
/>
<label className="btn btn-outline-primary w-100"
htmlFor="slotMorning">
10:00 AM
</label>
</div>
{/* 2 PM */}
<div className="col-md-4">
<input
type="radio"
className="btn-check"
name="timeSlot"
id="slotAfternoon"
value="AFTERNOON_2_PM"
checked={timeSlot === "AFTERNOON_2_PM"}
onChange={(event) =>
setTimeSlot(event.target.value)}
/>
<label className="btn btn-outline-primary
w-100" htmlFor="slotAfternoon">
2:00 PM
</label>
</div>
{/* 5 PM */}
<div className="col-md-4">
<input
type="radio"
className="btn-check"
name="timeSlot"
id="slotEvening"
value="EVENING_5_PM"
checked={timeSlot === "EVENING_5_PM"}
onChange={(event) =>
setTimeSlot(event.target.value)}
/>
<label className="btn btn-outline-primary
w-100" htmlFor="slotEvening">
5:00 PM
</label>
</div>
</div>
</div>
{/* FEE */}
<div className="alert alert-light border">
Consultation Fee: <strong>
₹{doctor.consultationFee}</strong>
</div>
<div className="d-flex gap-2">
<Link to="/patient/doctors"
className="btn btn-outline-secondary">
Back
</Link>
<button
type="submit"
className="btn btn-primary flex-grow-1"
disabled={booking}
>
{booking ? "Booking..." : "Book Appointment"}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
);
}
export default BookAppointment;
const getToday = () => {
const today = new Date();
const year = today.getFullYear();
const month = String(today.getMonth() + 1).padStart(2, "0");
const day = String(today.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
};
const handleSubmit = async (event) => {
event.preventDefault();
setError("");
setSuccess(null);
if (!appointmentDate) {
setError("Please select an appointment date");
return;
}
if (!timeSlot) {
setError("Please select a time slot");
return;
}
try {
setBooking(true);
const appointmentData = {
doctorId: Number(doctorId),appointmentDate,timeSlot
};
const response =
await appointmentService.createAppointment(appointmentData);
setSuccess(response);
setAppointmentDate("");
setTimeSlot("");
} catch (error) {
setError(error.response?.data?.message ||
"Unable to book appointment");
} finally {
setBooking(false);
}
};
if (loadingDoctor) {
return (
<div className="container py-5 text-center">
<div className="spinner-border text-primary"></div>
<p className="text-secondary mt-3">Loading doctor...</p>
</div>
);
}
if (!doctor) {
return (
<div className="container py-5">
<div className="alert alert-danger">
Doctor information could not be loaded.
</div>
<Link to="/patient/doctors" className="btn btn-primary">
Back to Doctors
</Link>
</div>
);
}
return (
<div className="container py-5">
<div className="row justify-content-center">
<div className="col-lg-8">
{/* PAGE HEADER */}
<div className="mb-4">
<h2>Book Appointment</h2>
<p className="text-secondary">
Select your preferred appointment date and time.
</p>
</div>
{/* DOCTOR INFORMATION */}
<div className="card shadow-sm mb-4">
<div className="card-body">
<div className="row align-items-center">
{doctor.photoUrl && (
<div className="col-md-3 mb-3 mb-md-0">
<img src={doctor.photoUrl} alt={doctor.name}
className="img-fluid rounded" />
</div>
)}
<div className={doctor.photoUrl ? "col-md-9" : "col-12"}>
<h4 className="mb-1">{doctor.name}</h4>
<p className="text-primary fw-semibold mb-2">
{doctor.specialization}
</p>
<p className="mb-1">
<strong>Experience:</strong>
{doctor.experienceYears} years
</p>
<p className="mb-0">
<strong>Consultation Fee:</strong>
₹{doctor.consultationFee}
</p>
</div>
</div>
</div>
</div>
{/* ERROR */}
{error && (
<div className="alert alert-danger">{error}</div>
)}
{/* SUCCESS */}
{success && (
<div className="alert alert-success">
<h5>Appointment Booked</h5>
<p className="mb-1">
Appointment ID: <strong>{success.id}</strong>
</p>
<p className="mb-1">
Doctor: <strong>{success.doctorName}</strong>
</p>
<p className="mb-1">
Date: <strong>{success.appointmentDate}</strong>
</p>
<p className="mb-1">
Slot: <strong>{success.timeSlot}</strong>
</p>
<p className="mb-1">
Status: <strong>{success.appointmentStatus}</strong>
</p>
<p className="mb-0">
Payment: <strong>{success.paymentStatus}</strong>
</p>
</div>
)}
{/* BOOKING FORM */}
<div className="card shadow-sm">
<div className="card-body p-4">
<form onSubmit={handleSubmit}>
{/* DATE */}
<div className="mb-4">
<label className="form-label">
Appointment Date</label>
<input
type="date"
className="form-control"
min={getToday()}
value={appointmentDate}
onChange={(event) =>
setAppointmentDate(event.target.value)}
required
/>
</div>
{/* TIME SLOT */}
<div className="mb-4">
<label className="form-label">
Select Time Slot</label>
<div className="row g-3">
{/* 10 AM */}
<div className="col-md-4">
<input
type="radio"
className="btn-check"
name="timeSlot"
id="slotMorning"
value="MORNING_10_AM"
checked={timeSlot === "MORNING_10_AM"}
onChange={(event) =>
setTimeSlot(event.target.value)}
/>
<label className="btn btn-outline-primary w-100"
htmlFor="slotMorning">
10:00 AM
</label>
</div>
{/* 2 PM */}
<div className="col-md-4">
<input
type="radio"
className="btn-check"
name="timeSlot"
id="slotAfternoon"
value="AFTERNOON_2_PM"
checked={timeSlot === "AFTERNOON_2_PM"}
onChange={(event) =>
setTimeSlot(event.target.value)}
/>
<label className="btn btn-outline-primary
w-100" htmlFor="slotAfternoon">
2:00 PM
</label>
</div>
{/* 5 PM */}
<div className="col-md-4">
<input
type="radio"
className="btn-check"
name="timeSlot"
id="slotEvening"
value="EVENING_5_PM"
checked={timeSlot === "EVENING_5_PM"}
onChange={(event) =>
setTimeSlot(event.target.value)}
/>
<label className="btn btn-outline-primary
w-100" htmlFor="slotEvening">
5:00 PM
</label>
</div>
</div>
</div>
{/* FEE */}
<div className="alert alert-light border">
Consultation Fee: <strong>
₹{doctor.consultationFee}</strong>
</div>
<div className="d-flex gap-2">
<Link to="/patient/doctors"
className="btn btn-outline-secondary">
Back
</Link>
<button
type="submit"
className="btn btn-primary flex-grow-1"
disabled={booking}
>
{booking ? "Booking..." : "Book Appointment"}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
);
}
export default BookAppointment;
const response =
await appointmentService.createAppointment(appointmentData);
setSuccess(response);
setAppointmentDate("");
setTimeSlot("");
} catch (error) {
setError(error.response?.data?.message ||
"Unable to book appointment");
} finally {
setBooking(false);
}
};
if (loadingDoctor) {
return (
<div className="container py-5 text-center">
<div className="spinner-border text-primary"></div>
<p className="text-secondary mt-3">Loading doctor...</p>
</div>
);
}
if (!doctor) {
return (
<div className="container py-5">
<div className="alert alert-danger">
Doctor information could not be loaded.
</div>
<Link to="/patient/doctors" className="btn btn-primary">
Back to Doctors
</Link>
</div>
);
}
return (
<div className="container py-5">
<div className="row justify-content-center">
<div className="col-lg-8">
{/* PAGE HEADER */}
<div className="mb-4">
<h2>Book Appointment</h2>
<p className="text-secondary">
Select your preferred appointment date and time.
</p>
</div>
{/* DOCTOR INFORMATION */}
<div className="card shadow-sm mb-4">
<div className="card-body">
<div className="row align-items-center">
{doctor.photoUrl && (
<div className="col-md-3 mb-3 mb-md-0">
<img src={doctor.photoUrl} alt={doctor.name}
className="img-fluid rounded" />
</div>
)}
<div className={doctor.photoUrl ?
"col-md-9" : "col-12"}>
<h4 className="mb-1">
{doctor.name}</h4>
<p className="text-primary
fw-semibold mb-2">
{doctor.specialization}
</p>
<p className="mb-1">
<strong>Experience:</strong>
{doctor.experienceYears} years
</p>
<p className="mb-0">
<strong>
Consultation Fee:</strong>
₹{doctor.consultationFee}
</p>
</div>
</div>
</div>
</div>
{/* ERROR */}
{error && (
<div className="alert alert-danger">
{error}</div>
)}
{/* SUCCESS */}
{success && (
<div className="alert alert-success">
<h5>Appointment Booked</h5>
<p className="mb-1">
Appointment ID: <strong>
{success.id}</strong>
</p>
<p className="mb-1">
Doctor: <strong>{success.doctorName}</strong>
</p>
<p className="mb-1">
Date: <strong>{success.appointmentDate}</strong>
</p>
<p className="mb-1">
Slot: <strong>{success.timeSlot}</strong>
</p>
<p className="mb-1">
Status: <strong>{success.appointmentStatus}</strong>
</p>
<p className="mb-0">
Payment: <strong>{success.paymentStatus}</strong>
</p>
</div>
)}
{/* BOOKING FORM */}
<div className="card shadow-sm">
<div className="card-body p-4">
<form onSubmit={handleSubmit}>
{/* DATE */}
<div className="mb-4">
<label className="form-label">
Appointment Date</label>
<input
type="date"
className="form-control"
min={getToday()}
value={appointmentDate}
onChange={(event) =>
setAppointmentDate(event.target.value)}
required
/>
</div>
{/* TIME SLOT */}
<div className="mb-4">
<label className="form-label">
Select Time Slot</label>
<div className="row g-3">
{/* 10 AM */}
<div className="col-md-4">
<input
type="radio"
className="btn-check"
name="timeSlot"
id="slotMorning"
value="MORNING_10_AM"
onChange={(event) =>
setTimeSlot(event.target.value)}
/>
<label
className="btn btn-outline-primary w-100"
htmlFor="slotMorning">
10:00 AM
</label>
</div>
{/* 2 PM */}
<div className="col-md-4">
<input
type="radio"
className="btn-check"
name="timeSlot"
id="slotAfternoon"
value="AFTERNOON_2_PM"
checked={timeSlot === "AFTERNOON_2_PM"}
onChange={(event) =>
setTimeSlot(event.target.value)}
/>
<label className="btn btn-outline-primary
w-100" htmlFor="slotAfternoon">
2:00 PM
</label>
</div>
{/* 5 PM */}
<div className="col-md-4">
<input
type="radio"
className="btn-check"
name="timeSlot"
id="slotEvening"
value="EVENING_5_PM"
checked={timeSlot === "EVENING_5_PM"}
onChange={(event) =>
setTimeSlot(event.target.value)}
/>
<label className="btn btn-outline-primary
w-100" htmlFor="slotEvening">
5:00 PM
</label>
</div>
</div>
</div>
{/* FEE */}
<div className="alert alert-light border">
Consultation Fee: <strong>
₹{doctor.consultationFee}</strong>
</div>
<div className="d-flex gap-2">
<Link to="/patient/doctors"
className="btn btn-outline-secondary">
Back
</Link>
<button
type="submit"
className="btn btn-primary flex-grow-1"
disabled={booking}>
{booking ? "Booking..." : "Book Appointment"}
</button>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
);
}
export default BookAppointment;Text
Why the Backend Receives Enum Values
10:00 AM 2:00 PM 5:00 PM
MORNING_10_AM AFTERNOON_2_PM EVENING_5_PM
public enum TimeSlot {
MORNING_10_AM,
AFTERNOON_2_PM,
EVENING_5_PM
}Human-friendly UI ↓ Enum value ↓ Spring Boot
Why We Don't Send Patient ID
{
"doctorId": 3,
"appointmentDate": "2026-09-26",
"timeSlot": "MORNING_10_AM"
}patientId ❌ consultationFee ❌ appointmentStatus ❌ paymentStatus ❌
JWT ↓ email ↓ User
Update Router
4
import BookAppointment from "../pages/patient/BookAppointment";{
path: "patient/doctors/:doctorId/book",
element: (
<ProtectedRoute allowedRoles={[
"PATIENT"
]} >
<BookAppointment />
</ProtectedRoute>
)
},Test Doctor Navigation
5
Doctor Name Specialization Experience Fee
Test Appointment Booking
6
Date: September 26 Slot: 10:00 AM
POST /api/appointments Authorization: Bearer <patient-token> Content-Type: application/json
{
"doctorId": 3,
"appointmentDate": "2026-09-26",
"timeSlot": "MORNING_10_AM"
}patient → from JWT doctor → ID 3 consultationFee → DoctorProfile appointmentStatus → PENDING paymentStatus → UNPAID
{
"id": 7,
"patientId": 1,
"patientName": "Rahul Shah",
"doctorId": 3,
"doctorName": "Dr. Ananya Sharma", "specialization": "Cardiologist", "appointmentDate": "2026-09-26", "timeSlot": "MORNING_10_AM", "consultationFee": 1200, "appointmentStatus": "PENDING", "paymentStatus": "UNPAID", "createdAt": "2026-09-24T18:10:00" }
Duplicate Slot Test
7
Doctor: Dr. Ananya
Date: 2026-09-26
Slot: 10 AMDoctor 3 + 2026-09-26 + MORNING_10_AM
Test Another Slot
8
{
"doctorId": 3,
"appointmentDate": "2026-09-26",
"timeSlot": "AFTERNOON_2_PM"
}Test Another Doctor
9
Dr. Ananya 10 AM
Cancelled Slot Behavior
11
Doctor + Date + Slot
Appointment Doctor 3 10 AM CANCELLED
We are done with this lab. The latest source code has been uploaded to GitHub. You can access the latest commit using the link below:
Great job!
You have successfully completed the appointment booking functionality in the Medicare application.
Checkpoint
Git Push
git push origin branchNameNext-Lab Preparation
Module:
1) React Basics & Backend Integration (Spring Boot)
By Content ITV