Skip to content

Modularize/patientprofilepage#86

Merged
aditya241104 merged 2 commits into
mainfrom
modularize/patientprofilepage
Nov 4, 2025
Merged

Modularize/patientprofilepage#86
aditya241104 merged 2 commits into
mainfrom
modularize/patientprofilepage

Conversation

@aditya241104

@aditya241104 aditya241104 commented Nov 4, 2025

Copy link
Copy Markdown
Collaborator

Project

  • QuickClinic
  • QuickMed

Change Type

  • New Feature/Page
  • Bug Fix
  • UI Redesign
  • Optimization
  • Other

Page Type

  • Public
  • Patient
  • Doctor
  • Admin

Stack

  • Frontend
  • Backend
  • Full Stack

Route Status

  • New Route
  • Existing Route

What Changed

Route/API Affected

Description

Screenshots

Mobile View

Desktop View

Files Changed

client/src/components/Patient/PatientProfile/AddressInformation.jsx
client/src/components/Patient/PatientProfile/BasicInformation.jsx
client/src/components/Patient/PatientProfile/EmergencyContact.jsx
client/src/components/Patient/PatientProfile/HealthRecordModal.jsx
client/src/components/Patient/PatientProfile/HealthRecords.jsx
client/src/components/Patient/PatientProfile/MessageAlerts.jsx
client/src/components/Patient/PatientProfile/ProfileContent.jsx
client/src/components/Patient/PatientProfile/ProfileHeader.jsx
client/src/pages/patient/PatientProfilePage.jsx
server/config/cloudinary.js

Code Quality

Prettier Check: ✅ Passed

Related Issues

Closes #


Auto-generated on 2025-11-04T06:15:29.705Z

- Modularized the patient profile code into separate folders for better maintainability
- Fixed incorrect imports in the frontend after restructuring
- Resolved Cloudinary environment variable issue in the backend by importing and configuring dotenv
@vercel

vercel Bot commented Nov 4, 2025

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
quick-clinic Ready Ready Preview Comment Nov 4, 2025 6:13am
quick-clinic-m9k7 Ready Ready Preview Comment Nov 4, 2025 6:13am

@coderabbitai

coderabbitai Bot commented Nov 4, 2025

Copy link
Copy Markdown
Contributor

Walkthrough

This PR modularizes the PatientProfilePage into reusable, single-responsibility React components for patient profile sections (Basic Information, Address, Emergency Contact, Health Records) with edit-mode support, and updates the Cloudinary configuration to load environment variables via dotenv.

Changes

Cohort / File(s) Summary
Profile Section Components
client/src/components/Patient/PatientProfile/AddressInformation.jsx, client/src/components/Patient/PatientProfile/BasicInformation.jsx, client/src/components/Patient/PatientProfile/EmergencyContact.jsx
New components render profile sections with display and edit modes; toggle between controlled inputs and static text based on isEditing prop; support profile picture upload in BasicInformation with date formatting helper.
Profile Container & Header
client/src/components/Patient/PatientProfile/ProfileContent.jsx, client/src/components/Patient/PatientProfile/ProfileHeader.jsx
ProfileContent composes section components into a three-column layout with nested state handlers for input changes and file uploads. ProfileHeader provides edit/save/cancel buttons and loading state management.
Health Records Components
client/src/components/Patient/PatientProfile/HealthRecords.jsx, client/src/components/Patient/PatientProfile/HealthRecordModal.jsx
HealthRecords displays records list with type icons, dates, and download buttons. HealthRecordModal provides form for uploading new records with validation and success/error handling.
Alert Component
client/src/components/Patient/PatientProfile/MessageAlerts.jsx
Renders conditional error and success alert blocks with icons.
Page Refactoring
client/src/pages/patient/PatientProfilePage.jsx
Replaces inline JSX with modular component composition; delegates form handling, file uploads, and health record logic to child components; eliminates redundant state and logic.
Configuration
server/config/cloudinary.js
Adds dotenv initialization to load environment variables for Cloudinary config.

Sequence Diagram

sequenceDiagram
    participant User
    participant PatientProfilePage
    participant ProfileHeader
    participant ProfileContent
    participant ProfileSection as Profile Sections<br/>(Basic, Address, etc.)
    participant HealthRecords
    participant HealthRecordModal

    User->>PatientProfilePage: Load profile
    PatientProfilePage->>ProfileHeader: Render with isEditing=false
    
    User->>ProfileHeader: Click Edit Profile
    ProfileHeader->>PatientProfilePage: setIsEditing(true)
    PatientProfilePage->>ProfileContent: Render with isEditing=true

    ProfileContent->>ProfileSection: Render inputs for editing
    ProfileSection->>User: Display form fields

    User->>ProfileSection: Enter/modify values
    ProfileSection->>ProfileContent: handleInputChange (nested state)
    ProfileContent->>PatientProfilePage: setEditFormData

    User->>ProfileHeader: Click Save Changes
    ProfileHeader->>PatientProfilePage: handleSaveProfile()
    PatientProfilePage->>PatientProfilePage: updatePatientProfile()
    
    User->>HealthRecords: Click Add Record
    HealthRecords->>PatientProfilePage: setShowHealthRecordForm(true)
    PatientProfilePage->>HealthRecordModal: Render modal
    HealthRecordModal->>User: Display upload form

    User->>HealthRecordModal: Submit record
    HealthRecordModal->>PatientProfilePage: Upload & refresh
    PatientProfilePage->>HealthRecords: Refresh records list
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Multiple new components with varied patterns: Eight new components across profile sections, layout composition, alerts, and health records—each requiring separate reasoning for completeness and prop flow
  • PatientProfilePage refactoring: Significant restructuring from monolithic to component-based; delegation of state, handlers, and lifecycle logic to children requires careful verification of prop chains and logic flow
  • Edit/display mode logic duplicated across components: Each section component implements conditional rendering and controlled inputs; consistency and edge cases need verification
  • Health records integration: Modal, list display, and download functionality span multiple new components with interconnected state
  • State management complexity: Nested state updates in ProfileContent (dot-notation support), file upload handling, and cascading prop dependencies increase surface area for bugs

Areas requiring extra attention:

  • Verify nested state update mechanism (handleInputChange with dot notation) handles all edge cases in ProfileContent
  • Validate prop chains between PatientProfilePage → ProfileContent → individual ProfileSection components for correctness
  • Check HealthRecordModal validation, error handling, and form reset logic
  • Confirm profile picture file upload flow and preview in BasicInformation
  • Test edit mode transitions and data consistency across all section components

Suggested labels

enhancement, UI, redesign

Poem

🐰 Components now dance where monoliths stood tall,
Each section a module, responsive to the call,
From profiles to health records, modular and bright,
The PatientProfile blooms—refactored just right! ✨

Pre-merge checks and finishing touches

✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Modularize/patientprofilepage' directly reflects the main objective of refactoring PatientProfilePage.jsx into modular components in the PatientProfile directory.
Linked Issues check ✅ Passed The PR successfully modularizes PatientProfilePage.jsx by extracting components into client/src/components/Patient/PatientProfile as required by issue #85.
Out of Scope Changes check ✅ Passed Changes are focused on modularization and fixing related issues; no out-of-scope additions detected beyond the required refactoring and dotenv fix.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch modularize/patientprofilepage

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
server/config/cloudinary.js (2)

6-10: Add validation for required environment variables.

Consider adding validation to ensure all required Cloudinary credentials are present before configuring the SDK. This will provide clearer error messages if environment variables are missing.

Apply this diff to add validation:

 dotenv.config();
+
+const requiredEnvVars = ['CLOUD_NAME', 'CLOUD_API_KEY', 'CLOUD_API_SECRET'];
+const missingVars = requiredEnvVars.filter(varName => !process.env[varName]);
+
+if (missingVars.length > 0) {
+  throw new Error(
+    `Missing required Cloudinary environment variables: ${missingVars.join(', ')}`
+  );
+}
+
 cloudinary.config({
   cloud_name: process.env.CLOUD_NAME,
   api_key: process.env.CLOUD_API_KEY,
   api_secret: process.env.CLOUD_API_SECRET,
 });

3-5: Remove redundant dotenv.config() from cloudinary.js—it's already loaded at the application entry point.

dotenv.config() is correctly placed in server/index.js (line 3) and runs before any modules are imported. The call in cloudinary.js is unnecessary redundancy.

Apply this diff to cloudinary.js:

 // config/cloudinary.js
 import { v2 as cloudinary } from 'cloudinary';
-import dotenv from 'dotenv';
-
-dotenv.config();
 cloudinary.config({
   cloud_name: process.env.CLOUD_NAME,
   api_key: process.env.CLOUD_API_KEY,
   api_secret: process.env.CLOUD_API_SECRET,
 });
client/src/components/Patient/PatientProfile/EmergencyContact.jsx (1)

19-52: Prevent controlled/uncontrolled input flips

When editFormData.emergencyContact is missing (e.g., after a failed fetch and the user enters edit mode) these value props become undefined, so the inputs render uncontrolled and flip to controlled as soon as a user types, triggering React warnings. Defaulting to an empty string keeps them controlled throughout.

-              value={editFormData.emergencyContact?.name}
+              value={editFormData.emergencyContact?.name ?? ''}-              value={editFormData.emergencyContact?.relationship}
+              value={editFormData.emergencyContact?.relationship ?? ''}-              value={editFormData.emergencyContact?.phoneNumber}
+              value={editFormData.emergencyContact?.phoneNumber ?? ''}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f53867 and 20bab34.

📒 Files selected for processing (10)
  • client/src/components/Patient/PatientProfile/AddressInformation.jsx (1 hunks)
  • client/src/components/Patient/PatientProfile/BasicInformation.jsx (1 hunks)
  • client/src/components/Patient/PatientProfile/EmergencyContact.jsx (1 hunks)
  • client/src/components/Patient/PatientProfile/HealthRecordModal.jsx (1 hunks)
  • client/src/components/Patient/PatientProfile/HealthRecords.jsx (1 hunks)
  • client/src/components/Patient/PatientProfile/MessageAlerts.jsx (1 hunks)
  • client/src/components/Patient/PatientProfile/ProfileContent.jsx (1 hunks)
  • client/src/components/Patient/PatientProfile/ProfileHeader.jsx (1 hunks)
  • client/src/pages/patient/PatientProfilePage.jsx (2 hunks)
  • server/config/cloudinary.js (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (9)
client/src/components/Patient/PatientProfile/MessageAlerts.jsx (1)
client/src/pages/patient/PatientProfilePage.jsx (2)
  • error (23-23)
  • success (24-24)
client/src/components/Patient/PatientProfile/HealthRecords.jsx (1)
client/src/pages/patient/PatientProfilePage.jsx (1)
  • profile (10-10)
client/src/components/Patient/PatientProfile/ProfileHeader.jsx (1)
client/src/pages/patient/PatientProfilePage.jsx (2)
  • isEditing (11-11)
  • isLoading (12-12)
client/src/components/Patient/PatientProfile/ProfileContent.jsx (5)
client/src/components/Patient/PatientProfile/BasicInformation.jsx (1)
  • BasicInformation (4-145)
client/src/pages/patient/PatientProfilePage.jsx (4)
  • profile (10-10)
  • isEditing (11-11)
  • editFormData (14-14)
  • profilePictureFile (22-22)
client/src/components/Patient/PatientProfile/AddressInformation.jsx (1)
  • AddressInformation (4-90)
client/src/components/Patient/PatientProfile/EmergencyContact.jsx (1)
  • EmergencyContact (4-64)
client/src/components/Patient/PatientProfile/HealthRecords.jsx (1)
  • HealthRecords (5-104)
client/src/components/Patient/PatientProfile/BasicInformation.jsx (3)
client/src/components/Patient/PatientProfile/HealthRecords.jsx (1)
  • formatDate (6-9)
client/src/pages/patient/PatientProfilePage.jsx (4)
  • profile (10-10)
  • isEditing (11-11)
  • profilePictureFile (22-22)
  • editFormData (14-14)
client/src/components/Patient/PatientProfile/ProfileContent.jsx (2)
  • handleProfilePictureChange (36-41)
  • handleInputChange (17-34)
client/src/components/Patient/PatientProfile/AddressInformation.jsx (2)
client/src/pages/patient/PatientProfilePage.jsx (3)
  • isEditing (11-11)
  • editFormData (14-14)
  • profile (10-10)
client/src/components/Patient/PatientProfile/ProfileContent.jsx (1)
  • handleInputChange (17-34)
client/src/pages/patient/PatientProfilePage.jsx (6)
client/src/components/ui/Loading.jsx (1)
  • Loading (4-63)
client/src/components/Patient/PatientProfile/ProfileHeader.jsx (1)
  • ProfileHeader (3-44)
client/src/service/patientApiService.js (2)
  • updatePatientProfile (39-63)
  • updatePatientProfile (39-63)
client/src/components/Patient/PatientProfile/MessageAlerts.jsx (1)
  • MessageAlerts (3-25)
client/src/components/Patient/PatientProfile/ProfileContent.jsx (1)
  • ProfileContent (7-77)
client/src/components/Patient/PatientProfile/HealthRecordModal.jsx (1)
  • HealthRecordModal (5-180)
client/src/components/Patient/PatientProfile/HealthRecordModal.jsx (2)
client/src/components/Patient/PatientProfile/HealthRecords.jsx (1)
  • formatRecordType (11-16)
client/src/pages/patient/PatientProfilePage.jsx (5)
  • selectedFile (21-21)
  • healthRecordForm (15-20)
  • fetchProfile (30-59)
  • error (23-23)
  • isLoading (12-12)
client/src/components/Patient/PatientProfile/EmergencyContact.jsx (2)
client/src/pages/patient/PatientProfilePage.jsx (3)
  • isEditing (11-11)
  • editFormData (14-14)
  • profile (10-10)
client/src/components/Patient/PatientProfile/ProfileContent.jsx (1)
  • handleInputChange (17-34)

Comment on lines +16 to +82
<input
type="text"
name="address.street"
value={editFormData.address?.street}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.address?.street || 'Not specified'}</p>
)}
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">City</label>
{isEditing ? (
<input
type="text"
name="address.city"
value={editFormData.address?.city}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.address?.city || 'Not specified'}</p>
)}
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">State</label>
{isEditing ? (
<input
type="text"
name="address.state"
value={editFormData.address?.state}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.address?.state || 'Not specified'}</p>
)}
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Zip Code</label>
{isEditing ? (
<input
type="text"
name="address.zipCode"
value={editFormData.address?.zipCode}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.address?.zipCode || 'Not specified'}</p>
)}
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Country</label>
{isEditing ? (
<input
type="text"
name="address.country"
value={editFormData.address?.country}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Avoid uncontrolled→controlled inputs in address form

When editing begins, editFormData.address may not yet hydrate every field, so each value={editFormData.address?.…} starts as undefined and becomes a string after the first keystroke. React will yell about switching from uncontrolled to controlled inputs, and some testing setups treat that as a failure. Please coalesce to an empty string so the inputs stay controlled from the first render.

-              value={editFormData.address?.street}
+              value={editFormData.address?.street ?? ''}
...
-              value={editFormData.address?.city}
+              value={editFormData.address?.city ?? ''}
...
-              value={editFormData.address?.state}
+              value={editFormData.address?.state ?? ''}
...
-              value={editFormData.address?.zipCode}
+              value={editFormData.address?.zipCode ?? ''}
...
-              value={editFormData.address?.country}
+              value={editFormData.address?.country ?? ''}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<input
type="text"
name="address.street"
value={editFormData.address?.street}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.address?.street || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">City</label>
{isEditing ? (
<input
type="text"
name="address.city"
value={editFormData.address?.city}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.address?.city || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">State</label>
{isEditing ? (
<input
type="text"
name="address.state"
value={editFormData.address?.state}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.address?.state || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Zip Code</label>
{isEditing ? (
<input
type="text"
name="address.zipCode"
value={editFormData.address?.zipCode}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.address?.zipCode || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Country</label>
{isEditing ? (
<input
type="text"
name="address.country"
value={editFormData.address?.country}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
<input
type="text"
name="address.street"
value={editFormData.address?.street ?? ''}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.address?.street || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">City</label>
{isEditing ? (
<input
type="text"
name="address.city"
value={editFormData.address?.city ?? ''}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.address?.city || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">State</label>
{isEditing ? (
<input
type="text"
name="address.state"
value={editFormData.address?.state ?? ''}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.address?.state || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Zip Code</label>
{isEditing ? (
<input
type="text"
name="address.zipCode"
value={editFormData.address?.zipCode ?? ''}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.address?.zipCode || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Country</label>
{isEditing ? (
<input
type="text"
name="address.country"
value={editFormData.address?.country ?? ''}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
🤖 Prompt for AI Agents
client/src/components/Patient/PatientProfile/AddressInformation.jsx around lines
16-82: the input value props use editFormData.address?.field which can be
undefined initially and cause uncontrolled→controlled warnings; for each
editable field (street, city, state, zipCode, country) coalesce the value to an
empty string (e.g. change value={editFormData.address?.street} to
value={editFormData.address?.street || ''}) so inputs are controlled from first
render, leaving name and onChange as-is.

Comment on lines +68 to +135
<input
type="text"
name="firstName"
value={editFormData.firstName}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.firstName || 'Not specified'}</p>
)}
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Last Name</label>
{isEditing ? (
<input
type="text"
name="lastName"
value={editFormData.lastName}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.lastName || 'Not specified'}</p>
)}
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Email</label>
<p className="text-gray-900">{profile?.email || 'Not specified'}</p>
{isEditing && <p className="text-xs text-gray-500 mt-1">Email cannot be changed</p>}
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Date of Birth</label>
<p className="text-gray-900">{formatDate(profile?.dateOfBirth)}</p>
{isEditing && (
<p className="text-xs text-gray-500 mt-1">Date of birth cannot be changed</p>
)}
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Gender</label>
{isEditing ? (
<select
name="gender"
value={editFormData.gender}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="">Select Gender</option>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="other">Other</option>
</select>
) : (
<p className="text-gray-900">{profile?.gender || 'Not specified'}</p>
)}
</div>

<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Phone Number</label>
{isEditing ? (
<input
type="tel"
name="phoneNumber"
value={editFormData.phoneNumber}
onChange={handleInputChange}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Keep basic-info inputs controlled from first render

editFormData.firstName (and friends) can be undefined until edit mode primes the form state. That leaves each input uncontrolled initially, then controlled after the first keystroke, triggering React warnings and flaky tests. Please coalesce to empty strings (and for the select, to '') so they stay controlled.

-              value={editFormData.firstName}
+              value={editFormData.firstName ?? ''}
...
-              value={editFormData.lastName}
+              value={editFormData.lastName ?? ''}
...
-              value={editFormData.gender}
+              value={editFormData.gender ?? ''}
...
-              value={editFormData.phoneNumber}
+              value={editFormData.phoneNumber ?? ''}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<input
type="text"
name="firstName"
value={editFormData.firstName}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.firstName || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Last Name</label>
{isEditing ? (
<input
type="text"
name="lastName"
value={editFormData.lastName}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.lastName || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Email</label>
<p className="text-gray-900">{profile?.email || 'Not specified'}</p>
{isEditing && <p className="text-xs text-gray-500 mt-1">Email cannot be changed</p>}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Date of Birth</label>
<p className="text-gray-900">{formatDate(profile?.dateOfBirth)}</p>
{isEditing && (
<p className="text-xs text-gray-500 mt-1">Date of birth cannot be changed</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Gender</label>
{isEditing ? (
<select
name="gender"
value={editFormData.gender}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="">Select Gender</option>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="other">Other</option>
</select>
) : (
<p className="text-gray-900">{profile?.gender || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Phone Number</label>
{isEditing ? (
<input
type="tel"
name="phoneNumber"
value={editFormData.phoneNumber}
onChange={handleInputChange}
<input
type="text"
name="firstName"
value={editFormData.firstName ?? ''}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.firstName || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Last Name</label>
{isEditing ? (
<input
type="text"
name="lastName"
value={editFormData.lastName ?? ''}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
/>
) : (
<p className="text-gray-900">{profile?.lastName || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Email</label>
<p className="text-gray-900">{profile?.email || 'Not specified'}</p>
{isEditing && <p className="text-xs text-gray-500 mt-1">Email cannot be changed</p>}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Date of Birth</label>
<p className="text-gray-900">{formatDate(profile?.dateOfBirth)}</p>
{isEditing && (
<p className="text-xs text-gray-500 mt-1">Date of birth cannot be changed</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Gender</label>
{isEditing ? (
<select
name="gender"
value={editFormData.gender ?? ''}
onChange={handleInputChange}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
>
<option value="">Select Gender</option>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="other">Other</option>
</select>
) : (
<p className="text-gray-900">{profile?.gender || 'Not specified'}</p>
)}
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">Phone Number</label>
{isEditing ? (
<input
type="tel"
name="phoneNumber"
value={editFormData.phoneNumber ?? ''}
onChange={handleInputChange}
🤖 Prompt for AI Agents
In client/src/components/Patient/PatientProfile/BasicInformation.jsx around
lines 68 to 135 the input/select value props use editFormData properties that
may be undefined until edit mode, causing uncontrolled→controlled warnings;
change each value prop to coalesce to an empty string (e.g.
value={editFormData.firstName || ''}) and for the gender select use
value={editFormData.gender || ''} (do the same for lastName, phoneNumber and any
other form fields in this block) so all inputs are controlled from first render.

Comment on lines +12 to +59
return type
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
};

const getRecordTypeIcon = (type) => {
const icons = {
allergy: <AlertCircle className="w-4 h-4" />,
condition: <Heart className="w-4 h-4" />,
immunization: <Shield className="w-4 h-4" />,
'lab-result': <FileText className="w-4 h-4" />,
medication: <Plus className="w-4 h-4" />,
procedure: <UserCheck className="w-4 h-4" />,
'vital-sign': <Heart className="w-4 h-4" />,
};
return icons[type] || <FileText className="w-4 h-4" />;
};

return (
<div className="space-y-6">
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-2">
<FileText className="w-5 h-5 text-green-600" />
<h2 className="text-lg font-semibold text-gray-900">Health Records</h2>
</div>
<button
onClick={() => setShowHealthRecordForm(true)}
className="inline-flex items-center gap-2 px-3 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors text-sm"
>
<Plus className="w-4 h-4" />
Add Record
</button>
</div>

<div className="space-y-4">
{profile?.healthRecords && profile.healthRecords.length > 0 ? (
profile.healthRecords.map((record, index) => (
<div key={index} className="border border-gray-200 rounded-lg p-4">
<div className="flex items-start justify-between mb-2">
<div className="flex items-center gap-2">
{getRecordTypeIcon(record.recordType)}
<h3 className="font-medium text-gray-900">{record.title}</h3>
</div>
<span className="text-xs bg-gray-100 text-gray-600 px-2 py-1 rounded">
{formatRecordType(record.recordType)}
</span>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Guard formatRecordType against missing record types

formatRecordType(record.recordType) will throw when recordType is null/undefined, which is easy to hit if older records or drafts don’t populate the field. On the web this surfaces as a runtime TypeError and breaks the entire profile view. Please default the input before splitting.

-  const formatRecordType = (type) => {
-    return type
-      .split('-')
+  const formatRecordType = (type) => {
+    if (!type) return 'Unknown';
+    return String(type)
+      .split('-')
       .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
       .join(' ');
   };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return type
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
};
const getRecordTypeIcon = (type) => {
const icons = {
allergy: <AlertCircle className="w-4 h-4" />,
condition: <Heart className="w-4 h-4" />,
immunization: <Shield className="w-4 h-4" />,
'lab-result': <FileText className="w-4 h-4" />,
medication: <Plus className="w-4 h-4" />,
procedure: <UserCheck className="w-4 h-4" />,
'vital-sign': <Heart className="w-4 h-4" />,
};
return icons[type] || <FileText className="w-4 h-4" />;
};
return (
<div className="space-y-6">
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-2">
<FileText className="w-5 h-5 text-green-600" />
<h2 className="text-lg font-semibold text-gray-900">Health Records</h2>
</div>
<button
onClick={() => setShowHealthRecordForm(true)}
className="inline-flex items-center gap-2 px-3 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors text-sm"
>
<Plus className="w-4 h-4" />
Add Record
</button>
</div>
<div className="space-y-4">
{profile?.healthRecords && profile.healthRecords.length > 0 ? (
profile.healthRecords.map((record, index) => (
<div key={index} className="border border-gray-200 rounded-lg p-4">
<div className="flex items-start justify-between mb-2">
<div className="flex items-center gap-2">
{getRecordTypeIcon(record.recordType)}
<h3 className="font-medium text-gray-900">{record.title}</h3>
</div>
<span className="text-xs bg-gray-100 text-gray-600 px-2 py-1 rounded">
{formatRecordType(record.recordType)}
</span>
if (!type) return 'Unknown';
return String(type)
.split('-')
.map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
};
const getRecordTypeIcon = (type) => {
const icons = {
allergy: <AlertCircle className="w-4 h-4" />,
condition: <Heart className="w-4 h-4" />,
immunization: <Shield className="w-4 h-4" />,
'lab-result': <FileText className="w-4 h-4" />,
medication: <Plus className="w-4 h-4" />,
procedure: <UserCheck className="w-4 h-4" />,
'vital-sign': <Heart className="w-4 h-4" />,
};
return icons[type] || <FileText className="w-4 h-4" />;
};
return (
<div className="space-y-6">
<div className="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<div className="flex items-center justify-between mb-6">
<div className="flex items-center gap-2">
<FileText className="w-5 h-5 text-green-600" />
<h2 className="text-lg font-semibold text-gray-900">Health Records</h2>
</div>
<button
onClick={() => setShowHealthRecordForm(true)}
className="inline-flex items-center gap-2 px-3 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors text-sm"
>
<Plus className="w-4 h-4" />
Add Record
</button>
</div>
<div className="space-y-4">
{profile?.healthRecords && profile.healthRecords.length > 0 ? (
profile.healthRecords.map((record, index) => (
<div key={index} className="border border-gray-200 rounded-lg p-4">
<div className="flex items-start justify-between mb-2">
<div className="flex items-center gap-2">
{getRecordTypeIcon(record.recordType)}
<h3 className="font-medium text-gray-900">{record.title}</h3>
</div>
<span className="text-xs bg-gray-100 text-gray-600 px-2 py-1 rounded">
{formatRecordType(record.recordType)}
</span>
🤖 Prompt for AI Agents
In client/src/components/Patient/PatientProfile/HealthRecords.jsx around lines
12 to 59, the call formatRecordType(record.recordType) can throw when
record.recordType is null/undefined; update the formatRecordType function to
guard its input (e.g., default to an empty string or 'unknown') before calling
split so it returns a safe string when recordType is missing; ensure the
function handles falsy values and still returns a user-friendly label (like
'Unknown' or an empty string) so rendering of the profile won't crash.

Comment on lines +17 to +34
const handleInputChange = (e) => {
const { name, value } = e.target;
if (name.includes('.')) {
const [parent, child] = name.split('.');
setEditFormData((prev) => ({
...prev,
[parent]: {
...prev[parent],
[child]: value,
},
}));
} else {
setEditFormData((prev) => ({
...prev,
[name]: value,
}));
}
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle nested form updates when parent state is missing

If editFormData lacks a nested object (common after fetchProfile fails, leaving it {}), this spread attempts ...prev[parent] where prev[parent] is undefined, throwing TypeError: Cannot convert undefined or null to object the moment a user edits an address/emergency-contact field. Guard the parent object so nested updates always work.

     if (name.includes('.')) {
       const [parent, child] = name.split('.');
       setEditFormData((prev) => ({
         ...prev,
         [parent]: {
-          ...prev[parent],
+          ...(prev[parent] ?? {}),
           [child]: value,
         },
       }));
🤖 Prompt for AI Agents
In client/src/components/Patient/PatientProfile/ProfileContent.jsx around lines
17 to 34, the nested update assumes prev[parent] exists and will throw when it's
undefined; ensure you guard and default the parent object before spreading
(e.g., use prev[parent] || {} or nullish coalescing) so setEditFormData always
creates the nested object when missing, preserving other fields by spreading
prev and prev[parent] safely and then setting [child]: value.

@aditya241104 aditya241104 merged commit 0fb6669 into main Nov 4, 2025
7 checks passed
@aditya241104 aditya241104 deleted the modularize/patientprofilepage branch November 4, 2025 09:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant