Skip to content

Changelog

All notable changes to ClinicFlow are documented here.

Format: Keep a Changelog


  • ES256 JWT — token signing algorithm migrated from HS256 to P-256 asymmetric (ES256). Public key registered with Supabase as a custom JWKS endpoint so the database can validate clinic claims directly in RLS policies without trusting the application layer.
  • Full RLS overhaul — all exposed tables now have policies that check the signed clinic_id JWT claim, live user/clinic active state, app role (admin/staff/system), and FK ownership of related rows. private.request_is_active(), private.request_is_admin(), and private.request_is_system() private functions centralize these checks.
  • Column-level privilege hardening — trigger-based enforce_user_update_scope prevents staff from escalating their own role or modifying protected columns.
  • Authorization boundary tighteningclinics_update RLS policy scoped to disallow cross-tenant mutations; staff self-edit guards added.
  • Dark mode — system-preference aware with manual override toggle in the topbar. All surfaces use CSS custom property tokens (--surface-*, --text-*).
  • Command palette (Cmd+K / Ctrl+K) — instant navigation to any dashboard page.
  • Notification drawer — shows unread WhatsApp-booked appointments with a live badge.
  • Onboarding checklist — guided setup flow for new clinics (WhatsApp, first doctor, first FAQ).
  • Quick-peek popover — hover or tap any appointment in the calendar to see patient details without opening the full modal.
  • Appointment reschedule flow — staff can move an appointment to a new date/time with a full availability re-check. Replaces the previous cancel-and-rebook workaround.
  • Radix UI modal system — all 14 dashboard modals migrated to Radix Dialog. Eliminates the class of scroll-lock and backdrop bugs present in the manual overlay approach.
  • OG imageog:image meta tag with a branded preview card for social sharing.
  • Print stylesheet — calendar and appointment list render cleanly when printed or exported to PDF.
  • Shared-phone patient identitypatients unique constraint changed from (clinic_id, phone) to (clinic_id, phone, full_name). Families sharing one WhatsApp number now get separate patient records, one per name. The chatbot asks “¿A nombre de quién?” on shared phones and scopes the booking/lookup to the confirmed name.
  • Premium UI overhaul — design token system (--brand, --surface-*, --text-*, spring easing, spotlight hover gradient), emerald rebrand (#00b96b), tighter radii (6–8px), component-level utility classes (.page-card, .cf-table, .pill, .modal-overlay, .cf-input).
  • Dental pages — full redesign with page-header component, pill badges for status, cf-table layout. Treatment Plans, Payments, and Recall Campaigns all updated.
  • Mobile calendar week view broken layout (React Big Calendar override).
  • Mobile calendar sticky gutter misalignment on scroll.
  • Mobile calendar grid scroll isolation (only the grid scrolls, not the toolbar).
  • Dark mode: dropdown text invisible, calendar event text clipped.
  • Session handling: sessionStorage key inconsistency on logout.
  • Attendance job: used doctor ID instead of full name in daily summary.
  • Post-polish regressions: z-index conflicts (command palette, notification drawer), sidebar tooltip crash on collapse, page-card spotlight invisible without stacking context.
  • waitlist table — deprecated; dropped in 20260611_drop_deprecated_tables.sql.
  • whatsapp_templates table — deprecated; dropped in same migration.
  • Redundant DB indexesidx_users_clinic_id duplicate, redundant index on invite_codes, redundant index on ls_orders.

  • Profile picture upload — Users upload JPEG/PNG/WebP from the profile modal. Stored in Supabase Storage (avatars bucket, public, 3 MB limit, MIME validated server-side). URL written to users.photo_url; displayed everywhere the initials avatar appears. POST /api/users/me/photo.
  • Email change — Self-service email update from profile modal with new/confirm accordion. Backend checks global uniqueness before writing.
  • migrate.js runner — Wraps schema.sql in a transaction, accepts SUPABASE_DB_URL (falls back to DATABASE_URL). Schema is now fully idempotent.
  • /health endpoint — Added alongside /healthz to match Railway health check config.
  • Profile name persistence — Login response returned fullName (camelCase) while all components read full_name (snake_case). Name appeared blank after every fresh login. Fixed in auth.js.
  • Profile modal centering.desktop-topbar backdrop-filter: blur(12px) created a new containing block for position: fixed descendants per CSS spec. Fixed via ReactDOM.createPortal into document.body.
  • WhatsApp rate limiter — Was silently a no-op: calling JSON.parse() on an already-parsed object, swallowing the error. Also had a path check always-truthy after Express mount prefix stripping. Now reads req.body directly.
  • Doctor creation blockedcreateDoctorSchema .strict() rejected phone field sent by the frontend. Every create/edit returned 400. Added phone: z.string().max(25).optional().
  • Staff role constraintusers.role DB CHECK allowed ('admin','receptionist') while code inserted 'staff'. Staff creation failed silently. Migrated to ('admin','staff').
  • Reminder column mismatch — Schema had reminder_2h_sent; code wrote reminder_1h_sent. Aligned to reminder_1h_sent.
  • Password minimum inconsistency — Backend PATCH /me validated < 8 chars; frontend and registration Zod require 12. All aligned to 12.
  • DB schema reproducibilityschema.sql was missing 4 tables and multiple columns. All DDL now idempotent (IF NOT EXISTS, DO/EXCEPTION policies, CREATE OR REPLACE TRIGGER, ALTER TABLE ADD COLUMN IF NOT EXISTS).
  • RLS docs — Docs incorrectly claimed backend sets app.clinic_id for RLS. Code uses service-role client (bypasses RLS). Docs corrected to describe actual app-layer enforcement.
  • Production startup guard: WHATSAPP_APP_SECRET + WHATSAPP_WEBHOOK_VERIFY_TOKEN required when NODE_ENV=production.
  • Tenant isolation: doctor lookups in availability.js and bookingFlow.js now scoped to clinicId. Chatbot booking aborts if doctor belongs to a different clinic.
  • Profile photo storage paths scoped to req.user.userId from signed JWT.
  • Profile modal: createPortal centering, 528px wide, gradient identity card header, email change accordion, mobile bottom sheet, 48px touch targets.
  • ProfileAvatar accepts photoUrl prop — renders <img objectFit:cover> when set, falls back to color initials.
  • RegisterPage password minimum updated to 12 chars.

  • User profile section — Avatar dropdown in top-right corner (desktop + mobile). Initials-based avatar with deterministic color from name hash.
  • Profile editing — Self-service name and password change via PATCH /api/users/me.
  • Quick user switching — Clinic staff can switch sessions without full logout/login (Clínica/Forever Free plans). User list from GET /api/users/switchable.
  • PWA forced updatecontrollerchange listener in SW registration; new deploys silently reload installed PWA instead of re-showing install prompt.
  • Sidebar footer user-info block removed; user identity now lives in profile dropdown.

  • Animated WhatsApp conversation in landing page hero section.
  • ESLint configuration (eslint.config.js) for frontend so npm run lint runs cleanly.
  • Accessibility pass (P0+P1): useDialog stabilization, trend query error logging, honest trend copy.
  • Mobile horizontal overflow on Billing plans and Analytics charts.
  • Unused Heart import removed from landing page.

  • Per-clinic AI context — Claude Haiku system prompt now injects clinic identity, doctor specialties, real working hours, and live availability slots. Prevents hallucinated appointment times.
  • Anti-fabrication rules added to chatbot prompt.
  • Contact info updated to clinicflow.lat channels (support, sales, info, billing).

  • WhatsApp self-signup onboarding — Clinics connect their own Meta Cloud API number from the Settings tab. Full OTP verification flow with self-heal for verifying → active state transitions.
  • Meta onboarding UI — WhatsApp tab in Settings (was previously orphaned).
  • WhatsApp migrated from Twilio to Meta Cloud API — Each clinic uses its own phone number directly through Meta Graph API. Twilio dependency removed entirely.
  • Per-clinic send adapter routes messages through correct Meta credentials.

  • In-dashboard subscription upgrade — Billing page links to LemonSqueezy checkout per plan. Webhook handler processes order_created events to upgrade clinic plan.

  • Conversational chatbot v2 (Haiku-driven) — Full Claude Haiku conversation engine replacing rule-based state machine. Handles booking, cancellation, and FAQ in natural language.
  • Booking confirmation interactive card (WhatsApp buttons: Confirm / Edit / Cancel).
  • Cancellation confirmation card.
  • Session store (chat_sessions table) for stateful conversations.
  • Concurrency lock — prevents duplicate bookings from double-tap.
  • Chatbot hardening: availability check before confirm, durable menu, short-answer routing.
  • Medium findings: history alternation, robust JSON parse, writer validation, serialization.

  • Test suite — 89 tests at launch, grown to 194. Covers auth, chatbot, WhatsApp, tenant isolation, subscription limits, encryption.
  • Security hardening: auth, tenancy, payments, crypto (L1–L5, M1–M5 audit findings).

  • Patient CSV import/export (Pro+ plans).
  • ICS calendar feeds — doctors get a shareable calendar URL for Google/Apple Calendar.
  • Analytics dashboard (Pro+ plans) — completion rates, no-shows, busy hours by day/time.
  • Superadmin panel — demo request triage, invite code management, clinic plan updates.

  • Initial launch: multi-tenant clinic SaaS.
  • Appointment management (create, update status, list with filters).
  • Doctor management with working hours, breaks, and vacations.
  • Patient records (create, search, block).
  • WhatsApp appointment notifications badge.
  • Subscription plan enforcement (Basic / Pro / Clínica / Trial / Forever Free).
  • JWT authentication with 8h expiry and subscription freshness checks.
  • BullMQ appointment reminders (24h + 2h via WhatsApp).
  • Daily agenda cron job (sent to staff every morning at 07:00 CST).
  • FAQ system with keyword matching.
  • PWA support with offline fallback page.