frontend tailwind half working without external styling

This commit is contained in:
2025-05-09 21:51:02 +05:30
parent ae99e25228
commit 9a431e63db
42 changed files with 1112 additions and 273 deletions

View File

@@ -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

View File

@@ -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>

View File

@@ -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>

View File

@@ -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() {

View File

@@ -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 = {