<ActionFilterPills/>ActionFilterPills
Dynamic pill row — All + every action that's actually in the log.
Audit logs, performance, settings, backups, suspension. The pages your team rarely opens — and that quietly carry every other page in the product. This is the unglamorous, load-bearing tier.
Parts 1 through 4 covered the surfaces clinical staff live on every day. Part 5 covers what supports them: the audit trail, the analytics, the settings, the backup. Boring on a good day. Critical on a bad one.
Doctor dashboard, reception dashboard, and the secondary pages that orbit them.
CoveredMulti-doctor admin, specialty routing, and per-doctor / per-receptionist screens in station mode.
CoveredAudit, analytics, settings, backups, suspension. Carries every page above without ever being the page.
You are hereTwo pages — Audit Logs and Chat Audit — make every sensitive action and every staff message reconstructable. Compliance asks 'what happened on April 18 at 3pm', and the answer is one filter pill away. The trail is permanent — there's no edit, no delete.
Numbered annotations on the mockup.
Action types are dynamic — pulled from what's actually in the data. 'All' is hardcoded as the first pill so reception always knows where 'unfiltered' lives.
destructive (delete), warning (edit), primary (state change), info (creation), success (login). Five categories, five colours, scannable in seconds.
Audit logs need to survive timezone shifts, daylight savings, and questions asked six months later. '14:32:07 yesterday' is a UX smell here.
Username is the canonical identity in audit. Email can change; usernames don't. dr-ahmed last year is dr-ahmed today.
Same source-of-truth concept (replayable record), different shape. Date-grouped feed with reply nesting. Live tail via WebSocket; filters apply in real time.
<ActionFilterPills/>Dynamic pill row — All + every action that's actually in the log.
<AuditTable/>Action · Message · User · When. Read-only, sortable, paginated.
<ChatAuditFeed/>Date-grouped message feed with reply nesting.
<AuditWriter (server-side)/>Writes one row per sensitive action across the entire app.
patient_deleted, queue_started, doctor_suspended — whatever you're chasing.
From/to date inputs above the table. Default is today.
Action · Message · User · ISO timestamp. Four fields, complete picture.
Improvement: CSV export for the regulator. Currently view-only.
There is no retention policy in the schema. If your jurisdiction requires one, add it as a scheduled job — but consult legal first. Most clinics keep the trail indefinitely.
Filters live in URL params. 'All patient_deleted entries last quarter' becomes a bookmark — same view, every time.
When something looks off in the action log, Chat Audit is where the human reasoning lives. 'Why was this patient removed?' often has an answer in the messages.
Improvement: 'patient_edited' currently shows a one-line message. A before/after diff would let admin see exactly what changed without opening the patient.
Performance, Medications, and (in solo) Financial share a layout: date-range presets, stat cards, charts. Different data, identical mental model. Open one, you've effectively learned all three.
Numbered annotations on the mockup.
Today · Yesterday · 7d · 30d · 90d · Custom. Custom expands to From/To inputs + Apply. The Apply button is critical — load doesn't fire on every keystroke.
Charts are for understanding, stats are for glancing. The stat row should answer 'is this period normal?' in 5 seconds. 8 cards is the upper bound.
Recharts and Chart.js add 100+ KB for what's mostly a single line and a pie. The product hand-rolls them — gradient fills, axes, hover circles, all in CSS-positioned SVG.
Top diagnoses, top medications — always ranked, always with counts and bars. Bar lengths are normalised within the list, so 'second most prescribed' is glanceable.
Solo doctor's Performance has no doctor selector — there's only one. Station's Performance defaults to 'All doctors' but lets admin scope to a single doctor.
<DateRangePresets/>Today · Yesterday · 7d · 30d · 90d · Custom — the same row across all three dashboards.
<StatCard/>Icon + label + tabular value + tone. Used everywhere data needs a header number.
<DailyTrendLine/>Custom SVG line with gradient fill — daily visits, prescriptions, or revenue.
<DonutChart (gender / visit type / active vs idle)/>Two- or three-segment donut with center label.
<TopNList/>Numbered ranking — top 10 diagnoses, top 10 medications.
<Sparkline (Medications table)/>Tiny per-row trend line in the Medications Analytics table.
Default Today is for live ops; review needs a window.
Visits, breakdown by type, avg duration, idle time — the period in eight numbers.
Line chart for volume, bar for top diagnoses, donut for type split.
One spike usually has one cause. Cross-reference Audit Logs if "what happened?" is the question.
Date range lives in URL. 'Last quarter, dr-ahmed only' becomes a bookmarked tab — open it monthly.
Station owners don't see /doctor/financial — billing in station mode is the admin's job, handled in /station/settings + external invoicing. Solo bundles the dashboard.
Improvement: every dashboard could ship a 'Export this view' button to CSV. Currently the data is reportable only by screenshot.
A "follow-up spike" can be flat revenue (often free or discounted). Always read the visit-type breakdown alongside the volume line.
If the top 10 diagnoses don't match what you think you treat, the chart is either teaching you something or revealing data-entry drift. Both are worth investigating.
Improvement: stat cards could surface a small ⚠ when the value is >2σ from the trailing 90d mean. Anomalies hide in plain sight today.
Settings is one form, five sections — clinic identity, working hours, queue flow, prescription footer, automatic backup. Consultation Reminder (solo only) is the WhatsApp follow-up template that fires N days after every completed examination. Both are 'edit rarely, depend everywhere'.
Numbered annotations on the mockup.
Tabs hide settings; sections expose them. The page is meant to be scanned end-to-end the first time, then jumped-to via Cmd+F.
Most clinics have one schedule; some have a different Friday. Defaults handle the common case; per-day overrides handle the exception without duplicating the form.
Two independent thresholds. 'Late' is a soft signal (warn reception); 'No-show' is a hard transition (auto-status flip). Separating them prevents 'just a bit late' from becoming a no-show.
Backup runs only if explicitly enabled. The clinic should know what's writing snapshots — silent automation is the wrong default for compliance-sensitive data.
Egypt and most of MyClinic's catchment use WhatsApp as the primary channel. Email reminders would have ~30% open rates; WhatsApp messages get read in minutes.
<SettingsForm/>One form, five collapsible sections, one Save.
<WorkingHoursEditor/>Day-of-week toggles + default start/end + optional per-day overrides.
<QueueFlowSettings/>Smart queue prioritisation toggle, auto-move, late tolerance, no-show after, reinsert-after-N.
<AutoBackupSettings/>Toggle + interval (minutes). Retains last 10 snapshots automatically.
<ReminderTemplateForm (solo)/>WhatsApp follow-up template with optional image.
Clinic name, doctor name, logo URL.
Working days + default hours + per-day overrides.
Late tolerance, no-show after, smart prioritisation.
Enable auto-backup, set the interval.
Changes apply immediately, no redeploy. This is by design — settings are operations, not configuration.
Tight thresholds create no-show inflation; loose thresholds let walk-ins block reception. Most clinics land at "late = 10 min, no-show = 30 min".
A two-sentence "hope you're well, reply if you'd like a follow-up" outperforms a six-sentence template. Patients reply to humans, not to forms.
Improvement: a follow-up examination needs a different message than a chronic-care follow-up. One template per visit_type would close that gap.
Improvement: clinic identity + reminder text could carry a per-language variant. Today they're a single string each.
Settings changes write to /audit-logs as edits. If a setting was changed 3 weeks ago, the trail tells you who and when.
The lifecycle layer covers what happens when accounts pause, when data needs to leave, and when the day before yesterday needs to come back. Suspension over deletion, snapshots over manual exports, soft-delete over hard. The recovery path always exists.
Download a complete JSON backup — patients, visits, prescriptions, medications, appointments, queue, attachments, and settings.
Restore data from a previously exported JSON file. Existing records with matching IDs are skipped — no overwrites.
deletedAt field. Deleting from the UI sets the timestamp; queries filter by where: { deletedAt: null }. Recovery is a SQL operation — intentional inconvenience for irreversible-feeling actions.Numbered annotations on the mockup.
Self-contained, human-readable, portable. SQL dumps are tied to a schema version; JSON is durable across migrations.
If a record with the same id exists, it's preserved. The import is additive — a safety guarantee that lets reception experiment without fear.
Every snapshot is a real file on disk, listed by name and size. No magical retention; admin can see what's there and how big.
Suspending dr-mona doesn't touch her past visits, her audit trail, or her patients. She can't log in; everything else continues.
Patients, visits, prescriptions all carry deletedAt. Queries filter by `where: { deletedAt: null }`. The data is recoverable; the UI just hides it.
<BackupExport/>One-click full-clinic export to JSON.
<BackupImport/>JSON file dropzone with validation.
<SnapshotList/>Filename · size · timestamp for every retained auto-snapshot.
<SuspensionFlow/>Confirm-then-suspend a doctor or reception account.
<ReactivationButton/>Flip a suspended user back to active.
<Soft-delete (Patient / Visit)/>`deletedAt` timestamp on the record; queries filter by null.
From /station/doctors → row → Suspend. Confirm.
Pinned items become invisible. Reception edits each pinned row's assignment to "Shared".
`doctor_suspended` lands in /audit-logs with the reason in the message field.
Suspended is a state, not a tombstone. Reactivation is one click whenever they return.
Deleting a doctor leaves orphan visits. Suspending preserves the trail. Deletion is for test accounts and typos only.
Backups are only as good as the restore. Take a snapshot, import it into a staging clinic, verify the patient count. Quarterly is the minimum.
Auto-snapshots live on the server's disk. For off-site backup, schedule a job to copy the latest snapshot to S3/Drive/your secure location.
Improvement: a built-in 'backup to S3-compatible bucket' option would close the off-site backup gap without scripting.
Improvement: "diff this snapshot against current state" would let admin see what's changed since a known-good point. Currently only re-import gives that signal.
JSON snapshots are sensitive — patient names, phones, diagnoses. Encrypt the storage volume; never copy snapshots into version control.
Five parts, every screen — from the consultation cockpit to the audit trail, from the routing model to the snapshot list. The product was designed to read in this order, but every page stands on its own. Bookmark the parts you need.