ldc-groupe/resources/ts/back/Components/Inputs/SimpleDatePicker.tsx
2026-02-12 16:13:54 +01:00

236 lines
10 KiB
TypeScript

// * Other
import { fr, enUS } from "date-fns/locale";
import { usePage } from "@inertiajs/react";
import { CalendarIcon, RefreshCcw } from "lucide-react";
// * Components shadcn
import { Calendar } from "@/components/ui/calendar";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Separator } from "@/components/ui/separator";
import { ScrollArea, ScrollBar } from "@/components/ui/scroll-area";
// * Other
import InputError from "@/back/Components/InputError";
import { cn, GetFormatedDate } from "@/lib/utils";
import str from "@/hooks/use-string";
import useLocale from "@/store/use-locale-store";
import useTrans from "@/hooks/use-translations";
type SimpleDatePickerProps = {
name: string;
placeholder?: string;
date?: Date | null;
required?: boolean;
disabled?: boolean;
className?: string;
label: string;
hasTime?: boolean;
parentErrors?: Record<string, string> | null;
// eslint-disable-next-line no-unused-vars
setData: (name: string, value: Date | null) => void;
};
export default function SimpleDatePicker({
name,
placeholder = "Sélectionner une date",
date,
required = false,
disabled = false,
className,
label,
hasTime = false,
parentErrors = null,
setData,
}: SimpleDatePickerProps) {
const { errors } = usePage().props;
const localErrors = parentErrors ?? errors;
let calendarLocale = fr;
switch (usePage().props.locale) {
case "en":
calendarLocale = enUS;
break;
default:
calendarLocale = fr;
break;
}
const parsedDate = date ? new Date(date) : undefined;
const hours = Array.from({ length: 24 }, (_, i) => i);
const minutes = Array.from({ length: 12 }, (_, i) => i * 5);
const handleDateSelect = (selectedDate: Date | undefined) => {
if (selectedDate) {
setData(name, selectedDate);
}
};
const resetDate = () => {
setData(name, null);
};
function handleTimeChange(type: "hour" | "minute", value: string) {
const currentDate = parsedDate || new Date();
const newDate = new Date(currentDate);
if (type === "hour") {
const hour = parseInt(value, 10);
newDate.setHours(hour);
} else if (type === "minute") {
newDate.setMinutes(parseInt(value, 10));
}
setData(name, newDate);
}
return (
<div className="flex flex-col items-start w-full gap-1.5">
<Label htmlFor={name}>{label + (required ? " *" : "")}</Label>
<Popover>
<PopoverTrigger asChild>
<Button
variant="outline"
className={cn(
"w-full font-normal",
{
"text-muted-foreground hover:text-muted-foreground":
!date,
},
className,
)}
disabled={disabled}
>
<span>
{date && date !== undefined
? GetFormatedDate(
date?.toString(),
useLocale.use.timezone(),
hasTime,
)
: placeholder}
</span>
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<div className="sm:flex">
<Calendar
required={required}
startMonth={new Date(1925, 0, 1)}
endMonth={new Date(2050, 11, 31)}
locale={calendarLocale}
mode="single"
captionLayout="dropdown"
selected={parsedDate ?? undefined}
onSelect={handleDateSelect}
disabled={disabled}
/>
{hasTime && (
<div className="flex flex-col sm:flex-row sm:h-[300px] divide-y sm:divide-y-0 sm:divide-x">
<ScrollArea className="w-64 sm:w-auto">
<div className="flex sm:flex-col p-2">
{hours.map((hour) => (
<Button
key={hour}
size="icon"
variant={
parsedDate &&
parsedDate.getHours() ===
hour
? "default"
: "ghost"
}
className="sm:w-full shrink-0 aspect-square"
onClick={() =>
handleTimeChange(
"hour",
hour.toString(),
)
}
>
<p
className={cn(
parsedDate &&
parsedDate.getHours() ===
hour
? "text-primary-foreground"
: "",
)}
>
{hour}
</p>
</Button>
))}
</div>
<ScrollBar
orientation="horizontal"
className="sm:hidden"
/>
</ScrollArea>
<ScrollArea className="w-64 sm:w-auto">
<div className="flex sm:flex-col p-2">
{minutes.map((minute) => (
<Button
key={minute}
size="icon"
variant={
parsedDate &&
parsedDate.getMinutes() ===
minute
? "default"
: "ghost"
}
className="sm:w-full shrink-0 aspect-square"
onClick={() =>
handleTimeChange(
"minute",
minute.toString(),
)
}
>
<p
className={cn(
parsedDate &&
parsedDate.getMinutes() ===
minute
? "text-primary-foreground"
: "",
)}
>
{minute
.toString()
.padStart(2, "0")}
</p>
</Button>
))}
</div>
<ScrollBar
orientation="horizontal"
className="sm:hidden"
/>
</ScrollArea>
</div>
)}
</div>
<div className="w-full ">
<Separator className="w-full" />
<Button
disabled={!date}
onClick={resetDate}
type="button"
variant="ghost"
className="rounded-none w-full py-1.5"
>
<RefreshCcw />{" "}
{str(useTrans("back.simple_date_picker.reset"))
.ucFirst()
.value()}
</Button>
</div>
</PopoverContent>
</Popover>
<InputError message={localErrors[name]} />
</div>
);
}