From e60110226b536078a281072dc7a7e3d8c8ae8920 Mon Sep 17 00:00:00 2001 From: Gitead Date: Fri, 3 Jul 2026 21:40:59 -0400 Subject: [PATCH] chore: remove all .jsx files from Frontend src Vite resolves .tsx before .jsx (per extensions order in vite.config.ts), so .jsx files were never loaded at runtime. Switching to TSX-only source. Co-Authored-By: Claude Sonnet 4.6 --- apps/Frontend/src/App.jsx | 74 - .../analytics/appointments-by-day.jsx | 58 - .../src/components/analytics/new-patients.jsx | 47 - .../appointment-procedures-dialog.jsx | 417 --- .../appointments/add-appointment-modal.jsx | 19 - .../appointments/appointment-form.jsx | 434 --- .../appointments/patient-status-badge.jsx | 34 - .../components/chart/lab-management-tab.jsx | 81 - .../src/components/chart/lab-rx-modal.jsx | 626 ----- .../src/components/chart/prescription-tab.jsx | 224 -- .../src/components/chart/teeth-chart.jsx | 155 -- .../components/chart/treatment-plan-tab.jsx | 202 -- .../claims/claim-document-upload-modal.jsx | 104 - .../components/claims/claim-edit-modal.jsx | 271 -- .../src/components/claims/claim-form.jsx | 2343 ----------------- .../components/claims/claim-view-modal.jsx | 272 -- .../claims/claims-of-patient-table.jsx | 52 - .../components/claims/claims-recent-table.jsx | 481 ---- .../src/components/claims/claims-ui.jsx | 58 - .../src/components/claims/tooth-ui.jsx | 161 -- .../components/cloud-storage/bread-crumb.jsx | 111 - .../cloud-storage/file-preview-modal.jsx | 210 -- .../cloud-storage/files-section.jsx | 463 ---- .../components/cloud-storage/folder-panel.jsx | 119 - .../cloud-storage/folder-section.jsx | 288 -- .../cloud-storage/new-folder-modal.jsx | 50 - .../recent-top-level-folder-modal.jsx | 244 -- .../components/cloud-storage/search-bar.jsx | 293 --- .../backup-destination-manager.jsx | 179 -- .../folder-browser-modal.jsx | 75 - .../import-database-section.jsx | 116 - .../network-backup-manager.jsx | 516 ---- .../documents/file-preview-modal.jsx | 221 -- .../file-upload/file-upload-zone.jsx | 195 -- .../file-upload/multiple-file-upload-zone.jsx | 326 --- .../insurance-status/bcbs-ma-button-modal.jsx | 212 -- .../insurance-status/cca-button-modal.jsx | 152 -- .../insurance-status/ddma-buton-modal.jsx | 327 --- .../deltains-button-modal.jsx | 270 -- .../dual-pdf-preview-modal.jsx | 157 -- .../insurance-status/pdf-preview-modal.jsx | 174 -- .../tufts-sco-button-modal.jsx | 267 -- .../united-sco-button-modal.jsx | 267 -- .../insurance/credentials-modal.jsx | 60 - .../src/components/layout/app-layout.jsx | 22 - .../src/components/layout/chatbot.jsx | 1070 -------- .../components/layout/notification-bell.jsx | 196 -- .../src/components/layout/sidebar.jsx | 302 --- .../src/components/layout/top-app-bar.jsx | 64 - .../patient-connection/dial-pad.jsx | 200 -- .../patient-connection/message-thread.jsx | 485 ---- .../patient-connection/sms-template-diaog.jsx | 157 -- .../components/patients/add-patient-modal.jsx | 107 - .../patients/patient-financial-modal.jsx | 265 -- .../src/components/patients/patient-form.jsx | 345 --- .../components/patients/patient-search.jsx | 181 -- .../src/components/patients/patient-table.jsx | 1190 --------- .../payments/payment-edit-modal.jsx | 811 ------ .../components/payments/payment-ocr-block.jsx | 318 --- .../payment-upload-documents-block.jsx | 216 -- .../payments/payments-of-patient-table.jsx | 90 - .../payments/payments-recent-table.jsx | 776 ------ .../procedure/procedure-combo-buttons.jsx | 140 - .../reports/collections-by-doctor-report.jsx | 232 -- .../components/reports/commission-section.jsx | 393 --- .../src/components/reports/export-button.jsx | 49 - .../reports/pagination-controls.jsx | 37 - .../reports/patients-balances-list.jsx | 77 - .../reports/patients-with-balance-report.jsx | 89 - .../src/components/reports/report-config.jsx | 107 - .../src/components/reports/summary-cards.jsx | 94 - .../components/settings/InsuranceCredForm.jsx | 119 - .../settings/ai-chat-settings-card.jsx | 1258 --------- .../settings/ai-chat-templates-card.jsx | 226 -- .../components/settings/ai-settings-card.jsx | 235 -- .../settings/insurance-contact-card.jsx | 197 -- .../settings/insuranceCredTable.jsx | 155 -- .../components/settings/npiProviderForm.jsx | 99 - .../components/settings/npiProviderTable.jsx | 162 -- .../settings/office-contact-card.jsx | 136 - .../components/settings/office-hours-card.jsx | 231 -- .../settings/procedure-timeslot-card.jsx | 550 ---- .../settings/program-bridge-table.jsx | 219 -- .../settings/selenium-settings-card.jsx | 106 - .../settings/twilio-settings-card.jsx | 123 - .../src/components/staffs/staff-form.jsx | 75 - .../src/components/staffs/staff-table.jsx | 161 -- .../src/components/ui/LoadingScreen.jsx | 12 - apps/Frontend/src/components/ui/accordion.jsx | 19 - .../src/components/ui/alert-dialog.jsx | 28 - apps/Frontend/src/components/ui/alert.jsx | 21 - .../src/components/ui/aspect-ratio.jsx | 3 - apps/Frontend/src/components/ui/avatar.jsx | 11 - apps/Frontend/src/components/ui/badge.jsx | 22 - .../Frontend/src/components/ui/breadcrumb.jsx | 27 - apps/Frontend/src/components/ui/button.jsx | 35 - apps/Frontend/src/components/ui/calendar.jsx | 44 - apps/Frontend/src/components/ui/card.jsx | 15 - apps/Frontend/src/components/ui/carousel.jsx | 109 - apps/Frontend/src/components/ui/chart.jsx | 164 -- apps/Frontend/src/components/ui/checkbox.jsx | 11 - .../src/components/ui/collapsible.jsx | 6 - apps/Frontend/src/components/ui/command.jsx | 36 - .../src/components/ui/confirmationDialog.jsx | 18 - .../src/components/ui/context-menu.jsx | 51 - .../Frontend/src/components/ui/data-table.jsx | 121 - apps/Frontend/src/components/ui/dateInput.jsx | 135 - .../src/components/ui/dateInputField.jsx | 13 - .../src/components/ui/deleteDialog.jsx | 29 - apps/Frontend/src/components/ui/dialog.jsx | 30 - apps/Frontend/src/components/ui/drawer.jsx | 28 - .../src/components/ui/dropdown-menu.jsx | 53 - apps/Frontend/src/components/ui/form.jsx | 68 - .../Frontend/src/components/ui/hover-card.jsx | 9 - apps/Frontend/src/components/ui/input-otp.jsx | 20 - apps/Frontend/src/components/ui/input.jsx | 7 - apps/Frontend/src/components/ui/label.jsx | 8 - apps/Frontend/src/components/ui/menubar.jsx | 64 - .../src/components/ui/navigation-menu.jsx | 33 - .../Frontend/src/components/ui/pagination.jsx | 33 - apps/Frontend/src/components/ui/popover.jsx | 10 - apps/Frontend/src/components/ui/progress.jsx | 9 - .../src/components/ui/radio-group.jsx | 17 - apps/Frontend/src/components/ui/resizable.jsx | 12 - .../src/components/ui/scroll-area.jsx | 18 - apps/Frontend/src/components/ui/select.jsx | 51 - .../components/ui/selenium-task-banner.jsx | 35 - apps/Frontend/src/components/ui/separator.jsx | 6 - apps/Frontend/src/components/ui/sheet.jsx | 45 - apps/Frontend/src/components/ui/sidebar.jsx | 246 -- apps/Frontend/src/components/ui/skeleton.jsx | 5 - apps/Frontend/src/components/ui/slider.jsx | 11 - apps/Frontend/src/components/ui/stat-card.jsx | 28 - apps/Frontend/src/components/ui/switch.jsx | 8 - apps/Frontend/src/components/ui/table.jsx | 21 - apps/Frontend/src/components/ui/tabs.jsx | 11 - apps/Frontend/src/components/ui/textarea.jsx | 7 - apps/Frontend/src/components/ui/toast.jsx | 34 - apps/Frontend/src/components/ui/toaster.jsx | 18 - .../src/components/ui/toggle-group.jsx | 26 - apps/Frontend/src/components/ui/toggle.jsx | 24 - apps/Frontend/src/components/ui/tooltip.jsx | 10 - apps/Frontend/src/hooks/use-auth.jsx | 144 - apps/Frontend/src/hooks/use-mobile.jsx | 15 - apps/Frontend/src/lib/protected-route.jsx | 17 - apps/Frontend/src/main.jsx | 5 - apps/Frontend/src/pages/activation-page.jsx | 144 - .../src/pages/ai-input-agent-page.jsx | 120 - apps/Frontend/src/pages/appointments-page.jsx | 1933 -------------- apps/Frontend/src/pages/auth-page.jsx | 164 -- apps/Frontend/src/pages/chart-page.jsx | 99 - apps/Frontend/src/pages/claims-page.jsx | 1221 --------- .../Frontend/src/pages/cloud-storage-page.jsx | 147 -- apps/Frontend/src/pages/dashboard.jsx | 286 -- .../src/pages/database-management-page.jsx | 284 -- .../pages/dental-shopping-login-info-page.jsx | 208 -- .../pages/dental-shopping-search-tag-page.jsx | 24 - apps/Frontend/src/pages/documents-page.jsx | 550 ---- .../src/pages/insurance-status-page.jsx | 1164 -------- apps/Frontend/src/pages/job-monitor-page.jsx | 251 -- apps/Frontend/src/pages/not-found.jsx | 18 - .../src/pages/patient-connection-page.jsx | 298 --- apps/Frontend/src/pages/patients-page.jsx | 438 --- apps/Frontend/src/pages/payments-page.jsx | 289 -- apps/Frontend/src/pages/reports-page.jsx | 53 - apps/Frontend/src/pages/settings-page.jsx | 262 -- packages/db/prisma.config.js.bak | 15 + 167 files changed, 15 insertions(+), 33539 deletions(-) delete mode 100755 apps/Frontend/src/App.jsx delete mode 100644 apps/Frontend/src/components/analytics/appointments-by-day.jsx delete mode 100644 apps/Frontend/src/components/analytics/new-patients.jsx delete mode 100644 apps/Frontend/src/components/appointment-procedures/appointment-procedures-dialog.jsx delete mode 100644 apps/Frontend/src/components/appointments/add-appointment-modal.jsx delete mode 100644 apps/Frontend/src/components/appointments/appointment-form.jsx delete mode 100644 apps/Frontend/src/components/appointments/patient-status-badge.jsx delete mode 100644 apps/Frontend/src/components/chart/lab-management-tab.jsx delete mode 100644 apps/Frontend/src/components/chart/lab-rx-modal.jsx delete mode 100644 apps/Frontend/src/components/chart/prescription-tab.jsx delete mode 100644 apps/Frontend/src/components/chart/teeth-chart.jsx delete mode 100644 apps/Frontend/src/components/chart/treatment-plan-tab.jsx delete mode 100644 apps/Frontend/src/components/claims/claim-document-upload-modal.jsx delete mode 100644 apps/Frontend/src/components/claims/claim-edit-modal.jsx delete mode 100644 apps/Frontend/src/components/claims/claim-form.jsx delete mode 100644 apps/Frontend/src/components/claims/claim-view-modal.jsx delete mode 100644 apps/Frontend/src/components/claims/claims-of-patient-table.jsx delete mode 100644 apps/Frontend/src/components/claims/claims-recent-table.jsx delete mode 100644 apps/Frontend/src/components/claims/claims-ui.jsx delete mode 100644 apps/Frontend/src/components/claims/tooth-ui.jsx delete mode 100644 apps/Frontend/src/components/cloud-storage/bread-crumb.jsx delete mode 100644 apps/Frontend/src/components/cloud-storage/file-preview-modal.jsx delete mode 100644 apps/Frontend/src/components/cloud-storage/files-section.jsx delete mode 100644 apps/Frontend/src/components/cloud-storage/folder-panel.jsx delete mode 100644 apps/Frontend/src/components/cloud-storage/folder-section.jsx delete mode 100644 apps/Frontend/src/components/cloud-storage/new-folder-modal.jsx delete mode 100644 apps/Frontend/src/components/cloud-storage/recent-top-level-folder-modal.jsx delete mode 100644 apps/Frontend/src/components/cloud-storage/search-bar.jsx delete mode 100644 apps/Frontend/src/components/database-management/backup-destination-manager.jsx delete mode 100644 apps/Frontend/src/components/database-management/folder-browser-modal.jsx delete mode 100644 apps/Frontend/src/components/database-management/import-database-section.jsx delete mode 100644 apps/Frontend/src/components/database-management/network-backup-manager.jsx delete mode 100644 apps/Frontend/src/components/documents/file-preview-modal.jsx delete mode 100644 apps/Frontend/src/components/file-upload/file-upload-zone.jsx delete mode 100644 apps/Frontend/src/components/file-upload/multiple-file-upload-zone.jsx delete mode 100644 apps/Frontend/src/components/insurance-status/bcbs-ma-button-modal.jsx delete mode 100644 apps/Frontend/src/components/insurance-status/cca-button-modal.jsx delete mode 100644 apps/Frontend/src/components/insurance-status/ddma-buton-modal.jsx delete mode 100644 apps/Frontend/src/components/insurance-status/deltains-button-modal.jsx delete mode 100644 apps/Frontend/src/components/insurance-status/dual-pdf-preview-modal.jsx delete mode 100644 apps/Frontend/src/components/insurance-status/pdf-preview-modal.jsx delete mode 100644 apps/Frontend/src/components/insurance-status/tufts-sco-button-modal.jsx delete mode 100644 apps/Frontend/src/components/insurance-status/united-sco-button-modal.jsx delete mode 100644 apps/Frontend/src/components/insurance/credentials-modal.jsx delete mode 100644 apps/Frontend/src/components/layout/app-layout.jsx delete mode 100644 apps/Frontend/src/components/layout/chatbot.jsx delete mode 100644 apps/Frontend/src/components/layout/notification-bell.jsx delete mode 100644 apps/Frontend/src/components/layout/sidebar.jsx delete mode 100644 apps/Frontend/src/components/layout/top-app-bar.jsx delete mode 100644 apps/Frontend/src/components/patient-connection/dial-pad.jsx delete mode 100644 apps/Frontend/src/components/patient-connection/message-thread.jsx delete mode 100644 apps/Frontend/src/components/patient-connection/sms-template-diaog.jsx delete mode 100644 apps/Frontend/src/components/patients/add-patient-modal.jsx delete mode 100644 apps/Frontend/src/components/patients/patient-financial-modal.jsx delete mode 100644 apps/Frontend/src/components/patients/patient-form.jsx delete mode 100644 apps/Frontend/src/components/patients/patient-search.jsx delete mode 100644 apps/Frontend/src/components/patients/patient-table.jsx delete mode 100644 apps/Frontend/src/components/payments/payment-edit-modal.jsx delete mode 100644 apps/Frontend/src/components/payments/payment-ocr-block.jsx delete mode 100644 apps/Frontend/src/components/payments/payment-upload-documents-block.jsx delete mode 100644 apps/Frontend/src/components/payments/payments-of-patient-table.jsx delete mode 100644 apps/Frontend/src/components/payments/payments-recent-table.jsx delete mode 100644 apps/Frontend/src/components/procedure/procedure-combo-buttons.jsx delete mode 100644 apps/Frontend/src/components/reports/collections-by-doctor-report.jsx delete mode 100644 apps/Frontend/src/components/reports/commission-section.jsx delete mode 100644 apps/Frontend/src/components/reports/export-button.jsx delete mode 100644 apps/Frontend/src/components/reports/pagination-controls.jsx delete mode 100644 apps/Frontend/src/components/reports/patients-balances-list.jsx delete mode 100644 apps/Frontend/src/components/reports/patients-with-balance-report.jsx delete mode 100644 apps/Frontend/src/components/reports/report-config.jsx delete mode 100644 apps/Frontend/src/components/reports/summary-cards.jsx delete mode 100644 apps/Frontend/src/components/settings/InsuranceCredForm.jsx delete mode 100644 apps/Frontend/src/components/settings/ai-chat-settings-card.jsx delete mode 100644 apps/Frontend/src/components/settings/ai-chat-templates-card.jsx delete mode 100644 apps/Frontend/src/components/settings/ai-settings-card.jsx delete mode 100644 apps/Frontend/src/components/settings/insurance-contact-card.jsx delete mode 100644 apps/Frontend/src/components/settings/insuranceCredTable.jsx delete mode 100644 apps/Frontend/src/components/settings/npiProviderForm.jsx delete mode 100644 apps/Frontend/src/components/settings/npiProviderTable.jsx delete mode 100644 apps/Frontend/src/components/settings/office-contact-card.jsx delete mode 100644 apps/Frontend/src/components/settings/office-hours-card.jsx delete mode 100644 apps/Frontend/src/components/settings/procedure-timeslot-card.jsx delete mode 100644 apps/Frontend/src/components/settings/program-bridge-table.jsx delete mode 100644 apps/Frontend/src/components/settings/selenium-settings-card.jsx delete mode 100644 apps/Frontend/src/components/settings/twilio-settings-card.jsx delete mode 100644 apps/Frontend/src/components/staffs/staff-form.jsx delete mode 100644 apps/Frontend/src/components/staffs/staff-table.jsx delete mode 100644 apps/Frontend/src/components/ui/LoadingScreen.jsx delete mode 100644 apps/Frontend/src/components/ui/accordion.jsx delete mode 100644 apps/Frontend/src/components/ui/alert-dialog.jsx delete mode 100644 apps/Frontend/src/components/ui/alert.jsx delete mode 100644 apps/Frontend/src/components/ui/aspect-ratio.jsx delete mode 100644 apps/Frontend/src/components/ui/avatar.jsx delete mode 100644 apps/Frontend/src/components/ui/badge.jsx delete mode 100644 apps/Frontend/src/components/ui/breadcrumb.jsx delete mode 100644 apps/Frontend/src/components/ui/button.jsx delete mode 100644 apps/Frontend/src/components/ui/calendar.jsx delete mode 100644 apps/Frontend/src/components/ui/card.jsx delete mode 100644 apps/Frontend/src/components/ui/carousel.jsx delete mode 100644 apps/Frontend/src/components/ui/chart.jsx delete mode 100644 apps/Frontend/src/components/ui/checkbox.jsx delete mode 100644 apps/Frontend/src/components/ui/collapsible.jsx delete mode 100644 apps/Frontend/src/components/ui/command.jsx delete mode 100644 apps/Frontend/src/components/ui/confirmationDialog.jsx delete mode 100644 apps/Frontend/src/components/ui/context-menu.jsx delete mode 100644 apps/Frontend/src/components/ui/data-table.jsx delete mode 100644 apps/Frontend/src/components/ui/dateInput.jsx delete mode 100644 apps/Frontend/src/components/ui/dateInputField.jsx delete mode 100644 apps/Frontend/src/components/ui/deleteDialog.jsx delete mode 100644 apps/Frontend/src/components/ui/dialog.jsx delete mode 100644 apps/Frontend/src/components/ui/drawer.jsx delete mode 100644 apps/Frontend/src/components/ui/dropdown-menu.jsx delete mode 100644 apps/Frontend/src/components/ui/form.jsx delete mode 100644 apps/Frontend/src/components/ui/hover-card.jsx delete mode 100644 apps/Frontend/src/components/ui/input-otp.jsx delete mode 100644 apps/Frontend/src/components/ui/input.jsx delete mode 100644 apps/Frontend/src/components/ui/label.jsx delete mode 100644 apps/Frontend/src/components/ui/menubar.jsx delete mode 100644 apps/Frontend/src/components/ui/navigation-menu.jsx delete mode 100644 apps/Frontend/src/components/ui/pagination.jsx delete mode 100644 apps/Frontend/src/components/ui/popover.jsx delete mode 100644 apps/Frontend/src/components/ui/progress.jsx delete mode 100644 apps/Frontend/src/components/ui/radio-group.jsx delete mode 100644 apps/Frontend/src/components/ui/resizable.jsx delete mode 100644 apps/Frontend/src/components/ui/scroll-area.jsx delete mode 100644 apps/Frontend/src/components/ui/select.jsx delete mode 100644 apps/Frontend/src/components/ui/selenium-task-banner.jsx delete mode 100644 apps/Frontend/src/components/ui/separator.jsx delete mode 100644 apps/Frontend/src/components/ui/sheet.jsx delete mode 100644 apps/Frontend/src/components/ui/sidebar.jsx delete mode 100644 apps/Frontend/src/components/ui/skeleton.jsx delete mode 100644 apps/Frontend/src/components/ui/slider.jsx delete mode 100644 apps/Frontend/src/components/ui/stat-card.jsx delete mode 100644 apps/Frontend/src/components/ui/switch.jsx delete mode 100644 apps/Frontend/src/components/ui/table.jsx delete mode 100644 apps/Frontend/src/components/ui/tabs.jsx delete mode 100644 apps/Frontend/src/components/ui/textarea.jsx delete mode 100644 apps/Frontend/src/components/ui/toast.jsx delete mode 100644 apps/Frontend/src/components/ui/toaster.jsx delete mode 100644 apps/Frontend/src/components/ui/toggle-group.jsx delete mode 100644 apps/Frontend/src/components/ui/toggle.jsx delete mode 100644 apps/Frontend/src/components/ui/tooltip.jsx delete mode 100644 apps/Frontend/src/hooks/use-auth.jsx delete mode 100644 apps/Frontend/src/hooks/use-mobile.jsx delete mode 100644 apps/Frontend/src/lib/protected-route.jsx delete mode 100644 apps/Frontend/src/main.jsx delete mode 100644 apps/Frontend/src/pages/activation-page.jsx delete mode 100644 apps/Frontend/src/pages/ai-input-agent-page.jsx delete mode 100644 apps/Frontend/src/pages/appointments-page.jsx delete mode 100644 apps/Frontend/src/pages/auth-page.jsx delete mode 100644 apps/Frontend/src/pages/chart-page.jsx delete mode 100644 apps/Frontend/src/pages/claims-page.jsx delete mode 100644 apps/Frontend/src/pages/cloud-storage-page.jsx delete mode 100644 apps/Frontend/src/pages/dashboard.jsx delete mode 100644 apps/Frontend/src/pages/database-management-page.jsx delete mode 100644 apps/Frontend/src/pages/dental-shopping-login-info-page.jsx delete mode 100644 apps/Frontend/src/pages/dental-shopping-search-tag-page.jsx delete mode 100644 apps/Frontend/src/pages/documents-page.jsx delete mode 100644 apps/Frontend/src/pages/insurance-status-page.jsx delete mode 100644 apps/Frontend/src/pages/job-monitor-page.jsx delete mode 100644 apps/Frontend/src/pages/not-found.jsx delete mode 100644 apps/Frontend/src/pages/patient-connection-page.jsx delete mode 100644 apps/Frontend/src/pages/patients-page.jsx delete mode 100644 apps/Frontend/src/pages/payments-page.jsx delete mode 100644 apps/Frontend/src/pages/reports-page.jsx delete mode 100644 apps/Frontend/src/pages/settings-page.jsx create mode 100644 packages/db/prisma.config.js.bak diff --git a/apps/Frontend/src/App.jsx b/apps/Frontend/src/App.jsx deleted file mode 100755 index 0a259b16..00000000 --- a/apps/Frontend/src/App.jsx +++ /dev/null @@ -1,74 +0,0 @@ -import { Switch, Route, Redirect } from "wouter"; -import React, { lazy, Suspense } from "react"; -import { Provider } from "react-redux"; -import { store } from "./redux/store"; -import { queryClient } from "./lib/queryClient"; -import { QueryClientProvider } from "@tanstack/react-query"; -import { Toaster } from "./components/ui/toaster"; -import { TooltipProvider } from "./components/ui/tooltip"; -import { ProtectedRoute } from "./lib/protected-route"; -import { AuthProvider } from "./hooks/use-auth"; -import Dashboard from "./pages/dashboard"; -import LoadingScreen from "./components/ui/LoadingScreen"; -const AuthPage = lazy(() => import("./pages/auth-page")); -const PatientConnectionPage = lazy(() => import("./pages/patient-connection-page")); -const AppointmentsPage = lazy(() => import("./pages/appointments-page")); -const PatientsPage = lazy(() => import("./pages/patients-page")); -const SettingsPage = lazy(() => import("./pages/settings-page")); -const ClaimsPage = lazy(() => import("./pages/claims-page")); -const PaymentsPage = lazy(() => import("./pages/payments-page")); -const InsuranceStatusPage = lazy(() => import("./pages/insurance-status-page")); -const DocumentPage = lazy(() => import("./pages/documents-page")); -const DatabaseManagementPage = lazy(() => import("./pages/database-management-page")); -const ReportsPage = lazy(() => import("./pages/reports-page")); -const CloudStoragePage = lazy(() => import("./pages/cloud-storage-page")); -const JobMonitorPage = lazy(() => import("./pages/job-monitor-page")); -const ChartPage = lazy(() => import("./pages/chart-page")); -const AiInputAgentPage = lazy(() => import("./pages/ai-input-agent-page")); -const DentalShoppingSearchTagPage = lazy(() => import("./pages/dental-shopping-search-tag-page")); -const DentalShoppingLoginInfoPage = lazy(() => import("./pages/dental-shopping-login-info-page")); -const ActivationPage = lazy(() => import("./pages/activation-page")); -const NotFound = lazy(() => import("./pages/not-found")); -function Router() { - return ( - }/> - - }/> - }/> - }/> - }/> - }/> - }/> - } adminOnly/> - } adminOnly/> - }/> - }/> - }/> - }/> - } adminOnly/> - }/> - }/> - }/> - }/> - }/> - } adminOnly/> - } adminOnly/> - }/> - }/> - ); -} -function App() { - return ( - - - - - }> - - - - - - ); -} -export default App; diff --git a/apps/Frontend/src/components/analytics/appointments-by-day.jsx b/apps/Frontend/src/components/analytics/appointments-by-day.jsx deleted file mode 100644 index e1bc2f24..00000000 --- a/apps/Frontend/src/components/analytics/appointments-by-day.jsx +++ /dev/null @@ -1,58 +0,0 @@ -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { BarChart, Bar, XAxis, YAxis, CartesianGrid, ResponsiveContainer, Tooltip, } from "recharts"; -export function AppointmentsByDay({ appointments }) { - const daysOfWeek = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]; - const countsByDay = daysOfWeek.map((day) => ({ day, count: 0 })); - // Get current date and set time to start of day (midnight) - const now = new Date(); - now.setHours(0, 0, 0, 0); - // Calculate Monday of the current week - const day = now.getDay(); // 0 = Sunday, 1 = Monday, ... - const diffToMonday = day === 0 ? -6 : 1 - day; // adjust if Sunday - const monday = new Date(now); - monday.setDate(now.getDate() + diffToMonday); - // Sunday of the current week - const sunday = new Date(monday); - sunday.setDate(monday.getDate() + 6); - // Filter appointments only from this week (Monday to Sunday) - const appointmentsThisWeek = appointments.filter((appointment) => { - if (!appointment.date) - return false; - const date = new Date(appointment.date); - // Reset time to compare just the date - date.setHours(0, 0, 0, 0); - return date >= monday && date <= sunday; - }); - // Count appointments by day for current week - appointmentsThisWeek.forEach((appointment) => { - const date = new Date(appointment.date); - const dayOfWeek = date.getDay(); // 0 = Sunday, 1 = Monday, ... - const dayIndex = dayOfWeek === 0 ? 6 : dayOfWeek - 1; // Monday=0, Sunday=6 - if (countsByDay[dayIndex]) { - countsByDay[dayIndex].count += 1; - } - }); - return ( - - - Appointments by Day - -

- Distribution of appointments throughout the week -

-
- -
- - - - - - [`${value} appointments`, "Count"]} labelFormatter={(value) => `${value}`}/> - - - -
-
-
); -} diff --git a/apps/Frontend/src/components/analytics/new-patients.jsx b/apps/Frontend/src/components/analytics/new-patients.jsx deleted file mode 100644 index 382a623b..00000000 --- a/apps/Frontend/src/components/analytics/new-patients.jsx +++ /dev/null @@ -1,47 +0,0 @@ -import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; -import { LineChart, Line, XAxis, YAxis, CartesianGrid, ResponsiveContainer, Tooltip } from "recharts"; -export function NewPatients({ patients }) { - // Get months for the chart - const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; - // Process patient data by registration month - const patientsByMonth = months.map(month => ({ name: month, count: 0 })); - // Count new patients by month - patients.forEach(patient => { - const createdDate = new Date(patient.createdAt); - const monthIndex = createdDate.getMonth(); - if (patientsByMonth[monthIndex]) { - patientsByMonth[monthIndex].count += 1; - } - }); - // Add some sample data for visual effect if no patients - if (patients.length === 0) { - // Sample data pattern similar to the screenshot - const sampleData = [17, 12, 22, 16, 15, 17, 22, 28, 20, 16]; - sampleData.forEach((value, index) => { - if (index < patientsByMonth.length) { - if (patientsByMonth[index]) { - patientsByMonth[index].count = value; - } - } - }); - } - return ( - - New Patients -

Monthly trend of new patient registrations

-
- -
- - - - - - [`${value} patients`, "Count"]} labelFormatter={(value) => `${value}`}/> - - - -
-
-
); -} diff --git a/apps/Frontend/src/components/appointment-procedures/appointment-procedures-dialog.jsx b/apps/Frontend/src/components/appointment-procedures/appointment-procedures-dialog.jsx deleted file mode 100644 index 9d73f3e2..00000000 --- a/apps/Frontend/src/components/appointment-procedures/appointment-procedures-dialog.jsx +++ /dev/null @@ -1,417 +0,0 @@ -import { useState, useEffect, useRef } from "react"; -import { useQuery, useMutation } from "@tanstack/react-query"; -import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; -import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; -import { Button } from "@/components/ui/button"; -import { Input } from "@/components/ui/input"; -import { Label } from "@/components/ui/label"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { Trash2, Plus, Save, X } from "lucide-react"; -import { apiRequest, queryClient } from "@/lib/queryClient"; -import { useToast } from "@/hooks/use-toast"; -import { PROCEDURE_COMBOS } from "@/utils/procedureCombos"; -import { findPriceMismatches, } from "@/utils/procedureCombosMapping"; -import { useLocation } from "wouter"; -import { DeleteConfirmationDialog } from "../ui/deleteDialog"; -import { DirectComboButtons, RegularComboButtons, } from "@/components/procedure/procedure-combo-buttons"; -export function AppointmentProceduresDialog({ open, onOpenChange, appointmentId, patientId, patient, serviceDate, }) { - const { toast } = useToast(); - const [, setLocation] = useLocation(); - // NPI provider state — stored per-appointment on the procedure rows - const [selectedNpiProviderId, setSelectedNpiProviderId] = useState(null); - const emptyRow = () => ({ code: "", label: "", fee: "", tooth: "", surface: "" }); - const [pendingRows, setPendingRows] = useState([emptyRow(), emptyRow(), emptyRow()]); - // reset pending rows when dialog opens - useEffect(() => { - if (open) - setPendingRows([emptyRow(), emptyRow(), emptyRow()]); - }, [open]); - // inline edit state - const [editingId, setEditingId] = useState(null); - const [editRow, setEditRow] = useState({}); - const [clearAllOpen, setClearAllOpen] = useState(false); - // price mismatch dialog - const [priceMismatches, setPriceMismatches] = useState([]); - const pendingAction = useRef(null); - const deriveInsuranceSiteKey = (provider) => { - const p = (provider || "").toLowerCase().trim(); - if (!p) - return ""; - if (p.includes("masshealth") || p === "mh" || p === "mass health") - return "MH"; - if (p.includes("commonwealth care alliance") || p === "cca") - return "CCA"; - if (p.includes("ddma") || p.includes("delta dental ma")) - return "DDMA"; - if (p.includes("tufts") || p.includes("dentaquest") || p === "tuftssco") - return "TuftsSCO"; - if ((p.includes("united") && p.includes("sco")) || p === "unitedsco") - return "UnitedSCO"; - return ""; - }; - const runWithPriceCheck = (procedureCode, fee, action) => { - const siteKey = deriveInsuranceSiteKey(patient?.insuranceProvider); - if (!siteKey || !procedureCode.trim() || !fee) { - action(); - return; - } - const mismatches = findPriceMismatches([{ procedureCode, totalBilled: fee, procedureDate: "" }], siteKey, patient?.dateOfBirth || "", serviceDate ?? new Date().toISOString().slice(0, 10)); - if (mismatches.length === 0) { - action(); - } - else { - pendingAction.current = action; - setPriceMismatches(mismatches); - } - }; - const savePricesToSchedule = async (mismatches) => { - const siteKey = deriveInsuranceSiteKey(patient?.insuranceProvider); - await Promise.all(mismatches.map(m => apiRequest("POST", "/api/fee-schedule/update-price", { - siteKey, - procedureCode: m.procedureCode, - price: m.enteredPrice, - }))); - }; - // ── NPI Providers ────────────────────────────────────────────── - const { data: npiProviders = [] } = useQuery({ - queryKey: ["/api/npiProviders/"], - queryFn: async () => { - const res = await apiRequest("GET", "/api/npiProviders/"); - if (!res.ok) - throw new Error("Failed to fetch NPI providers"); - return res.json(); - }, - enabled: open, - }); - // ── Procedures ───────────────────────────────────────────────── - const { data: procedures = [], isLoading } = useQuery({ - queryKey: ["appointment-procedures", appointmentId], - queryFn: async () => { - const res = await apiRequest("GET", `/api/appointment-procedures/${appointmentId}`); - if (!res.ok) - throw new Error("Failed to load procedures"); - return res.json(); - }, - enabled: open && !!appointmentId, - }); - // Sync NPI provider from saved procedures when they load - useEffect(() => { - if (!procedures.length) - return; - const saved = procedures[0]?.npiProviderId ?? null; - if (saved != null) - setSelectedNpiProviderId(Number(saved)); - }, [procedures]); - // Default NPI provider to Mary Scannell / first when none saved yet - useEffect(() => { - if (selectedNpiProviderId != null || !npiProviders.length) - return; - const mary = npiProviders.find((p) => p.providerName.toLowerCase() === "mary scannell"); - setSelectedNpiProviderId((mary ?? npiProviders[0])?.id ?? null); - }, [npiProviders, selectedNpiProviderId]); - // ── Mutations ────────────────────────────────────────────────── - const setNpiMutation = useMutation({ - mutationFn: async (npiProviderId) => { - const res = await apiRequest("PUT", `/api/appointment-procedures/set-npi-provider/${appointmentId}`, { npiProviderId }); - if (!res.ok) - throw new Error("Failed to update provider"); - }, - onSuccess: () => { - toast({ title: "Rendering provider saved" }); - queryClient.invalidateQueries({ queryKey: ["appointment-procedures", appointmentId] }); - }, - onError: (err) => { - toast({ title: "Error", description: err.message, variant: "destructive" }); - }, - }); - const bulkAddMutation = useMutation({ - mutationFn: async (rows) => { - const res = await apiRequest("POST", "/api/appointment-procedures/bulk", rows); - if (!res.ok) - throw new Error("Failed to add procedures"); - return res.json(); - }, - onSuccess: () => { - toast({ title: "Procedures saved" }); - setPendingRows([emptyRow(), emptyRow(), emptyRow()]); - queryClient.invalidateQueries({ queryKey: ["appointment-procedures", appointmentId] }); - }, - }); - const deleteMutation = useMutation({ - mutationFn: async (id) => { - const res = await apiRequest("DELETE", `/api/appointment-procedures/${id}`); - if (!res.ok) - throw new Error("Failed to delete"); - }, - onSuccess: () => { - toast({ title: "Deleted" }); - queryClient.invalidateQueries({ queryKey: ["appointment-procedures", appointmentId] }); - }, - }); - const clearAllMutation = useMutation({ - mutationFn: async () => { - const res = await apiRequest("DELETE", `/api/appointment-procedures/clear/${appointmentId}`); - if (!res.ok) - throw new Error("Failed to clear procedures"); - }, - onSuccess: () => { - toast({ title: "All procedures cleared" }); - queryClient.invalidateQueries({ queryKey: ["appointment-procedures", appointmentId] }); - setClearAllOpen(false); - }, - onError: (err) => { - toast({ title: "Error", description: err.message ?? "Failed to clear procedures", variant: "destructive" }); - }, - }); - const updateMutation = useMutation({ - mutationFn: async () => { - if (!editingId) - return; - const res = await apiRequest("PUT", `/api/appointment-procedures/${editingId}`, editRow); - if (!res.ok) - throw new Error("Failed to update"); - return res.json(); - }, - onSuccess: () => { - toast({ title: "Updated" }); - setEditingId(null); - setEditRow({}); - queryClient.invalidateQueries({ queryKey: ["appointment-procedures", appointmentId] }); - }, - }); - // ── Handlers ─────────────────────────────────────────────────── - const handleAddCombo = (comboKey) => { - const combo = PROCEDURE_COMBOS[comboKey]; - if (!combo) - return; - const rows = combo.codes.map((code, idx) => ({ - appointmentId, - patientId, - npiProviderId: selectedNpiProviderId ?? null, - procedureCode: code, - procedureLabel: combo.label, - fee: 0, - source: "COMBO", - comboKey, - toothNumber: combo.toothNumbers?.[idx] ?? null, - })); - bulkAddMutation.mutate(rows); - }; - const startEdit = (row) => { - if (!row.id) - return; - setEditingId(row.id); - setEditRow({ - procedureCode: row.procedureCode, - procedureLabel: row.procedureLabel, - fee: row.fee, - toothNumber: row.toothNumber, - toothSurface: row.toothSurface, - }); - }; - const cancelEdit = () => { setEditingId(null); setEditRow({}); }; - const handleSavePendingRows = () => { - const rows = pendingRows - .filter((r) => r.code.trim()) - .map((r) => ({ - appointmentId, - patientId, - npiProviderId: selectedNpiProviderId ?? null, - procedureCode: r.code.trim().toUpperCase(), - procedureLabel: r.label || null, - fee: r.fee ? Number(r.fee) : 0, - toothNumber: r.tooth || null, - toothSurface: r.surface || null, - source: "MANUAL", - })); - if (!rows.length) - return; - bulkAddMutation.mutate(rows); - }; - const handleDirectClaim = () => { - setLocation(`/claims?appointmentId=${appointmentId}&mode=direct`); - onOpenChange(false); - }; - const handleManualClaim = () => { - setLocation(`/claims?appointmentId=${appointmentId}&mode=manual`); - onOpenChange(false); - }; - const selectedProvider = npiProviders.find((p) => p.id === selectedNpiProviderId); - // ── UI ───────────────────────────────────────────────────────── - return ( - { if (clearAllOpen) - e.preventDefault(); }} onInteractOutside={(e) => { if (clearAllOpen) - e.preventDefault(); }}> - - - Appointment Procedures - {serviceDate && {serviceDate}} - - - - {/* ── Rendering Provider ─────────────────────────────── */} -
-
- - -
- - {selectedProvider && ( - ✓ {selectedProvider.providerName} - )} -
- - {/* ── Combos ─────────────────────────────────────────── */} -
- - -
- - {/* ── Pending Lines ───────────────────────────────────── */} -
-
Add Procedures
- {/* Column headers */} -
-
Code
Label
Fee
Tooth
Surface
-
- {pendingRows.map((row, i) => (
- setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, code: e.target.value } : r))}/> - setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, label: e.target.value } : r))}/> - setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, fee: e.target.value } : r))}/> - setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, tooth: e.target.value } : r))}/> - setPendingRows((prev) => prev.map((r, idx) => idx === i ? { ...r, surface: e.target.value } : r))}/> - -
))} -
- - -
-
- - {/* ── Procedures List ─────────────────────────────────── */} -
-
-
Saved Procedures ({procedures.length})
- -
- -
-
-
Code
Label
Fee
Tooth
Surface
-
Edit
Delete
-
- - {isLoading &&
Loading...
} - {!isLoading && procedures.length === 0 && (
No procedures added yet
)} - - {procedures.map((p) => (
- {editingId === p.id ? (<> - setEditRow({ ...editRow, procedureCode: e.target.value })}/> - setEditRow({ ...editRow, procedureLabel: e.target.value })}/> - setEditRow({ ...editRow, fee: Number(e.target.value) })}/> - setEditRow({ ...editRow, toothNumber: e.target.value })}/> - setEditRow({ ...editRow, toothSurface: e.target.value })}/> -
- -
-
- -
- ) : (<> -
{p.procedureCode}
-
{p.procedureLabel}
-
{p.fee !== null && p.fee !== undefined ? String(p.fee) : ""}
-
{p.toothNumber}
-
{p.toothSurface}
-
- -
-
- -
- )} -
))} -
-
- - {/* ── Footer ─────────────────────────────────────────── */} -
-
- - -
- -
- - - setClearAllOpen(false)} onConfirm={() => { setClearAllOpen(false); clearAllMutation.mutate(); }}/> - - {/* Price mismatch dialog */} - 0} onOpenChange={open => { if (!open) - setPriceMismatches([]); }}> - - - Save new price to the app? - -
-

The following procedure prices differ from the fee schedule:

-
    - {priceMismatches.map(m => (
  • - {m.procedureCode} - Schedule: ${m.schedulePrice.toFixed(2)} - Entered: ${m.enteredPrice.toFixed(2)} -
  • ))} -
-

Do you want to save the new price(s) to the fee schedule for future use?

-
-
-
- - { - setPriceMismatches([]); - pendingAction.current?.(); - pendingAction.current = null; - }}> - No - - { - await savePricesToSchedule(priceMismatches); - setPriceMismatches([]); - pendingAction.current?.(); - pendingAction.current = null; - }}> - Yes - - -
-
-
); -} diff --git a/apps/Frontend/src/components/appointments/add-appointment-modal.jsx b/apps/Frontend/src/components/appointments/add-appointment-modal.jsx deleted file mode 100644 index 21f0b1b7..00000000 --- a/apps/Frontend/src/components/appointments/add-appointment-modal.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; -import { AppointmentForm } from "./appointment-form"; -export function AddAppointmentModal({ open, onOpenChange, onSubmit, onDelete, isLoading, appointment, prefillData, }) { - return ( - - - - {appointment ? "Edit Appointment" : "Add New Appointment"} - - -
- { - onSubmit(data); - onOpenChange(false); - }} isLoading={isLoading} onDelete={onDelete} onOpenChange={onOpenChange}/> -
-
-
); -} diff --git a/apps/Frontend/src/components/appointments/appointment-form.jsx b/apps/Frontend/src/components/appointments/appointment-form.jsx deleted file mode 100644 index 478a2440..00000000 --- a/apps/Frontend/src/components/appointments/appointment-form.jsx +++ /dev/null @@ -1,434 +0,0 @@ -import { useEffect, useRef, useState } from "react"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { format } from "date-fns"; -import { apiRequest } from "@/lib/queryClient"; -import { APPOINTMENT_TYPES } from "@/utils/appointmentTypeUtils"; -import { Button } from "@/components/ui/button"; -import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage, } from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { Textarea } from "@/components/ui/textarea"; -import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; -import { Clock } from "lucide-react"; -import { useQuery } from "@tanstack/react-query"; -import { useAuth } from "@/hooks/use-auth"; -import { useDebounce } from "use-debounce"; -import { insertAppointmentSchema, } from "@repo/db/types"; -import { DateInputField } from "@/components/ui/dateInputField"; -import { formatLocalDate, parseLocalDate } from "@/utils/dateUtils"; -import { toast } from "@/hooks/use-toast"; -export function AppointmentForm({ appointment, prefillData, onSubmit, onDelete, onOpenChange, isLoading = false, }) { - const { user } = useAuth(); - const inputRef = useRef(null); - const [prefillPatient, setPrefillPatient] = useState(null); - const [otherTypeDesc, setOtherTypeDesc] = useState(() => { - const t = appointment?.type ?? ""; - return t.startsWith("other:") ? t.slice(6) : ""; - }); - // Track whether the user explicitly changed the type during this edit session. - // Used to set typeLocked so the auto-sync won't overwrite a deliberate choice. - const originalType = useRef(appointment?.type ?? ""); - const [typeChangedByUser, setTypeChangedByUser] = useState(false); - useEffect(() => { - const timeout = setTimeout(() => { - inputRef.current?.focus(); - }, 50); // small delay ensures content is mounted - return () => clearTimeout(timeout); - }, []); - const { data: staffMembersRaw = [] } = useQuery({ - queryKey: ["/api/staffs/"], - queryFn: async () => { - const res = await apiRequest("GET", "/api/staffs/"); - return res.json(); - }, - enabled: !!user, - }); - const colorMap = { - "Dr. Kai Gao": "bg-blue-600", - "Dr. Jane Smith": "bg-emerald-600", - }; - const staffMembers = staffMembersRaw.map((staff) => ({ - ...staff, - color: colorMap[staff.name] || "bg-gray-400", - })); - // Format the date and times for the form - const defaultValues = appointment - ? { - userId: user?.id, - patientId: appointment.patientId, - title: appointment.title, - date: parseLocalDate(appointment.date), - startTime: appointment.startTime || "09:00", - endTime: appointment.endTime || "09:30", - type: appointment.type?.startsWith("other:") ? "other" : appointment.type, - notes: appointment.notes || "", - status: appointment.status || "scheduled", - staffId: typeof appointment.staffId === "number" - ? appointment.staffId - : undefined, - } - : prefillData - ? { - userId: user?.id, - patientId: prefillData.patientId, - date: prefillData.date ? parseLocalDate(prefillData.date) : new Date(), - title: "", - startTime: prefillData.startTime, - endTime: prefillData.endTime, - type: prefillData.type || "checkup", - status: "scheduled", - notes: "", - staffId: prefillData.staffId, - } - : { - userId: user?.id ?? 0, - date: new Date(), - title: "", - startTime: "09:00", - endTime: "09:30", - type: "checkup", - status: "scheduled", - staffId: staffMembers?.[0]?.id ?? undefined, - }; - const form = useForm({ - resolver: zodResolver(insertAppointmentSchema), - defaultValues, - }); - // ----------------------------- - // PATIENT SEARCH (simple inline search) - // ----------------------------- - const [selectOpen, setSelectOpen] = useState(false); - const [patientSearchTerm, setPatientSearchTerm] = useState(""); - const [debouncedPatientSearch] = useDebounce(patientSearchTerm, 300); - const searchKeyPart = debouncedPatientSearch.trim() || "recent"; - const queryFn = async () => { - const trimmed = debouncedPatientSearch.trim(); - const url = trimmed - ? `/api/patients/search?name=${encodeURIComponent(trimmed)}&limit=50&offset=0` - : `/api/patients/recent?limit=50&offset=0`; - const res = await apiRequest("GET", url); - if (!res.ok) { - const err = await res.json().catch(() => ({ message: "Failed to fetch patients" })); - throw new Error(err.message || "Failed to fetch patients"); - } - const payload = await res.json(); - return Array.isArray(payload) ? payload : (payload.patients ?? []); - }; - const { data: patients = [], isFetching: isFetchingPatients, refetch: refetchPatients, } = useQuery({ - queryKey: ["patients-dropdown", searchKeyPart], - queryFn, - enabled: selectOpen || debouncedPatientSearch.trim().length > 0, - }); - useEffect(() => { - if (selectOpen && patients.length === 0) { - refetchPatients(); - } - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectOpen]); - // Prefill form from prefillData prop (new appointment slot click) - useEffect(() => { - if (!prefillData) - return; - form.setValue("staffId", prefillData.staffId); - form.setValue("startTime", prefillData.startTime); - form.setValue("endTime", prefillData.endTime); - form.setValue("date", parseLocalDate(prefillData.date)); - if (prefillData.type) - form.setValue("type", prefillData.type); - if (prefillData.patientId) { - form.setValue("patientId", prefillData.patientId); - (async () => { - try { - const res = await apiRequest("GET", `/api/patients/${prefillData.patientId}`); - if (res.ok) - setPrefillPatient(await res.json()); - } - catch { } - })(); - } - }, [prefillData]); - // When editing an appointment, ensure we prefill the patient so SelectValue can render - useEffect(() => { - if (!appointment?.patientId) - return; - const pid = Number(appointment.patientId); - if (Number.isNaN(pid)) - return; - // set form value immediately so the select has a value - form.setValue("patientId", pid); - // fetch the single patient record and set prefill - (async () => { - try { - const res = await apiRequest("GET", `/api/patients/${pid}`); - if (res.ok) { - const patientRecord = await res.json(); - setPrefillPatient(patientRecord); - } - else { - let msg = `Failed to load patient (status ${res.status})`; - try { - const body = await res.json().catch(() => null); - if (body && body.message) - msg = body.message; - } - catch { } - toast({ - title: "Could not load patient", - description: msg, - variant: "destructive", - }); - } - } - catch (err) { - toast({ - title: "Error fetching patient", - description: err?.message || - "An unknown error occurred while fetching patient details.", - variant: "destructive", - }); - } - })(); - // note: we intentionally do NOT remove prefillPatientd here; it will be cleared when dropdown opens and main list contains the patient - }, [appointment?.patientId]); - const handleSubmit = (data) => { - // Make sure patientId is a number - const patientId = typeof data.patientId === "string" - ? parseInt(data.patientId, 10) - : data.patientId; - // Auto-create title if it's empty - let title = data.title; - if (!title || title.trim() === "") { - // Format: "April 19" - just the date - title = format(data.date, "MMMM d"); - } - const notes = data.notes || ""; - const selectedStaff = staffMembers.find((staff) => staff.id?.toString() === data.staffId) || - staffMembers[0]; - if (!selectedStaff) { - console.error("No staff selected and no available staff in the list"); - return; - } - const formattedDate = formatLocalDate(data.date); - const resolvedType = data.type === "other" && otherTypeDesc.trim() - ? `other:${otherTypeDesc.trim()}` - : data.type; - onSubmit({ - ...data, - userId: Number(user?.id), - title, - notes, - patientId, - date: formattedDate, - startTime: data.startTime, - endTime: data.endTime, - type: resolvedType, - // Lock the type when the user has explicitly changed it on an existing appointment - ...(appointment && typeChangedByUser ? { typeLocked: true } : {}), - }); - }; - return (
-
- { - handleSubmit(data); - }, (errors) => { - console.error("Validation failed:", errors); - })} className="space-y-6"> - ( - Patient - - setPatientSearchTerm(e.target.value)} onClick={(e) => e.stopPropagation()}/> -
- - {/* Prefill patient only if main list does not already include them */} - {prefillPatient && - !patients.some((p) => Number(p.id) === Number(prefillPatient.id)) && ( -
- - {prefillPatient.firstName}{" "} - {prefillPatient.lastName} - - - DOB:{" "} - {prefillPatient.dateOfBirth - ? new Date(prefillPatient.dateOfBirth).toLocaleDateString() - : ""}{" "} - • {prefillPatient.phone ?? ""} - -
-
)} - -
- {isFetchingPatients ? (
- Loading... -
) : patients && patients.length > 0 ? (patients.map((patient) => ( -
- - {patient.firstName} {patient.lastName} - - - DOB:{" "} - {new Date(patient.dateOfBirth).toLocaleDateString()}{" "} - • {patient.phone} - -
-
))) : (
- No patients found -
)} -
- - - - - )}/> - - ( - - Appointment Title{" "} - - (optional) - - - - - - - )}/> - - - -
- ( - Start Time - -
- - -
-
- -
)}/> - - ( - End Time - -
- - -
-
- -
)}/> -
- - ( - Appointment Type - - {field.value === "other" && ( setOtherTypeDesc(e.target.value)} disabled={isLoading} autoFocus/>)} - - )}/> - - ( - Status - - - )}/> - - ( - Doctor/Hygienist - - - )}/> - - ( - Notes - -