DeveloperQueue
CMS Queue Management
Overview
The Queue Management system in the CMS app provides real-time patient queue tracking, appointment status management, and doctor consultation workflow automation. It's designed for healthcare clinics to efficiently manage patient flow throughout the day.
Architecture
Component Structure
apps/cms/src/
├── pages/
│ └── QueuePage.tsx # Main queue page component
├── components/queue/
│ ├── QueueHeader.tsx # Header with search and summary
│ ├── QueueFilters.tsx # Filter controls (doctor, category, date)
│ ├── QueueTable.tsx # Main patient queue table
│ ├── QueueTableRow.tsx # Individual patient row
│ ├── QueueStats.tsx # Summary statistics display
│ ├── QueueEmptyState.tsx # Empty state component
│ └── QueueErrorState.tsx # Error state component
├── hooks/
│ ├── useQueue.ts # Core queue data fetching
│ ├── useQueueData.ts # Composed hook with filtering
│ ├── useQueueActions.ts # Queue-related actions
│ ├── useQueueModals.ts # Modal state management
│ ├── useQueueFilters.ts # Filter state management
│ └── useRealtimeQueue.ts # Real-time updates via Ably
├── services/
│ └── queueApi.ts # API service layer
└── types/
└── queue.ts # TypeScript typesKey Features
- Real-time Updates: Live patient status changes using Ably WebSockets
- Multi-doctor Support: Filter by specific doctors or view all
- Status Workflow: Manage patient flow through different statuses
- Telemedicine: Support for video consultations
- Quick Actions: Notes, billing, prescriptions, follow-up scheduling
- Responsive Design: Works across desktop and mobile devices
Data Flow
Queue Data Structure
interface QueuePatient {
id: string
tokenNumber: string
patientId: string
clinicPatientId: string
patientName: string
patientAge: number
patientGender: "male" | "female" | "other"
patientPhone: string
doctorId: string
doctorName: string
appointmentTime: string
appointmentType: "consultation" | "follow-up" | "emergency" | "telemedicine"
status: "in-queue" | "checked-in" | "with-doc" | "checked-out" |
"removed" | "cancelled" | "reassign" | "not-confirmed"
checkInTime?: string
waitingTime?: number
consultationTime?: number
// ... additional fields
}Status Workflow
in-queue → checked-in → with-doc → checked-out
↓ ↓ ↓
removed removed removedAPI Integration
Queue Endpoints
// Get queue for a clinic/date
GET /clinics/{clinicId}/queue?date=YYYY-MM-DD
// Get doctor-specific queue
GET /clinics/{clinicId}/clinic_doctors/{doctorId}/queue?date=YYYY-MM-DD
// Update appointment status
PUT /appointments/{appointmentId}
Body: {
clinic_id: number,
doctor_id: number,
appointment_status: string
}
// Save patient notes
POST /clinic_patients/{clinicPatientId}/notes
Body: {
note_text: string,
appointment_id: number,
clinic_doctor_id: number
}
// Schedule next visit
PUT /clinic_patients/{clinicPatientId}/next_visit/{appointmentId}
Body: {
clinic_doctor_id: number,
next_visit_units: "DAYS" | "WEEKS" | "MONTHS",
next_visit_value: number
}Real-time Integration
Ably Configuration
// Environment variables
VITE_ABLY_KEY=your-ably-key
VITE_ABLY_ENVIRONMENT=us // Optional: 'us' or 'eu'Real-time Events
// Channel format
`clinic:${clinicId}:queue:${date}`
// Event types
- appointment.updated
- appointment.created
- appointment.deleted
- queue.reorderedUsage Examples
Basic Queue Implementation
import { useQueueData } from "../hooks"
export default function QueuePage() {
const { filters, setFilters } = useQueueFilters()
const {
filteredPatients,
summary,
isLoading,
isError,
refetch,
isRealTimeConnected
} = useQueueData({ filters })
return (
<div>
<QueueHeader
summary={summary}
isRealTimeConnected={isRealTimeConnected}
/>
<QueueTable
patients={filteredPatients}
isLoading={isLoading}
/>
</div>
)
}Custom Hooks Usage
// Queue filters
const { filters, setFilters, resetFilters } = useQueueFilters()
// Queue actions
const {
handleStatusChange,
handlePatientClick,
handleCreatePrescription,
updateAppointmentStatus
} = useQueueActions()
// Modal management
const {
showNotesModal,
setShowNotesModal,
openNotesModal,
selectedPatient
} = useQueueModals()Status Update Flow
// Handle status change with confirmation
const handleStatusChange = async (patientId: string, newStatus: string) => {
// Show confirmation for critical status changes
if (needsConfirmation(currentStatus, newStatus)) {
setStatusConfirmation({ patientId, newStatus })
return
}
// Direct update
await updateAppointmentStatus.mutateAsync({
appointmentId: patientId,
status: newStatus,
doctorId: patient.doctorId
})
}Component Details
QueueTable Features
- Density Control: Compact/Normal/Comfortable view modes
- Column Visibility: Customizable column display
- Row Actions: Dropdown menu with patient actions
- Loading States: Row-level loading during updates
- Empty States: Contextual messages for no patients
QueueFilters
- Doctor Filter: Select specific doctor or "All Doctors"
- Category Filter: Filter by appointment category
- Date Picker: Select queue date
- Search: Real-time patient search
- Reset: Clear all filters
Quick Actions
- Start Consultation: Begin patient consultation
- Create Prescription: Navigate to care plan
- Billing: Open billing panel
- Quick Notes: Add patient notes
- Next Visit: Schedule follow-up
- Remove from Queue: Remove patient with reason
Performance Optimizations
- Query Caching: 10-minute staleTime for queue data
- Optimistic Updates: Immediate UI feedback
- Debounced Search: 300ms debounce on search input
- Memoized Filtering: Efficient patient filtering
- Lazy Loading: Modals load on demand
Error Handling
// API error handling
if (isError) {
return <QueueErrorState
error={error}
onRetry={refetch}
/>
}
// Network status
if (!isRealTimeConnected) {
// Show offline indicator
}Testing Considerations
- Mock Data: Use
@kulcare/mock-datafor development - Real-time Testing: Test with multiple browser tabs
- Status Transitions: Verify all status workflows
- Error Scenarios: Test network failures
- Performance: Test with large patient lists (100+)
Common Patterns
Adding New Actions
// 1. Add to QueueTableRow dropdown
<DropdownMenuItem onClick={() => onCustomAction(patient)}>
Custom Action
</DropdownMenuItem>
// 2. Handle in QueuePage
const handleCustomAction = (patient: QueuePatient) => {
// Action logic
}
// 3. Pass to QueueTable
<QueueTable
onCustomAction={handleCustomAction}
/>Custom Status Rendering
// Add to queue constants
export const STATUS_CONFIG = {
"custom-status": {
label: "Custom Status",
color: "bg-purple-500",
icon: CustomIcon
}
}