Skip to content

Repository files navigation

CQMP

Clinic Queue Management Platform

CQMP β€” Real-time patient queue management for modern clinics

Laravel React TypeScript WebSockets PWA License

Register patients Β· Manage queues Β· Display live status Β· All in real-time


πŸ“‹ Table of Contents


πŸ₯ Overview

CQMP is a full-stack, real-time clinic queue management system that eliminates the chaos of paper-based patient queuing. It connects receptionists, doctors, and waiting patients through a live-updating dashboard β€” ensuring smooth patient flow, minimal wait-time confusion, and a professional clinic experience.

Built on Laravel 13 + Laravel Reverb (WebSockets) on the backend, and React 19 + Zustand + TypeScript on the frontend, the system delivers sub-second queue updates to all connected screens simultaneously.

Authentication uses a custom JWT implementation (JwtService + JwtMiddleware) for stateless, cookie-free token auth β€” ideal for shared-hosting deployments where Sanctum sessions are impractical.


✨ Key Features

Feature Description
🟒 Live Queue Board Patient queue updates in real-time via WebSockets β€” no page refresh needed
🚨 Emergency Insert Instantly push an emergency patient to the front, with automatic serial renumbering
πŸ”’ Serial Reordering Move any patient to any position by entering a target serial number
πŸ–¨οΈ Thermal Slip Printing One-click print of a formatted receipt with serial and estimated wait
⏸️ Queue Freeze / Resume Pause walk-in registrations while keeping the existing queue intact
⏱️ Estimated Wait Times Auto-calculated from average consultation time + doctor delay
πŸ“Ί TV Display Mode Full-screen patient-facing display β€” accessible at /tv without login
πŸ”’ Role-Based Access Doctor, Receptionist, and Admin roles via Spatie Permission
πŸ“‹ Audit Trail Every action (call, complete, skip, emergency, reinsert, delete) is logged
πŸŒ™ Dark / Light Mode System-aware theme with instant toggle, persisted to localStorage
⌨️ Full Keyboard Control Every core action has a single-key hotkey β€” no mouse required
πŸ“± PWA Installable Install as a native-like app on Android, iOS, or desktop
πŸ”” Doctor Delay Tracking Doctors can log delays; wait times auto-update for all waiting patients
🌐 Visitor Self-Booking Public booking form (no login) β€” name only, phone optional
πŸ›‘οΈ Admin Panel Filament-powered admin for managing clinics, doctors, and patients

πŸ›  Tech Stack

Backend

Technology Version Purpose
PHP 8.3+ Runtime
Laravel 13.x Application framework
Laravel Reverb 1.x Native WebSocket server
Custom JWT β€” Stateless API token auth (JwtService + JwtMiddleware)
Laravel Filament 5.x Admin panel UI
Spatie Permission 8.x Role & permission management
SQLite / MySQL β€” Database (SQLite by default in dev)

Frontend

Technology Version Purpose
React 19.x UI framework
TypeScript 6.x Type safety
Vite 8.x Build tool & dev server
Zustand 5.x Global state management
Laravel Echo 2.x WebSocket client
Pusher JS 8.x WebSocket transport adapter
Tailwind CSS 3.x Utility-first styling
Lucide React β€” Icon library
vite-plugin-pwa β€” Service Worker & PWA manifest

πŸ— Architecture

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                          CQMP Architecture                           β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚    Frontend    β”‚       Backend API         β”‚     WebSocket Layer      β”‚
β”‚  React 19 + TS β”‚   Laravel 13 REST API    β”‚    Laravel Reverb        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€                          β”‚                          β”‚
β”‚ LoginForm      β”‚  POST  /queue/open        β”‚  ws://host:8080/app/..   β”‚
β”‚ Receptionist   β”‚  GET   /queue/today       β”‚                          β”‚
β”‚  Dashboard     β”‚  POST  /queue/create      β”‚  Channels:               β”‚
β”‚ Doctor         β”‚  POST  /queue/call-next   β”‚  β”œβ”€ queue.{queueDayId}   β”‚
β”‚  Dashboard     β”‚  POST  /queue/complete    β”‚  └─ doctor-queue.{docId} β”‚
β”‚ TV Display     β”‚  POST  /queue/skip        β”‚                          β”‚
β”‚ Visitor        β”‚  POST  /queue/reinsert    β”‚  Events Fired:           β”‚
β”‚  Booking       β”‚  POST  /queue/emergency   β”‚  β”œβ”€ QueueCreated         β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€  POST  /queue/freeze      β”‚  β”œβ”€ QueueUpdated         β”‚
β”‚  State (Zustandβ”‚  DELETE /queue/:id        β”‚  β”œβ”€ QueueCompleted       β”‚
β”‚  stores)       β”‚  POST  /doctor/delay      β”‚  β”œβ”€ QueueDeleted         β”‚
β”‚  β”œβ”€ useQueue   β”‚                           β”‚  β”œβ”€ EmergencyInserted     β”‚
β”‚  β”œβ”€ useAuth    β”‚  Public (no auth):        β”‚  β”œβ”€ QueueFrozen          β”‚
β”‚  └─ useTheme   β”‚  GET  /public/doctors     β”‚  β”œβ”€ QueueResumed         β”‚
β”‚                β”‚  GET  /public/queue       β”‚  └─ EstimatedTimeUpdated β”‚
β”‚                β”‚  POST /public/book        β”‚                          β”‚
β”‚                β”‚                           β”‚                          β”‚
β”‚                β”‚  Admin Panel:             β”‚                          β”‚
β”‚                β”‚  /admin  (Filament)       β”‚                          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Authentication Flow (JWT)

Staff Login: POST /api/v1/login  { email, password }
          β”‚
          β–Ό
AuthController validates credentials
          β”‚
          β–Ό
JwtService::generate()  β†’  signed JWT (HS256, 8-hour TTL)
          β”‚
          β–Ό
Token returned in JSON  β†’  stored in localStorage ('cqmp_token')
          β”‚
          β–Ό
All protected requests:  Authorization: Bearer {token}
          β”‚
          β–Ό
JwtMiddleware::handle()  β†’  validates signature + expiry

TV Display β€” Public Mode vs Auth Mode

Unauthenticated visitor navigates to /tv
          β”‚
          β–Ό
TvDisplay detects isPublicView = true (no cqmp_token in localStorage)
          β”‚
          β–Ό
Uses publicApi β†’ GET /api/v1/public/queue?doctor_id=N  (no auth)
          β”‚  Polls every 10 seconds (WebSocket requires auth)
          β–Ό
Queue displayed: currently called serial + waiting list

─────────────────────────────────────────────────────

Authenticated TV staff opens /tv
          β”‚
          β–Ό
TvDisplay detects isPublicView = false (token present)
          β”‚
          β–Ό
Uses authenticated api β†’ GET /api/v1/queue/today?doctor_id=N
          β”‚  + WebSocket (Echo/Reverb) for real-time push
          β–Ό
Queue displayed with instant WebSocket updates

Request Flow (Walk-in Registration)

Receptionist types name β†’ clicks "Add Queue" [R]
          β”‚
          β–Ό
React β†’ POST /api/v1/patients  (create / find patient)
          β”‚
          β–Ό
React β†’ POST /api/v1/queue/create
          β”‚
          β–Ό
QueueController β†’ QueueEngine::createWalkIn()
          β”‚ (DB transaction with row-level lock)
          β–Ό
QueueItem saved β†’ broadcast(QueueCreated)
          β”‚
          β–Ό
Laravel Reverb β†’ WebSocket β†’ All subscribers on queue.{id}
          β”‚
          β–Ό
useQueueStore listener β†’ state.items updated
          β”‚
          β–Ό
React re-renders: Doctor Dashboard + TV Display (instantly)

πŸ“ Project Structure

Clinic Queue Management Platform (CQMP)/
β”‚
β”œβ”€β”€ backend/                              # Laravel 13 API + Admin
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ Events/                       # 9 WebSocket broadcast events
β”‚   β”‚   β”‚   β”œβ”€β”€ QueueCreated.php
β”‚   β”‚   β”‚   β”œβ”€β”€ QueueUpdated.php
β”‚   β”‚   β”‚   β”œβ”€β”€ QueueCompleted.php
β”‚   β”‚   β”‚   β”œβ”€β”€ QueueDeleted.php
β”‚   β”‚   β”‚   β”œβ”€β”€ EmergencyInserted.php
β”‚   β”‚   β”‚   β”œβ”€β”€ QueueFrozen.php
β”‚   β”‚   β”‚   β”œβ”€β”€ QueueResumed.php
β”‚   β”‚   β”‚   β”œβ”€β”€ QueueOpened.php
β”‚   β”‚   β”‚   └── EstimatedTimeUpdated.php
β”‚   β”‚   β”‚
β”‚   β”‚   β”œβ”€β”€ Filament/Resources/           # Admin panel CRUD resources
β”‚   β”‚   β”‚   β”œβ”€β”€ Clinics/
β”‚   β”‚   β”‚   β”œβ”€β”€ Doctors/
β”‚   β”‚   β”‚   β”œβ”€β”€ Patients/
β”‚   β”‚   β”‚   └── Appointments/
β”‚   β”‚   β”‚
β”‚   β”‚   β”œβ”€β”€ Http/
β”‚   β”‚   β”‚   β”œβ”€β”€ Controllers/Api/
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ AuthController.php        # Login / logout / me
β”‚   β”‚   β”‚   β”‚   β”œβ”€β”€ PatientController.php     # Patient CRUD (phone optional)
β”‚   β”‚   β”‚   β”‚   └── QueueController.php       # All queue ops + public endpoints
β”‚   β”‚   β”‚   β”‚
β”‚   β”‚   β”‚   └── Middleware/
β”‚   β”‚   β”‚       └── JwtMiddleware.php         # Custom JWT auth guard
β”‚   β”‚   β”‚
β”‚   β”‚   β”œβ”€β”€ Models/                       # 11 Eloquent models
β”‚   β”‚   β”‚   β”œβ”€β”€ Clinic.php
β”‚   β”‚   β”‚   β”œβ”€β”€ Doctor.php
β”‚   β”‚   β”‚   β”œβ”€β”€ DoctorDelay.php
β”‚   β”‚   β”‚   β”œβ”€β”€ Patient.php
β”‚   β”‚   β”‚   β”œβ”€β”€ QueueDay.php
β”‚   β”‚   β”‚   β”œβ”€β”€ QueueItem.php
β”‚   β”‚   β”‚   β”œβ”€β”€ Appointment.php
β”‚   β”‚   β”‚   β”œβ”€β”€ Announcement.php
β”‚   β”‚   β”‚   β”œβ”€β”€ AuditLog.php
β”‚   β”‚   β”‚   β”œβ”€β”€ Receptionist.php
β”‚   β”‚   β”‚   └── User.php
β”‚   β”‚   β”‚
β”‚   β”‚   └── Services/
β”‚   β”‚       β”œβ”€β”€ QueueEngine.php           # Core queue business logic
β”‚   β”‚       β”œβ”€β”€ JwtService.php            # JWT sign / verify (HS256)
β”‚   β”‚       └── AuditService.php          # Action audit logging
β”‚   β”‚
β”‚   β”œβ”€β”€ database/migrations/              # 16 migrations
β”‚   β”œβ”€β”€ routes/api.php                    # All API routes under /api/v1
β”‚   └── tests/Feature/
β”‚       └── QueueApiTest.php              # Feature tests for queue operations
β”‚
└── frontend/                             # React 19 + TypeScript SPA
    β”œβ”€β”€ public/
    β”‚   β”œβ”€β”€ favicon.svg                   # Browser tab icon (queue + cross mark)
    β”‚   β”œβ”€β”€ logo.svg                      # Full horizontal wordmark
    β”‚   β”œβ”€β”€ icon-192.svg                  # PWA home screen icon
    β”‚   β”œβ”€β”€ icon-512.svg                  # PWA splash / store icon
    β”‚   └── manifest.json                 # Web App Manifest
    β”‚
    └── src/
        β”œβ”€β”€ components/
        β”‚   β”œβ”€β”€ LoginForm.tsx             # Booking portal + staff login modal
        β”‚   β”œβ”€β”€ ReceptionistDashboard.tsx # Patient registration + queue management
        β”‚   β”œβ”€β”€ DoctorDashboard.tsx       # Doctor's call / complete / skip panel
        β”‚   └── TvDisplay.tsx             # Public-facing live TV board
        β”‚
        β”œβ”€β”€ hooks/
        β”‚   └── useKeyboardShortcut.ts   # Global hotkey system (auto-disabled in inputs)
        β”‚
        β”œβ”€β”€ store/
        β”‚   β”œβ”€β”€ useQueueStore.ts         # Queue state + WebSocket subscriptions
        β”‚   β”œβ”€β”€ useAuthStore.ts          # JWT token + user state
        β”‚   └── useThemeStore.ts         # Dark / light theme
        β”‚
        └── utils/
            β”œβ”€β”€ api.ts                   # Axios: auth instance + public instance
            └── echo.ts                  # Laravel Echo + Reverb WebSocket config

πŸ—„ Database Schema

clinics
  id | name | address | phone | timestamps

doctors
  id | user_id | clinic_id | name | specialization
     | average_consultation_time | is_available | timestamps

patients
  id | name | phone (nullable) | notes | is_blocked
     | blocked_reason | qr_identifier | timestamps

queue_days
  id | doctor_id | clinic_id | date | status (opened|paused|closed)
     | opened_by | opened_at | closed_at | timestamps

queue_items
  id | queue_day_id | patient_id | serial_no | appointment_type
     | status (Waiting|Called|Completed|Skipped)
     | priority (Normal|Emergency) | estimated_wait
     | called_at | completed_at | timestamps

appointments
  id | patient_id | doctor_id | scheduled_at | status | notes | timestamps

doctor_delays
  id | doctor_id | delay_minutes | reason | start_time | end_time | timestamps

audit_logs
  id | user_id | action | target_patient_id | details
     | ip_address | user_agent | created_at

announcements
  id | clinic_id | message | is_active | timestamps

⚑ Real-time Events (WebSocket)

All events are broadcast over Laravel Reverb and received by Laravel Echo on the frontend. The TV display uses 10-second polling when accessed without a staff login (WebSocket channels require authentication).

Event Channel Key Payload Trigger
QueueCreated queue.{id} queue_item New patient registered
QueueUpdated queue.{id} queue_item Patient reordered / called
QueueCompleted queue.{id} queue_item_id Doctor marks done
QueueDeleted queue.{id} queue_item_id, doctor_id Entry removed
EmergencyInserted queue.{id} queue_item Emergency patient added
QueueFrozen queue.{id} queue_day Queue paused
QueueResumed queue.{id} queue_day Queue unpaused
QueueOpened doctor-queue.{docId} queue_day New queue day opened
EstimatedTimeUpdated queue.{id} wait_times: {id: minutes} Any status change

πŸ”Œ API Reference

All routes are prefixed /api/v1. Protected routes require Authorization: Bearer {token}.

Authentication

POST   /api/v1/login              Body: { email, password }  β†’ { token, user }
POST   /api/v1/logout             Invalidates current JWT
GET    /api/v1/me                 Returns current user profile

Patients

GET    /api/v1/patients           ?search=name_or_phone β†’ paginated list
POST   /api/v1/patients           { name, phone? }  β€” phone is optional
GET    /api/v1/patients/{id}
PUT    /api/v1/patients/{id}
DELETE /api/v1/patients/{id}

Queue Operations (require auth)

GET    /api/v1/queue/today        ?doctor_id=1 β†’ { queue_day, items[] }
POST   /api/v1/queue/open         { doctor_id, date? }
POST   /api/v1/queue/create       { queue_day_id, patient_id, serial_no? }
POST   /api/v1/queue/call-next    { queue_day_id }
POST   /api/v1/queue/complete     { queue_item_id }
POST   /api/v1/queue/skip         { queue_item_id }
POST   /api/v1/queue/reinsert     { queue_item_id, position }
POST   /api/v1/queue/emergency    { queue_day_id, patient_id }
POST   /api/v1/queue/freeze       { queue_day_id }
POST   /api/v1/queue/resume       { queue_day_id }
DELETE /api/v1/queue/{queueItem}

Doctor Panel (require auth)

POST   /api/v1/doctor/delay       { doctor_id, delay_minutes, reason }

Public β€” No Auth Required

GET    /api/v1/public/doctors     List of available doctors (cached 60 s)
GET    /api/v1/public/queue       ?doctor_id=1  β†’  read-only queue for TV display
POST   /api/v1/public/book        { name, phone?, doctor_id }  β€” visitor self-booking
GET    /api/v1/settings/public    Clinic display settings (title, logo, etc.)

TV Display uses GET /public/queue when accessed without login. This endpoint returns only display-safe fields: serial_no, status, priority, estimated_wait, and patient name. Phone numbers and internal IDs are not exposed.


πŸ‘₯ User Roles & Panels

πŸ₯ Receptionist Dashboard

  • Type patient name (phone is optional) and register instantly
  • Look up existing patients by phone to auto-fill name
  • Set a custom serial position or let the system assign one
  • Insert emergency patients β€” all other serials shift automatically
  • View live waiting list, current patient in chamber, skipped, and completed
  • Reorder any patient: enter a target serial number to move them
  • Print thermal receipt slips with serial number and wait estimate
  • Delete queue entries

πŸ‘¨β€βš•οΈ Doctor Dashboard

  • Open a new queue day for the current session
  • Call next patient β€” highest priority first, lowest serial within priority
  • Mark the current patient as Completed or Skipped
  • Freeze / Resume the queue (pauses new walk-in registrations)
  • Log a delay in minutes β€” all waiting patient wait times update immediately
  • View the full waiting list with live estimated times

πŸ“Ί TV Display (/tv)

  • Accessible at /tv β€” no login required
  • Navigate to it directly in the browser; no button on the booking portal
  • Full-screen animated board showing the current serial number being called
  • Single-doctor mode: select a doctor to watch one queue
  • Lobby mode: see all doctors' queues side-by-side
  • Bilingual voice announcement (Bangla + English) when a serial is called
  • When accessed without login: polls GET /public/queue every 10 seconds
  • When accessed with a staff token: uses WebSocket (Reverb) for instant updates

🌐 Visitor Self-Booking

  • No login required β€” main screen of the portal (/)
  • Patient enters their name only (phone is optional)
  • Selects a doctor and receives a serial number on screen
  • Can download a token image for reference

πŸ”§ Admin Panel (Filament)

  • Accessible at /admin with admin credentials
  • Full CRUD for Clinics, Doctors, Patients, and Appointments
  • Search, filter, and manage all records
  • Role and permission assignment per user

⌨️ Keyboard Shortcuts

All shortcuts are automatically disabled when the cursor is inside any <input> or <textarea>.

Login Screen

Key Action
V Open Visitor Booking form

Receptionist β€” Doctor Selection

Key Action
1 2 3 Select doctor by position in list
B / Esc Go back to portal
Q Sign out
T Toggle dark / light theme

Receptionist β€” Queue Management

Key Action
N Focus Name field (primary input)
F Focus Phone field (lookup)
S Focus Custom Serial field
R Register patient (normal queue)
E Register patient (emergency β€” front of queue)
B / Esc Back to doctor selection
Q Sign out
T Toggle theme

Doctor Dashboard β€” Queue Control

Key Action
1 2 3 Select doctor (on selection screen)
O Open queue (new day)
N Call next patient
C Complete current patient
S Skip current patient
P Pause / Resume queue
D Focus delay input
B / Esc Back to portal
Q Sign out
T Toggle theme

Visitor Booking

Key Action
1 2 3 Select doctor
N Focus name field
F Focus phone field
S Submit booking
B / Esc Back to login

πŸ“± PWA Support

CQMP is a fully installable Progressive Web App.

Feature Detail
Service Worker Workbox-generated via vite-plugin-pwa
Offline Caching All static assets cached; API calls are NetworkOnly
Auto-update New SW activates immediately (skipWaiting + clientsClaim)
App Shortcuts Long-press icon β†’ jump to Receptionist Desk or Doctor Dashboard
Theme Color Indigo #6366F1 (light) / Navy #0F172A (dark)

Install:

  • Android β†’ Chrome menu β†’ Add to Home Screen
  • iOS β†’ Safari Share β†’ Add to Home Screen
  • Desktop β†’ Chrome/Edge address bar β†’ install icon

πŸš€ Getting Started

Prerequisites

  • PHP 8.4.1+
  • Composer 2.x
  • Node.js 18+ with npm 9+
  • SQLite (bundled) or MySQL / PostgreSQL

⚑ Quick Start (All-in-One)

git clone https://github.com/beingmushfiq/cqmp.git
cd cqmp/backend

# Install deps, generate key, run migrations
composer setup

# Start all services in one terminal
composer dev

composer dev starts four processes concurrently with color-coded output:

Process Port Description
php artisan serve 8000 Laravel API
php artisan reverb:start 8080 WebSocket server
php artisan queue:listen β€” Background job worker
npm run dev (Vite) 5173 React frontend

πŸ”§ Manual Setup

# ── Backend ─────────────────────────────────────────────────────────
cd backend

composer install
php -r "file_exists('.env') || copy('.env.example', '.env');"
php artisan key:generate

# SQLite (default)
php -r "file_exists('database/database.sqlite') || touch('database/database.sqlite');"
php artisan migrate --seed

# Start each in a separate terminal
php artisan serve           # Terminal 1
php artisan reverb:start    # Terminal 2
php artisan queue:listen    # Terminal 3

# ── Frontend ────────────────────────────────────────────────────────
cd ../frontend
npm install
npm run dev

🌐 Access Points

URL Description
http://localhost:5173 React SPA β€” visitor booking portal
http://localhost:5173/tv TV Display (public, no login required)
http://localhost:5173/login Staff login page
http://localhost:8000/api/v1 REST API base
ws://localhost:8080 WebSocket server (Reverb)
http://localhost:8000/admin Filament Admin Panel

πŸ”‘ Default Dev Credentials

For local development only. Change all passwords before deploying.

Role Email Password
Receptionist receptionist@cqmp.local password
Doctor doctor@cqmp.local password
Admin admin@cqmp.local password

πŸ”§ Environment Variables

Backend (backend/.env)

APP_NAME="Clinic Queue Management Platform"
APP_ENV=local
APP_URL=http://localhost:8000

# CORS β€” allow the frontend origin
FRONTEND_URL=http://localhost:5173

# Database β€” SQLite (default) or switch to MySQL
DB_CONNECTION=sqlite
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=cqmp
# DB_USERNAME=root
# DB_PASSWORD=secret

# JWT authentication
JWT_SECRET=your-256-bit-secret-here
JWT_TTL=480   # Token lifetime in minutes (default: 8 hours)

# WebSocket via Laravel Reverb
BROADCAST_CONNECTION=reverb
REVERB_APP_ID=cqmp-local
REVERB_APP_KEY=cqmp-reverb-key
REVERB_APP_SECRET=cqmp-reverb-secret
REVERB_HOST=127.0.0.1
REVERB_PORT=8080
REVERB_SCHEME=http

# Passed to Vite (frontend reads these)
VITE_REVERB_APP_KEY="${REVERB_APP_KEY}"
VITE_REVERB_HOST="${REVERB_HOST}"
VITE_REVERB_PORT="${REVERB_PORT}"
VITE_REVERB_SCHEME="${REVERB_SCHEME}"

Frontend (frontend/.env)

VITE_API_URL=http://localhost:8000/api/v1
VITE_REVERB_APP_KEY=cqmp-reverb-key
VITE_REVERB_HOST=127.0.0.1
VITE_REVERB_PORT=8080
VITE_REVERB_SCHEME=http

πŸ” Authentication

CQMP uses a custom HS256 JWT implementation instead of Laravel Sanctum. This was chosen for shared-hosting compatibility β€” no session cookies, no database token table queries on every request.

How it works

Step Detail
Login POST /api/v1/login returns a signed JWT
Storage Token stored in localStorage as cqmp_token
Transport Sent as Authorization: Bearer {token} on every protected request
Validation JwtMiddleware verifies signature and expiry on each request
Expiry 8 hours by default (configurable via JWT_TTL)
Logout Token is discarded client-side; no server-side revocation needed

API clients (frontend/src/utils/api.ts)

The frontend maintains two separate Axios instances:

api          β†’ includes Bearer token, used for all staff/authenticated routes
publicApi    β†’ no token, used for /public/* endpoints and TV display

The api instance has a 401 interceptor that clears the token and redirects to /. The redirect is suppressed when on the /tv route, so the TV display remains visible even if a session expires.


πŸ§ͺ Running Tests

cd backend

# Run all tests
php artisan test

# Run only queue API tests
php artisan test --filter=QueueApiTest

# Verbose output
php artisan test --filter=QueueApiTest --verbose

Test Coverage

The QueueApiTest suite covers:

Test Case What It Verifies
Open queue Queue day created for a doctor
Walk-in registration Serial assigned, broadcast fired
Custom serial Patient placed at specified position
Emergency insert Patient jumps to front, others shift
Call next Correct priority + serial ordering
Skip patient Status changes, wait times recalc
Reinsert patient Moved to target position, all serials corrected
Queue freeze New walk-ins rejected with 422
Queue resume Walk-ins accepted again
Delete entry Removed from DB, recalc triggered

πŸ›‘ Admin Panel (Filament)

Visit http://localhost:8000/admin β€” log in with admin credentials.

Section Features
Clinics Create, edit, view clinic profiles
Doctors Assign to clinics, set consultation time, toggle availability
Patients Search, block / unblock, view history
Appointments Overview of all bookings and their current status

πŸ”¬ Core Business Logic β€” QueueEngine

The QueueEngine service is the heart of the system. All write operations run inside database transactions with row-level locking to prevent race conditions.

QueueEngine::openQueue()             // Start a new queue day (idempotent)
QueueEngine::createWalkIn()          // Atomic serial assignment with DB lock
QueueEngine::callNext()              // Highest priority β†’ lowest serial
QueueEngine::complete()              // Mark done, recalculate wait times
QueueEngine::skip()                  // Move to Skipped, recalculate
QueueEngine::reinsert()              // Move to target position, bump others
QueueEngine::insertEmergency()       // Front of queue, all others shift +1
QueueEngine::freeze()                // Block new walk-ins
QueueEngine::resume()                // Re-enable walk-ins
QueueEngine::recalculateWaitTimes()  // EWT = avg_time Γ— position + delay
QueueEngine::deleteItem()            // Remove and recalculate

🀝 Contributing

Contributions are welcome! Please follow these steps:

# 1. Fork and create a feature branch
git checkout -b feature/your-feature-name

# 2. Make changes β€” backend and frontend are separate directories

# 3. Run backend tests
cd backend && php artisan test

# 4. Type-check the frontend
cd frontend && npx tsc -b --noEmit

# 5. Format PHP code
./vendor/bin/pint

# 6. Open a pull request with a clear description

Code Standards

Language Tool Command
PHP Laravel Pint (PSR-12) ./vendor/bin/pint
TypeScript OxLint npm run lint
Commits Conventional Commits feat:, fix:, docs:, refactor:

πŸ“„ License

This project is licensed under the MIT License. See LICENSE for details.


Built with ❀️ for efficient, humane healthcare

Report a Bug Β· Request a Feature Β· Documentation

About

CQMP is a full-stack, real-time clinic queue management system that eliminates the chaos of paper-based patient queuing. It connects receptionists, doctors, and waiting patients through a live-updating dashboard, ensuring smooth patient flow, minimal wait-time confusion, and a professional clinic experience.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages