236 lines
10 KiB
TypeScript
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>
|
|
);
|
|
}
|