feat(Calendar UI fixes) - shrink
This commit is contained in:
@@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { format } from "date-fns";
|
||||
import { apiRequest, queryClient } from "@/lib/queryClient";
|
||||
import { apiRequest } from "@/lib/queryClient";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Form,
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Clock } from "lucide-react";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { useDebounce } from "use-debounce";
|
||||
import {
|
||||
@@ -39,7 +39,6 @@ import {
|
||||
PatientSearch,
|
||||
SearchCriteria,
|
||||
} from "@/components/patients/patient-search";
|
||||
import { QK_PATIENTS_BASE } from "../patients/patient-table";
|
||||
import { toast } from "@/hooks/use-toast";
|
||||
|
||||
interface AppointmentFormProps {
|
||||
@@ -59,7 +58,6 @@ export function AppointmentForm({
|
||||
}: AppointmentFormProps) {
|
||||
const { user } = useAuth();
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
const queryClient = useQueryClient();
|
||||
const [prefillPatient, setPrefillPatient] = useState<Patient | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -70,15 +68,14 @@ export function AppointmentForm({
|
||||
return () => clearTimeout(timeout);
|
||||
}, []);
|
||||
|
||||
const { data: staffMembersRaw = [] as Staff[], isLoading: isLoadingStaff } =
|
||||
useQuery<Staff[]>({
|
||||
queryKey: ["/api/staffs/"],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", "/api/staffs/");
|
||||
return res.json();
|
||||
},
|
||||
enabled: !!user,
|
||||
});
|
||||
const { data: staffMembersRaw = [] as Staff[] } = useQuery<Staff[]>({
|
||||
queryKey: ["/api/staffs/"],
|
||||
queryFn: async () => {
|
||||
const res = await apiRequest("GET", "/api/staffs/");
|
||||
return res.json();
|
||||
},
|
||||
enabled: !!user,
|
||||
});
|
||||
|
||||
const colorMap: Record<string, string> = {
|
||||
"Dr. Kai Gao": "bg-blue-600",
|
||||
|
||||
@@ -133,6 +133,10 @@ export function ClaimForm({
|
||||
const [serviceDate, setServiceDate] = useState<string>(
|
||||
formatLocalDate(new Date())
|
||||
);
|
||||
const [serviceDateOpen, setServiceDateOpen] = useState(false);
|
||||
const [openProcedureDateIndex, setOpenProcedureDateIndex] = useState<
|
||||
number | null
|
||||
>(null);
|
||||
|
||||
// Update service date when calendar date changes
|
||||
const onServiceDateChange = (date: Date | undefined) => {
|
||||
@@ -559,7 +563,10 @@ export function ClaimForm({
|
||||
{/* Service Date */}
|
||||
<div className="flex gap-2">
|
||||
<Label className="flex items-center">Service Date</Label>
|
||||
<Popover>
|
||||
<Popover
|
||||
open={serviceDateOpen}
|
||||
onOpenChange={setServiceDateOpen}
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -569,11 +576,14 @@ export function ClaimForm({
|
||||
{form.serviceDate}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0" align="start">
|
||||
<PopoverContent className="w-auto">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={serviceDateValue}
|
||||
onSelect={onServiceDateChange}
|
||||
onSelect={(date) => {
|
||||
onServiceDateChange(date);
|
||||
}}
|
||||
onClose={() => setServiceDateOpen(false)}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
@@ -702,7 +712,12 @@ export function ClaimForm({
|
||||
</div>
|
||||
|
||||
{/* Date Picker */}
|
||||
<Popover>
|
||||
<Popover
|
||||
open={openProcedureDateIndex === i}
|
||||
onOpenChange={(open) =>
|
||||
setOpenProcedureDateIndex(open ? i : null)
|
||||
}
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -712,11 +727,16 @@ export function ClaimForm({
|
||||
{line.procedureDate || "Pick Date"}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-auto p-0">
|
||||
<PopoverContent className="w-auto">
|
||||
<Calendar
|
||||
mode="single"
|
||||
selected={new Date(line.procedureDate)}
|
||||
selected={
|
||||
line.procedureDate
|
||||
? new Date(line.procedureDate)
|
||||
: undefined
|
||||
}
|
||||
onSelect={(date) => updateProcedureDate(i, date)}
|
||||
onClose={() => setOpenProcedureDateIndex(null)}
|
||||
/>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
@@ -43,6 +43,8 @@ export function PatientSearch({
|
||||
onClearSearch,
|
||||
isSearchActive,
|
||||
}: PatientSearchProps) {
|
||||
const [dobOpen, setDobOpen] = useState(false);
|
||||
const [advanceDobOpen, setAdvanceDobOpen] = useState(false);
|
||||
const [searchTerm, setSearchTerm] = useState("");
|
||||
const [searchBy, setSearchBy] = useState<SearchCriteria["searchBy"]>("name");
|
||||
const [showAdvanced, setShowAdvanced] = useState(false);
|
||||
@@ -84,7 +86,7 @@ export function PatientSearch({
|
||||
<div className="flex gap-2 mb-4">
|
||||
<div className="relative flex-1">
|
||||
{searchBy === "dob" ? (
|
||||
<Popover>
|
||||
<Popover open={dobOpen} onOpenChange={setDobOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
@@ -112,6 +114,7 @@ export function PatientSearch({
|
||||
if (date) {
|
||||
const formattedDate = format(date, "yyyy-MM-dd");
|
||||
setSearchTerm(String(formattedDate));
|
||||
setDobOpen(false);
|
||||
}
|
||||
}}
|
||||
disabled={(date) => date > new Date()}
|
||||
@@ -153,9 +156,10 @@ export function PatientSearch({
|
||||
|
||||
<Select
|
||||
value={searchBy}
|
||||
onValueChange={(value) =>
|
||||
setSearchBy(value as SearchCriteria["searchBy"])
|
||||
}
|
||||
onValueChange={(value) => {
|
||||
setSearchBy(value as SearchCriteria["searchBy"]);
|
||||
setSearchTerm("");
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Search by..." />
|
||||
@@ -189,12 +193,13 @@ export function PatientSearch({
|
||||
</label>
|
||||
<Select
|
||||
value={advancedCriteria.searchBy}
|
||||
onValueChange={(value) =>
|
||||
updateAdvancedCriteria(
|
||||
"searchBy",
|
||||
value as SearchCriteria["searchBy"]
|
||||
)
|
||||
}
|
||||
onValueChange={(value) => {
|
||||
setAdvancedCriteria((prev) => ({
|
||||
...prev,
|
||||
searchBy: value as SearchCriteria["searchBy"],
|
||||
searchTerm: "",
|
||||
}));
|
||||
}}
|
||||
>
|
||||
<SelectTrigger className="col-span-3">
|
||||
<SelectValue placeholder="Name" />
|
||||
@@ -215,9 +220,13 @@ export function PatientSearch({
|
||||
Search term
|
||||
</label>
|
||||
{advancedCriteria.searchBy === "dob" ? (
|
||||
<Popover>
|
||||
<Popover
|
||||
open={advanceDobOpen}
|
||||
onOpenChange={setAdvanceDobOpen}
|
||||
>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") handleSearch();
|
||||
@@ -251,6 +260,7 @@ export function PatientSearch({
|
||||
"searchTerm",
|
||||
String(formattedDate)
|
||||
);
|
||||
setAdvanceDobOpen(false);
|
||||
}
|
||||
}}
|
||||
disabled={(date) => date > new Date()}
|
||||
|
||||
@@ -13,20 +13,34 @@ type CalendarProps =
|
||||
mode: "single";
|
||||
selected?: Date;
|
||||
onSelect?: (date: Date | undefined) => void;
|
||||
closeOnSelect?: boolean /** whether to request closing after selection (default true for single) */;
|
||||
onClose?: () => void;
|
||||
})
|
||||
| (BaseProps & {
|
||||
mode: "range";
|
||||
selected?: DateRange;
|
||||
onSelect?: (range: DateRange | undefined) => void;
|
||||
closeOnSelect?: boolean; // will close only when range is complete
|
||||
onClose?: () => void;
|
||||
})
|
||||
| (BaseProps & {
|
||||
mode: "multiple";
|
||||
selected?: Date[];
|
||||
onSelect?: (dates: Date[] | undefined) => void;
|
||||
closeOnSelect?: boolean; // default false for multi
|
||||
onClose?: () => void;
|
||||
});
|
||||
|
||||
export function Calendar(props: CalendarProps) {
|
||||
const { mode, selected, onSelect, className, ...rest } = props;
|
||||
const {
|
||||
mode,
|
||||
selected,
|
||||
onSelect,
|
||||
className,
|
||||
closeOnSelect,
|
||||
onClose,
|
||||
...rest
|
||||
} = props;
|
||||
|
||||
const [internalSelected, setInternalSelected] =
|
||||
useState<typeof selected>(selected);
|
||||
@@ -37,7 +51,30 @@ export function Calendar(props: CalendarProps) {
|
||||
|
||||
const handleSelect = (value: typeof selected) => {
|
||||
setInternalSelected(value);
|
||||
onSelect?.(value as any); // We'll narrow this properly below
|
||||
// forward original callback
|
||||
onSelect?.(value as any);
|
||||
|
||||
// Decide whether to request closing
|
||||
const shouldClose =
|
||||
typeof closeOnSelect !== "undefined"
|
||||
? closeOnSelect
|
||||
: mode === "single"
|
||||
? true
|
||||
: false;
|
||||
|
||||
if (!shouldClose) return;
|
||||
|
||||
// For range: only close when both from and to exist
|
||||
if (mode === "range") {
|
||||
const range = value as DateRange | undefined;
|
||||
if (range?.from && range?.to) {
|
||||
onClose?.();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// For single or multiple (when allowed), close immediately
|
||||
onClose?.();
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user