From 9a431e63dbb17a833475e4ff2145c6912fb1b140 Mon Sep 17 00:00:00 2001 From: Vishnu Date: Fri, 9 May 2025 21:51:02 +0530 Subject: [PATCH] frontend tailwind half working without external styling --- .npmrc | 1 - apps/Frontend/index.html | 2 +- apps/Frontend/package.json | 9 +- apps/Frontend/postcss.config.js | 3 + .../appointments/add-appointment-modal.tsx | 23 +- .../appointments/appointment-form.tsx | 38 +- .../appointments/appointment-table.tsx | 101 ++-- .../src/components/claims/claim-form.tsx | 25 +- .../src/components/claims/claim-modal.tsx | 9 +- .../src/components/layout/sidebar.tsx | 3 +- .../src/components/layout/top-app-bar.tsx | 3 +- .../components/patients/add-patient-modal.tsx | 24 +- .../src/components/patients/patient-form.tsx | 23 +- .../src/components/patients/patient-table.tsx | 10 +- apps/Frontend/src/components/ui/badge.tsx | 2 + apps/Frontend/src/hooks/use-auth.tsx | 26 +- apps/Frontend/src/hooks/use-toast.ts | 2 +- apps/Frontend/src/index.css | 2 +- apps/Frontend/src/lib/protected-route.tsx | 2 +- apps/Frontend/src/main.tsx | 2 +- apps/Frontend/src/pages/appointments-page.tsx | 119 ++-- apps/Frontend/src/pages/auth-page.tsx | 34 +- apps/Frontend/src/pages/dashboard.tsx | 67 ++- apps/Frontend/src/pages/not-found.tsx | 2 +- apps/Frontend/src/pages/patients-page.tsx | 47 +- apps/Frontend/tailwind.config.ts | 4 + apps/Frontend/tsconfig.json | 14 +- apps/Frontend/vite.config.ts | 8 +- package-lock.json | 518 +++++++++++++++++- package.json | 4 +- packages/db/package.json | 2 +- packages/db/src/schema_todelete.txt | 85 --- packages/tailwind-config/postcss.config.js | 8 +- packages/tailwind-config/shared-styles.css | 5 - packages/ui/eslint.config.mjs | 4 + packages/ui/package.json | 36 ++ packages/ui/src/card.tsx | 28 + packages/ui/src/gradient.tsx | 21 + packages/ui/src/styles.css | 1 + packages/ui/src/turborepo-logo.tsx | 35 ++ packages/ui/tsconfig.json | 8 + packages/ui/turbo.json | 25 + 42 files changed, 1112 insertions(+), 273 deletions(-) delete mode 100644 .npmrc create mode 100644 apps/Frontend/postcss.config.js create mode 100644 apps/Frontend/tailwind.config.ts delete mode 100644 packages/db/src/schema_todelete.txt create mode 100644 packages/ui/eslint.config.mjs create mode 100644 packages/ui/package.json create mode 100644 packages/ui/src/card.tsx create mode 100644 packages/ui/src/gradient.tsx create mode 100644 packages/ui/src/styles.css create mode 100644 packages/ui/src/turborepo-logo.tsx create mode 100644 packages/ui/tsconfig.json create mode 100644 packages/ui/turbo.json diff --git a/.npmrc b/.npmrc deleted file mode 100644 index ded82e2..0000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -auto-install-peers = true diff --git a/apps/Frontend/index.html b/apps/Frontend/index.html index e4b78ea..7455d36 100644 --- a/apps/Frontend/index.html +++ b/apps/Frontend/index.html @@ -4,7 +4,7 @@ - Vite + React + TS + Dental Management
diff --git a/apps/Frontend/package.json b/apps/Frontend/package.json index d075fe5..a9bdd3d 100644 --- a/apps/Frontend/package.json +++ b/apps/Frontend/package.json @@ -10,8 +10,6 @@ "preview": "vite preview" }, "dependencies": { - "@repo/ui": "*", - "@repo/db": "*", "@hookform/resolvers": "^3.10.0", "@jridgewell/trace-mapping": "^0.3.25", "@radix-ui/react-accordion": "^1.2.4", @@ -42,8 +40,13 @@ "@radix-ui/react-toggle-group": "^1.1.3", "@radix-ui/react-tooltip": "^1.2.0", "@replit/vite-plugin-shadcn-theme-json": "^0.0.4", + "@repo/db": "*", + "@repo/tailwind-config": "*", + "@repo/typescript-config": "*", + "@repo/ui": "*", "@tailwindcss/vite": "^4.1.3", "@tanstack/react-query": "^5.60.5", + "autoprefixer": "^10.4.21", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -59,6 +62,7 @@ "next-themes": "^0.4.6", "passport": "^0.7.0", "passport-local": "^1.0.0", + "postcss": "^8.5.3", "react": "^19.1.0", "react-contexify": "^6.0.0", "react-day-picker": "^8.10.1", @@ -70,6 +74,7 @@ "react-resizable-panels": "^2.1.7", "recharts": "^2.15.2", "tailwind-merge": "^2.6.0", + "tailwindcss": "^4.1.5", "tailwindcss-animate": "^1.0.7", "tw-animate-css": "^1.2.5", "vaul": "^1.1.2", diff --git a/apps/Frontend/postcss.config.js b/apps/Frontend/postcss.config.js new file mode 100644 index 0000000..b2391c1 --- /dev/null +++ b/apps/Frontend/postcss.config.js @@ -0,0 +1,3 @@ +import { postcssConfig } from "@repo/tailwind-config/postcss"; + +export default postcssConfig; diff --git a/apps/Frontend/src/components/appointments/add-appointment-modal.tsx b/apps/Frontend/src/components/appointments/add-appointment-modal.tsx index 1d1bd74..c38f7c5 100644 --- a/apps/Frontend/src/components/appointments/add-appointment-modal.tsx +++ b/apps/Frontend/src/components/appointments/add-appointment-modal.tsx @@ -1,7 +1,28 @@ import { useState } from "react"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { AppointmentForm } from "./appointment-form"; -import { Appointment, InsertAppointment, UpdateAppointment, Patient } from "@shared/schema"; +// import { Appointment, InsertAppointment, UpdateAppointment, Patient } from "@repo/db/shared/schemas"; +import { AppointmentUncheckedCreateInputObjectSchema, PatientUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas"; + +import {z} from "zod"; +type Appointment = z.infer; + +const insertAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + id: true, + createdAt: true, +}); +type InsertAppointment = z.infer; + +const updateAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + id: true, + createdAt: true, +}).partial(); +type UpdateAppointment = z.infer; + +const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + appointments: true, +}); +type Patient = z.infer; interface AddAppointmentModalProps { open: boolean; diff --git a/apps/Frontend/src/components/appointments/appointment-form.tsx b/apps/Frontend/src/components/appointments/appointment-form.tsx index 7d6cc6e..c0250d0 100644 --- a/apps/Frontend/src/components/appointments/appointment-form.tsx +++ b/apps/Frontend/src/components/appointments/appointment-form.tsx @@ -1,9 +1,30 @@ -import { useState, useEffect } from "react"; +import { useEffect } from "react"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; -import { z } from "zod"; import { format } from "date-fns"; -import { InsertAppointment, UpdateAppointment, Appointment, Patient } from "@shared/schema"; +// import { InsertAppointment, UpdateAppointment, Appointment, Patient } from "@repo/db/shared/schemas"; +import { AppointmentUncheckedCreateInputObjectSchema, PatientUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas"; + +import {z} from "zod"; +type Appointment = z.infer; + +const insertAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + id: true, + createdAt: true, +}); +type InsertAppointment = z.infer; + +const updateAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + id: true, + createdAt: true, +}).partial(); +type UpdateAppointment = z.infer; + +const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + appointments: true, +}); +type Patient = z.infer; + // Define staff members (should match those in appointments-page.tsx) const staffMembers = [ @@ -36,7 +57,6 @@ import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover import { CalendarIcon, Clock } from "lucide-react"; import { cn } from "@/lib/utils"; -// Create a schema for appointment validation const appointmentSchema = z.object({ patientId: z.coerce.number().positive(), title: z.string().optional(), @@ -52,7 +72,7 @@ const appointmentSchema = z.object({ type: z.string().min(1, "Appointment type is required"), notes: z.string().optional(), status: z.string().default("scheduled"), - staff: z.string().default(staffMembers[0].id), + staff: z.string().default(staffMembers?.[0]?.id ?? "default-id"), }); export type AppointmentFormValues = z.infer; @@ -96,8 +116,8 @@ export function AppointmentForm({ patientId: appointment.patientId, title: appointment.title, date: new Date(appointment.date), - startTime: appointment.startTime.slice(0, 5), // HH:MM from HH:MM:SS - endTime: appointment.endTime.slice(0, 5), // HH:MM from HH:MM:SS + startTime: typeof appointment.startTime === 'string' ? appointment.startTime.slice(0, 5) : "", + endTime: typeof appointment.endTime === 'string' ? appointment.endTime.slice(0, 5) : "", type: appointment.type, notes: appointment.notes || "", status: appointment.status || "scheduled", @@ -112,7 +132,7 @@ export function AppointmentForm({ type: parsedStoredData.type || "checkup", status: parsedStoredData.status || "scheduled", notes: parsedStoredData.notes || "", - staff: parsedStoredData.staff || staffMembers[0].id, + staff: parsedStoredData.staff || (staffMembers?.[0]?.id ?? "default-id") } : { date: new Date(), @@ -187,7 +207,7 @@ export function AppointmentForm({ // If there's no staff information in the notes, add it if (!notes.includes('Appointment with')) { - notes = notes ? `${notes}\nAppointment with ${selectedStaff.name}` : `Appointment with ${selectedStaff.name}`; + notes = notes ? `${notes}\nAppointment with ${selectedStaff?.name}` : `Appointment with ${selectedStaff?.name}`; } onSubmit({ diff --git a/apps/Frontend/src/components/appointments/appointment-table.tsx b/apps/Frontend/src/components/appointments/appointment-table.tsx index 6d0b502..1697f4a 100644 --- a/apps/Frontend/src/components/appointments/appointment-table.tsx +++ b/apps/Frontend/src/components/appointments/appointment-table.tsx @@ -1,6 +1,4 @@ -import { useState } from "react"; import { format } from "date-fns"; -import { Appointment, Patient } from "@shared/schema"; import { Table, TableBody, @@ -11,13 +9,13 @@ import { } from "@/components/ui/table"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; -import { - MoreHorizontal, - Edit, - Trash2, +import { + MoreHorizontal, + Edit, + Trash2, Eye, Calendar, - Clock + Clock, } from "lucide-react"; import { DropdownMenu, @@ -27,6 +25,21 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +// import { Appointment, Patient } from "@repo/db/shared/schemas"; +import { + AppointmentUncheckedCreateInputObjectSchema, + PatientUncheckedCreateInputObjectSchema, +} from "@repo/db/shared/schemas"; + +import { z } from "zod"; +type Appointment = z.infer; + +const PatientSchema = ( + PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject +).omit({ + appointments: true, +}); +type Patient = z.infer; interface AppointmentTableProps { appointments: Appointment[]; @@ -35,21 +48,34 @@ interface AppointmentTableProps { onDelete: (id: number) => void; } -export function AppointmentTable({ - appointments, - patients, - onEdit, - onDelete +export function AppointmentTable({ + appointments, + patients, + onEdit, + onDelete, }: AppointmentTableProps) { // Helper function to get patient name const getPatientName = (patientId: number) => { - const patient = patients.find(p => p.id === patientId); - return patient ? `${patient.firstName} ${patient.lastName}` : "Unknown Patient"; + const patient = patients.find((p) => p.id === patientId); + return patient + ? `${patient.firstName} ${patient.lastName}` + : "Unknown Patient"; }; // Helper function to get status badge const getStatusBadge = (status: string) => { - const statusConfig: Record = { + const statusConfig: Record< + string, + { + variant: + | "default" + | "secondary" + | "destructive" + | "outline" + | "success"; + label: string; + } + > = { scheduled: { variant: "default", label: "Scheduled" }, confirmed: { variant: "secondary", label: "Confirmed" }, completed: { variant: "success", label: "Completed" }, @@ -57,20 +83,20 @@ export function AppointmentTable({ "no-show": { variant: "outline", label: "No Show" }, }; - const config = statusConfig[status] || { variant: "default", label: status }; - - return ( - - {config.label} - - ); + const config = statusConfig[status] || { + variant: "default", + label: status, + }; + + return {config.label}; }; // Sort appointments by date and time (newest first) const sortedAppointments = [...appointments].sort((a, b) => { - const dateComparison = new Date(b.date).getTime() - new Date(a.date).getTime(); + const dateComparison = + new Date(b.date).getTime() - new Date(a.date).getTime(); if (dateComparison !== 0) return dateComparison; - return a.startTime.localeCompare(b.startTime); + return a.startTime.toString().localeCompare(b.startTime.toString()); }); return ( @@ -108,15 +134,20 @@ export function AppointmentTable({
- {appointment.startTime.slice(0, 5)} - {appointment.endTime.slice(0, 5)} + {appointment.startTime instanceof Date + ? appointment.startTime.toISOString().slice(11, 16) + : appointment.startTime.slice(0, 5)}{" "} + - + {appointment.endTime instanceof Date + ? appointment.endTime.toISOString().slice(11, 16) + : appointment.endTime.slice(0, 5)} + {/* {appointment.startTime.slice(0, 5)} - {appointment.endTime.slice(0, 5)} */}
- {appointment.type.replace('-', ' ')} - - - {getStatusBadge(appointment.status!)} + {appointment.type.replace("-", " ")} + {getStatusBadge(appointment.status!)} @@ -132,8 +163,14 @@ export function AppointmentTable({ Edit - onDelete(appointment.id)} + { + if (typeof appointment.id === "number") { + onDelete(appointment.id); + } else { + console.error("Invalid appointment ID"); + } + }} className="text-destructive focus:text-destructive" > @@ -149,4 +186,4 @@ export function AppointmentTable({ ); -} \ No newline at end of file +} diff --git a/apps/Frontend/src/components/claims/claim-form.tsx b/apps/Frontend/src/components/claims/claim-form.tsx index 4b30940..7c7e56b 100644 --- a/apps/Frontend/src/components/claims/claim-form.tsx +++ b/apps/Frontend/src/components/claims/claim-form.tsx @@ -1,21 +1,28 @@ import { useState, useEffect } from "react"; -import { Button } from "../ui/button"; -import { Input } from "../ui/input"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, -} from "../ui/select"; +} from "@/components/ui/select"; import { format, parse } from "date-fns"; -import { Patient } from "@shared/schema"; -import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"; -import { Label } from "../ui/label"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Label } from "@/components/ui/label"; import { X, Calendar as CalendarIcon } from "lucide-react"; -import { useToast } from "../../hooks/use-toast"; -import { Calendar } from "../ui/calendar"; -import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; +import { useToast } from "@/hooks/use-toast"; +import { Calendar } from "@/components/ui/calendar"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +// import { Patient } from "@repo/db/shared/schemas"; +import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas"; +import {z} from "zod"; + +const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + appointments: true, +}); +type Patient = z.infer; interface ClaimFormProps { patientId: number; diff --git a/apps/Frontend/src/components/claims/claim-modal.tsx b/apps/Frontend/src/components/claims/claim-modal.tsx index c51e246..b2057ac 100644 --- a/apps/Frontend/src/components/claims/claim-modal.tsx +++ b/apps/Frontend/src/components/claims/claim-modal.tsx @@ -1,6 +1,13 @@ import { useState, useEffect } from "react"; import { ClaimForm } from "./claim-form"; -import { Patient } from "@shared/schema"; +// import { Patient } from "@repo/db/shared/schemas"; +import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas"; +import {z} from "zod"; + +const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + appointments: true, +}); +type Patient = z.infer; interface ClaimModalProps { open: boolean; diff --git a/apps/Frontend/src/components/layout/sidebar.tsx b/apps/Frontend/src/components/layout/sidebar.tsx index 8597663..9abfb06 100644 --- a/apps/Frontend/src/components/layout/sidebar.tsx +++ b/apps/Frontend/src/components/layout/sidebar.tsx @@ -1,6 +1,5 @@ import { Link, useLocation } from "wouter"; -import { Search, LayoutDashboard, Users, Calendar, FileText, Settings } from "lucide-react"; -import { Input } from "@/components/ui/input"; +import { LayoutDashboard, Users, Calendar, FileText, Settings } from "lucide-react"; import { cn } from "@/lib/utils"; interface SidebarProps { diff --git a/apps/Frontend/src/components/layout/top-app-bar.tsx b/apps/Frontend/src/components/layout/top-app-bar.tsx index e0d2f25..b5a49d0 100644 --- a/apps/Frontend/src/components/layout/top-app-bar.tsx +++ b/apps/Frontend/src/components/layout/top-app-bar.tsx @@ -1,5 +1,4 @@ -import { Bell, Menu, Search } from "lucide-react"; -import { Input } from "@/components/ui/input"; +import { Bell, Menu} from "lucide-react"; import { Button } from "@/components/ui/button"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { useAuth } from "@/hooks/use-auth"; diff --git a/apps/Frontend/src/components/patients/add-patient-modal.tsx b/apps/Frontend/src/components/patients/add-patient-modal.tsx index 83b91ed..c2ae29d 100644 --- a/apps/Frontend/src/components/patients/add-patient-modal.tsx +++ b/apps/Frontend/src/components/patients/add-patient-modal.tsx @@ -9,10 +9,32 @@ import { DialogFooter, } from "@/components/ui/dialog"; import { PatientForm } from "./patient-form"; -import { InsertPatient, Patient, UpdatePatient } from "@shared/schema"; import { useToast } from "@/hooks/use-toast"; import { X, Calendar } from "lucide-react"; import { useLocation } from "wouter"; +// import { InsertPatient, Patient, UpdatePatient } from "@repo/db/shared/schemas"; +import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas"; +import {z} from "zod"; + +const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + appointments: true, +}); +type Patient = z.infer; + +const insertPatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + id: true, + createdAt: true, +}); +type InsertPatient = z.infer; + +const updatePatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + id: true, + createdAt: true, + userId: true, +}).partial(); + +type UpdatePatient = z.infer; + interface AddPatientModalProps { open: boolean; diff --git a/apps/Frontend/src/components/patients/patient-form.tsx b/apps/Frontend/src/components/patients/patient-form.tsx index 47fa3d5..eec47e0 100644 --- a/apps/Frontend/src/components/patients/patient-form.tsx +++ b/apps/Frontend/src/components/patients/patient-form.tsx @@ -1,7 +1,8 @@ import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; -import { insertPatientSchema, InsertPatient, Patient, updatePatientSchema, UpdatePatient } from "@shared/schema"; +import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas"; +// import { insertPatientSchema, InsertPatient, Patient, updatePatientSchema, UpdatePatient } from "@repo/db/shared/schemas"; import { useAuth } from "@/hooks/use-auth"; import { Form, @@ -21,6 +22,26 @@ import { SelectValue, } from "@/components/ui/select"; +const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + appointments: true, +}); +type Patient = z.infer; + +const insertPatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + id: true, + createdAt: true, +}); +type InsertPatient = z.infer; + +const updatePatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + id: true, + createdAt: true, + userId: true, +}).partial(); + +type UpdatePatient = z.infer; + + interface PatientFormProps { patient?: Patient; extractedInfo?: { diff --git a/apps/Frontend/src/components/patients/patient-table.tsx b/apps/Frontend/src/components/patients/patient-table.tsx index b1d3731..e5f5de7 100644 --- a/apps/Frontend/src/components/patients/patient-table.tsx +++ b/apps/Frontend/src/components/patients/patient-table.tsx @@ -1,5 +1,4 @@ import { useState } from "react"; -import { Patient } from "@shared/schema"; import { Table, TableBody, @@ -26,6 +25,15 @@ import { PaginationNext, PaginationPrevious, } from "@/components/ui/pagination"; +// import { Patient } from "@repo/db/shared/schemas"; +import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas"; +import {z} from "zod"; + +const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + appointments: true, +}); +type Patient = z.infer; + interface PatientTableProps { patients: Patient[]; diff --git a/apps/Frontend/src/components/ui/badge.tsx b/apps/Frontend/src/components/ui/badge.tsx index f000e3e..045d35a 100644 --- a/apps/Frontend/src/components/ui/badge.tsx +++ b/apps/Frontend/src/components/ui/badge.tsx @@ -15,6 +15,8 @@ const badgeVariants = cva( destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", outline: "text-foreground", + success: "border-transparent bg-success text-success-foreground hover:bg-success/80", // ✅ Added success variant + warning: "border-transparent bg-warning text-warning-foreground hover:bg-warning/80", // ✅ Added warning variant }, }, defaultVariants: { diff --git a/apps/Frontend/src/hooks/use-auth.tsx b/apps/Frontend/src/hooks/use-auth.tsx index 05c5109..a8e77f4 100644 --- a/apps/Frontend/src/hooks/use-auth.tsx +++ b/apps/Frontend/src/hooks/use-auth.tsx @@ -4,9 +4,25 @@ import { useMutation, UseMutationResult, } from "@tanstack/react-query"; -import { insertUserSchema, User as SelectUser, InsertUser } from "@shared/schema"; +// import { insertUserSchema, User as SelectUser, InsertUser } from "@repo/db/shared/schemas"; +import { UserUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas"; +import {z} from "zod"; import { getQueryFn, apiRequest, queryClient } from "../lib/queryClient"; -import { useToast } from "../hooks/use-toast"; +import { useToast } from "@/hooks/use-toast"; + +type SelectUser = z.infer; + +const insertUserSchema = (UserUncheckedCreateInputObjectSchema as unknown as z.ZodObject<{ + username: z.ZodString; + password: z.ZodString; + }>).pick({ + username: true, + password: true, +}); + +type InsertUser = z.infer; + + type AuthContextType = { user: SelectUser | null; @@ -17,7 +33,11 @@ type AuthContextType = { registerMutation: UseMutationResult; }; -type LoginData = Pick; +// type LoginData = Pick; +type LoginData = { + username: InsertUser["username"]; + password: InsertUser["password"]; +}; export const AuthContext = createContext(null); export function AuthProvider({ children }: { children: ReactNode }) { diff --git a/apps/Frontend/src/hooks/use-toast.ts b/apps/Frontend/src/hooks/use-toast.ts index 46bf0cb..2c14125 100644 --- a/apps/Frontend/src/hooks/use-toast.ts +++ b/apps/Frontend/src/hooks/use-toast.ts @@ -3,7 +3,7 @@ import * as React from "react" import type { ToastActionElement, ToastProps, -} from "../components/ui/toast" +} from "@/components/ui/toast" const TOAST_LIMIT = 1 const TOAST_REMOVE_DELAY = 1000000 diff --git a/apps/Frontend/src/index.css b/apps/Frontend/src/index.css index 478acbb..0a14cef 100644 --- a/apps/Frontend/src/index.css +++ b/apps/Frontend/src/index.css @@ -1,3 +1,3 @@ @import "tailwindcss"; @import "@repo/tailwind-config"; -@import "@repo/ui/styles.css"; +@import "@repo/ui/styles.css"; \ No newline at end of file diff --git a/apps/Frontend/src/lib/protected-route.tsx b/apps/Frontend/src/lib/protected-route.tsx index 8442e18..bdb4614 100644 --- a/apps/Frontend/src/lib/protected-route.tsx +++ b/apps/Frontend/src/lib/protected-route.tsx @@ -1,4 +1,4 @@ -import { useAuth } from "../hooks/use-auth"; +import { useAuth } from "@/hooks/use-auth"; import { Loader2 } from "lucide-react"; import { Redirect, Route } from "wouter"; diff --git a/apps/Frontend/src/main.tsx b/apps/Frontend/src/main.tsx index 69ebb37..de2ba7d 100644 --- a/apps/Frontend/src/main.tsx +++ b/apps/Frontend/src/main.tsx @@ -1,6 +1,6 @@ import { createRoot } from 'react-dom/client' import './index.css' -import App from './App.tsx' +import App from './App' document.title = "DentalConnect - Patient Management System"; diff --git a/apps/Frontend/src/pages/appointments-page.tsx b/apps/Frontend/src/pages/appointments-page.tsx index 6bb08e3..e6e5e38 100644 --- a/apps/Frontend/src/pages/appointments-page.tsx +++ b/apps/Frontend/src/pages/appointments-page.tsx @@ -1,15 +1,15 @@ -import { useState, useEffect } from "react"; +import { useState, useRef, useEffect } from "react"; import { useQuery, useMutation } from "@tanstack/react-query"; -import { format, addDays, startOfToday, addMinutes } from "date-fns"; -import { TopAppBar } from "../components/layout/top-app-bar"; -import { Sidebar } from "../components/layout/sidebar"; -import { AddAppointmentModal } from "../components/appointments/add-appointment-modal"; -import { ClaimModal } from "../components/claims/claim-modal"; -import { Button } from "../components/ui/button"; -import { z } from "zod"; +import { format, addDays, parse, startOfToday, startOfDay, addMinutes, isEqual } from "date-fns"; +import { TopAppBar } from "@/components/layout/top-app-bar"; +import { Sidebar } from "@/components/layout/sidebar"; +import { AddAppointmentModal } from "@/components/appointments/add-appointment-modal"; +import { ClaimModal } from "@/components/claims/claim-modal"; +import { Button } from "@/components/ui/button"; import { Calendar as CalendarIcon, Plus, + Users, ChevronLeft, ChevronRight, RefreshCw, @@ -17,17 +17,42 @@ import { Trash2, FileText } from "lucide-react"; -import { useToast } from "../hooks/use-toast"; -import { Calendar } from "../components/ui/calendar"; -import { apiRequest, queryClient } from "../lib/queryClient"; -import { useAuth } from "../hooks/use-auth"; -import { Card, CardContent, CardHeader, CardDescription, CardTitle } from "../components/ui/card"; +import { useToast } from "@/hooks/use-toast"; +import { z } from "zod"; +import { Calendar } from "@/components/ui/calendar"; +import { AppointmentUncheckedCreateInputObjectSchema, PatientUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas"; +// import { Appointment, InsertAppointment, UpdateAppointment, Patient } from "@repo/db/shared/schemas"; +import { apiRequest, queryClient } from "@/lib/queryClient"; +import { useAuth } from "@/hooks/use-auth"; +import { Card, CardContent, CardHeader, CardDescription, CardTitle } from "@/components/ui/card"; import { DndProvider, useDrag, useDrop } from 'react-dnd'; import { HTML5Backend } from 'react-dnd-html5-backend'; import { Menu, Item, useContextMenu } from 'react-contexify'; import 'react-contexify/ReactContexify.css'; import { useLocation } from "wouter"; +//creating types out of schema auto generated. +type Appointment = z.infer; + +const insertAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + id: true, + createdAt: true, +}); +type InsertAppointment = z.infer; + +const updateAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + id: true, + createdAt: true, +}).partial(); +type UpdateAppointment = z.infer; + +const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject).omit({ + appointments: true, +}); +type Patient = z.infer; + + + // Define types for scheduling interface TimeSlot { time: string; @@ -42,13 +67,13 @@ interface Staff { } interface ScheduledAppointment { - id: number; + id?: number; patientId: number; patientName: string; staffId: string; - date: string; - startTime: string; - endTime: string; + date: string | Date; // Allow both string and Date + startTime: string | Date; // Allow both string and Date + endTime: string | Date; // Allow both string and Date status: string | null; type: string; } @@ -63,7 +88,7 @@ export default function AppointmentsPage() { const [isClaimModalOpen, setIsClaimModalOpen] = useState(false); const [claimAppointmentId, setClaimAppointmentId] = useState(null); const [claimPatientId, setClaimPatientId] = useState(null); - const [editingAppointment, setEditingAppointment] = useState(undefined); + const [editingAppointment, setEditingAppointment] = useState(undefined); const [selectedDate, setSelectedDate] = useState(startOfToday()); const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); const [location] = useLocation(); @@ -96,10 +121,10 @@ export default function AppointmentsPage() { // Fetch appointments const { - data: appointments, + data: appointments = [] as Appointment[], isLoading: isLoadingAppointments, refetch: refetchAppointments, - } = useQuery({ + } = useQuery({ queryKey: ["/api/appointments"], enabled: !!user, }); @@ -108,7 +133,7 @@ export default function AppointmentsPage() { const { data: patients = [], isLoading: isLoadingPatients, - } = useQuery({ + } = useQuery({ queryKey: ["/api/patients"], enabled: !!user, }); @@ -167,8 +192,7 @@ export default function AppointmentsPage() { if (newPatientId) { const patientId = parseInt(newPatientId); // Find the patient in our list - const patient = patients.find((p: { id: number }) => p.id === patientId) ?? undefined; - + const patient = (patients as Patient[]).find(p => p.id === patientId); if (patient) { toast({ @@ -177,7 +201,6 @@ export default function AppointmentsPage() { }); // Select first available staff member - const staffId = staffMembers[0]!.id; // Find first time slot today (9:00 AM is a common starting time) @@ -207,7 +230,7 @@ export default function AppointmentsPage() { // Create appointment mutation const createAppointmentMutation = useMutation({ - mutationFn: async (appointment: any) => { + mutationFn: async (appointment: InsertAppointment) => { const res = await apiRequest("POST", "/api/appointments", appointment); return await res.json(); }, @@ -230,7 +253,7 @@ export default function AppointmentsPage() { // Update appointment mutation const updateAppointmentMutation = useMutation({ - mutationFn: async ({ id, appointment }: { id: number; appointment: any }) => { + mutationFn: async ({ id, appointment }: { id: number; appointment: UpdateAppointment }) => { const res = await apiRequest("PUT", `/api/appointments/${id}`, appointment); return await res.json(); }, @@ -274,7 +297,7 @@ export default function AppointmentsPage() { }); // Handle appointment submission (create or update) - const handleAppointmentSubmit = (appointmentData: any) => { + const handleAppointmentSubmit = (appointmentData: InsertAppointment | UpdateAppointment) => { // Make sure the date is for the selected date const updatedData = { ...appointmentData, @@ -285,13 +308,13 @@ export default function AppointmentsPage() { if (editingAppointment && 'id' in editingAppointment && typeof editingAppointment.id === 'number') { updateAppointmentMutation.mutate({ id: editingAppointment.id, - appointment: updatedData as any, + appointment: updatedData as unknown as UpdateAppointment, }); } else { // This is a new appointment if (user) { createAppointmentMutation.mutate({ - ...updatedData as any, + ...updatedData as unknown as InsertAppointment, userId: user.id }); } @@ -299,7 +322,7 @@ export default function AppointmentsPage() { }; // Handle edit appointment - const handleEditAppointment = (appointment: any) => { + const handleEditAppointment = (appointment: Appointment) => { setEditingAppointment(appointment); setIsAddModalOpen(true); }; @@ -319,21 +342,19 @@ export default function AppointmentsPage() { const formattedDate = format(selectedDate, 'MMMM d, yyyy'); // Filter appointments for the selected date - const selectedDateAppointments = appointments.filter((apt: { date: string }) => + const selectedDateAppointments = appointments.filter(apt => apt.date === format(selectedDate, 'yyyy-MM-dd') ); - // Add debugging logs console.log("Selected date:", format(selectedDate, 'yyyy-MM-dd')); console.log("All appointments:", appointments); console.log("Filtered appointments for selected date:", selectedDateAppointments); - - + // Process appointments for the scheduler view - const processedAppointments: ScheduledAppointment[] = selectedDateAppointments.map((apt: any) => { + const processedAppointments: ScheduledAppointment[] = selectedDateAppointments.map(apt => { // Find patient name - const patient = patients.find((p: any) => p.id === apt.patientId); + const patient = patients.find(p => p.id === apt.patientId); const patientName = patient ? `${patient.firstName} ${patient.lastName}` : 'Unknown Patient'; // Try to determine the staff from the notes or title @@ -376,7 +397,9 @@ export default function AppointmentsPage() { const processed = { ...apt, patientName, - staffId + staffId, + status: apt.status ?? null, // Default to null if status is undefined + date: apt.date instanceof Date ? apt.date.toISOString() : apt.date, // Ensure d }; console.log("Processed appointment:", processed); @@ -393,7 +416,8 @@ export default function AppointmentsPage() { // In a real application, you might want to show multiple or stack them const appointmentsAtSlot = processedAppointments.filter(apt => { // Fix time format comparison - the database adds ":00" seconds to the time - const dbTime = apt.startTime.substring(0, 5); // Get just HH:MM, removing seconds + const dbTime = typeof apt.startTime === 'string' ? apt.startTime.substring(0, 5) : ''; + const timeMatches = dbTime === timeSlot.time; const staffMatches = apt.staffId === staffId; @@ -417,7 +441,7 @@ export default function AppointmentsPage() { // Handle moving an appointment to a new time slot and staff const handleMoveAppointment = (appointmentId: number, newTimeSlot: TimeSlot, newStaffId: string) => { - const appointment = appointments.find((a:any) => a.id === appointmentId); + const appointment = appointments.find(a => a.id === appointmentId); if (!appointment) return; // Calculate new end time (30 minutes from start) @@ -434,7 +458,7 @@ export default function AppointmentsPage() { // Update appointment data // Make sure we handle the time format correctly - backend expects HH:MM but stores as HH:MM:SS - const updatedAppointment: any = { + const updatedAppointment: UpdateAppointment = { ...appointment, startTime: newTimeSlot.time, // Already in HH:MM format endTime: endTime, // Already in HH:MM format @@ -475,7 +499,6 @@ export default function AppointmentsPage() { return (
} // Type assertion to make TypeScript happy - className={`${staff.color} border border-white shadow-md text-white rounded p-1 text-xs h-full overflow-hidden cursor-move relative ${ isDragging ? 'opacity-50' : 'opacity-100' }`} @@ -483,14 +506,14 @@ export default function AppointmentsPage() { onClick={(e) => { // Only allow edit on click if we're not dragging if (!isDragging) { - const fullAppointment = appointments.find((a:any) => a.id === appointment.id); + const fullAppointment = appointments.find(a => a.id === appointment.id); if (fullAppointment) { e.stopPropagation(); handleEditAppointment(fullAppointment); } } }} - onContextMenu={(e) => handleContextMenu(e, appointment.id)} + onContextMenu={(e) => handleContextMenu(e, appointment.id ?? 0)} >
@@ -531,8 +554,6 @@ export default function AppointmentsPage() { return ( } - - key={`${timeSlot.time}-${staffId}`} className={`px-1 py-1 border relative h-14 ${isOver && canDrop ? 'bg-green-100' : ''}`} > @@ -583,7 +604,7 @@ export default function AppointmentsPage() { { - const fullAppointment = appointments.find((a:any) => a.id === props.appointmentId); + const fullAppointment = appointments.find(a => a.id === props.appointmentId); if (fullAppointment) { handleEditAppointment(fullAppointment); } @@ -596,14 +617,14 @@ export default function AppointmentsPage() { { - const fullAppointment = appointments.find((a:any) => a.id === props.appointmentId); + const fullAppointment = appointments.find(a => a.id === props.appointmentId); if (fullAppointment) { // Set the appointment and patient IDs for the claim modal - setClaimAppointmentId(fullAppointment.id); + setClaimAppointmentId(fullAppointment.id ?? null); setClaimPatientId(fullAppointment.patientId); // Find the patient name for the toast notification - const patient = patients.find((p:any) => p.id === fullAppointment.patientId); + const patient = patients.find(p => p.id === fullAppointment.patientId); const patientName = patient ? `${patient.firstName} ${patient.lastName}` : `Patient #${fullAppointment.patientId}`; // Show a toast notification diff --git a/apps/Frontend/src/pages/auth-page.tsx b/apps/Frontend/src/pages/auth-page.tsx index 900ab46..bfb831e 100644 --- a/apps/Frontend/src/pages/auth-page.tsx +++ b/apps/Frontend/src/pages/auth-page.tsx @@ -1,11 +1,12 @@ import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { z } from "zod"; -import { UserCreateOneSchema } from "@repo/db/shared"; +import { UserUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas"; +// import { insertUserSchema } from "@repo/db/shared/schemas"; import { useState } from "react"; -import { useAuth } from "../hooks/use-auth"; +import { useAuth } from "@/hooks/use-auth"; import { Redirect } from "wouter"; -import { Button } from "../components/ui/button"; +import { Button } from "@/components/ui/button"; import { Form, FormControl, @@ -13,18 +14,25 @@ import { FormItem, FormLabel, FormMessage, -} from "../components/ui/form"; -import { Input } from "../components/ui/input"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "../components/ui/tabs"; -import { Checkbox } from "../components/ui/checkbox"; -import { Card, CardContent } from "../components/ui/card"; +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Card, CardContent } from "@/components/ui/card"; import { BriefcaseMedical, CheckCircle, Torus } from "lucide-react"; +import { CheckedState } from "@radix-ui/react-checkbox"; -const loginSchema = UserCreateOneSchema.extend({ +const insertUserSchema = (UserUncheckedCreateInputObjectSchema as unknown as z.ZodObject).pick({ + username: true, + password: true, +}); + +const loginSchema = (insertUserSchema as unknown as z.ZodObject).extend({ rememberMe: z.boolean().optional(), }); -const registerSchema = UserCreateOne.extend({ + +const registerSchema = (insertUserSchema as unknown as z.ZodObject).extend({ confirmPassword: z.string().min(6, { message: "Password must be at least 6 characters long", }), @@ -143,7 +151,7 @@ export default function AuthPage() {