Skip to content

Conversation

@Shubhang-Sagar-Shukla
Copy link

@Shubhang-Sagar-Shukla Shubhang-Sagar-Shukla commented Oct 30, 2025

Closes #75
i have followed all the things that were mentioned to be resolved in the Version 2.0 checklist (parent issue):

Code Quality & Standards

✅ ESLint-ready: Proper PropTypes, no unused variables
✅ Consistent naming: camelCase for functions, PascalCase for components
✅ Clean code: Removed all dead code, organized imports
✅ Performance optimized: useCallback and useMemo for expensive operations

Comprehensive Documentation

✅ JSDoc comments for all functions explaining purpose, params, returns
✅ Inline comments for complex logic
✅ Component documentation with feature lists
✅ Clear function names that describe what they do

Error Handling & Validation

✅ Null checks on all API responses and object properties
✅ Try-catch blocks with proper error logging
✅ Console warnings for invalid data (not errors)
✅ User-friendly error messages with retry functionality
✅ 404 handling for prescriptions (expected behavior, no log spam)

Accessibility (a11y)

✅ ARIA labels on all interactive elements
✅ Keyboard navigation support (Enter/Space on cards)
✅ Screen reader friendly with proper roles and labels
✅ Focus states on inputs and buttons

UI/UX Improvements

✅ Loading states with user feedback
✅ Error boundaries with retry option
✅ Empty state messages adapt to search/filter
✅ Responsive design maintained
✅ Hover effects and transitions

Best Practices

✅ Separation of concerns: Extracted AppointmentCard and StatCard
✅ Memoization: Prevents unnecessary re-renders
✅ Event handlers: Properly bound with useCallback
✅ Array methods: Safe operations with spreading
✅ Conditional rendering: Proper null checks

Security & Safety

✅ Input sanitization: Trim search terms
✅ Safe navigation: ?. operator for nested objects
✅ Validated data: Array.isArray checks
✅ Error logging: Prefixed with component name for debugging

🎯 Key Improvements

Zero Console Errors: All operations are null-safe
Professional Logging: Prefixed with [PatientAppointments] for debugging
Scalable Architecture: Easy to extend with new features
Production Ready: No warnings, proper error handling
Developer Friendly: Well-documented, easy to maintain
Screenshot (137)
Screenshot (136)

The ui changes are more appreciable on interacting with it .

Summary by CodeRabbit

  • New Features

    • Search appointments by doctor, clinic, specialization, or reason
    • Filter appointments by status and view tabs (Upcoming, Today, Past) with counts
    • Prescription availability checks per appointment and retryable error alerts
    • Monitor appointment statistics via stat tiles and navigate to appointment details
    • Modern card-based layout with improved accessibility and keyboard support
  • Chores

    • Updated app title to "QuickClinic"

@vercel
Copy link

vercel bot commented Oct 30, 2025

@Shubhang-Sagar-Shukla is attempting to deploy a commit to the Aditya Shah's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 30, 2025

Walkthrough

Replaces the patient appointments page with a self-contained React component that fetches appointments, checks per-appointment prescription status, adds client-side search/filter/tabs, new inline AppointmentCard and StatCard components, improved error handling with retry, and updates the HTML title to "QuickClinic". (47 words)

Changes

Cohort / File(s) Summary
Patient Appointments Component Rewrite
client/src/pages/patient/PatientAppointments.jsx
Full rewrite into a self-contained React component: useCallback-based fetching, per-appointment prescription checks (map by id), loading/error/retry states, client-side search and status filters, tabbed views (Upcoming/Today/Past), stats via useMemo, inline AppointmentCard and StatCard, accessible card UI and navigation handlers.
HTML Title Update
client/index.html
Changes document title from Vite + React to QuickClinic. No other structural edits.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Comp as PatientAppointments
    participant API as Backend/API

    User->>Comp: mount page / interact
    Comp->>Comp: init state (tab, filters, search)
    Comp->>API: GET /appointments (fetchAppointments)
    API-->>Comp: appointments[]
    loop per appointment
        Comp->>API: GET /appointments/{id}/prescription (checkPrescriptionStatus)
        API-->>Comp: 200/404/error
    end
    Comp->>Comp: compute stats & filtered sets (useMemo)
    Comp-->>User: render header, stats, tabs, appointment cards

    User->>Comp: change search/filter/tab
    Comp->>Comp: apply client-side filtering
    Comp-->>User: re-render list

    User->>Comp: click appointment card
    Comp->>Comp: navigate to appointment details
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Focus points:
    • client/src/pages/patient/PatientAppointments.jsx — verify fetchAppointments/checkPrescriptionStatus logic, response shape validation, error handling, and retry.
    • Client-side filter/search and tab-date logic — ensure correctness and edge cases (timezones, boundary dates).
    • useMemo stats and sync with asynchronous prescription checks.
    • Accessibility/keyboard interaction handlers on AppointmentCard.

Possibly related PRs

Suggested labels

enhancement, UI, redesign

Poem

🐰 I hopped through JSX, tidy paws at play,

Cards and tabs aligned to greet the day,
Prescriptions checked, the stats in view,
QuickClinic hums — a fresher patient view! ✨

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings, 1 inconclusive)
Check name Status Explanation Resolution
Out of Scope Changes Check ⚠️ Warning The changes to PatientAppointments.jsx directly address the UI redesign requirement from issue #75. However, the modification to client/index.html, which changes the document title from "Vite + React" to "QuickClinic", appears to be out of scope. This change is application-wide and was not mentioned in the linked issue, which specifically focuses on redesigning the Patient Appointments page at /patient/appointments. While the HTML title change is minor and beneficial, it represents a separate concern unrelated to the stated objectives. Consider separating the client/index.html title change into a separate pull request or issue, as it is not directly related to the Patient Appointments page redesign. This keeps the scope of PR #78 focused on the specific UI redesign objective outlined in issue #75 and makes it easier to track changes by their related issues.
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Title Check ❓ Inconclusive The title "modified Patient_appointments page" is technically accurate and references the main component being changed, but it lacks specificity about the nature of the modification. The term "modified" is generic and non-descriptive, failing to convey what the actual change entails (i.e., whether it's a UI redesign, bug fix, refactor, or feature addition). While the title directly relates to the changeset and is not misleading, it falls short of being meaningfully informative to someone scanning the commit history. Consider revising the title to be more descriptive, such as "Redesign Patient Appointments page with tabs, filters, and improved error handling" or "Redesign Patient Appointments UI with new components and features". This would make the pull request more discoverable and clearer about the scope of changes when reviewing history.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues Check ✅ Passed The pull request successfully addresses the primary objective stated in linked issue #75, which is to redesign the Patient Appointments page for the patient role at the route /patient/appointments. The code changes demonstrate a comprehensive UI overhaul of PatientAppointments.jsx with new internal components (AppointmentCard, StatCard), improved data handling, enhanced filtering and search capabilities, better error management, and accessibility improvements. These changes align with the UI redesign requirement and provide improvements for both desktop and mobile views as shown in the attached screenshots referenced in the issue.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f9f373b and f61dae9.

📒 Files selected for processing (1)
  • client/src/pages/patient/PatientAppointments.jsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
client/src/pages/patient/PatientAppointments.jsx (6)
client/src/service/appointmentApiService.js (2)
  • getPatientAppointments (16-18)
  • getPatientAppointments (16-18)
server/Controllers/appointmentController.js (11)
  • getPatientAppointments (154-202)
  • getPatientAppointments (154-202)
  • appointment (120-130)
  • appointment (336-336)
  • appointment (463-470)
  • appointment (505-509)
  • appointment (544-544)
  • appointment (595-595)
  • appointments (185-189)
  • appointments (236-240)
  • appointments (305-310)
server/Controllers/prescriptionController.js (5)
  • appointment (27-30)
  • appointment (173-173)
  • appointment (228-228)
  • getPatientAppointmentPrescription (215-267)
  • getPatientAppointmentPrescription (215-267)
client/src/service/prescriptionApiSevice.js (2)
  • getPatientAppointmentPrescription (50-52)
  • getPatientAppointmentPrescription (50-52)
client/src/components/Patient/PatientAppointments/AppointmentStats.jsx (1)
  • stats (7-37)
client/src/components/Patient/PatientAppointments/AppointmentCard.jsx (3)
  • AppointmentCard (16-159)
  • getStatusBadge (29-68)
  • hasPrescription (74-74)

Comment on lines +310 to +334
key={tab.id}
onClick={() => handleTabChange(tab.id)}
role="tab"
aria-selected={activeTab === tab.id}
aria-controls={`${tab.id}-panel`}
className={`pb-3 px-1 border-b-2 font-medium text-sm transition-colors relative ${
activeTab === tab.id
? 'border-blue-600 text-blue-600'
: 'border-transparent text-gray-600 hover:text-gray-900 hover:border-gray-300'
}`}
>
{tab.label}
{tab.count > 0 && (
<span
className={`ml-2 px-2 py-0.5 rounded-full text-xs font-semibold ${
activeTab === tab.id
? 'bg-blue-100 text-blue-600'
: 'bg-gray-100 text-gray-600'
}`}
aria-label={`${tab.count} ${tab.label.toLowerCase()} appointments`}
>
{tab.count}
</span>
)}
</button>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Provide ids for tabs referenced by aria-labelledby.

The tabpanel’s aria-labelledby points to ${activeTab}-tab, but the buttons never declare that id, leaving screen readers without a label. Add matching ids to the buttons.

-                <button
-                  key={tab.id}
+                <button
+                  id={`${tab.id}-tab`}
+                  key={tab.id}

Also applies to: 344-345

🤖 Prompt for AI Agents
In client/src/pages/patient/PatientAppointments.jsx around lines 310-334 (and
also apply the same fix at lines ~344-345), the tab buttons are missing id
attributes referenced by the tabpanel's aria-labelledby (which uses
`${activeTab}-tab`), so add an id to each button matching `${tab.id}-tab`;
ensure the id is unique per tab and keep existing aria attributes intact so
screen readers can correctly associate each tabpanel with its corresponding tab.

Comment on lines 420 to 427
const statusConfig = {
scheduled: { label: 'Pending', bg: 'bg-yellow-50', text: 'text-yellow-700', border: 'border-yellow-200' },
completed: { label: 'Completed', bg: 'bg-green-50', text: 'text-green-700', border: 'border-green-200' },
cancelled: { label: 'Cancelled', bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-200' },
};

const config = statusConfig[status] || statusConfig.scheduled;

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Map all expected appointment statuses.

Appointments with pending, confirmed, or no-show now fall through to the default branch and appear as “Pending”, which is confusing. Please add explicit entries for the known statuses.

-    const statusConfig = {
-      scheduled: { label: 'Pending', bg: 'bg-yellow-50', text: 'text-yellow-700', border: 'border-yellow-200' },
-      completed: { label: 'Completed', bg: 'bg-green-50', text: 'text-green-700', border: 'border-green-200' },
-      cancelled: { label: 'Cancelled', bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-200' },
-    };
+    const statusConfig = {
+      pending: { label: 'Pending', bg: 'bg-yellow-50', text: 'text-yellow-700', border: 'border-yellow-200' },
+      confirmed: { label: 'Confirmed', bg: 'bg-blue-50', text: 'text-blue-700', border: 'border-blue-200' },
+      scheduled: { label: 'Scheduled', bg: 'bg-yellow-50', text: 'text-yellow-700', border: 'border-yellow-200' },
+      completed: { label: 'Completed', bg: 'bg-green-50', text: 'text-green-700', border: 'border-green-200' },
+      cancelled: { label: 'Cancelled', bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-200' },
+      'no-show': { label: 'No Show', bg: 'bg-gray-100', text: 'text-gray-700', border: 'border-gray-200' },
+    };
@@
-    const config = statusConfig[status] || statusConfig.scheduled;
+    const config = statusConfig[status] || statusConfig.pending;

Based on relevant snippet.

📝 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
const statusConfig = {
scheduled: { label: 'Pending', bg: 'bg-yellow-50', text: 'text-yellow-700', border: 'border-yellow-200' },
completed: { label: 'Completed', bg: 'bg-green-50', text: 'text-green-700', border: 'border-green-200' },
cancelled: { label: 'Cancelled', bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-200' },
};
const config = statusConfig[status] || statusConfig.scheduled;
const statusConfig = {
pending: { label: 'Pending', bg: 'bg-yellow-50', text: 'text-yellow-700', border: 'border-yellow-200' },
confirmed: { label: 'Confirmed', bg: 'bg-blue-50', text: 'text-blue-700', border: 'border-blue-200' },
scheduled: { label: 'Scheduled', bg: 'bg-yellow-50', text: 'text-yellow-700', border: 'border-yellow-200' },
completed: { label: 'Completed', bg: 'bg-green-50', text: 'text-green-700', border: 'border-green-200' },
cancelled: { label: 'Cancelled', bg: 'bg-red-50', text: 'text-red-700', border: 'border-red-200' },
'no-show': { label: 'No Show', bg: 'bg-gray-100', text: 'text-gray-700', border: 'border-gray-200' },
};
const config = statusConfig[status] || statusConfig.pending;
🤖 Prompt for AI Agents
In client/src/pages/patient/PatientAppointments.jsx around lines 420 to 427, the
statusConfig object only defines scheduled, completed, and cancelled so other
known statuses like pending, confirmed, and no-show fall through to scheduled;
add explicit entries for "pending", "confirmed", and "no-show" to the
statusConfig with appropriate label and tailwind classes (e.g., pending → label
"Pending" with same styles as scheduled; confirmed → label "Confirmed" with
positive styles similar to completed; no-show → label "No-show" with negative
styles similar to cancelled) and keep the fallback to statusConfig.scheduled
unchanged.

Comment on lines +479 to +482
<span className="text-sm">{appointment.date || 'N/A'}</span>
<Clock className="w-4 h-4 text-gray-400 ml-1" aria-hidden="true" />
<span className="text-sm">{appointment.time || 'N/A'}</span>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Display the actual start time field.

appointment.time is never populated by the API, so the card shows “N/A” for every entry. Use startTime, which is what the backend returns.

-          <span className="text-sm">{appointment.time || 'N/A'}</span>
+          <span className="text-sm">{appointment.startTime || 'N/A'}</span>
📝 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
<span className="text-sm">{appointment.date || 'N/A'}</span>
<Clock className="w-4 h-4 text-gray-400 ml-1" aria-hidden="true" />
<span className="text-sm">{appointment.time || 'N/A'}</span>
</div>
<span className="text-sm">{appointment.date || 'N/A'}</span>
<Clock className="w-4 h-4 text-gray-400 ml-1" aria-hidden="true" />
<span className="text-sm">{appointment.startTime || 'N/A'}</span>
</div>
🤖 Prompt for AI Agents
In client/src/pages/patient/PatientAppointments.jsx around lines 479 to 482, the
UI is rendering appointment.time which the API never populates; replace usage of
appointment.time with appointment.startTime so the actual start time returned by
the backend is displayed (update the span to use appointment.startTime || 'N/A'
and keep the Clock icon and surrounding markup intact).

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (5)
client/src/pages/patient/PatientAppointments.jsx (5)

153-179: Restore pending/confirmed logic for upcoming views

The API still emits pending/confirmed for upcoming slots, so forcing 'scheduled' zeroes the badge and empties the default tab again—same regression we called out earlier.

Apply this diff to reinstate the correct filters:

-    const upcoming = appointments.filter((a) => a.status === 'scheduled').length;
+    const upcoming = appointments.filter(
+      (a) => a.status === 'pending' || a.status === 'confirmed'
+    ).length;
@@
-        return aptDate >= today && apt.status === 'scheduled';
+        return (
+          aptDate >= today &&
+          (apt.status === 'pending' || apt.status === 'confirmed')
+        );

268-272: Offer the real status filters

Users still can’t filter to pending, confirmed, or no-show because the dropdown lists the obsolete 'scheduled' value. This is the same mismatch we noted earlier.

Align the options with the API statuses:

-                <option value="scheduled">Scheduled</option>
-                <option value="completed">Completed</option>
-                <option value="cancelled">Cancelled</option>
+                <option value="pending">Pending</option>
+                <option value="confirmed">Confirmed</option>
+                <option value="completed">Completed</option>
+                <option value="cancelled">Cancelled</option>
+                <option value="no-show">No Show</option>

307-332: Give each tab button an id

The panels still reference ${tab.id}-tab via aria-labelledby, but the buttons never expose that id, so screen readers can’t make the association. We already asked for this fix.

Add the matching id:

-                <button
-                  key={tab.id}
+                <button
+                  id={`${tab.id}-tab`}
+                  key={tab.id}

418-446: Cover every appointment status in the badge

pending, confirmed, and no-show still fall through to the fallback and render as “Pending”, so patients see the wrong state. Let’s enumerate all known statuses and keep the fallback on pending.

Broaden the config and expose the right icon/label:

-    const statusConfig = {
-      scheduled: {
-        label: 'Pending',
-        bg: 'bg-yellow-50',
-        text: 'text-yellow-700',
-        border: 'border-yellow-200',
-      },
-      completed: {
-        label: 'Completed',
-        bg: 'bg-green-50',
-        text: 'text-green-700',
-        border: 'border-green-200',
-      },
-      cancelled: {
-        label: 'Cancelled',
-        bg: 'bg-red-50',
-        text: 'text-red-700',
-        border: 'border-red-200',
-      },
-    };
-
-    const config = statusConfig[status] || statusConfig.scheduled;
+    const statusConfig = {
+      pending: {
+        label: 'Pending',
+        bg: 'bg-yellow-50',
+        text: 'text-yellow-700',
+        border: 'border-yellow-200',
+        icon: '⏱',
+      },
+      confirmed: {
+        label: 'Confirmed',
+        bg: 'bg-blue-50',
+        text: 'text-blue-700',
+        border: 'border-blue-200',
+        icon: '✔️',
+      },
+      scheduled: {
+        label: 'Scheduled',
+        bg: 'bg-yellow-50',
+        text: 'text-yellow-700',
+        border: 'border-yellow-200',
+        icon: '⏱',
+      },
+      completed: {
+        label: 'Completed',
+        bg: 'bg-green-50',
+        text: 'text-green-700',
+        border: 'border-green-200',
+        icon: '✔️',
+      },
+      cancelled: {
+        label: 'Cancelled',
+        bg: 'bg-red-50',
+        text: 'text-red-700',
+        border: 'border-red-200',
+        icon: '✖️',
+      },
+      'no-show': {
+        label: 'No Show',
+        bg: 'bg-gray-100',
+        text: 'text-gray-700',
+        border: 'border-gray-200',
+        icon: '⚠️',
+      },
+    };
+
+    const config = statusConfig[status] || statusConfig.pending;
@@
-        ⏱ {config.label}
+        {config.icon} {config.label}

490-493: Display the start time field returned by the API

appointment.time is never populated, so the card always shows “N/A”. The backend sends startTime, and we already rely on it elsewhere.

Switch to the correct property:

-          <span className="text-sm">{appointment.time || 'N/A'}</span>
+          <span className="text-sm">{appointment.startTime || 'N/A'}</span>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f61dae9 and d377057.

📒 Files selected for processing (2)
  • client/index.html (1 hunks)
  • client/src/pages/patient/PatientAppointments.jsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
client/src/pages/patient/PatientAppointments.jsx (7)
client/src/service/appointmentApiService.js (2)
  • getPatientAppointments (16-18)
  • getPatientAppointments (16-18)
server/Controllers/appointmentController.js (11)
  • getPatientAppointments (154-202)
  • getPatientAppointments (154-202)
  • appointment (120-130)
  • appointment (336-336)
  • appointment (463-470)
  • appointment (505-509)
  • appointment (544-544)
  • appointment (595-595)
  • appointments (185-189)
  • appointments (236-240)
  • appointments (305-310)
server/Controllers/prescriptionController.js (5)
  • appointment (27-30)
  • appointment (173-173)
  • appointment (228-228)
  • getPatientAppointmentPrescription (215-267)
  • getPatientAppointmentPrescription (215-267)
client/src/service/prescriptionApiSevice.js (2)
  • getPatientAppointmentPrescription (50-52)
  • getPatientAppointmentPrescription (50-52)
client/src/components/Patient/PatientAppointments/AppointmentStats.jsx (1)
  • stats (7-37)
client/src/components/ui/Loading.jsx (1)
  • Loading (4-63)
client/src/components/Patient/PatientAppointments/AppointmentCard.jsx (3)
  • AppointmentCard (16-159)
  • getStatusBadge (29-68)
  • hasPrescription (74-74)

Comment on lines 303 to 306
{ id: 'upcoming', label: 'Upcoming', count: stats.upcoming },
{ id: 'today', label: 'Today', count: 0 },
{ id: 'past', label: 'Past', count: stats.completed },
].map((tab) => (
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Show real counts in the tab badges

Hard-coding Today to 0 and Past to “completed” means the badges never match what the panels show. Patients see misleading numbers.

Compute the counts from the same filters driving each tab:

+  const tabCounts = useMemo(() => {
+    const now = new Date();
+    const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
+
+    const upcomingCount = filteredAppointments.filter((apt) => {
+      const aptDate = new Date(apt.date);
+      return (
+        aptDate >= today &&
+        (apt.status === 'pending' || apt.status === 'confirmed')
+      );
+    }).length;
+
+    const todayCount = filteredAppointments.filter((apt) => {
+      const aptDate = new Date(apt.date);
+      return aptDate.toDateString() === today.toDateString();
+    }).length;
+
+    const pastCount = filteredAppointments.filter((apt) => {
+      const aptDate = new Date(apt.date);
+      return aptDate < today || apt.status === 'completed';
+    }).length;
+
+    return { upcoming: upcomingCount, today: todayCount, past: pastCount };
+  }, [filteredAppointments]);
@@
-                { id: 'upcoming', label: 'Upcoming', count: stats.upcoming },
-                { id: 'today', label: 'Today', count: 0 },
-                { id: 'past', label: 'Past', count: stats.completed },
+                { id: 'upcoming', label: 'Upcoming', count: tabCounts.upcoming },
+                { id: 'today', label: 'Today', count: tabCounts.today },
+                { id: 'past', label: 'Past', count: tabCounts.past },
🤖 Prompt for AI Agents
In client/src/pages/patient/PatientAppointments.jsx around lines 303 to 306, the
tab badge counts are hard-coded (Today=0, Past=stats.completed) causing
mismatches; change the tab definitions to compute counts using the same
appointment filters used by each panel (e.g., derive upcoming, today, and past
counts by filtering the appointments array or reusing the existing stats
calculations), replacing the hard-coded values with the computed counts so
badges reflect the actual filtered data.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (7)
client/src/pages/patient/PatientAppointments.jsx (7)

303-305: Fix critical runtime error: tabCounts is undefined.

The tab definitions reference tabCounts.upcoming, tabCounts.today, and tabCounts.past, but tabCounts is never defined. This will throw a ReferenceError and crash the application.

Apply this diff to compute the tab counts (reusing logic from the previous review):

+  const tabCounts = useMemo(() => {
+    const now = new Date();
+    const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
+
+    const upcomingCount = filteredAppointments.filter((apt) => {
+      const aptDate = new Date(apt.date);
+      return (
+        aptDate >= today &&
+        (apt.status === 'pending' || apt.status === 'confirmed')
+      );
+    }).length;
+
+    const todayCount = filteredAppointments.filter((apt) => {
+      const aptDate = new Date(apt.date);
+      return aptDate.toDateString() === today.toDateString();
+    }).length;
+
+    const pastCount = filteredAppointments.filter((apt) => {
+      const aptDate = new Date(apt.date);
+      return aptDate < today || apt.status === 'completed';
+    }).length;
+
+    return { upcoming: upcomingCount, today: todayCount, past: pastCount };
+  }, [filteredAppointments]);
+
   const filteredAppointments = useMemo(() => {

154-160: Fix status filters for upcoming appointments.

Line 155 filters by status === 'scheduled', but the backend API returns 'pending' and 'confirmed' for upcoming appointments (as shown in the AppointmentStats component snippet). This causes upcoming counts to be zero and the default tab to be empty.

Apply this diff:

   const stats = useMemo(() => {
     const total = appointments.length;
-    const upcoming = appointments.filter((a) => a.status === 'scheduled').length;
+    const upcoming = appointments.filter(
+      (a) => a.status === 'pending' || a.status === 'confirmed'
+    ).length;
     const completed = appointments.filter((a) => a.status === 'completed').length;

Based on relevant snippet.


173-179: Fix tab filtering to match actual appointment statuses.

The upcoming tab filter uses status === 'scheduled', but appointments in the system have 'pending' or 'confirmed' status. This makes the upcoming tab always empty.

Apply this diff:

       if (activeTab === 'upcoming') {
-        return aptDate >= today && apt.status === 'scheduled';
+        return (
+          aptDate >= today &&
+          (apt.status === 'pending' || apt.status === 'confirmed')
+        );
       } else if (activeTab === 'today') {

Based on relevant snippet.


269-271: Update status dropdown options to match backend values.

The dropdown shows 'scheduled' as an option, but the backend never returns this status. Add the actual statuses: 'pending', 'confirmed', and 'no-show'.

Apply this diff:

                 <option value="all">All Status</option>
-                <option value="scheduled">Scheduled</option>
+                <option value="pending">Pending</option>
+                <option value="confirmed">Confirmed</option>
                 <option value="completed">Completed</option>
                 <option value="cancelled">Cancelled</option>
+                <option value="no-show">No Show</option>

Based on relevant snippet.


307-332: Add missing id attributes to tab buttons for accessibility.

The tabpanel's aria-labelledby="${activeTab}-tab" (line 343) references IDs that don't exist. Add id="${tab.id}-tab" to each button so screen readers can correctly associate tabpanels with their tabs.

Apply this diff:

               ].map((tab) => (
                 <button
+                  id={`${tab.id}-tab`}
                   key={tab.id}
                   onClick={() => handleTabChange(tab.id)}

418-440: Add missing appointment statuses to statusConfig.

The statusConfig only defines 'scheduled', 'completed', and 'cancelled'. Appointments with 'pending', 'confirmed', or 'no-show' statuses fall through to the default and display incorrectly.

Apply this diff:

     const statusConfig = {
+      pending: {
+        label: 'Pending',
+        bg: 'bg-yellow-50',
+        text: 'text-yellow-700',
+        border: 'border-yellow-200',
+      },
+      confirmed: {
+        label: 'Confirmed',
+        bg: 'bg-blue-50',
+        text: 'text-blue-700',
+        border: 'border-blue-200',
+      },
       scheduled: {
         label: 'Pending',
         bg: 'bg-yellow-50',
         text: 'text-yellow-700',
         border: 'border-yellow-200',
       },
       completed: {
         label: 'Completed',
         bg: 'bg-green-50',
         text: 'text-green-700',
         border: 'border-green-200',
       },
       cancelled: {
         label: 'Cancelled',
         bg: 'bg-red-50',
         text: 'text-red-700',
         border: 'border-red-200',
       },
+      'no-show': {
+        label: 'No Show',
+        bg: 'bg-gray-100',
+        text: 'text-gray-700',
+        border: 'border-gray-200',
+      },
     };
 
-    const config = statusConfig[status] || statusConfig.scheduled;
+    const config = statusConfig[status] || statusConfig.pending;

Based on relevant snippet.


491-494: Display the actual start time field.

appointment.time is never populated by the API. The backend returns startTime, so the card currently shows "N/A" for every appointment.

Apply this diff:

           <Calendar className="w-4 h-4 text-gray-400" aria-hidden="true" />
           <span className="text-sm">{appointment.date || 'N/A'}</span>
           <Clock className="w-4 h-4 text-gray-400 ml-1" aria-hidden="true" />
-          <span className="text-sm">{appointment.time || 'N/A'}</span>
+          <span className="text-sm">{appointment.startTime || 'N/A'}</span>
         </div>

Based on relevant snippet.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d377057 and 767a230.

📒 Files selected for processing (1)
  • client/src/pages/patient/PatientAppointments.jsx (2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
client/src/pages/patient/PatientAppointments.jsx (6)
client/src/service/appointmentApiService.js (2)
  • getPatientAppointments (16-18)
  • getPatientAppointments (16-18)
server/Controllers/appointmentController.js (11)
  • getPatientAppointments (154-202)
  • getPatientAppointments (154-202)
  • appointment (120-130)
  • appointment (336-336)
  • appointment (463-470)
  • appointment (505-509)
  • appointment (544-544)
  • appointment (595-595)
  • appointments (185-189)
  • appointments (236-240)
  • appointments (305-310)
server/Controllers/prescriptionController.js (5)
  • appointment (27-30)
  • appointment (173-173)
  • appointment (228-228)
  • getPatientAppointmentPrescription (215-267)
  • getPatientAppointmentPrescription (215-267)
client/src/service/prescriptionApiSevice.js (2)
  • getPatientAppointmentPrescription (50-52)
  • getPatientAppointmentPrescription (50-52)
client/src/components/Patient/PatientAppointments/AppointmentStats.jsx (1)
  • stats (7-37)
client/src/components/Patient/PatientAppointments/AppointmentCard.jsx (3)
  • AppointmentCard (16-159)
  • getStatusBadge (29-68)
  • hasPrescription (74-74)
🔇 Additional comments (4)
client/src/pages/patient/PatientAppointments.jsx (4)

51-107: Excellent error handling and validation.

The data fetching logic demonstrates best practices:

  • Validates API response structure before using it
  • Handles 404 responses gracefully (expected when prescriptions don't exist)
  • Uses consistent error logging with component name prefix
  • Proper null checks and array validation

118-147: Good use of memoization for filtering logic.

The filteredAppointments memo correctly depends on appointments, searchTerm, and filterStatus. The search implementation is case-insensitive with proper trimming and safely handles undefined fields with optional chaining.


188-221: Well-structured event handlers with proper memoization.

All event handlers are correctly wrapped in useCallback with appropriate dependencies. The handleAppointmentClick includes validation for the appointment ID before navigation, preventing potential errors.


499-504: Verify that visitType field exists in the API response.

The code references appointment.visitType, but this field doesn't appear in the backend API response (based on the getPatientAppointments controller snippet). The backend provides isTeleconsultation instead. This block will never render.

Run this script to confirm whether visitType exists in the appointment model:

Comment on lines 17 to 20
import AppointmentStats from '../../components/Patient/PatientAppointments/AppointmentStats';
import AppointmentTabs from '../../components/Patient/PatientAppointments/AppointmentTabs';
import AppointmentHeader from '../../components/Patient/PatientAppointments/AppointmentHeader';
import ErrorState from '../../components/Patient/PatientAppointments/ErrorState';
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Remove unused imports.

The imported components AppointmentStats, AppointmentTabs, AppointmentHeader, and ErrorState are never used in the refactored code. Removing them will reduce bundle size and prevent confusion.

Apply this diff:

-import AppointmentStats from '../../components/Patient/PatientAppointments/AppointmentStats';
-import AppointmentTabs from '../../components/Patient/PatientAppointments/AppointmentTabs';
-import AppointmentHeader from '../../components/Patient/PatientAppointments/AppointmentHeader';
-import ErrorState from '../../components/Patient/PatientAppointments/ErrorState';
 import Loading from '../../components/ui/Loading';
📝 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
import AppointmentStats from '../../components/Patient/PatientAppointments/AppointmentStats';
import AppointmentTabs from '../../components/Patient/PatientAppointments/AppointmentTabs';
import AppointmentHeader from '../../components/Patient/PatientAppointments/AppointmentHeader';
import ErrorState from '../../components/Patient/PatientAppointments/ErrorState';
import Loading from '../../components/ui/Loading';
🤖 Prompt for AI Agents
In client/src/pages/patient/PatientAppointments.jsx around lines 17 to 20, the
imports AppointmentStats, AppointmentTabs, AppointmentHeader, and ErrorState are
unused; remove these four import lines from the top of the file so only actually
used modules remain, save the file, and run a quick lint/build to ensure no
other references remain.

Comment on lines +527 to +530
onClick={(e) => {
e.stopPropagation();
// Handle prescription view
}}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Implement or remove the prescription view button.

The prescription view button has an empty handler with only a comment. Either implement the navigation to the prescription view or remove the button if the feature isn't ready.

Consider this implementation:

             onClick={(e) => {
               e.stopPropagation();
-              // Handle prescription view
+              navigate(`/patient/appointment/${appointment._id}/prescription`);
             }}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In client/src/pages/patient/PatientAppointments.jsx around lines 527-530, the
onClick handler for the prescription view button is empty; either implement
navigation to the prescription view or remove the button. To fix: replace the
comment with a real action — e.g., navigate to the prescription route (use the
app router/history hook to push a path like
`/patients/:patientId/appointments/:appointmentId/prescription` or dispatch an
action to open the prescription modal, passing appointmentId/patientId), or if
the feature isn't ready, remove the button and any related UI/props so no inert
control remains.

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.

[UI REDESIGN]:Patient Appointments Page

1 participant