seach bar addded in form
This commit is contained in:
@@ -76,6 +76,7 @@
|
|||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"tw-animate-css": "^1.2.5",
|
"tw-animate-css": "^1.2.5",
|
||||||
|
"use-debounce": "^10.0.4",
|
||||||
"vaul": "^1.1.2",
|
"vaul": "^1.1.2",
|
||||||
"wouter": "^3.7.0",
|
"wouter": "^3.7.0",
|
||||||
"ws": "^8.18.0",
|
"ws": "^8.18.0",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect } from "react";
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { format } from "date-fns";
|
import { format } from "date-fns";
|
||||||
@@ -31,7 +31,7 @@ import { CalendarIcon, Clock } from "lucide-react";
|
|||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useAuth } from "@/hooks/use-auth";
|
||||||
|
import { useDebounce } from "use-debounce";
|
||||||
import {
|
import {
|
||||||
AppointmentUncheckedCreateInputObjectSchema,
|
AppointmentUncheckedCreateInputObjectSchema,
|
||||||
PatientUncheckedCreateInputObjectSchema,
|
PatientUncheckedCreateInputObjectSchema,
|
||||||
@@ -83,6 +83,15 @@ export function AppointmentForm({
|
|||||||
isLoading = false,
|
isLoading = false,
|
||||||
}: AppointmentFormProps) {
|
}: AppointmentFormProps) {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
const timeout = setTimeout(() => {
|
||||||
|
inputRef.current?.focus();
|
||||||
|
}, 50); // small delay ensures content is mounted
|
||||||
|
|
||||||
|
return () => clearTimeout(timeout);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
const { data: staffMembersRaw = [] as Staff[], isLoading: isLoadingStaff } =
|
const { data: staffMembersRaw = [] as Staff[], isLoading: isLoadingStaff } =
|
||||||
useQuery<Staff[]>({
|
useQuery<Staff[]>({
|
||||||
@@ -163,6 +172,24 @@ export function AppointmentForm({
|
|||||||
defaultValues,
|
defaultValues,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [searchTerm, setSearchTerm] = useState("");
|
||||||
|
const [debouncedSearchTerm] = useDebounce(searchTerm, 200); // 1 seconds
|
||||||
|
const [filteredPatients, setFilteredPatients] = useState(patients);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!debouncedSearchTerm.trim()) {
|
||||||
|
setFilteredPatients(patients);
|
||||||
|
} else {
|
||||||
|
const term = debouncedSearchTerm.toLowerCase();
|
||||||
|
setFilteredPatients(
|
||||||
|
patients.filter((p) =>
|
||||||
|
`${p.firstName} ${p.lastName} ${p.phone} ${p.dob}`.toLowerCase().includes(term)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [debouncedSearchTerm, patients]);
|
||||||
|
|
||||||
|
|
||||||
// Force form field values to update and clean up storage
|
// Force form field values to update and clean up storage
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (parsedStoredData) {
|
if (parsedStoredData) {
|
||||||
@@ -277,14 +304,45 @@ export function AppointmentForm({
|
|||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{patients.map((patient) => (
|
<div className="p-2" onKeyDown={(e) => e.stopPropagation()}>
|
||||||
<SelectItem
|
|
||||||
key={patient.id}
|
<Input
|
||||||
value={patient.id.toString()}
|
ref={inputRef}
|
||||||
>
|
placeholder="Search patients..."
|
||||||
{patient.firstName} {patient.lastName}
|
className="w-full"
|
||||||
</SelectItem>
|
value={searchTerm}
|
||||||
))}
|
onChange={(e) => setSearchTerm(e.target.value)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
const navKeys = ['ArrowDown', 'ArrowUp', 'Enter'];
|
||||||
|
if (!navKeys.includes(e.key)) {
|
||||||
|
e.stopPropagation(); // Only stop keys that affect select state
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="max-h-60 overflow-y-auto scrollbar-thin scrollbar-thumb-muted-foreground/30">
|
||||||
|
|
||||||
|
{filteredPatients.length > 0 ? (
|
||||||
|
filteredPatients.map((patient) => (
|
||||||
|
<SelectItem
|
||||||
|
key={patient.id}
|
||||||
|
value={patient.id.toString()}
|
||||||
|
><div className="flex flex-col">
|
||||||
|
<span className="font-medium">
|
||||||
|
{patient.firstName} {patient.lastName}
|
||||||
|
</span>
|
||||||
|
<span className="text-xs text-muted-foreground">
|
||||||
|
DOB: {new Date(patient.dateOfBirth).toLocaleDateString()} • {patient.phone}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</SelectItem>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<div className="p-2 text-muted-foreground text-sm">
|
||||||
|
No patients found
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
<FormMessage />
|
<FormMessage />
|
||||||
|
|||||||
67
package-lock.json
generated
67
package-lock.json
generated
@@ -471,12 +471,12 @@
|
|||||||
"react-hook-form": "^7.55.0",
|
"react-hook-form": "^7.55.0",
|
||||||
"react-icons": "^5.4.0",
|
"react-icons": "^5.4.0",
|
||||||
"react-resizable-panels": "^2.1.7",
|
"react-resizable-panels": "^2.1.7",
|
||||||
"react-router-dom": "^7.6.0",
|
|
||||||
"recharts": "^2.15.2",
|
"recharts": "^2.15.2",
|
||||||
"tailwind-merge": "^3.2.0",
|
"tailwind-merge": "^3.2.0",
|
||||||
"tailwindcss": "^3.4.17",
|
"tailwindcss": "^3.4.17",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"tw-animate-css": "^1.2.5",
|
"tw-animate-css": "^1.2.5",
|
||||||
|
"use-debounce": "^10.0.4",
|
||||||
"vaul": "^1.1.2",
|
"vaul": "^1.1.2",
|
||||||
"wouter": "^3.7.0",
|
"wouter": "^3.7.0",
|
||||||
"ws": "^8.18.0",
|
"ws": "^8.18.0",
|
||||||
@@ -11572,53 +11572,6 @@
|
|||||||
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
"react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/react-router": {
|
|
||||||
"version": "7.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-7.6.0.tgz",
|
|
||||||
"integrity": "sha512-GGufuHIVCJDbnIAXP3P9Sxzq3UUsddG3rrI3ut1q6m0FI6vxVBF3JoPQ38+W/blslLH4a5Yutp8drkEpXoddGQ==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"cookie": "^1.0.1",
|
|
||||||
"set-cookie-parser": "^2.6.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">=18",
|
|
||||||
"react-dom": ">=18"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"react-dom": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-router-dom": {
|
|
||||||
"version": "7.6.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.6.0.tgz",
|
|
||||||
"integrity": "sha512-DYgm6RDEuKdopSyGOWZGtDfSm7Aofb8CCzgkliTjtu/eDuB0gcsv6qdFhhi8HdtmA+KHkt5MfZ5K2PdzjugYsA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"dependencies": {
|
|
||||||
"react-router": "7.6.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=20.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": ">=18",
|
|
||||||
"react-dom": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-router/node_modules/cookie": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==",
|
|
||||||
"license": "MIT",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/react-smooth": {
|
"node_modules/react-smooth": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
|
||||||
@@ -12266,12 +12219,6 @@
|
|||||||
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
|
||||||
"license": "ISC"
|
"license": "ISC"
|
||||||
},
|
},
|
||||||
"node_modules/set-cookie-parser": {
|
|
||||||
"version": "2.7.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.1.tgz",
|
|
||||||
"integrity": "sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==",
|
|
||||||
"license": "MIT"
|
|
||||||
},
|
|
||||||
"node_modules/set-function-length": {
|
"node_modules/set-function-length": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
|
||||||
@@ -13916,6 +13863,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/use-debounce": {
|
||||||
|
"version": "10.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/use-debounce/-/use-debounce-10.0.4.tgz",
|
||||||
|
"integrity": "sha512-6Cf7Yr7Wk7Kdv77nnJMf6de4HuDE4dTxKij+RqE9rufDsI6zsbjyAxcH5y2ueJCQAnfgKbzXbZHYlkFwmBlWkw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 16.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"react": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/use-sidecar": {
|
"node_modules/use-sidecar": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
||||||
|
|||||||
Reference in New Issue
Block a user