search checkpoint 1
This commit is contained in:
@@ -86,6 +86,64 @@ router.get("/recent", async (req: Request, res: Response) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get("/search", async (req: Request, res: Response) => {
|
||||||
|
try {
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
phone,
|
||||||
|
insuranceId,
|
||||||
|
gender,
|
||||||
|
dob,
|
||||||
|
limit = "10",
|
||||||
|
offset = "0",
|
||||||
|
} = req.query as Record<string, string>;
|
||||||
|
|
||||||
|
const filters: any = {
|
||||||
|
userId: req.user!.id, // Assumes auth middleware sets this
|
||||||
|
};
|
||||||
|
|
||||||
|
if (name) {
|
||||||
|
filters.OR = [
|
||||||
|
{ firstName: { contains: name, mode: "insensitive" } },
|
||||||
|
{ lastName: { contains: name, mode: "insensitive" } },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (phone) {
|
||||||
|
filters.phone = { contains: phone, mode: "insensitive" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (insuranceId) {
|
||||||
|
filters.insuranceId = { contains: insuranceId, mode: "insensitive" };
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gender) {
|
||||||
|
filters.gender = gender;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dob) {
|
||||||
|
const parsedDate = new Date(dob);
|
||||||
|
if (!isNaN(parsedDate.getTime())) {
|
||||||
|
filters.dateOfBirth = parsedDate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const [patients, totalCount] = await Promise.all([
|
||||||
|
storage.searchPatients({
|
||||||
|
filters,
|
||||||
|
limit: parseInt(limit),
|
||||||
|
offset: parseInt(offset),
|
||||||
|
}),
|
||||||
|
storage.countPatients(filters),
|
||||||
|
]);
|
||||||
|
|
||||||
|
res.json({ patients, totalCount });
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Search error:", error);
|
||||||
|
res.status(500).json({ message: "Failed to search patients" });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Get a single patient by ID
|
// Get a single patient by ID
|
||||||
router.get(
|
router.get(
|
||||||
"/:id",
|
"/:id",
|
||||||
|
|||||||
@@ -171,6 +171,24 @@ export interface IStorage {
|
|||||||
createPatient(patient: InsertPatient): Promise<Patient>;
|
createPatient(patient: InsertPatient): Promise<Patient>;
|
||||||
updatePatient(id: number, patient: UpdatePatient): Promise<Patient>;
|
updatePatient(id: number, patient: UpdatePatient): Promise<Patient>;
|
||||||
deletePatient(id: number): Promise<void>;
|
deletePatient(id: number): Promise<void>;
|
||||||
|
searchPatients(args: {
|
||||||
|
filters: any;
|
||||||
|
limit: number;
|
||||||
|
offset: number;
|
||||||
|
}): Promise<
|
||||||
|
{
|
||||||
|
id: number;
|
||||||
|
firstName: string | null;
|
||||||
|
lastName: string | null;
|
||||||
|
phone: string | null;
|
||||||
|
gender: string | null;
|
||||||
|
dateOfBirth: Date;
|
||||||
|
insuranceId: string | null;
|
||||||
|
insuranceProvider: string | null;
|
||||||
|
status: string;
|
||||||
|
}[]
|
||||||
|
>;
|
||||||
|
countPatients(filters: any): Promise<number>; // optional but useful
|
||||||
|
|
||||||
// Appointment methods
|
// Appointment methods
|
||||||
getAppointment(id: number): Promise<Appointment | undefined>;
|
getAppointment(id: number): Promise<Appointment | undefined>;
|
||||||
@@ -305,10 +323,42 @@ export const storage: IStorage = {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async searchPatients({
|
||||||
|
filters,
|
||||||
|
limit,
|
||||||
|
offset,
|
||||||
|
}: {
|
||||||
|
filters: any;
|
||||||
|
limit: number;
|
||||||
|
offset: number;
|
||||||
|
}) {
|
||||||
|
return db.patient.findMany({
|
||||||
|
where: filters,
|
||||||
|
orderBy: { createdAt: "desc" },
|
||||||
|
take: limit,
|
||||||
|
skip: offset,
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
phone: true,
|
||||||
|
gender: true,
|
||||||
|
dateOfBirth: true,
|
||||||
|
insuranceId: true,
|
||||||
|
insuranceProvider: true,
|
||||||
|
status: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
async getTotalPatientCount(): Promise<number> {
|
async getTotalPatientCount(): Promise<number> {
|
||||||
return db.patient.count();
|
return db.patient.count();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async countPatients(filters: any) {
|
||||||
|
return db.patient.count({ where: filters });
|
||||||
|
},
|
||||||
|
|
||||||
async createPatient(patient: InsertPatient): Promise<Patient> {
|
async createPatient(patient: InsertPatient): Promise<Patient> {
|
||||||
return await db.patient.create({ data: patient as Patient });
|
return await db.patient.create({ data: patient as Patient });
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ import {
|
|||||||
|
|
||||||
export type SearchCriteria = {
|
export type SearchCriteria = {
|
||||||
searchTerm: string;
|
searchTerm: string;
|
||||||
searchBy: "name" | "insuranceProvider" | "phone" | "insuranceId" | "all";
|
searchBy: "name" | "insuranceId" | "phone" | "gender" | "dob" | "all";
|
||||||
};
|
};
|
||||||
|
|
||||||
interface PatientSearchProps {
|
interface PatientSearchProps {
|
||||||
@@ -61,7 +61,10 @@ export function PatientSearch({
|
|||||||
setShowAdvanced(false);
|
setShowAdvanced(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateAdvancedCriteria = (field: keyof SearchCriteria, value: string) => {
|
const updateAdvancedCriteria = (
|
||||||
|
field: keyof SearchCriteria,
|
||||||
|
value: string
|
||||||
|
) => {
|
||||||
setAdvancedCriteria({
|
setAdvancedCriteria({
|
||||||
...advancedCriteria,
|
...advancedCriteria,
|
||||||
[field]: value,
|
[field]: value,
|
||||||
@@ -104,7 +107,9 @@ export function PatientSearch({
|
|||||||
|
|
||||||
<Select
|
<Select
|
||||||
value={searchBy}
|
value={searchBy}
|
||||||
onValueChange={(value) => setSearchBy(value as SearchCriteria["searchBy"])}
|
onValueChange={(value) =>
|
||||||
|
setSearchBy(value as SearchCriteria["searchBy"])
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="w-[180px]">
|
<SelectTrigger className="w-[180px]">
|
||||||
<SelectValue placeholder="Search by..." />
|
<SelectValue placeholder="Search by..." />
|
||||||
@@ -113,8 +118,9 @@ export function PatientSearch({
|
|||||||
<SelectItem value="all">All Fields</SelectItem>
|
<SelectItem value="all">All Fields</SelectItem>
|
||||||
<SelectItem value="name">Name</SelectItem>
|
<SelectItem value="name">Name</SelectItem>
|
||||||
<SelectItem value="phone">Phone</SelectItem>
|
<SelectItem value="phone">Phone</SelectItem>
|
||||||
<SelectItem value="insuranceProvider">Insurance Provider</SelectItem>
|
<SelectItem value="insuranceId">InsuranceId</SelectItem>
|
||||||
<SelectItem value="insuranceId">Insurance ID</SelectItem>
|
<SelectItem value="gender">Gender</SelectItem>
|
||||||
|
<SelectItem value="dob">DOB</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
|
|
||||||
@@ -132,11 +138,16 @@ export function PatientSearch({
|
|||||||
|
|
||||||
<div className="grid gap-4 py-4">
|
<div className="grid gap-4 py-4">
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
<label className="text-right text-sm font-medium">Search by</label>
|
<label className="text-right text-sm font-medium">
|
||||||
|
Search by
|
||||||
|
</label>
|
||||||
<Select
|
<Select
|
||||||
value={advancedCriteria.searchBy}
|
value={advancedCriteria.searchBy}
|
||||||
onValueChange={(value) =>
|
onValueChange={(value) =>
|
||||||
updateAdvancedCriteria("searchBy", value as SearchCriteria["searchBy"])
|
updateAdvancedCriteria(
|
||||||
|
"searchBy",
|
||||||
|
value as SearchCriteria["searchBy"]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<SelectTrigger className="col-span-3">
|
<SelectTrigger className="col-span-3">
|
||||||
@@ -146,14 +157,17 @@ export function PatientSearch({
|
|||||||
<SelectItem value="all">All Fields</SelectItem>
|
<SelectItem value="all">All Fields</SelectItem>
|
||||||
<SelectItem value="name">Name</SelectItem>
|
<SelectItem value="name">Name</SelectItem>
|
||||||
<SelectItem value="phone">Phone</SelectItem>
|
<SelectItem value="phone">Phone</SelectItem>
|
||||||
<SelectItem value="insuranceProvider">Insurance Provider</SelectItem>
|
<SelectItem value="insuranceId">InsuranceId</SelectItem>
|
||||||
<SelectItem value="insuranceId">Insurance ID</SelectItem>
|
<SelectItem value="gender">Gender</SelectItem>
|
||||||
|
<SelectItem value="dob">DOB</SelectItem>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid grid-cols-4 items-center gap-4">
|
<div className="grid grid-cols-4 items-center gap-4">
|
||||||
<label className="text-right text-sm font-medium">Search term</label>
|
<label className="text-right text-sm font-medium">
|
||||||
|
Search term
|
||||||
|
</label>
|
||||||
<Input
|
<Input
|
||||||
className="col-span-3"
|
className="col-span-3"
|
||||||
value={advancedCriteria.searchTerm}
|
value={advancedCriteria.searchTerm}
|
||||||
@@ -182,4 +196,4 @@ export function PatientSearch({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ import {
|
|||||||
import { AddPatientModal } from "./add-patient-modal";
|
import { AddPatientModal } from "./add-patient-modal";
|
||||||
import { DeleteConfirmationDialog } from "../ui/deleteDialog";
|
import { DeleteConfirmationDialog } from "../ui/deleteDialog";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { PatientSearch, SearchCriteria } from "./patient-search";
|
||||||
|
import { useDebounce } from "use-debounce";
|
||||||
|
|
||||||
const PatientSchema = (
|
const PatientSchema = (
|
||||||
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
|
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
|
||||||
@@ -43,7 +45,6 @@ const PatientSchema = (
|
|||||||
});
|
});
|
||||||
type Patient = z.infer<typeof PatientSchema>;
|
type Patient = z.infer<typeof PatientSchema>;
|
||||||
|
|
||||||
|
|
||||||
const updatePatientSchema = (
|
const updatePatientSchema = (
|
||||||
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
|
PatientUncheckedCreateInputObjectSchema as unknown as z.ZodObject<any>
|
||||||
)
|
)
|
||||||
@@ -56,7 +57,6 @@ const updatePatientSchema = (
|
|||||||
|
|
||||||
type UpdatePatient = z.infer<typeof updatePatientSchema>;
|
type UpdatePatient = z.infer<typeof updatePatientSchema>;
|
||||||
|
|
||||||
|
|
||||||
interface PatientApiResponse {
|
interface PatientApiResponse {
|
||||||
patients: Patient[];
|
patients: Patient[];
|
||||||
totalCount: number;
|
totalCount: number;
|
||||||
@@ -76,7 +76,6 @@ export function PatientTable({
|
|||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
|
||||||
|
|
||||||
const [isAddPatientOpen, setIsAddPatientOpen] = useState(false);
|
const [isAddPatientOpen, setIsAddPatientOpen] = useState(false);
|
||||||
const [isViewPatientOpen, setIsViewPatientOpen] = useState(false);
|
const [isViewPatientOpen, setIsViewPatientOpen] = useState(false);
|
||||||
const [isDeletePatientOpen, setIsDeletePatientOpen] = useState(false);
|
const [isDeletePatientOpen, setIsDeletePatientOpen] = useState(false);
|
||||||
@@ -88,96 +87,111 @@ export function PatientTable({
|
|||||||
const patientsPerPage = 5;
|
const patientsPerPage = 5;
|
||||||
const offset = (currentPage - 1) * patientsPerPage;
|
const offset = (currentPage - 1) * patientsPerPage;
|
||||||
|
|
||||||
|
const [isSearchActive, setIsSearchActive] = useState(false);
|
||||||
|
const [searchCriteria, setSearchCriteria] = useState<SearchCriteria | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
const [debouncedSearchCriteria] = useDebounce(searchCriteria, 500);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
data: patientsData,
|
data: patientsData,
|
||||||
isLoading,
|
isLoading,
|
||||||
isError,
|
isError,
|
||||||
} = useQuery<PatientApiResponse>({
|
} = useQuery<PatientApiResponse>({
|
||||||
queryKey: ["patients", currentPage],
|
queryKey: ["patients", currentPage, debouncedSearchCriteria?.searchTerm || "recent"],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
const res = await apiRequest(
|
const trimmedTerm = debouncedSearchCriteria?.searchTerm?.trim();
|
||||||
"GET",
|
const isSearch = trimmedTerm && trimmedTerm.length > 0;
|
||||||
`/api/patients/recent?limit=${patientsPerPage}&offset=${offset}`
|
|
||||||
);
|
const baseUrl = isSearch
|
||||||
|
? `/api/patients/search?term=${encodeURIComponent(trimmedTerm)}&by=${debouncedSearchCriteria!.searchBy}`
|
||||||
|
: `/api/patients/recent`;
|
||||||
|
|
||||||
|
const hasQueryParams = baseUrl.includes("?");
|
||||||
|
const url = `${baseUrl}${hasQueryParams ? "&" : "?"}limit=${patientsPerPage}&offset=${offset}`;
|
||||||
|
|
||||||
|
const res = await apiRequest("GET", url);
|
||||||
|
return res.json();
|
||||||
|
},
|
||||||
|
placeholderData: {
|
||||||
|
patients: [],
|
||||||
|
totalCount: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Update patient mutation
|
||||||
|
const updatePatientMutation = useMutation({
|
||||||
|
mutationFn: async ({
|
||||||
|
id,
|
||||||
|
patient,
|
||||||
|
}: {
|
||||||
|
id: number;
|
||||||
|
patient: UpdatePatient;
|
||||||
|
}) => {
|
||||||
|
const res = await apiRequest("PUT", `/api/patients/${id}`, patient);
|
||||||
return res.json();
|
return res.json();
|
||||||
},
|
},
|
||||||
placeholderData: {
|
onSuccess: () => {
|
||||||
patients: [],
|
setIsAddPatientOpen(false);
|
||||||
totalCount: 0,
|
queryClient.invalidateQueries({ queryKey: ["patients", currentPage] });
|
||||||
|
toast({
|
||||||
|
title: "Success",
|
||||||
|
description: "Patient updated successfully!",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast({
|
||||||
|
title: "Error",
|
||||||
|
description: `Failed to update patient: ${error.message}`,
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update patient mutation
|
const deletePatientMutation = useMutation({
|
||||||
const updatePatientMutation = useMutation({
|
mutationFn: async (id: number) => {
|
||||||
mutationFn: async ({
|
const res = await apiRequest("DELETE", `/api/patients/${id}`);
|
||||||
id,
|
return;
|
||||||
patient,
|
},
|
||||||
}: {
|
onSuccess: () => {
|
||||||
id: number;
|
setIsDeletePatientOpen(false);
|
||||||
patient: UpdatePatient;
|
queryClient.invalidateQueries({ queryKey: ["patients", currentPage] });
|
||||||
}) => {
|
toast({
|
||||||
const res = await apiRequest("PUT", `/api/patients/${id}`, patient);
|
title: "Success",
|
||||||
return res.json();
|
description: "Patient deleted successfully!",
|
||||||
},
|
variant: "default",
|
||||||
onSuccess: () => {
|
});
|
||||||
setIsAddPatientOpen(false);
|
},
|
||||||
queryClient.invalidateQueries({ queryKey: ["patients", currentPage] });
|
onError: (error) => {
|
||||||
toast({
|
console.log(error);
|
||||||
title: "Success",
|
toast({
|
||||||
description: "Patient updated successfully!",
|
title: "Error",
|
||||||
variant: "default",
|
description: `Failed to delete patient: ${error.message}`,
|
||||||
});
|
variant: "destructive",
|
||||||
},
|
});
|
||||||
onError: (error) => {
|
},
|
||||||
toast({
|
});
|
||||||
title: "Error",
|
|
||||||
description: `Failed to update patient: ${error.message}`,
|
const handleUpdatePatient = (patient: UpdatePatient & { id?: number }) => {
|
||||||
variant: "destructive",
|
if (currentPatient && user) {
|
||||||
});
|
const { id, ...sanitizedPatient } = patient;
|
||||||
},
|
updatePatientMutation.mutate({
|
||||||
});
|
id: currentPatient.id,
|
||||||
|
patient: sanitizedPatient,
|
||||||
const deletePatientMutation = useMutation({
|
});
|
||||||
mutationFn: async (id: number) => {
|
} else {
|
||||||
const res = await apiRequest("DELETE", `/api/patients/${id}`);
|
console.error("No current patient or user found for update");
|
||||||
return;
|
toast({
|
||||||
},
|
title: "Error",
|
||||||
onSuccess: () => {
|
description: "Cannot update patient: No patient or user found",
|
||||||
setIsDeletePatientOpen(false);
|
variant: "destructive",
|
||||||
queryClient.invalidateQueries({ queryKey: ["patients", currentPage] });
|
});
|
||||||
toast({
|
}
|
||||||
title: "Success",
|
};
|
||||||
description: "Patient deleted successfully!",
|
|
||||||
variant: "default",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
console.log(error);
|
|
||||||
toast({
|
|
||||||
title: "Error",
|
|
||||||
description: `Failed to delete patient: ${error.message}`,
|
|
||||||
variant: "destructive",
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleUpdatePatient = (patient: UpdatePatient & { id?: number }) => {
|
|
||||||
if (currentPatient && user) {
|
|
||||||
const { id, ...sanitizedPatient } = patient;
|
|
||||||
updatePatientMutation.mutate({
|
|
||||||
id: currentPatient.id,
|
|
||||||
patient: sanitizedPatient,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.error("No current patient or user found for update");
|
|
||||||
toast({
|
|
||||||
title: "Error",
|
|
||||||
description: "Cannot update patient: No patient or user found",
|
|
||||||
variant: "destructive",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleEditPatient = (patient: Patient) => {
|
const handleEditPatient = (patient: Patient) => {
|
||||||
setCurrentPatient(patient);
|
setCurrentPatient(patient);
|
||||||
setIsAddPatientOpen(true);
|
setIsAddPatientOpen(true);
|
||||||
@@ -244,6 +258,19 @@ export function PatientTable({
|
|||||||
return (
|
return (
|
||||||
<div className="bg-white shadow rounded-lg overflow-hidden">
|
<div className="bg-white shadow rounded-lg overflow-hidden">
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
|
<PatientSearch
|
||||||
|
onSearch={(criteria) => {
|
||||||
|
setSearchCriteria(criteria);
|
||||||
|
setCurrentPage(1); // reset page on new search
|
||||||
|
setIsSearchActive(true);
|
||||||
|
}}
|
||||||
|
onClearSearch={() => {
|
||||||
|
setSearchCriteria({ searchTerm: "", searchBy: "name" }); // triggers `recent`
|
||||||
|
setCurrentPage(1);
|
||||||
|
setIsSearchActive(false);
|
||||||
|
}}
|
||||||
|
isSearchActive={isSearchActive}
|
||||||
|
/>
|
||||||
<Table>
|
<Table>
|
||||||
<TableHeader>
|
<TableHeader>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
|
|||||||
@@ -4,10 +4,6 @@ import { TopAppBar } from "@/components/layout/top-app-bar";
|
|||||||
import { Sidebar } from "@/components/layout/sidebar";
|
import { Sidebar } from "@/components/layout/sidebar";
|
||||||
import { PatientTable } from "@/components/patients/patient-table";
|
import { PatientTable } from "@/components/patients/patient-table";
|
||||||
import { AddPatientModal } from "@/components/patients/add-patient-modal";
|
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 { FileUploadZone } from "@/components/file-upload/file-upload-zone";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Plus, RefreshCw, File, FilePlus } from "lucide-react";
|
import { Plus, RefreshCw, File, FilePlus } from "lucide-react";
|
||||||
@@ -56,9 +52,6 @@ export default function PatientsPage() {
|
|||||||
undefined
|
undefined
|
||||||
);
|
);
|
||||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||||
const [searchCriteria, setSearchCriteria] = useState<SearchCriteria | null>(
|
|
||||||
null
|
|
||||||
);
|
|
||||||
const addPatientModalRef = useRef<AddPatientModalRef | null>(null);
|
const addPatientModalRef = useRef<AddPatientModalRef | null>(null);
|
||||||
|
|
||||||
// File upload states
|
// File upload states
|
||||||
@@ -112,14 +105,6 @@ export default function PatientsPage() {
|
|||||||
|
|
||||||
const isLoading = addPatientMutation.isPending;
|
const isLoading = addPatientMutation.isPending;
|
||||||
|
|
||||||
// Search handling
|
|
||||||
const handleSearch = (criteria: SearchCriteria) => {
|
|
||||||
setSearchCriteria(criteria);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleClearSearch = () => {
|
|
||||||
setSearchCriteria(null);
|
|
||||||
};
|
|
||||||
|
|
||||||
// File upload handling
|
// File upload handling
|
||||||
const handleFileUpload = (file: File) => {
|
const handleFileUpload = (file: File) => {
|
||||||
@@ -252,12 +237,6 @@ export default function PatientsPage() {
|
|||||||
</CardDescription>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<PatientSearch
|
|
||||||
onSearch={handleSearch}
|
|
||||||
onClearSearch={handleClearSearch}
|
|
||||||
isSearchActive={!!searchCriteria}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PatientTable
|
<PatientTable
|
||||||
allowDelete={true}
|
allowDelete={true}
|
||||||
allowEdit={true}
|
allowEdit={true}
|
||||||
|
|||||||
Reference in New Issue
Block a user