Implementing Appointment Booking Using Spring Boot and React

Business Scenario

In this lab, you will develop an Appointment Booking System using Spring Boot and React.

Pre-Lab Preparation

Module :

1. Set Up the Development Environment: Ensure Spring Boot, React, MySQL, and the required dependencies are installed and configured.

2. Verify Existing Features: Ensure user authentication, JWT security, doctor listing, and doctor details APIs are working correctly.

3. Understand Appointment Booking: Review REST API integration, date and time-slot selection, appointment validation, and booking status management.

Phase 12E — Appointment Booking

Now we connect:

Find Doctor
    ↓
Doctor Card
    ↓
Book Appointment
    ↓
Select Date
    ↓
Select Slot
    ↓
POST /api/appointments
    ↓
PENDING + UNPAID
    ↓
Booking Confirmation
Find Doctor
    ↓
Doctor Card
    ↓
Book Appointment
    ↓
Select Date
    ↓
Select Slot
    ↓
POST /api/appointments
    ↓
PENDING + UNPAID
    ↓
Booking Confirmation

This matches the original project flow where the patient selects a consultation date and one of the fixed slots before proceeding toward payment.

For now we stop at:

PENDING
UNPAID
because payment is still Phase 13.

1

Create appointmentService.js

Create:

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;
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;

For now only one function.

We will add:

getMyAppointments()
cancelAppointment()
getDoctorAppointments()
completeAppointment()

in the next phase when those screens are implemented.

2

Update DoctorCard

Currently the button is disabled.

Open:

src/components/doctor/DoctorCard.jsx
src/components/doctor/DoctorCard.jsx

Add:

import {
    Link
} from "react-router";

Then replace:

<button
    className="btn btn-primary w-100"
    disabled
>
    Book Appointment
</button>

with:

<Link
    to={`/patient/doctors/${doctor.id}/book`}
    className="btn btn-primary w-100"
>
    Book Appointment
</Link>

Your complete DoctorCard.jsx becomes:

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
                                justify-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;
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
                                justify-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;
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
                                justify-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;
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
                                justify-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;

3

Create Booking Page

Create:

src/pages/patient/BookAppointment.jsx

We need:

doctorId
date
slot
doctor details
loading
error
booking result

Use:

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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;
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;

4

Why the Backend Receives Enum Values

The user sees:

10:00 AM
2:00 PM
5:00 PM

But React sends:

MORNING_10_AM
AFTERNOON_2_PM
EVENING_5_PM

because our Spring enum is:

public enum TimeSlot {

    MORNING_10_AM,
    AFTERNOON_2_PM,
    EVENING_5_PM
}
public enum TimeSlot {

    MORNING_10_AM,
    AFTERNOON_2_PM,
    EVENING_5_PM
}

So:

Human-friendly UI

       ↓

Enum value

       ↓

Spring Boot

This is better than exposing ugly enum names to the user.

5

Why We Don't Send Patient ID

Our request becomes:

{
    "doctorId": 3,
    "appointmentDate": "2026-09-26",
    "timeSlot": "MORNING_10_AM"
}

Notice:

patientId ❌
consultationFee ❌
appointmentStatus ❌
paymentStatus ❌

The backend gets the patient from:

JWT

 ↓

email

 ↓

User

and consultation fee from:

DoctorProfile

This preserves the security design from Phase 10.

6

Update Router

Open:

src/routes/router.jsx

Add:

import BookAppointment
    from "../pages/patient/BookAppointment";

Then inside MainLayout → children, add:

{
    path:
        "patient/doctors/:doctorId/book",

    element: (

        <ProtectedRoute
            allowedRoles={[
                "PATIENT"
            ]}
        >

            <BookAppointment />

        </ProtectedRoute>
    )
},
{
    path:
        "patient/doctors/:doctorId/book",

    element: (

        <ProtectedRoute
            allowedRoles={[
                "PATIENT"
            ]}
        >

            <BookAppointment />

        </ProtectedRoute>
    )
},

So now:

/patient/doctors

contains doctor cards.

Clicking doctor 3 goes to:

/patient/doctors/3/book

And:

useParams()

extracts:

doctorId = "3"

7

Test Doctor Navigation

Login as PATIENT.

Open:

/patient/doctors

Click:

Book Appointment

Suppose doctor ID is 3.

You should reach:

/patient/doctors/3/book

The frontend calls:

GET /api/doctors/3

and displays:

Doctor Name

Specialization

Experience

Fee

8

Test Appointment Booking

Select:

Date:
September 26

Slot:
10:00 AM
Date:
September 26

Slot:
10:00 AM

Frontend sends:

POST /api/appointments

Authorization: Bearer <patient-token>

Content-Type: application/json

Body:

{
    "doctorId": 3,
    "appointmentDate": "2026-09-26",
    "timeSlot": "MORNING_10_AM"
}

Backend should create:

patient
→ from JWT

doctor
→ ID 3

consultationFee
→ DoctorProfile

appointmentStatus
→ PENDING

paymentStatus
→ UNPAID

Expected:

201 CREATED

9

Expected Response

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>

Something approximately like:

{
    "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"
}

React then shows the confirmation box.

10

Duplicate Slot Test

Suppose this already exists:

Doctor:
Dr. Ananya

Date:
2026-09-26

Slot:
10 AM
Doctor:
Dr. Ananya

Date:
2026-09-26

Slot:
10 AM

Try booking it again.

Backend should detect:

Doctor 3
+
2026-09-26
+
MORNING_10_AM

and return:

409 CONFLICT
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;

Pre-Lab Preparation

  • Explore different restaurants
  • View food items and menus

  • Add items to cart

  • Place orders online

Topic : Weaving the world of Web

1) Navigating the world of Web

2) Building Blocks of Website,

3) Tag Titans: Fundamental Web Construct.

git pull origin branchName

Git Pull

Task 1: Understanding BRD

Before you start building anything, you need to clearly understand what the client actually wants.So, let’s begin by understanding the BRD (Business Requirement Document) shared by the client.

BRD Full Form is Business Requirement Details.BRD like a plan for building a house. This plan helps the builder understand what to build.In the same way,BRD tells developers what the client wants to build

Click to download BRD : BiteBox_BRD.pdf

Activity

After going through BRD list down the Core Features and  Web Pages in the tabulated Format as shown Below.

Col 1Col 2Col 3
Row 1
Row 2
Row 3

Formula

Profit = Revenue - Cost

Task 2: Create WireFrame

Now that you understand the requirements, don’t jump into coding yet. Before development, we always visualize the layout.

Now lets  create a simple wireframe for the homepage.

A wireframe is like a layout plan of a house. Before building, you decide where rooms, doors, and windows will be placed.Similarly, a wireframe helps you plan where elements like headers, images, and buttons will appear on a webpage—before adding design or colours.

Task 3: Code Editor Installation

Good work on completing the planning phase.

Now we will start development. Before that, make sure your system is ready with the required tools.

In this step we will install the VS code editor that will help to Write code efficiently,Organize files , Run and test your application

Go to the visual studio code official website  

1

Click to download Homepage Wireframe : Homepage Wireframe

Choose your operating system(windows / Mac) and download the installation file.

Double click on the download app and Accept the agreement and click next

2

It is a long established fact that a reader will be distracted

b

Sub Steps

a

 Double click on the download app and Accept the agreement and click next 

public class MathSample {
    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        int sum = x + y;
        
        System.out.println("The sum is: " + sum);
    }
}
public class MathSample {
    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        int sum = x + y;
        
        System.out.println("The sum is: " + sum);
    }
}

public class MathSample {
    public static void main(String[] args) {
        int x = 10;
        int y = 20;
        int sum = x + y;
        
        System.out.println("The sum is: " + sum);
    }
}

 

Great job!
You have successfully completed your first lab on BiteBox Project Onboarding.

In this lab, you have: Understood the BRD, Created a wireframe, Set up your development environment, Organised your project structure, Run your first program

You are now ready to move to the next stage of development

Checkpoint

Next-Lab Preparation

   Git Push

git push origin branchName

Topic : Working with a Text and Listin HTML

1) Power of HTML text tags
2) Customizing your style with CSS
3) Listing it right using HTML
4) HTML Link up , attributes of tag, block vs inline elements

Text box Width : 887
Business Scenario, Pre-lab Preparation, Next-lab Preparation, Task, Activity, Checkpoint : 90%.
Steps : 1,2,3 [Sub Steps - a,b,c]
Normal Text, Topic Name : 80%
Subtopic : 70%
Code Box font Size : 16px

       ↓
Frontend stores JWT
       ↓
Every protected request sends:

Authorization: Bearer <token>
       ↓
JWT Filter
       ↓
Token validated
       ↓
SecurityContextHolder
       ↓
Controller
       ↓
Frontend stores JWT
       ↓
Every protected request sends:

Authorization: Bearer <token>
       ↓
JWT Filter
       ↓
Token validated
       ↓
SecurityContextHolder
       ↓
Controller