- Remove "Claim Status" from appointment context menu
- Rename "Eligibility Status" → "Check Eligibility"
- Check Eligibility now navigates to insurance-status page and auto-starts
the correct selenium flow based on the patient's stored insurance provider:
MassHealth 21+ → MH Eligibility & History
MassHealth <21 → CMSP Eligibility & History & Remaining
Delta Dental MA → DDMA selenium
Delta Dental Ins → Delta Ins selenium (OTP modal if needed)
United Healthcare SCO → United SCO selenium
DentaQuest/Tufts → Tufts SCO selenium
Commonwealth Care Alliance → CCA selenium
Unknown → scroll to Other provider checks section
- Add autoTrigger/onAutoTriggered props to all five button components
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The insuranceId format check on the backend rejected updates when the member ID
contained non-digit characters. Tagging insuranceProvider on the patient is not
required for selenium to run, so the calls are removed from handleMHSubmit,
handleMHPreAuth, and handleAddService.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Insurance Forms modal: split into Insurance Claim / PreAuth tabs
- PreAuth tab: same patient info + service lines, no toggle/direct combos
- Excluded Recalls & New Patients, Composite Fillings (Front/Back), Pedo from PreAuth combos
- Extractions: replaced Simple/Surg/Baby Teeth EXT with Full Bony EXT (D7240)
- MH PreAuth button: rewritten selenium worker to use masshealth-dental.org,
selects Dental Prior Authorization (2nd option), skips Date of Service field
- agent.py: convert pdf_path to pdf_url for /claim-pre-auth endpoint
- nginx + Express: raise body size limit to 50mb (fix 413 errors)
- DB schema: appointmentId optional on Claim, add preAuthNumber field, add PREAUTH status
- Backend: create PREAUTH claim record on preauth submit, save preAuthNumber on completion
- Claims table: add PreAuth No column (blue) next to Claim No
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Claims & Payments: save npiProviderId when submitting MH claim; sync between claim and payment on update
- Claims table: add Provider column showing rendering provider name
- Payments table: add Provider column + purple Commissioned badge on status
- Claim edit modal: add Rendering Provider dropdown (defaults to Mary Scannell)
- Payment edit modal: add Rendering Provider dropdown + Commissioned metadata display
- Reports page: add Provider filter dropdown (dynamic from NPI providers settings)
- Reports page: remove Collections by Doctor report type and Select Doctor dropdown
- Commission section: new section in reports page with date range + provider filter, shows eligible paid claims/payments per provider, multi-select checkboxes, Pay Commission modal with print + save, marks payments as commissioned so they are excluded from future cycles
- DB: add CommissionBatch and CommissionBatchItem tables; backfill Payment.npiProviderId from linked claims
- Backend: PATCH /api/payments/:id/provider syncs to linked claim; PUT /api/claims/:id syncs to linked payment; new /api/commissions routes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After the MH claim selenium job completes and the PDF is saved to the
database, immediately open the PdfPreviewModal so the user can view the
confirmation without navigating to the Documents page.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Socket.IO reconnects through Cloudflare proxy get a new socket.id, causing
the backend to emit job:update to a stale socket that no longer exists. The
PDF viewer modal never opened even though PDFs were saved successfully.
Adds a GET /api/insurance-status/job-status/:jobId endpoint backed by
InProcessQueue.getJob(), and a waitForSeleniumJob() helper on the frontend
that races socket events against HTTP polling every 3s. Whichever resolves
first wins, so local (socket) and external (Cloudflare) both work reliably.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the old after-hours/new-patient SVG with an accurate diagram of
the current conversation flow: 3-message TwiML entry → insurance check →
MassHealth consent + ID/DOB → Selenium eligibility → ACTIVE/INACTIVE paths,
inactive branch asks for other insurance then collects contact info, and
office-hours-validated date/time scheduling with appointment creation.
Existing patient branch shows same-insurance confirmation, MassHealth
auto-check (stored ID+DOB), and Selenium result paths.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add 3-message intro (self-intro → empathetic ack → new/existing question) via single TwiML response to guarantee delivery order
- Detect reschedule intent from first message; look up existing appointment date
- New patient flow: ask insurance type → MassHealth consent → member ID + DOB → Selenium eligibility check
- Post-eligibility: active → ask appointment date/time with office-hours validation; inactive → ask other insurance or collect contact info
- Date/time collection mirrors reschedule flow: check office day open, ask time, validate against office hours
- Auto-create appointment in schedule for known patients on confirmation; use first available staff member
- Add openPhoneReply toggle (Settings → AI Chat) to respond to any number at any time
- Add 5-minute inactivity timeout: reset conversation to initial stage and clear pending state
- Normalize MassHealth DOB to zero-padded MM/DD/YYYY before Selenium submission
- Expand isExistingPatient classifier to recognize "old patient", "old", "previous", "prior"
- Existing patient confirmation message now acknowledges patient type before asking about insurance
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add ChatbotButton component to top-app-bar (bot icon, upper right)
- Slide-in chat panel with 4 options: Check Eligibility, Schedule, Claims, Chat
- Single paste area accepts member ID + DOB in either order
- Age-based routing: ≥21 → MH Eligibility & History, <21 → CMSP auto-triggered
- Insurance-status page prefills fields and auto-fires the correct button via sessionStorage + custom event
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- reminder-graph: add stripIntroFromFallback() to remove 'Hi! My name is Lisa...'
from any saved rescheduleGreeting template before using it as the MSG 2 fallback
- reminder-graph: add explicit 'Do NOT introduce yourself' to rescheduleNode Gemini
prompt so the AI never adds its own intro to MSG 2
- ai-chat-templates-card: add hasIntroPattern() warning on the Reschedule Patients
template field — shows an amber alert if the saved template starts with a
self-introduction, guiding users to remove it
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- reminderSms default: remove {officeAddress} (never replaced by backend) to prevent
patients receiving literal '{officeAddress}' in reminder texts
- reminderGreeting default: fix typo 'reply you message' → 'reply to your message'
- rescheduleGreeting default: remove duplicate AI intro (intro is now sent separately
as MSG 1; fallback text should only contain the intent response)
- Add unsupportedVars() detector: highlights any {variable} in the SMS template that
the backend does not replace, with an amber warning showing the supported list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Member info is always present on the accumulator page regardless of
whether dollar amounts exist. Removing the button wait eliminates the
15s freeze for patients with no financial data.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of navigating back and forth, step4 opens the member details URL
in a new tab. Tab A clicks service history and CDP-prints the history PDF.
Tab B clicks View Accumulator and CDP-prints the accumulator PDF (waits
up to 15s for vm.hasResults, then captures whatever is on screen).
Eliminates the Chrome freeze from back-navigation and the empty-accumulator
race condition.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously waited up to 60s for vm.hasResults button causing long freeze.
Now caps at 15s then always proceeds — captures data or no-results state.
Extra 5s sleep ensures Angular finishes rendering table rows before CDP print.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
step7 now waits for button[ng-click='vm.printResults()'][ng-if='vm.hasResults']
to become visible before printing — that element only renders once Angular
has loaded the accumulator data, so the CDP capture is no longer racing
against the async data fetch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After clicking the member ID link, print the member details page via CDP
before navigating to service history. Adds member details as a panel in
the side-by-side PDF viewer: MH History shows 3 panels (eligibility,
member details, service history); CMSP shows 4 panels (eligibility,
member details, service history, accumulator).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add MH Eligibility & History button: runs full MH eligibility flow then
clicks member ID → service history, prints both PDFs via CDP, opens
dual side-by-side PDF modal (eligibility auto-downloads, history does not)
- Add CMSP Eligibility & History & Remaining button: same flow plus
navigates back to member details, clicks View Accumulator, prints
accumulator PDF via CDP; opens 3-panel side-by-side PDF modal
- Generalize DualPdfPreviewModal to accept panels[] array (works for 2 or 3 PDFs)
- Auto-download eligibility PDF via direct API URL to avoid Chrome Safe
Browsing pause on blob: URL downloads
- New backend processors, job types, and routes for both flows
- New Python Selenium workers with stable CSS selectors (ng-bind, href*)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Reschedule for Column button on schedule page with AI follow-up toggle (default on)
- Add POST /api/twilio/send-reschedule-batch — sends Reschedule by Office template, starts AI reschedule conversation per patient
- Add {officePhone} (office call-in number) and {twilioPhone} (SMS number) variable replacement in both batch endpoints
- Fix broken variable names in Reschedule by Office template ({office phone number) → {officePhone}, {Twilio phone number} → {twilioPhone})
- Fix reschedule-graph: when patient replies with date in same message as YES/NO (e.g. "ok, 5/18"), AI now checks day open and asks for time instead of asking "what day and time?"
- Fix twilio-webhooks: same date-shortcut logic for reminder flow — "no, 5/18" skips "when to reschedule?" and goes straight to day check
- Update LangGraph SVG: rename to Reminder & Reschedule Flow, combine both entry points (Reminder SMS + Reschedule SMS) into one diagram with date-shortcut annotations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add streetAddress/city/state/zipCode fields to OfficeContact (schema + storage + UI)
- Support {officeAddress} variable in batch reminder SMS
- Replace single SMS template field with full CRUD template list (add/rename/edit/delete)
- Store SMS template list under _sms_template_list; first template synced to batch reminder
- Hardcode all AI chat template defaults into codebase (reminder SMS, greetings, fallback)
- Add seed-templates.ts that auto-seeds default templates for all users on server boot
- Update README: note that templates are auto-configured on first boot
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the old ASAP/next-week/Mon-Tue-Wed/morning-afternoon subtree with
the new direct datetime flow:
- MSG 1: AI self-introduction (sent first via REST API)
- MSG 2: "When would you like to reschedule?" (intent response)
- Patient gives date → day-open check (office hours) with fail loop
- AI asks time → hours check (within AM/PM session) with fail loop
- AI confirms: "Just to confirm — [date at time]?"
- Patient YES/NO → slot availability check with fail loop
- DB: appointment moved, AI badge shown on schedule grid
- Patient thanks → closing message
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
time <= amEnd incorrectly allowed 12:00 when the office closes at 12:00.
Changed to time < amEnd (and time < pmEnd) so the session end time is
treated as a closed boundary — a patient cannot start an appointment at
exactly the time the session ends.
Fixes both the SMS reschedule flow and the schedule grid slot highlighting.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add movedByAi boolean column to Appointment table (default false)
- reschedule-graph: set movedByAi=true when AI moves an appointment
- PATCH /api/appointments/:id/confirm endpoint clears the movedByAi flag
- Schedule grid: show teal AI badge on appointment cards where movedByAi=true
- Right-click context menu: 'Manually Confirmed' removes the AI badge via the confirm endpoint
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Reminder flow: send AI self-introduction as message 1 (Twilio REST API), intent response as message 2 (TwiML) so intro always arrives first
- LangGraph reminder graph: classify yes/no/other from patient reply; 'no' now asks 'When would you like to reschedule?' directly
- Reschedule flow: new asked_reschedule_datetime stage replaces multi-step ASAP/next-week flow
- Date-only reply (e.g. '5/18'): ask for time separately, then confirm
- Date+time reply (e.g. '5/18 at 10am'): go straight to confirmation
- new asked_reschedule_time_for_date and asked_reschedule_confirm_datetime stages
- Date/time parsing: regex handles M/D and am/pm formats first; falls back to Gemini for natural language
- Day-level office hours check: if requested day is closed (e.g. Sunday), reply 'Our office is closed on [date]. Choose another day?'
- Time-level office hours check: if requested time is outside working hours (e.g. 12pm during lunch), reply with actual hours (e.g. '9:00 am – 12:00 pm and 1:00 pm – 5:00 pm')
- Slot availability check: verifies no conflicting appointment for same staff member
- After appointment confirmed: patient thank-you reply triggers warm closing with upcoming appointment time
- Schedule page: office hours summary bar above grid showing today's configured hours with link to settings
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Text Reminder for Column button with per-column checkboxes and AI follow-up toggle (default on)
- Batch reminder endpoint resolves {firstName}, {officeName}, {appointmentDate}, {appointmentTime} from AI chat templates
- Add Reschedule for Column UI (logic TBD)
- Move Download Claim PDF for Column below Reschedule for Column
- Add reminderSms template field to AI Chat Settings with variable hints
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Replace in-memory Maps in aiHandoffStore with DB-backed async functions
using new patient_conversation table (stage + aiHandoff per patient)
- Add afterHoursEnabled to ai_settings table (persists across restarts)
- Fix runtime crash in reschedule-graph: mon/tue/wed variables were out
of scope in the next-week fallback branch (ReferenceError)
- Wire rescheduleGreeting and generalFallback chat templates through to
LangGraph nodes so user-configured messages take effect
- Add otherNode to reminder-graph to handle unclassified patient replies
(e.g. "I want another appointment") and route to booking flow
- Fetch chatTemplates once per webhook request instead of per stage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When a member ID is typed on the insurance eligibility page, debounced
lookup fills in date of birth, first name, and last name if the patient
already exists in the database.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add floating chat window Hand-off to AI toggle (per-patient) and after-hours AI toggle (global)
- Add LangGraph-powered appointment reminder flow: AI introduces itself, classifies YES/NO, handles confirmation with appointment date/time
- Add multi-step rescheduling flow: ASAP vs next week, tomorrow offer, Mon/Tue/Wed picker, morning/afternoon time slot — automatically updates appointment in DB
- Add new patient / after-hours flow: new vs existing patient, dental insurance check, MassHealth Selenium eligibility check (auto-uses saved member ID + DOB for existing patients), self-pay fallback
- Add AI Chat Settings page (Settings → Advanced) with editable greeting templates and LangGraph flow diagrams for both reminder and new-patient flows
- Add Schedule a New Patient template option in chat window, starts new-patient conversation flow
- Add GET/PUT endpoints for AI handoff, after-hours handoff, and AI chat templates
- Add multilingual support (7 languages) across all AI reply nodes with LLM generation and hardcoded fallbacks
- Add pending reschedule in-memory store and conversation stage tracking across all flows
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Upload Payment Documents section with Extract & Download (Excel)
and Extract & Import (database) buttons
- PDF extractor (pdfplumber) parses MassHealth RA PDFs: two-pass
strategy joins summary-page ICN/patient map with detail-page
procedure data (CDT code, paid code, tooth, date, allowed amount)
- RA cover-page summary (Payee ID, RA #, Payment Amount, etc.)
included as separate Excel sheet; numeric values written as numbers
- Backend PDF import route groups rows by Member #, finds/creates
patient, creates Payment + ServiceLines with ICN per procedure
- Add icn, paidCode, allowedAmount fields to ServiceLine schema
- Payments table: status simplified to Paid in Full / Balance;
adjustment auto-computed on mhPaidAmount/copayment change;
Paid in Full and Revert buttons with confirmation dialogs
- Edit Payment modal: shows ICN, Paid Code, Allowed Amount per line
- PDF Import badge distinguishes from OCR imports in payments table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Status is now derived from the numbers — no manual switching needed:
- Balance = 0 → Paid in Full (teal)
- Balance > 0, Collected > 0 → Partially Paid (blue)
- Balance > 0, Collected = 0 → Pending (red)
VOID/DENIED/OVERPAID still show from DB as manual decisions.
Removed Revert Full Due button (tied to old status system).
Void button now shows for all non-VOID, non-DENIED payments.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Balance = totalBilled - mhPaidAmount - copayment - adjustment
Collected = mhPaidAmount + copayment (adjustment is a write-off)
- Frontend breakdown now shows Collected, Adjustment (if >0), and Balance
- Reports: totalCollected and totalOutstanding use the new formula
- Both date-range and staff summary queries updated
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added copayment and adjustment fields (Decimal, default 0) to Payment
model in schema and directly to DB via ALTER TABLE
- Added PATCH /api/payments/:id/copayment and /adjustment routes
- Added inline-editable Copayment and Adjustment columns after MH Paid
with same click-to-edit format; Copayment in blue, Adjustment in orange
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Switching from PAID to Pending/Partially Paid now calls revert-full-claim
first to undo the collected amount, then updates the status — so the
report totals stay accurate.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The inline status dropdown now handles this via pay-absolute-full-claim,
making the standalone button a duplicate.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Selecting "Paid in Full" from the inline status dropdown now calls
pay-absolute-full-claim (same as the Pay in Full button) instead of
only patching the status label.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Pending: yellow → light red
- Paid: green → teal, label updated to "Paid in Full"
- Status badge for Pending/Partially Paid/Paid is now a styled inline
dropdown to switch between the 3 statuses without opening a modal
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add PATCH /api/payments/:id/mh-paid-amount for direct value updates.
Clicking the MH Paid cell opens an input; Enter/blur saves and refreshes,
Escape cancels. Dash placeholder is also clickable to enter a value.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add selenium_MHPaymentCheckWorker.py: logs into MassHealth portal, navigates to Search Claims, enters claim number, extracts totalPaidAmount from results table
- Register /mh-payment-check endpoint in Selenium agent
- Add mhPaidAmount field to Payment model with migration
- Add PATCH /api/payments/:id/mh-payment-check backend route: fetches MH credentials, calls selenium, stores result
- Add Claim No. column (MassHealth claim number) as first data column in payments table
- Move Payment ID and Claim ID columns to end of table
- Add MH Paid column showing mhPaidAmount in green
- Wire Check MH Payment button to call API for each selected payment and refresh table
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Added Check Payments Online card above Payment's Records with From/To date pickers and Check All MH Payment button (logic TBD)
- Added multi-select checkboxes to each payment record row with select-all header checkbox
- When records are checked, a Check MH Payment action bar appears with count and clear option (logic TBD)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add Settings > Advanced > Procedure Duration/Time Slot page with three sections:
1. Procedure Duration: CDT codes with durations (editable table, save per section)
2. Doctor Time Slot: drag-to-block visual grid (A/B/C columns, 8 AM–9 PM, edit/delete slots)
3. Hygienist Time Slot: procedure descriptions with durations
- Backend: ProcedureTimeslot Prisma model, storage, and GET/PUT /api/procedure-timeslot route
- DB migration: procedure_timeslot table
- Appointment form: when type is "Other", show a free-text input for custom description; saved as other:<description> and decoded for display on the schedule card
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Time slots now run 8:00 AM – 9:00 PM (was 8:00 AM – 6:00 PM)
- Appointments visually span the correct number of 15-min rows based on startTime/endTime using HTML rowSpan
- Covered rows are skipped so the grid layout stays consistent
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Schedule columns default to labels A–F (localStorage, per-browser, click to rename)
- Settings → Advanced → Office Hours: configure Doctors (A-C) and Hygienists (D-F) AM/PM hours per weekday
- Gray out schedule slots outside office hours; override dialog for manual exceptions
- Override Office Hours toggle: select specific dates where all slots are open
- Fix appointment move: send only real DB fields to avoid Zod strict-mode rejection of computed fields (hasProcedures, hasClaimWithNumber)
- Fix backend PUT /appointments: safe error logging to prevent Prisma error crashing Node inspect
- Add OfficeHours Prisma model and GET/PUT /api/office-hours route
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix dateOfBirth default from empty string to null (caused Invalid date error)
- Add noValidate to form to prevent browser native email validation blocking submit
- Reset form when switching from edit to add mode
- Export API_BASE_URL from queryClient; switch patient table to raw fetch (prevents token wipe on 401)
- Add Authorization header forwarding in Vite proxy (was stripped by nginx Connection:upgrade)
- Make only firstName, lastName, dateOfBirth, phone required; gender optional
- Add +1 prefix to phone number input (stores as 1XXXXXXXXXX)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>