Content ITV PRO
This is Itvedant Content department
Business Scenario
In this lab, you will enhance the Medicare application by implementing the Doctor Profile Management feature, allowing doctors to create, view, update, and delete their professional profiles.
Pre-Lab Preparation
Module:
1) React Basics & Backend Integration (Spring Boot)
Phase 12H — Doctor Profile Management
DOCTOR Login ↓ My Profile ↓ GET /api/doctors/profile/me ↓
Profile exists? ↙ ↘ NO YES ↓ ↓ Create View/Edit ↓ ↓ POST PUT /profile /profile/me
Delete Profile ↓ DELETE /api/doctors/profile/me
Update doctorService.js
1
import api, {getAuthConfig} from "../api/api";
const getDoctors = async ({
specialization = "",
maxFee = "",
name = "",
page = 0,
size = 6,
sortBy = "consultationFee",
direction = "asc"
}) => {
const params = {
page,
size,
sortBy, direction
};
if (specialization) {
params.specialization =
specialization;
}
if (
maxFee !== ""
&&
Number(maxFee) > 0
) {
params.maxFee =
maxFee;
}
if (name.trim()) {
params.name =
name.trim();
}
const response = await api.get(
"/doctors",
{ ...getAuthConfig(),
params
}
);
return response.data;
};
const getDoctorById =
async (doctorId) => {
const response =
await api.get(
`/doctors/${doctorId}`,
getAuthConfig()
);
return response.data;
};
const createMyProfile =
async (profileData) => {
const response =
await api.post(
"/doctors/profile",
profileData,
getAuthConfig() );
return response.data;
};
const getMyProfile =
async () => {
const response =
await api.get(
"/doctors/profile/me",
getAuthConfig()
);
return response.data;
};
const updateMyProfile =
async (profileData) => {
const response =
await api.put(
"/doctors/profile/me",
profileData,
getAuthConfig()
);
return response.data; };
const deleteMyProfile =
async () => {
await api.delete(
"/doctors/profile/me",
getAuthConfig()
);
};
const doctorService = {
getDoctors,
getDoctorById,
createMyProfile,
getMyProfile,
updateMyProfile,
deleteMyProfile
};
export default doctorService;Create Doctor Profile Page
2
src/pages/doctor/DoctorProfile.jsx
import
{ useEffect, useState } from "react";
import
doctorService from "../../services/doctorService";
const emptyProfile = {
specialization: "",
experienceYears: "",
consultationFee: "",
photoUrl: ""
function DoctorProfile() {
const [formData, setFormData] = useState(emptyProfile);
const [profileExists, setProfileExists] = useState(false);
const [loading, setLoading] = useState(true);
const [saving, setSaving] = useState(false);
const [deleting, setDeleting] = useState(false);
const [error, setError] = useState("");
const [success, setSuccess] = useState("");
useEffect(() => {
loadProfile();
}, []);
const loadProfile = async () => {
try {
setLoading(true);
setError("");
const profile = await doctorService.getMyProfile();
setProfileExists(true);
setFormData({
specialization: profile.specialization || "",
experienceYears: profile.experienceYears ?? "", consultationFee: profile.consultationFee ?? "",
photoUrl: profile.photoUrl || ""
});
} catch (error) {
/*
* 404 means this doctor has
* not created a profile yet.
*
* This is not treated as a
* frontend error.
*/
if (error.response?.status === 404) {
setProfileExists(false);
setFormData(emptyProfile);
return;
}
setError(
error.response?.data?.message ||
"Unable to load doctor profile"
);
} finally {
setLoading(false);
}
};
const handleChange = (event) => {
const { name, value } = event.target; setFormData(previous => ({
...previous,
[name]: value
}));
};
const handleSubmit = async (event) => {
event.preventDefault();
setError("");
setSuccess("");
const profileData = {
specialization: formData.specialization.trim(),
experienceYears: Number(formData.experienceYears),
consultationFee: Number(formData.consultationFee),
photoUrl: formData.photoUrl.trim()
};
try {
setSaving(true);
let response;
if (profileExists) {
response = await doctorService.updateMyProfile(profileData);
setSuccess(
"Doctor profile updated successfully."
); } else {
response = await doctorService.createMyProfile(profileData);
setProfileExists(true);
setSuccess(
"Doctor profile created successfully."
);
}
setFormData({
specialization: response.specialization || "",
experienceYears: response.experienceYears ?? "",
consultationFee: response.consultationFee ?? "",
photoUrl: response.photoUrl || ""
});
} catch (error) {
setError(
error.response?.data?.message ||
"Unable to save doctor profile"
);
} finally {
setSaving(false);
}
};
const handleDelete = async () => {
const confirmed = window.confirm( "Are you sure you want to delete your doctor profile?"
);
if (!confirmed) {
return;
}
try {
setDeleting(true);
setError("");
setSuccess("");
await doctorService.deleteMyProfile();
setProfileExists(false);
setFormData(emptyProfile);
setSuccess(
"Doctor profile deleted successfully."
);
} catch (error) {
setError(
error.response?.data?.message ||
"Unable to delete doctor profile"
);
} finally {
setDeleting(false);
} };
if (loading) {
return (
<div className="container py-5 text-center">
<div
className="
spinner-border
text-primary
"
>
</div>
<p className="text-secondary mt-3">
Loading doctor profile...
</p>
</div>
);
}
return (
<div className="container py-5">
<div className="row justify-content-center">
<div className="col-lg-8">
{/* HEADING */}
<div className="mb-4">
<h2>
{ profileExists
? "My Doctor Profile"
: "Create Doctor Profile"
}
</h2>
<p className="text-secondary">
Manage the information
patients see when
searching for doctors.
</p>
</div>
{/* SUCCESS */}
{success && (
<div className="alert alert-success">
{success}
</div>
)}
{/* ERROR */}
{error && (
<div className="alert alert-danger">
{error}
</div>
)}
{/* PROFILE FORM */} <div className="card shadow-sm">
<div className="card-body p-4">
{/* PHOTO PREVIEW */}
{
formData.photoUrl
&& (
<div className="text-center mb-4">
<img
src={formData.photoUrl}
alt="Doctor"
className="rounded"
style={{
width: "140px",
height: "140px",
objectFit: "cover"
}}
/>
</div>
)
}
<form onSubmit={handleSubmit}>
{/* SPECIALIZATION */}
<div className="mb-3">
<label className="form-label"> Specialization
</label> <select
name="specialization"
className="form-select"
value={formData.specialization}
onChange={handleChange}
required
>
<option value="">
Select specialization
</option>
<option value="Orthopedic">
Orthopedic
</option>
<option value="Dentist">
Dentist
</option>
<option value="General Physician">
General Physician
</option>
<option value="Cardiologist">
Cardiologist
</option>
</select>
</div>
{/* EXPERIENCE */} <div className="mb-3">
<label className="form-label">
Experience Years
</label>
<input
type="number"
name="experienceYears"
className="form-control"
min="0"
value={formData.experienceYears}
onChange={handleChange}
required
/>
</div>
{/* CONSULTATION FEE */}
<div className="mb-3">
<label className="form-label">
Consultation Fee
</label>
<div className="input-group">
<span className="input-group-text">
₹
</span>
<input
type="number" name="consultationFee"
className="form-control"
min="1"
step="0.01"
value={formData.consultationFee}
onChange={handleChange}
required
/>
</div>
</div>
{/* PHOTO URL */}
<div className="mb-4">
<label className="form-label">
Photo URL
</label>
<input
type="url"
name="photoUrl"
className="form-control"
placeholder="https://example.com/doctor.jpg"
value={formData.photoUrl}
onChange={handleChange}
/>
</div>
<button
type="submit" className="btn btn-primary w-100"
disabled={saving}
>
{
saving
? "Saving..."
: profileExists
? "Update Profile"
: "Create Profile"
}
</button>
{
profileExists
&& (
<button
type="button"
className="btn btn-outline-danger
w-100 mt-3"
onClick={handleDelete}
disabled={deleting}
>
{
deleting
? "Deleting..."
: "Delete Profile"
}
</button>
)
} </form>
</div>
</div>
</div>
</div>
</div>
);
}
export default DoctorProfile;Why One Page Handles Create + Update
Why 404 Is Special Here
Doctor account exists DoctorProfile doesn't exist yet
404 --> Show Create Profile form
Add Doctor Profile Route
3
import DoctorProfile from "../pages/doctor/DoctorProfile";
{
path:
"doctor/profile",
element: ( <ProtectedRoute allowedRoles={["DOCTOR"]}>
<DoctorProfile />
</ProtectedRoute>
)
},
Update Doctor Navbar
4
<>
<li className="nav-item">
<Link className="nav-link" to="/doctor/dashboard">
Dashboard
</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/doctor/schedule">
My Schedule </Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/doctor/profile">
My Profile
</Link>
</li>
</>
Update Doctor Dashboard
5
My Schedule Prescriptions
</Link>
</li>
<li className="nav-item">
<Link className="nav-link" to="/doctor/profile">
My Profile
</Link>
</li>
</>
Update Doctor Dashboard
5
My Schedule Prescriptions
import {Link} from "react-router";
<div className="col-md-6 col-lg-4">
<div className="card h-100 shadow-sm">
<div className="card-body">
<h5> My Profile
</h5>
<p className="text-secondary">
Manage specialization,
experience, consultation
fee and profile photo. </p>
<Link to="/doctor/profile" className="btn btn-outline-primary">
Manage Profile
</Link>
</div>
</div>
</div>Test a Doctor With No Profile
6
GET /api/doctors/profile/me Authorization: Bearer <doctor-token>
POST /api/doctors/profile Authorization: Bearer <doctor-token>
Create Doctor Profile
Specialization: Cardiologist Experience: 8 Consultation Fee: 1200 Photo URL: https://...
Expected Creation Response
7
{
"id": 3,
"name": "Dr. Ananya Sharma",
"specialization": "Cardiologist",
"experienceYears": 8,
"consultationFee": 1200,
"photoUrl": "https://example.com/doctor.jpg"
}
Verify Patient Doctor Search
8
Dr. Ananya Sharma
Cardiologist
8 years
₹1200
[ Book Appointment ]
Delete Doctor Profile
9
Are you sure you want to delete your doctor profile?
DELETE /api/doctors/profile/meCreate Doctor Profile
Important Delete Note
Unable to delete doctor profile
Test Second Profile Creation
10
We are done with this lab. The latest source code has been uploaded to GitHub. You can access the latest commit using the link below:
Great job!
You have successfully implemented the Doctor Profile Management feature in the Medicare application.
Checkpoint
Git Push
git push origin branchNameNext-Lab Preparation
Module:
1) React Basics & Backend Integration (Spring Boot)
By Content ITV