diff --git a/apps/Backend/src/app.ts b/apps/Backend/src/app.ts index 472a62f..ad7a72d 100644 --- a/apps/Backend/src/app.ts +++ b/apps/Backend/src/app.ts @@ -17,13 +17,10 @@ app.use(express.json()); app.use(express.urlencoded({ extended: true })); // For form data app.use(apiLogger); - -console.log(FRONTEND_URL); - app.use(cors({ - origin: FRONTEND_URL, // Make sure this matches the frontend URL - methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], // Allow these HTTP methods - allowedHeaders: ['Content-Type', 'Authorization'], // Allow necessary headers + origin: FRONTEND_URL, + methods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization'], credentials: true, })); diff --git a/apps/Backend/src/routes/appointements.ts b/apps/Backend/src/routes/appointements.ts index 0cd1df1..efc0a4f 100644 --- a/apps/Backend/src/routes/appointements.ts +++ b/apps/Backend/src/routes/appointements.ts @@ -111,16 +111,12 @@ router.post( async (req: Request, res: Response): Promise => { try { - console.log("Appointment creation request body:", req.body); - // Validate request body const appointmentData = insertAppointmentSchema.parse({ ...req.body, userId: req.user!.id, }); - console.log("Validated appointment data:", appointmentData); - // Verify patient exists and belongs to user const patient = await storage.getPatient(appointmentData.patientId); if (!patient) { @@ -165,7 +161,6 @@ router.post( // Create appointment const appointment = await storage.createAppointment(appointmentData); - console.log("Appointment created successfully:", appointment); res.status(201).json(appointment); } catch (error) { console.error("Error creating appointment:", error); @@ -201,12 +196,6 @@ router.put( } const appointmentId = parseInt(appointmentIdParam); - console.log( - "Update appointment request. ID:", - appointmentId, - "Body:", - req.body - ); // Check if appointment exists and belongs to user const existingAppointment = await storage.getAppointment(appointmentId); diff --git a/apps/Frontend/package.json b/apps/Frontend/package.json index b776be3..15619ea 100644 --- a/apps/Frontend/package.json +++ b/apps/Frontend/package.json @@ -42,8 +42,8 @@ "@replit/vite-plugin-shadcn-theme-json": "^0.0.4", "@repo/db": "*", "@repo/typescript-config": "*", - "@tailwindcss/vite": "^4.1.6", "@tailwindcss/typography": "^0.5.15", + "@tailwindcss/vite": "^4.1.6", "@tanstack/react-query": "^5.60.5", "autoprefixer": "^10.4.20", "class-variance-authority": "^0.7.1", @@ -60,8 +60,8 @@ "memorystore": "^1.6.7", "next-themes": "^0.4.6", "passport": "^0.7.0", - "postcss": "^8.4.47", "passport-local": "^1.0.0", + "postcss": "^8.4.47", "react": "^19.1.0", "react-contexify": "^6.0.0", "react-day-picker": "^8.10.1", diff --git a/apps/Frontend/src/components/layout/sidebar.tsx b/apps/Frontend/src/components/layout/sidebar.tsx index 9abfb06..8bc78bc 100644 --- a/apps/Frontend/src/components/layout/sidebar.tsx +++ b/apps/Frontend/src/components/layout/sidebar.tsx @@ -26,11 +26,6 @@ export function Sidebar({ isMobileOpen, setIsMobileOpen }: SidebarProps) { path: "/patients", icon: , }, - { - name: "Reports", - path: "/reports", - icon: , - }, { name: "Settings", path: "/settings", diff --git a/apps/Frontend/src/components/layout/top-app-bar.tsx b/apps/Frontend/src/components/layout/top-app-bar.tsx index b5a49d0..1e44141 100644 --- a/apps/Frontend/src/components/layout/top-app-bar.tsx +++ b/apps/Frontend/src/components/layout/top-app-bar.tsx @@ -9,6 +9,9 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { useLocation } from "wouter"; + + interface TopAppBarProps { toggleMobileMenu: () => void; @@ -16,6 +19,7 @@ interface TopAppBarProps { export function TopAppBar({ toggleMobileMenu }: TopAppBarProps) { const { user, logoutMutation } = useAuth(); + const [location, setLocation] = useLocation(); const handleLogout = () => { logoutMutation.mutate(); @@ -68,7 +72,8 @@ export function TopAppBar({ toggleMobileMenu }: TopAppBarProps) { {user?.username} My Profile - Account Settings + setLocation("/settings")}> + Account Settings Log out diff --git a/apps/Frontend/src/components/patients/add-patient-modal.tsx b/apps/Frontend/src/components/patients/add-patient-modal.tsx index da28c05..9b0a9ce 100644 --- a/apps/Frontend/src/components/patients/add-patient-modal.tsx +++ b/apps/Frontend/src/components/patients/add-patient-modal.tsx @@ -109,9 +109,12 @@ export const AddPatientModal = forwardRef< }; const handleSaveAndSchedule = () => { - setSaveAndSchedule(true); - document.querySelector("form")?.requestSubmit(); - }; + setSaveAndSchedule(true); + if (patientFormRef.current) { + patientFormRef.current.submit(); + } +}; + return ( @@ -137,6 +140,7 @@ export const AddPatientModal = forwardRef< @@ -64,333 +65,340 @@ export type PatientFormRef = { submit: () => void; }; -export function PatientForm({ - patient, - extractedInfo, - onSubmit, -}: PatientFormProps) { - const { user } = useAuth(); - const isEditing = !!patient; +export const PatientForm = forwardRef( + ({ patient, extractedInfo, onSubmit }, ref) => { + const { user } = useAuth(); + const isEditing = !!patient; - const schema = useMemo( - () => - isEditing - ? updatePatientSchema - : insertPatientSchema.extend({ userId: z.number().optional() }), - [isEditing] - ); + const schema = useMemo( + () => + isEditing + ? updatePatientSchema + : insertPatientSchema.extend({ userId: z.number().optional() }), + [isEditing] + ); - const computedDefaultValues = useMemo(() => { - if (isEditing && patient) { - const { id, userId, createdAt, ...sanitizedPatient } = patient; + const computedDefaultValues = useMemo(() => { + if (isEditing && patient) { + const { id, userId, createdAt, ...sanitizedPatient } = patient; + return { + ...sanitizedPatient, + dateOfBirth: patient.dateOfBirth + ? new Date(patient.dateOfBirth).toISOString().split("T")[0] + : "", + }; + } return { - ...sanitizedPatient, - dateOfBirth: patient.dateOfBirth - ? new Date(patient.dateOfBirth).toISOString().split("T")[0] - : "", + firstName: extractedInfo?.firstName || "", + lastName: extractedInfo?.lastName || "", + dateOfBirth: extractedInfo?.dateOfBirth || "", + gender: "", + phone: "", + email: "", + address: "", + city: "", + zipCode: "", + insuranceProvider: "", + insuranceId: extractedInfo?.insuranceId || "", + groupNumber: "", + policyHolder: "", + allergies: "", + medicalConditions: "", + status: "active", + userId: user?.id, }; - } + }, [isEditing, patient, extractedInfo, user?.id]); - return { - firstName: extractedInfo?.firstName || "", - lastName: extractedInfo?.lastName || "", - dateOfBirth: extractedInfo?.dateOfBirth || "", - gender: "", - phone: "", - email: "", - address: "", - city: "", - zipCode: "", - insuranceProvider: "", - insuranceId: extractedInfo?.insuranceId || "", - groupNumber: "", - policyHolder: "", - allergies: "", - medicalConditions: "", - status: "active", - userId: user?.id, + const form = useForm({ + resolver: zodResolver(schema), + defaultValues: computedDefaultValues, + }); + + useImperativeHandle(ref, () => ({ + submit() { + (document.getElementById("patient-form") as HTMLFormElement | null)?.requestSubmit(); + }, + })); + + // Debug form errors + useEffect(() => { + const errors = form.formState.errors; + if (Object.keys(errors).length > 0) { + console.log("❌ Form validation errors:", errors); + } + }, [form.formState.errors]); + + useEffect(() => { + if (patient) { + const { id, userId, createdAt, ...sanitizedPatient } = patient; + const resetValues: Partial = { + ...sanitizedPatient, + dateOfBirth: patient.dateOfBirth + ? new Date(patient.dateOfBirth).toISOString().split("T")[0] + : "", + }; + form.reset(resetValues); + } + }, [patient, computedDefaultValues, form]); + + const handleSubmit2 = (data: InsertPatient | UpdatePatient) => { + onSubmit(data); }; - }, [isEditing, patient, extractedInfo, user?.id]); - const form = useForm({ - resolver: zodResolver(schema), - defaultValues: computedDefaultValues, - }); - - // Debug form errors - useEffect(() => { - const errors = form.formState.errors; - if (Object.keys(errors).length > 0) { - console.log("❌ Form validation errors:", errors); - } - }, [form.formState.errors]); - - useEffect(() => { - if (patient) { - const { id, userId, createdAt, ...sanitizedPatient } = patient; - const resetValues: Partial = { - ...sanitizedPatient, - dateOfBirth: patient.dateOfBirth - ? new Date(patient.dateOfBirth).toISOString().split("T")[0] - : "", - }; - form.reset(resetValues); - } - }, [patient, computedDefaultValues, form]); - - const handleSubmit2 = (data: InsertPatient | UpdatePatient) => { - onSubmit(data); - }; - - return ( -
- { - handleSubmit2(data); - })} - className="space-y-6" - > - {/* Personal Information */} -
-

- Personal Information -

-
- ( - - First Name * - - - - - - )} - /> - - ( - - Last Name * - - - - - - )} - /> - - ( - - Date of Birth * - - - - - - )} - /> - - ( - - Gender * - - - Male - Female - Other - - - - - )} - /> -
-
+ + + )} + /> - {/* Contact Information */} -
-

- Contact Information -

-
- ( - - Phone Number * - - - - - - )} - /> - - ( - - Email - - - - - - )} - /> - - ( - - Address - - - - - - )} - /> - - ( - - City - - - - - - )} - /> - - ( - - ZIP Code - - - - - - )} - /> -
-
- - {/* Insurance Information */} -
-

- Insurance Information -

-
- ( - - Insurance Provider - - - - Select provider - - Delta Dental - MetLife - Cigna - Aetna - Other - None - - - - - )} - /> + + + )} + /> - ( - - Insurance ID - - - - - - )} - /> + ( + + Date of Birth * + + + + + + )} + /> - ( - - Group Number - - - - - - )} - /> - - ( - - Policy Holder (if not self) - - - - - - )} - /> + ( + + Gender * + + + + )} + /> +
- - {/* Hidden submit button for form validation */} - - - - ); -} + {/* Contact Information */} +
+

+ Contact Information +

+
+ ( + + Phone Number * + + + + + + )} + /> + + ( + + Email + + + + + + )} + /> + + ( + + Address + + + + + + )} + /> + + ( + + City + + + + + + )} + /> + + ( + + ZIP Code + + + + + + )} + /> +
+
+ + {/* Insurance Information */} +
+

+ Insurance Information +

+
+ ( + + Insurance Provider + + + + )} + /> + + ( + + Insurance ID + + + + + + )} + /> + + ( + + Group Number + + + + + + )} + /> + + ( + + Policy Holder (if not self) + + + + + + )} + /> +
+
+ + {/* Hidden submit button for form validation */} + + + + ); + } +); diff --git a/apps/Frontend/src/components/patients/patient-table.tsx b/apps/Frontend/src/components/patients/patient-table.tsx index e5f5de7..1599e99 100644 --- a/apps/Frontend/src/components/patients/patient-table.tsx +++ b/apps/Frontend/src/components/patients/patient-table.tsx @@ -56,17 +56,19 @@ export function PatientTable({ patients, onEdit, onView }: PatientTableProps) { }; const getAvatarColor = (id: number) => { - const colors = [ - "bg-blue-500", - "bg-teal-500", - "bg-amber-500", - "bg-rose-500", - "bg-indigo-500", - "bg-green-500", - "bg-purple-500", - ]; - return colors[id % colors.length]; - }; + const colorClasses = [ + "bg-blue-500", + "bg-teal-500", + "bg-amber-500", + "bg-rose-500", + "bg-indigo-500", + "bg-green-500", + "bg-purple-500", + ]; + + // This returns a literal string from above — not a generated string + return colorClasses[id % colorClasses.length]; +}; const formatDate = (dateString: string | Date) => { const date = new Date(dateString); @@ -108,6 +110,7 @@ export function PatientTable({ patients, onEdit, onView }: PatientTableProps) { {getInitials(patient.firstName, patient.lastName)} +
{patient.firstName} {patient.lastName} diff --git a/apps/Frontend/src/components/ui/avatar.tsx b/apps/Frontend/src/components/ui/avatar.tsx index 51e507b..68178b2 100644 --- a/apps/Frontend/src/components/ui/avatar.tsx +++ b/apps/Frontend/src/components/ui/avatar.tsx @@ -39,7 +39,7 @@ const AvatarFallback = React.forwardRef< = { + primary: { bg: "bg-primary bg-opacity-10", text: "text-primary" }, + secondary: { bg: "bg-teal-500 bg-opacity-10", text: "text-teal-500" }, + success: { bg: "bg-green-500 bg-opacity-10", text: "text-green-500" }, + warning: { bg: "bg-orange-500 bg-opacity-10", text: "text-orange-500" }, + blue: { bg: "bg-blue-100", text: "text-blue-600" }, + teal: { bg: "bg-teal-100", text: "text-teal-600" }, + green: { bg: "bg-green-100", text: "text-green-600" }, + orange: { bg: "bg-orange-100", text: "text-orange-600" }, + rose: { bg: "bg-rose-100", text: "text-rose-600" }, + violet: { bg: "bg-violet-100", text: "text-violet-600" }, +}; + export function StatCard({ title, value, icon: Icon, color }: StatCardProps) { - const getBackgroundColorClass = (color: string) => { - switch(color) { - case 'primary': - return 'bg-primary bg-opacity-10 text-primary'; - case 'secondary': - return 'bg-teal-500 bg-opacity-10 text-teal-500'; - case 'success': - return 'bg-green-500 bg-opacity-10 text-green-500'; - case 'warning': - return 'bg-orange-500 bg-opacity-10 text-orange-500'; - default: - return 'bg-primary bg-opacity-10 text-primary'; - } - }; + const { bg, text } = colorMap[color] ?? colorMap.primary; return ( -
- +
+ +

{title}

diff --git a/apps/Frontend/src/pages/appointments-page.tsx b/apps/Frontend/src/pages/appointments-page.tsx index 49c2e49..71c0289 100644 --- a/apps/Frontend/src/pages/appointments-page.tsx +++ b/apps/Frontend/src/pages/appointments-page.tsx @@ -127,14 +127,20 @@ export default function AppointmentsPage() { enabled: !!user, }); - const colorMap: Record = { - "Dr. Kai Gao": "bg-blue-600", - "Dr. Jane Smith": "bg-emerald-600", - }; + const colors = [ + "bg-blue-600", + "bg-emerald-600", + "bg-purple-600", + "bg-pink-600", + "bg-yellow-500", + "bg-red-600", + ]; - const staffMembers = staffMembersRaw.map((staff) => ({ + // Assign colors cycling through the list + const staffMembers = staffMembersRaw.map((staff, index) => ({ ...staff, - color: colorMap[staff.name] || "bg-gray-400", + + color: colors[index % colors.length] || "bg-gray-400", })); // Generate time slots from 8:00 AM to 6:00 PM in 30-minute increments diff --git a/apps/Frontend/src/pages/dashboard.tsx b/apps/Frontend/src/pages/dashboard.tsx index aaaf578..89fb84e 100644 --- a/apps/Frontend/src/pages/dashboard.tsx +++ b/apps/Frontend/src/pages/dashboard.tsx @@ -326,7 +326,7 @@ export default function Dashboard() { title="Total Patients" value={patients.length} icon={Users} - color="primary" + color="blue" />
-
+
diff --git a/apps/Frontend/src/pages/patients-page.tsx b/apps/Frontend/src/pages/patients-page.tsx index 394707d..a975941 100644 --- a/apps/Frontend/src/pages/patients-page.tsx +++ b/apps/Frontend/src/pages/patients-page.tsx @@ -219,109 +219,6 @@ export default function PatientsPage() { }); }; - // Process file and extract patient information - const handleExtractInfo = async () => { - if (!uploadedFile) { - toast({ - title: "No file selected", - description: "Please select a file first.", - variant: "destructive", - }); - return; - } - - setIsExtracting(true); - - try { - // Read the file as base64 - const reader = new FileReader(); - - // Set up a Promise to handle file reading - const fileReadPromise = new Promise((resolve, reject) => { - reader.onload = (event) => { - if (event.target && typeof event.target.result === "string") { - resolve(event.target.result); - } else { - reject(new Error("Failed to read file as base64")); - } - }; - - reader.onerror = () => { - reject(new Error("Error reading file")); - }; - - // Read the file as a data URL (base64) - reader.readAsDataURL(uploadedFile); - }); - - // Get the base64 data - const base64Data = await fileReadPromise; - - // Send file to server as base64 - const response = await fetch("/api/upload-file", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - pdfData: base64Data, - filename: uploadedFile.name, - }), - credentials: "include", - }); - - if (!response.ok) { - throw new Error( - `Server returned ${response.status}: ${response.statusText}` - ); - } - - const data = await response.json(); - - if (data.success) { - // Only keep firstName, lastName, dateOfBirth, and insuranceId from the extracted info - const simplifiedInfo = { - firstName: data.extractedInfo.firstName, - lastName: data.extractedInfo.lastName, - dateOfBirth: data.extractedInfo.dateOfBirth, - insuranceId: data.extractedInfo.insuranceId, - }; - - setExtractedInfo(simplifiedInfo); - - // Show success message - toast({ - title: "Information Extracted", - description: - "Basic patient information (name, DOB, ID) has been extracted successfully.", - variant: "default", - }); - - // Open patient form pre-filled with extracted data - setCurrentPatient(undefined); - - // Pre-fill the form by opening the modal with the extracted information - setTimeout(() => { - setIsAddPatientOpen(true); - }, 500); - } else { - throw new Error(data.message || "Failed to extract information"); - } - } catch (error) { - console.error("Error extracting information:", error); - toast({ - title: "Error", - description: - error instanceof Error - ? error.message - : "Failed to extract information from file", - variant: "destructive", - }); - } finally { - setIsExtracting(false); - } - }; - // Filter patients based on search criteria const filteredPatients = useMemo(() => { if (!searchCriteria || !searchCriteria.searchTerm) { @@ -424,7 +321,6 @@ export default function PatientsPage() {