frontend tailwind half working without external styling
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
<title>Dental Management</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -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",
|
||||
|
||||
3
apps/Frontend/postcss.config.js
Normal file
3
apps/Frontend/postcss.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
import { postcssConfig } from "@repo/tailwind-config/postcss";
|
||||
|
||||
export default postcssConfig;
|
||||
@@ -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<typeof AppointmentUncheckedCreateInputObjectSchema>;
|
||||
|
||||
const insertAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
});
|
||||
type InsertAppointment = z.infer<typeof insertAppointmentSchema>;
|
||||
|
||||
const updateAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
}).partial();
|
||||
type UpdateAppointment = z.infer<typeof updateAppointmentSchema>;
|
||||
|
||||
const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
appointments: true,
|
||||
});
|
||||
type Patient = z.infer<typeof PatientSchema>;
|
||||
|
||||
interface AddAppointmentModalProps {
|
||||
open: boolean;
|
||||
|
||||
@@ -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<typeof AppointmentUncheckedCreateInputObjectSchema>;
|
||||
|
||||
const insertAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
});
|
||||
type InsertAppointment = z.infer<typeof insertAppointmentSchema>;
|
||||
|
||||
const updateAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
}).partial();
|
||||
type UpdateAppointment = z.infer<typeof updateAppointmentSchema>;
|
||||
|
||||
const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
appointments: true,
|
||||
});
|
||||
type Patient = z.infer<typeof PatientSchema>;
|
||||
|
||||
|
||||
// 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<typeof appointmentSchema>;
|
||||
@@ -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({
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
import { useState } from "react";
|
||||
import { format } from "date-fns";
|
||||
import { Appointment, Patient } from "@shared/schema";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
@@ -17,7 +15,7 @@ import {
|
||||
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<typeof AppointmentUncheckedCreateInputObjectSchema>;
|
||||
|
||||
const PatientSchema = (
|
||||
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
|
||||
).omit({
|
||||
appointments: true,
|
||||
});
|
||||
type Patient = z.infer<typeof PatientSchema>;
|
||||
|
||||
interface AppointmentTableProps {
|
||||
appointments: Appointment[];
|
||||
@@ -39,17 +52,30 @@ export function AppointmentTable({
|
||||
appointments,
|
||||
patients,
|
||||
onEdit,
|
||||
onDelete
|
||||
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<string, { variant: "default" | "secondary" | "destructive" | "outline" | "success"; label: string }> = {
|
||||
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 };
|
||||
const config = statusConfig[status] || {
|
||||
variant: "default",
|
||||
label: status,
|
||||
};
|
||||
|
||||
return (
|
||||
<Badge variant={config.variant as any}>
|
||||
{config.label}
|
||||
</Badge>
|
||||
);
|
||||
return <Badge variant={config.variant as any}>{config.label}</Badge>;
|
||||
};
|
||||
|
||||
// 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({
|
||||
<TableCell>
|
||||
<div className="flex items-center">
|
||||
<Clock className="mr-2 h-4 w-4 text-muted-foreground" />
|
||||
{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)} */}
|
||||
</div>
|
||||
</TableCell>
|
||||
<TableCell className="capitalize">
|
||||
{appointment.type.replace('-', ' ')}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{getStatusBadge(appointment.status!)}
|
||||
{appointment.type.replace("-", " ")}
|
||||
</TableCell>
|
||||
<TableCell>{getStatusBadge(appointment.status!)}</TableCell>
|
||||
<TableCell className="text-right">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
@@ -133,7 +164,13 @@ export function AppointmentTable({
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
onClick={() => onDelete(appointment.id)}
|
||||
onClick={() => {
|
||||
if (typeof appointment.id === "number") {
|
||||
onDelete(appointment.id);
|
||||
} else {
|
||||
console.error("Invalid appointment ID");
|
||||
}
|
||||
}}
|
||||
className="text-destructive focus:text-destructive"
|
||||
>
|
||||
<Trash2 className="mr-2 h-4 w-4" />
|
||||
|
||||
@@ -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<any>).omit({
|
||||
appointments: true,
|
||||
});
|
||||
type Patient = z.infer<typeof PatientSchema>;
|
||||
|
||||
interface ClaimFormProps {
|
||||
patientId: number;
|
||||
|
||||
@@ -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<any>).omit({
|
||||
appointments: true,
|
||||
});
|
||||
type Patient = z.infer<typeof PatientSchema>;
|
||||
|
||||
interface ClaimModalProps {
|
||||
open: boolean;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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<any>).omit({
|
||||
appointments: true,
|
||||
});
|
||||
type Patient = z.infer<typeof PatientSchema>;
|
||||
|
||||
const insertPatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
});
|
||||
type InsertPatient = z.infer<typeof insertPatientSchema>;
|
||||
|
||||
const updatePatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
userId: true,
|
||||
}).partial();
|
||||
|
||||
type UpdatePatient = z.infer<typeof updatePatientSchema>;
|
||||
|
||||
|
||||
interface AddPatientModalProps {
|
||||
open: boolean;
|
||||
|
||||
@@ -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<any>).omit({
|
||||
appointments: true,
|
||||
});
|
||||
type Patient = z.infer<typeof PatientSchema>;
|
||||
|
||||
const insertPatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
});
|
||||
type InsertPatient = z.infer<typeof insertPatientSchema>;
|
||||
|
||||
const updatePatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
userId: true,
|
||||
}).partial();
|
||||
|
||||
type UpdatePatient = z.infer<typeof updatePatientSchema>;
|
||||
|
||||
|
||||
interface PatientFormProps {
|
||||
patient?: Patient;
|
||||
extractedInfo?: {
|
||||
|
||||
@@ -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<any>).omit({
|
||||
appointments: true,
|
||||
});
|
||||
type Patient = z.infer<typeof PatientSchema>;
|
||||
|
||||
|
||||
interface PatientTableProps {
|
||||
patients: Patient[];
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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<typeof UserUncheckedCreateInputObjectSchema>;
|
||||
|
||||
const insertUserSchema = (UserUncheckedCreateInputObjectSchema as unknown as z.ZodObject<{
|
||||
username: z.ZodString;
|
||||
password: z.ZodString;
|
||||
}>).pick({
|
||||
username: true,
|
||||
password: true,
|
||||
});
|
||||
|
||||
type InsertUser = z.infer<typeof insertUserSchema>;
|
||||
|
||||
|
||||
|
||||
type AuthContextType = {
|
||||
user: SelectUser | null;
|
||||
@@ -17,7 +33,11 @@ type AuthContextType = {
|
||||
registerMutation: UseMutationResult<SelectUser, Error, InsertUser>;
|
||||
};
|
||||
|
||||
type LoginData = Pick<InsertUser, "username" | "password">;
|
||||
// type LoginData = Pick<InsertUser, "username" | "password">;
|
||||
type LoginData = {
|
||||
username: InsertUser["username"];
|
||||
password: InsertUser["password"];
|
||||
};
|
||||
|
||||
export const AuthContext = createContext<AuthContextType | null>(null);
|
||||
export function AuthProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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<typeof AppointmentUncheckedCreateInputObjectSchema>;
|
||||
|
||||
const insertAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
});
|
||||
type InsertAppointment = z.infer<typeof insertAppointmentSchema>;
|
||||
|
||||
const updateAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
}).partial();
|
||||
type UpdateAppointment = z.infer<typeof updateAppointmentSchema>;
|
||||
|
||||
const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
appointments: true,
|
||||
});
|
||||
type Patient = z.infer<typeof PatientSchema>;
|
||||
|
||||
|
||||
|
||||
// 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<number | null>(null);
|
||||
const [claimPatientId, setClaimPatientId] = useState<number | null>(null);
|
||||
const [editingAppointment, setEditingAppointment] = useState<any | undefined>(undefined);
|
||||
const [editingAppointment, setEditingAppointment] = useState<Appointment | undefined>(undefined);
|
||||
const [selectedDate, setSelectedDate] = useState<Date>(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<any>({
|
||||
} = useQuery<Appointment[]>({
|
||||
queryKey: ["/api/appointments"],
|
||||
enabled: !!user,
|
||||
});
|
||||
@@ -108,7 +133,7 @@ export default function AppointmentsPage() {
|
||||
const {
|
||||
data: patients = [],
|
||||
isLoading: isLoadingPatients,
|
||||
} = useQuery<any>({
|
||||
} = useQuery<Patient[]>({
|
||||
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 (
|
||||
<div
|
||||
ref={drag as unknown as React.RefObject<HTMLDivElement>} // 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)}
|
||||
>
|
||||
<div className="font-bold truncate flex items-center gap-1">
|
||||
<Move className="h-3 w-3" />
|
||||
@@ -531,8 +554,6 @@ export default function AppointmentsPage() {
|
||||
return (
|
||||
<td
|
||||
ref={drop as unknown as React.RefObject<HTMLTableCellElement>}
|
||||
|
||||
|
||||
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() {
|
||||
<Menu id={APPOINTMENT_CONTEXT_MENU_ID} animation="fade">
|
||||
<Item
|
||||
onClick={({ props }) => {
|
||||
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() {
|
||||
</Item>
|
||||
<Item
|
||||
onClick={({ props }) => {
|
||||
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
|
||||
|
||||
@@ -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<any>).pick({
|
||||
username: true,
|
||||
password: true,
|
||||
});
|
||||
|
||||
const loginSchema = (insertUserSchema as unknown as z.ZodObject<any>).extend({
|
||||
rememberMe: z.boolean().optional(),
|
||||
});
|
||||
|
||||
const registerSchema = UserCreateOne.extend({
|
||||
|
||||
const registerSchema = (insertUserSchema as unknown as z.ZodObject<any>).extend({
|
||||
confirmPassword: z.string().min(6, {
|
||||
message: "Password must be at least 6 characters long",
|
||||
}),
|
||||
@@ -143,7 +151,7 @@ export default function AuthPage() {
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="remember-me"
|
||||
checked={field.value}
|
||||
checked={field.value as CheckedState}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
<label
|
||||
@@ -216,6 +224,8 @@ export default function AuthPage() {
|
||||
placeholder="••••••••"
|
||||
type="password"
|
||||
{...field}
|
||||
value={typeof field.value === 'string' ? field.value : ''}
|
||||
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -230,7 +240,7 @@ export default function AuthPage() {
|
||||
<FormItem className="flex items-start space-x-2 mt-4">
|
||||
<FormControl>
|
||||
<Checkbox
|
||||
checked={field.value}
|
||||
checked={field.value as CheckedState}
|
||||
onCheckedChange={field.onChange}
|
||||
/>
|
||||
</FormControl>
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import { useState } from "react";
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { format } from "date-fns";
|
||||
import { TopAppBar } from "../components/layout/top-app-bar";
|
||||
import { Sidebar } from "../components/layout/sidebar";
|
||||
import { StatCard } from "../components/ui/stat-card";
|
||||
import { PatientTable } from "../components/patients/patient-table";
|
||||
import { AddPatientModal } from "../components/patients/add-patient-modal";
|
||||
import { AddAppointmentModal } from "../components/appointments/add-appointment-modal";
|
||||
import { Card, CardContent } from "../components/ui/card";
|
||||
import { Button } from "../components/ui/button";
|
||||
import { useToast } from "../hooks/use-toast";
|
||||
import { useAuth } from "../hooks/use-auth";
|
||||
import { apiRequest, queryClient } from "../lib/queryClient";
|
||||
import { InsertPatient, Patient, UpdatePatient, Appointment, InsertAppointment, UpdateAppointment } from "@repo/db/schema";
|
||||
import { TopAppBar } from "@/components/layout/top-app-bar";
|
||||
import { Sidebar } from "@/components/layout/sidebar";
|
||||
import { StatCard } from "@/components/ui/stat-card";
|
||||
import { PatientTable } from "@/components/patients/patient-table";
|
||||
import { AddPatientModal } from "@/components/patients/add-patient-modal";
|
||||
import { AddAppointmentModal } from "@/components/appointments/add-appointment-modal";
|
||||
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { apiRequest, queryClient } from "@/lib/queryClient";
|
||||
import { AppointmentUncheckedCreateInputObjectSchema, PatientUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas";
|
||||
// import { InsertPatient, Patient, UpdatePatient, Appointment, InsertAppointment, UpdateAppointment } from "@repo/db/shared/schemas";
|
||||
import { Users, Calendar, CheckCircle, CreditCard, Plus, Clock } from "lucide-react";
|
||||
import { Link } from "wouter";
|
||||
import {
|
||||
@@ -21,7 +22,43 @@ import {
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "../components/ui/dialog";
|
||||
} from "@/components/ui/dialog";
|
||||
import {z} from "zod";
|
||||
|
||||
//creating types out of schema auto generated.
|
||||
type Appointment = z.infer<typeof AppointmentUncheckedCreateInputObjectSchema>;
|
||||
|
||||
const insertAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
});
|
||||
type InsertAppointment = z.infer<typeof insertAppointmentSchema>;
|
||||
|
||||
const updateAppointmentSchema = (AppointmentUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
}).partial();
|
||||
type UpdateAppointment = z.infer<typeof updateAppointmentSchema>;
|
||||
|
||||
const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
appointments: true,
|
||||
});
|
||||
type Patient = z.infer<typeof PatientSchema>;
|
||||
|
||||
const insertPatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
});
|
||||
type InsertPatient = z.infer<typeof insertPatientSchema>;
|
||||
|
||||
const updatePatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
userId: true,
|
||||
}).partial();
|
||||
|
||||
type UpdatePatient = z.infer<typeof updatePatientSchema>;
|
||||
|
||||
|
||||
export default function Dashboard() {
|
||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||
@@ -174,7 +211,7 @@ export default function Dashboard() {
|
||||
|
||||
// Handle appointment submission (create or update)
|
||||
const handleAppointmentSubmit = (appointmentData: InsertAppointment | UpdateAppointment) => {
|
||||
if (selectedAppointment) {
|
||||
if (selectedAppointment && typeof selectedAppointment.id === 'number') {
|
||||
updateAppointmentMutation.mutate({
|
||||
id: selectedAppointment.id,
|
||||
appointment: appointmentData as UpdateAppointment,
|
||||
@@ -274,7 +311,7 @@ export default function Dashboard() {
|
||||
{patient ? `${patient.firstName} ${patient.lastName}` : 'Unknown Patient'}
|
||||
</h3>
|
||||
<div className="text-sm text-gray-500 flex items-center space-x-2">
|
||||
<span>{appointment.startTime} - {appointment.endTime}</span>
|
||||
<span>{new Date(appointment.startTime).toLocaleString()} - {new Date(appointment.endTime).toLocaleString()}</span>
|
||||
<span>•</span>
|
||||
<span>{appointment.type.charAt(0).toUpperCase() + appointment.type.slice(1)}</span>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Card, CardContent } from "../components/ui/card";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { AlertCircle } from "lucide-react";
|
||||
|
||||
export default function NotFound() {
|
||||
|
||||
@@ -1,18 +1,41 @@
|
||||
import { useState, useMemo, useRef } from "react";
|
||||
import { useQuery, useMutation } from "@tanstack/react-query";
|
||||
import { TopAppBar } from "../components/layout/top-app-bar";
|
||||
import { Sidebar } from "../components/layout/sidebar";
|
||||
import { PatientTable } from "../components/patients/patient-table";
|
||||
import { AddPatientModal } from "../components/patients/add-patient-modal";
|
||||
import { PatientSearch, SearchCriteria } from "../components/patients/patient-search";
|
||||
import { FileUploadZone } from "../components/file-upload/file-upload-zone";
|
||||
import { Button } from "../components/ui/button";
|
||||
import { TopAppBar } from "@/components/layout/top-app-bar";
|
||||
import { Sidebar } from "@/components/layout/sidebar";
|
||||
import { PatientTable } from "@/components/patients/patient-table";
|
||||
import { AddPatientModal } from "@/components/patients/add-patient-modal";
|
||||
import { PatientSearch, SearchCriteria } from "@/components/patients/patient-search";
|
||||
import { FileUploadZone } from "@/components/file-upload/file-upload-zone";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Plus, RefreshCw, File, FilePlus } from "lucide-react";
|
||||
import { useToast } from "../hooks/use-toast";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "../components/ui/card";
|
||||
import { Patient, InsertPatient, UpdatePatient } from "@shared/schema";
|
||||
import { apiRequest, queryClient } from "../lib/queryClient";
|
||||
import { useAuth } from "../hooks/use-auth";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { PatientUncheckedCreateInputObjectSchema } from "@repo/db/shared/schemas";
|
||||
// import { Patient, InsertPatient, UpdatePatient } from "@repo/db/shared/schemas";
|
||||
import { apiRequest, queryClient } from "@/lib/queryClient";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import {z} from "zod";
|
||||
|
||||
|
||||
const PatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
appointments: true,
|
||||
});
|
||||
type Patient = z.infer<typeof PatientSchema>;
|
||||
|
||||
const insertPatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
});
|
||||
type InsertPatient = z.infer<typeof insertPatientSchema>;
|
||||
|
||||
const updatePatientSchema = (PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
userId: true,
|
||||
}).partial();
|
||||
|
||||
type UpdatePatient = z.infer<typeof updatePatientSchema>;
|
||||
|
||||
|
||||
// Type for the ref to access modal methods
|
||||
type AddPatientModalRef = {
|
||||
|
||||
4
apps/Frontend/tailwind.config.ts
Normal file
4
apps/Frontend/tailwind.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
const tailwindConfig = require("tailwindcss");
|
||||
type Config = typeof tailwindConfig;
|
||||
|
||||
export default Config;
|
||||
@@ -1,11 +1,13 @@
|
||||
{
|
||||
"extends": "@repo/typescript-config/nextjs.json",
|
||||
"extends": "@repo/typescript-config/base.json",
|
||||
"compilerOptions": {
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next"
|
||||
}
|
||||
]
|
||||
"moduleResolution": "node",
|
||||
"module": "ESNext",
|
||||
"jsx": "preserve",
|
||||
"baseUrl": ".", // This tells TypeScript where to start resolving paths
|
||||
"paths": {
|
||||
"@/*": ["src/*"] // The @ alias will point to src folder inside the Frontend app
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import path from 'path';
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
})
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.resolve(__dirname, 'src')
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
518
package-lock.json
generated
518
package-lock.json
generated
@@ -12,7 +12,9 @@
|
||||
"packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"prisma": "^6.7.0"
|
||||
"@tailwindcss/postcss": "^4.1.6",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"prettier": "^3.5.3",
|
||||
@@ -83,9 +85,12 @@
|
||||
"@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",
|
||||
@@ -101,6 +106,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",
|
||||
@@ -112,6 +118,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",
|
||||
@@ -195,11 +202,22 @@
|
||||
"typescript": "5.8.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@alloc/quick-lru": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
||||
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||
"integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@jridgewell/gen-mapping": "^0.3.5",
|
||||
@@ -1187,11 +1205,22 @@
|
||||
"url": "https://github.com/sponsors/nzakas"
|
||||
}
|
||||
},
|
||||
"node_modules/@isaacs/fs-minipass": {
|
||||
"version": "4.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
|
||||
"integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"minipass": "^7.0.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@jridgewell/gen-mapping": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
|
||||
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/set-array": "^1.2.1",
|
||||
@@ -1215,7 +1244,6 @@
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
|
||||
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
@@ -1648,6 +1676,7 @@
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/config/-/config-6.7.0.tgz",
|
||||
"integrity": "sha512-di8QDdvSz7DLUi3OOcCHSwxRNeW7jtGRUD2+Z3SdNE3A+pPiNT8WgUJoUyOwJmUr5t+JA2W15P78C/N+8RXrOA==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"esbuild": ">=0.12 <1",
|
||||
@@ -1658,12 +1687,14 @@
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.7.0.tgz",
|
||||
"integrity": "sha512-RabHn9emKoYFsv99RLxvfG2GHzWk2ZI1BuVzqYtmMSIcuGboHY5uFt3Q3boOREM9de6z5s3bQoyKeWnq8Fz22w==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.7.0.tgz",
|
||||
"integrity": "sha512-3wDMesnOxPrOsq++e5oKV9LmIiEazFTRFZrlULDQ8fxdub5w4NgRBoxtWbvXmj2nJVCnzuz6eFix3OhIqsZ1jw==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -1677,12 +1708,14 @@
|
||||
"version": "6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.7.0-36.3cff47a7f5d65c3ea74883f1d736e41d68ce91ed.tgz",
|
||||
"integrity": "sha512-EvpOFEWf1KkJpDsBCrih0kg3HdHuaCnXmMn7XFPObpFTzagK1N0Q0FMnYPsEhvARfANP5Ok11QyoTIRA2hgJTA==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/@prisma/fetch-engine": {
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.7.0.tgz",
|
||||
"integrity": "sha512-zLlAGnrkmioPKJR4Yf7NfW3hftcvqeNNEHleMZK9yX7RZSkhmxacAYyfGsCcqRt47jiZ7RKdgE0Wh2fWnm7WsQ==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/debug": "6.7.0",
|
||||
@@ -1754,6 +1787,7 @@
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.7.0.tgz",
|
||||
"integrity": "sha512-i9IH5lO4fQwnMLvQLYNdgVh9TK3PuWBfQd7QLk/YurnAIg+VeADcZDbmhAi4XBBDD+hDif9hrKyASu0hbjwabw==",
|
||||
"devOptional": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@prisma/debug": "6.7.0"
|
||||
@@ -3844,6 +3878,297 @@
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.6.tgz",
|
||||
"integrity": "sha512-ELq+gDMBuRXPJlpE3PEen+1MhnHAQQrh2zF0dI1NXOlEWfr2qWf2CQdr5jl9yANv8RErQaQ2l6nIFO9OSCVq/g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@alloc/quick-lru": "^5.2.0",
|
||||
"@tailwindcss/node": "4.1.6",
|
||||
"@tailwindcss/oxide": "4.1.6",
|
||||
"postcss": "^8.4.41",
|
||||
"tailwindcss": "4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/node": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.6.tgz",
|
||||
"integrity": "sha512-ed6zQbgmKsjsVvodAS1q1Ld2BolEuxJOSyyNc+vhkjdmfNUDCmQnlXBfQkHrlzNmslxHsQU/bFmzcEbv4xXsLg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@ampproject/remapping": "^2.3.0",
|
||||
"enhanced-resolve": "^5.18.1",
|
||||
"jiti": "^2.4.2",
|
||||
"lightningcss": "1.29.2",
|
||||
"magic-string": "^0.30.17",
|
||||
"source-map-js": "^1.2.1",
|
||||
"tailwindcss": "4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.6.tgz",
|
||||
"integrity": "sha512-0bpEBQiGx+227fW4G0fLQ8vuvyy5rsB1YIYNapTq3aRsJ9taF3f5cCaovDjN5pUGKKzcpMrZst/mhNaKAPOHOA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"detect-libc": "^2.0.4",
|
||||
"tar": "^7.4.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@tailwindcss/oxide-android-arm64": "4.1.6",
|
||||
"@tailwindcss/oxide-darwin-arm64": "4.1.6",
|
||||
"@tailwindcss/oxide-darwin-x64": "4.1.6",
|
||||
"@tailwindcss/oxide-freebsd-x64": "4.1.6",
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.6",
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": "4.1.6",
|
||||
"@tailwindcss/oxide-linux-arm64-musl": "4.1.6",
|
||||
"@tailwindcss/oxide-linux-x64-gnu": "4.1.6",
|
||||
"@tailwindcss/oxide-linux-x64-musl": "4.1.6",
|
||||
"@tailwindcss/oxide-wasm32-wasi": "4.1.6",
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": "4.1.6",
|
||||
"@tailwindcss/oxide-win32-x64-msvc": "4.1.6"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide-android-arm64": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.6.tgz",
|
||||
"integrity": "sha512-VHwwPiwXtdIvOvqT/0/FLH/pizTVu78FOnI9jQo64kSAikFSZT7K4pjyzoDpSMaveJTGyAKvDjuhxJxKfmvjiQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide-darwin-arm64": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.6.tgz",
|
||||
"integrity": "sha512-weINOCcqv1HVBIGptNrk7c6lWgSFFiQMcCpKM4tnVi5x8OY2v1FrV76jwLukfT6pL1hyajc06tyVmZFYXoxvhQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide-darwin-x64": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.6.tgz",
|
||||
"integrity": "sha512-3FzekhHG0ww1zQjQ1lPoq0wPrAIVXAbUkWdWM8u5BnYFZgb9ja5ejBqyTgjpo5mfy0hFOoMnMuVDI+7CXhXZaQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide-freebsd-x64": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.6.tgz",
|
||||
"integrity": "sha512-4m5F5lpkBZhVQJq53oe5XgJ+aFYWdrgkMwViHjRsES3KEu2m1udR21B1I77RUqie0ZYNscFzY1v9aDssMBZ/1w==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.6.tgz",
|
||||
"integrity": "sha512-qU0rHnA9P/ZoaDKouU1oGPxPWzDKtIfX7eOGi5jOWJKdxieUJdVV+CxWZOpDWlYTd4N3sFQvcnVLJWJ1cLP5TA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.6.tgz",
|
||||
"integrity": "sha512-jXy3TSTrbfgyd3UxPQeXC3wm8DAgmigzar99Km9Sf6L2OFfn/k+u3VqmpgHQw5QNfCpPe43em6Q7V76Wx7ogIQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide-linux-arm64-musl": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.6.tgz",
|
||||
"integrity": "sha512-8kjivE5xW0qAQ9HX9reVFmZj3t+VmljDLVRJpVBEoTR+3bKMnvC7iLcoSGNIUJGOZy1mLVq7x/gerVg0T+IsYw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide-linux-x64-gnu": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.6.tgz",
|
||||
"integrity": "sha512-A4spQhwnWVpjWDLXnOW9PSinO2PTKJQNRmL/aIl2U/O+RARls8doDfs6R41+DAXK0ccacvRyDpR46aVQJJCoCg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide-linux-x64-musl": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.6.tgz",
|
||||
"integrity": "sha512-YRee+6ZqdzgiQAHVSLfl3RYmqeeaWVCk796MhXhLQu2kJu2COHBkqlqsqKYx3p8Hmk5pGCQd2jTAoMWWFeyG2A==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide-wasm32-wasi": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.6.tgz",
|
||||
"integrity": "sha512-qAp4ooTYrBQ5pk5jgg54/U1rCJ/9FLYOkkQ/nTE+bVMseMfB6O7J8zb19YTpWuu4UdfRf5zzOrNKfl6T64MNrQ==",
|
||||
"bundleDependencies": [
|
||||
"@napi-rs/wasm-runtime",
|
||||
"@emnapi/core",
|
||||
"@emnapi/runtime",
|
||||
"@tybys/wasm-util",
|
||||
"@emnapi/wasi-threads",
|
||||
"tslib"
|
||||
],
|
||||
"cpu": [
|
||||
"wasm32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"dependencies": {
|
||||
"@emnapi/core": "^1.4.3",
|
||||
"@emnapi/runtime": "^1.4.3",
|
||||
"@emnapi/wasi-threads": "^1.0.2",
|
||||
"@napi-rs/wasm-runtime": "^0.2.9",
|
||||
"@tybys/wasm-util": "^0.9.0",
|
||||
"tslib": "^2.8.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.6.tgz",
|
||||
"integrity": "sha512-nqpDWk0Xr8ELO/nfRUDjk1pc9wDJ3ObeDdNMHLaymc4PJBWj11gdPCWZFKSK2AVKjJQC7J2EfmSmf47GN7OuLg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/@tailwindcss/oxide-win32-x64-msvc": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.6.tgz",
|
||||
"integrity": "sha512-5k9xF33xkfKpo9wCvYcegQ21VwIBU1/qEbYlVukfEIyQbEA47uK8AAwS7NVjNE3vHzcmxMYwd0l6L4pPjjm1rQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/detect-libc": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
|
||||
"integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/postcss/node_modules/tailwindcss": {
|
||||
"version": "4.1.6",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.6.tgz",
|
||||
"integrity": "sha512-j0cGLTreM6u4OWzBeLBpycK0WIh8w7kSwcUsQZoGLHZ7xDTdM69lN64AgoIEEwFi0tnhs4wSykUa5YWxAzgFYg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@tailwindcss/typography": {
|
||||
"version": "0.5.16",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz",
|
||||
"integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"lodash.castarray": "^4.4.0",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.merge": "^4.6.2",
|
||||
"postcss-selector-parser": "6.0.10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tailwindcss/vite": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.5.tgz",
|
||||
@@ -4736,6 +5061,43 @@
|
||||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/autoprefixer": {
|
||||
"version": "10.4.21",
|
||||
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz",
|
||||
"integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/postcss/"
|
||||
},
|
||||
{
|
||||
"type": "tidelift",
|
||||
"url": "https://tidelift.com/funding/github/npm/autoprefixer"
|
||||
},
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/ai"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"browserslist": "^4.24.4",
|
||||
"caniuse-lite": "^1.0.30001702",
|
||||
"fraction.js": "^4.3.7",
|
||||
"normalize-range": "^0.1.2",
|
||||
"picocolors": "^1.1.1",
|
||||
"postcss-value-parser": "^4.2.0"
|
||||
},
|
||||
"bin": {
|
||||
"autoprefixer": "bin/autoprefixer"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12 || >=14"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"postcss": "^8.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/available-typed-arrays": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz",
|
||||
@@ -4836,7 +5198,6 @@
|
||||
"version": "4.24.5",
|
||||
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.5.tgz",
|
||||
"integrity": "sha512-FDToo4Wo82hIdgc1CQ+NQD0hEhmpPjrZ3hiUgwgOG6IuTdlpr8jdjyG24P6cNP1yJpTLzS5OcGgSw0xmDU1/Tw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -4972,7 +5333,6 @@
|
||||
"version": "1.0.30001717",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001717.tgz",
|
||||
"integrity": "sha512-auPpttCq6BDEG8ZAuHJIplGw6GODhjw+/11e7IjpnYCxZcW/ONgPs0KVBJ0d1bY3e2+7PRe5RCLyP+PfwVgkYw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -5021,6 +5381,15 @@
|
||||
"uuid": "9.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/chownr": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
|
||||
"integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/ci-info": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz",
|
||||
@@ -5297,6 +5666,18 @@
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/cssesc": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
|
||||
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"cssesc": "bin/cssesc"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
@@ -5701,7 +6082,6 @@
|
||||
"version": "1.5.151",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.151.tgz",
|
||||
"integrity": "sha512-Rl6uugut2l9sLojjS4H4SAr3A4IgACMLgpuEMPYCVcKydzfyPrn5absNRju38IhQOf/NwjJY8OGWjlteqYeBCA==",
|
||||
"dev": true,
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/embla-carousel": {
|
||||
@@ -6006,6 +6386,7 @@
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz",
|
||||
"integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.4"
|
||||
@@ -6018,7 +6399,6 @@
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
|
||||
"integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
@@ -6615,6 +6995,19 @@
|
||||
"integrity": "sha512-bLq+KgbiXdTEoT1zcARrWEpa5z6A/8b7PcDW7Gef3NSisQ+VS7ll2Xbf1E+xsgik0rWub/8u0qP/iTTjj+PhxQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/fraction.js": {
|
||||
"version": "4.3.7",
|
||||
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz",
|
||||
"integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"funding": {
|
||||
"type": "patreon",
|
||||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "11.18.2",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz",
|
||||
@@ -8295,6 +8688,12 @@
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.castarray": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz",
|
||||
"integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
@@ -8323,7 +8722,6 @@
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/lodash.union": {
|
||||
@@ -8363,6 +8761,15 @@
|
||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.17",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
|
||||
"integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/make-dir": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
|
||||
@@ -8521,12 +8928,48 @@
|
||||
"node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/minipass": {
|
||||
"version": "7.1.2",
|
||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/minizlib": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
|
||||
"integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"minipass": "^7.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 18"
|
||||
}
|
||||
},
|
||||
"node_modules/mitt": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
|
||||
"integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mkdirp": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
|
||||
"integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"mkdirp": "dist/cjs/src/bin.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "11.18.1",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
|
||||
@@ -8643,7 +9086,6 @@
|
||||
"version": "2.0.19",
|
||||
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
|
||||
"integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/normalize-package-data": {
|
||||
@@ -8696,6 +9138,15 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/normalize-range": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
|
||||
"integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/npm-bundled": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-2.0.1.tgz",
|
||||
@@ -9426,6 +9877,25 @@
|
||||
"node": "^10 || ^12 || >=14"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-selector-parser": {
|
||||
"version": "6.0.10",
|
||||
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
|
||||
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cssesc": "^3.0.0",
|
||||
"util-deprecate": "^1.0.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postcss-value-parser": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
@@ -9574,6 +10044,7 @@
|
||||
"version": "6.7.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-6.7.0.tgz",
|
||||
"integrity": "sha512-vArg+4UqnQ13CVhc2WUosemwh6hr6cr6FY2uzDvCIFwH8pu8BXVv38PktoMLVjtX7sbYThxbnZF5YiR8sN2clw==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@@ -11067,6 +11538,23 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tar": {
|
||||
"version": "7.4.3",
|
||||
"resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
|
||||
"integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@isaacs/fs-minipass": "^4.0.0",
|
||||
"chownr": "^3.0.0",
|
||||
"minipass": "^7.1.2",
|
||||
"minizlib": "^3.0.1",
|
||||
"mkdirp": "^3.0.1",
|
||||
"yallist": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/tar-stream": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
|
||||
@@ -11083,6 +11571,15 @@
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/tar/node_modules/yallist": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
|
||||
"integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
|
||||
"license": "BlueOak-1.0.0",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/temp-dir": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz",
|
||||
@@ -11630,7 +12127,6 @@
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
|
||||
"integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
"packages/*"
|
||||
],
|
||||
"dependencies": {
|
||||
"prisma": "^6.7.0"
|
||||
"@tailwindcss/postcss": "^4.1.6",
|
||||
"@tailwindcss/typography": "^0.5.16",
|
||||
"tailwindcss-animate": "^1.0.7"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
"type": "commonjs",
|
||||
"exports": {
|
||||
"./client": "./src/index.ts",
|
||||
"./shared" : "./shared/schemas/index.ts"
|
||||
"./shared/schemas" : "./shared/schemas/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"@prisma/client": "^6.7.0",
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
import { pgTable, text, serial, integer, boolean, date, timestamp, time } from "drizzle-orm/pg-core";
|
||||
import { createInsertSchema } from "drizzle-zod";
|
||||
import { z } from "zod";
|
||||
|
||||
// User schema
|
||||
export const users = pgTable("users", {
|
||||
id: serial("id").primaryKey(),
|
||||
username: text("username").notNull().unique(),
|
||||
password: text("password").notNull(),
|
||||
});
|
||||
|
||||
export const insertUserSchema = createInsertSchema(users).pick({
|
||||
username: true,
|
||||
password: true,
|
||||
});
|
||||
|
||||
export type InsertUser = z.infer<typeof insertUserSchema>;
|
||||
export type User = typeof users.$inferSelect;
|
||||
|
||||
// Patient schema
|
||||
export const patients = pgTable("patients", {
|
||||
id: serial("id").primaryKey(),
|
||||
firstName: text("first_name").notNull(),
|
||||
lastName: text("last_name").notNull(),
|
||||
dateOfBirth: date("date_of_birth").notNull(),
|
||||
gender: text("gender").notNull(),
|
||||
phone: text("phone").notNull(),
|
||||
email: text("email"),
|
||||
address: text("address"),
|
||||
city: text("city"),
|
||||
zipCode: text("zip_code"),
|
||||
insuranceProvider: text("insurance_provider"),
|
||||
insuranceId: text("insurance_id"),
|
||||
groupNumber: text("group_number"),
|
||||
policyHolder: text("policy_holder"),
|
||||
allergies: text("allergies"),
|
||||
medicalConditions: text("medical_conditions"),
|
||||
status: text("status").default("active"),
|
||||
userId: integer("user_id").notNull().references(() => users.id),
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
});
|
||||
|
||||
export const insertPatientSchema = createInsertSchema(patients).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
});
|
||||
|
||||
export const updatePatientSchema = createInsertSchema(patients).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
userId: true,
|
||||
}).partial();
|
||||
|
||||
export type InsertPatient = z.infer<typeof insertPatientSchema>;
|
||||
export type UpdatePatient = z.infer<typeof updatePatientSchema>;
|
||||
export type Patient = typeof patients.$inferSelect;
|
||||
|
||||
// Appointment schema
|
||||
export const appointments = pgTable("appointments", {
|
||||
id: serial("id").primaryKey(),
|
||||
patientId: integer("patient_id").notNull().references(() => patients.id),
|
||||
userId: integer("user_id").notNull().references(() => users.id),
|
||||
title: text("title").notNull(), // Added title field
|
||||
date: date("date").notNull(),
|
||||
startTime: time("start_time").notNull(),
|
||||
endTime: time("end_time").notNull(),
|
||||
type: text("type").notNull(), // e.g., "checkup", "cleaning", "filling", etc.
|
||||
notes: text("notes"),
|
||||
status: text("status").default("scheduled"), // "scheduled", "completed", "cancelled", "no-show"
|
||||
createdAt: timestamp("created_at").defaultNow(),
|
||||
});
|
||||
|
||||
export const insertAppointmentSchema = createInsertSchema(appointments).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
});
|
||||
|
||||
export const updateAppointmentSchema = createInsertSchema(appointments).omit({
|
||||
id: true,
|
||||
createdAt: true,
|
||||
}).partial();
|
||||
|
||||
export type InsertAppointment = z.infer<typeof insertAppointmentSchema>;
|
||||
export type UpdateAppointment = z.infer<typeof updateAppointmentSchema>;
|
||||
export type Appointment = typeof appointments.$inferSelect;
|
||||
@@ -1,6 +1,6 @@
|
||||
// Optional PostCSS configuration for applications that need it
|
||||
import tailwindcss from "@tailwindcss/postcss";
|
||||
import autoprefixer from "autoprefixer";
|
||||
|
||||
export const postcssConfig = {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
plugins: [tailwindcss(), autoprefixer()]
|
||||
};
|
||||
|
||||
@@ -1,7 +1,2 @@
|
||||
@import "tailwindcss";
|
||||
|
||||
@theme {
|
||||
--blue-1000: #2a8af6;
|
||||
--purple-1000: #a853ba;
|
||||
--red-1000: #e92a67;
|
||||
}
|
||||
|
||||
4
packages/ui/eslint.config.mjs
Normal file
4
packages/ui/eslint.config.mjs
Normal file
@@ -0,0 +1,4 @@
|
||||
import { config } from "@repo/eslint-config/react-internal";
|
||||
|
||||
/** @type {import("eslint").Linter.Config} */
|
||||
export default config;
|
||||
36
packages/ui/package.json
Normal file
36
packages/ui/package.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "@repo/ui",
|
||||
"version": "0.0.0",
|
||||
"sideEffects": [
|
||||
"**/*.css"
|
||||
],
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"exports": {
|
||||
"./styles.css": "./dist/index.css",
|
||||
"./*": "./dist/*.js"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"build:styles": "tailwindcss -i ./src/styles.css -o ./dist/index.css",
|
||||
"build:components": "tsc",
|
||||
"check-types": "tsc --noEmit",
|
||||
"dev:styles": "tailwindcss -i ./src/styles.css -o ./dist/index.css --watch",
|
||||
"dev:components": "tsc --watch",
|
||||
"lint": "eslint src --max-warnings 0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@repo/eslint-config": "*",
|
||||
"@repo/tailwind-config": "*",
|
||||
"@repo/typescript-config": "*",
|
||||
"@tailwindcss/cli": "^4.1.5",
|
||||
"@types/react": "^19.1.0",
|
||||
"eslint": "^9.26.0",
|
||||
"tailwindcss": "^4.1.5",
|
||||
"typescript": "5.8.2"
|
||||
}
|
||||
}
|
||||
28
packages/ui/src/card.tsx
Normal file
28
packages/ui/src/card.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { type ReactNode } from "react";
|
||||
|
||||
export function Card({
|
||||
title,
|
||||
children,
|
||||
href,
|
||||
}: {
|
||||
title: string;
|
||||
children: ReactNode;
|
||||
href: string;
|
||||
}) {
|
||||
return (
|
||||
<a
|
||||
className="group rounded-lg border border-transparent px-5 py-4 transition-colors hover:border-neutral-700 hover:bg-neutral-800/30"
|
||||
href={`${href}?utm_source=create-turbo&utm_medium=with-tailwind&utm_campaign=create-turbo"`}
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<h2 className="mb-3 text-2xl font-semibold">
|
||||
{title}{" "}
|
||||
<span className="inline-block transition-transform group-hover:translate-x-1 motion-reduce:transform-none">
|
||||
->
|
||||
</span>
|
||||
</h2>
|
||||
<p className="m-0 max-w-[30ch] text-sm opacity-50">{children}</p>
|
||||
</a>
|
||||
);
|
||||
}
|
||||
21
packages/ui/src/gradient.tsx
Normal file
21
packages/ui/src/gradient.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
export function Gradient({
|
||||
conic,
|
||||
className,
|
||||
small,
|
||||
}: {
|
||||
small?: boolean;
|
||||
conic?: boolean;
|
||||
className?: string;
|
||||
}) {
|
||||
return (
|
||||
<span
|
||||
className={`absolute mix-blend-normal will-change-[filter] rounded-[100%] ${
|
||||
small ? "blur-[32px]" : "blur-[75px]"
|
||||
} ${
|
||||
conic
|
||||
? "bg-[conic-gradient(from_180deg_at_50%_50%,var(--red-1000)_0deg,_var(--purple-1000)_180deg,_var(--blue-1000)_360deg)]"
|
||||
: ""
|
||||
} ${className ?? ""}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
1
packages/ui/src/styles.css
Normal file
1
packages/ui/src/styles.css
Normal file
@@ -0,0 +1 @@
|
||||
@import "tailwindcss";
|
||||
35
packages/ui/src/turborepo-logo.tsx
Normal file
35
packages/ui/src/turborepo-logo.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
export const TurborepoLogo = () => {
|
||||
return (
|
||||
<svg
|
||||
aria-label="Turbo logomark"
|
||||
height="80"
|
||||
role="img"
|
||||
viewBox="0 0 40 40"
|
||||
width="80"
|
||||
>
|
||||
<path
|
||||
d="M19.9845 6.99291C12.818 6.99291 6.98755 12.8279 6.98755 19.9999C6.98755 27.1721 12.818 33.0071 19.9845 33.0071C27.1509 33.0071 32.9814 27.1721 32.9814 19.9999C32.9814 12.8279 27.1509 6.99291 19.9845 6.99291ZM19.9845 26.7313C16.2694 26.7313 13.2585 23.718 13.2585 19.9999C13.2585 16.282 16.2694 13.2687 19.9845 13.2687C23.6996 13.2687 26.7105 16.282 26.7105 19.9999C26.7105 23.718 23.6996 26.7313 19.9845 26.7313Z"
|
||||
fill="currentcolor"
|
||||
></path>
|
||||
<path
|
||||
clipRule="evenodd"
|
||||
d="M21.0734 4.85648V0C31.621 0.564369 40 9.30362 40 19.9999C40 30.6963 31.621 39.4332 21.0734 40V35.1435C28.9344 34.5815 35.1594 28.0078 35.1594 19.9999C35.1594 11.9922 28.9344 5.41843 21.0734 4.85648ZM8.52181 29.931C6.43794 27.5233 5.09469 24.4568 4.85508 21.09H0C0.251709 25.8011 2.13468 30.0763 5.08501 33.368L8.51938 29.931H8.52181ZM18.8951 40V35.1435C15.5285 34.9037 12.4644 33.5619 10.0587 31.4739L6.62435 34.9109C9.91593 37.866 14.1876 39.7481 18.8927 40H18.8951Z"
|
||||
fill="url(#:Sb:paint0_linear_902_224)"
|
||||
fillRule="evenodd"
|
||||
></path>
|
||||
<defs>
|
||||
<linearGradient
|
||||
gradientUnits="userSpaceOnUse"
|
||||
id=":Sb:paint0_linear_902_224"
|
||||
x1="21.8576"
|
||||
x2="2.17018"
|
||||
y1="2.81244"
|
||||
y2="22.4844"
|
||||
>
|
||||
<stop stopColor="#0096FF"></stop>
|
||||
<stop offset="1" stopColor="#FF1E56"></stop>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
8
packages/ui/tsconfig.json
Normal file
8
packages/ui/tsconfig.json
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "@repo/typescript-config/react-library.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["dist", "build", "node_modules"]
|
||||
}
|
||||
25
packages/ui/turbo.json
Normal file
25
packages/ui/turbo.json
Normal file
@@ -0,0 +1,25 @@
|
||||
{
|
||||
"extends": ["//"],
|
||||
"tasks": {
|
||||
"build": {
|
||||
"dependsOn": ["build:styles", "build:components"]
|
||||
},
|
||||
"build:styles": {
|
||||
"outputs": ["dist/**"]
|
||||
},
|
||||
"build:components": {
|
||||
"outputs": ["dist/**"]
|
||||
},
|
||||
"dev": {
|
||||
"with": ["dev:styles", "dev:components"]
|
||||
},
|
||||
"dev:styles": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
},
|
||||
"dev:components": {
|
||||
"cache": false,
|
||||
"persistent": true
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user