97 lines
3.0 KiB
TypeScript
97 lines
3.0 KiB
TypeScript
"use client";
|
|
|
|
import * as React from "react";
|
|
import DateObject from "react-date-object";
|
|
import persian from "react-date-object/calendars/persian";
|
|
import persian_fa from "react-date-object/locales/persian_fa";
|
|
import DatePicker from "react-multi-date-picker";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Label } from "@/components/ui/label";
|
|
|
|
type AdminDateTimeFieldProps = {
|
|
label: string;
|
|
value?: string | null;
|
|
onChange: (value: string | null) => void;
|
|
required?: boolean;
|
|
disabled?: boolean;
|
|
};
|
|
|
|
function splitDateTime(value?: string | null) {
|
|
if (!value) {
|
|
return { date: null as DateObject | null, time: "" };
|
|
}
|
|
const date = new Date(value);
|
|
if (Number.isNaN(date.getTime())) {
|
|
return { date: null, time: "" };
|
|
}
|
|
return {
|
|
date: new DateObject({ date, calendar: persian, locale: persian_fa }),
|
|
time: `${String(date.getHours()).padStart(2, "0")}:${String(date.getMinutes()).padStart(2, "0")}`,
|
|
};
|
|
}
|
|
|
|
function combineDateTime(date: DateObject | null, time: string) {
|
|
if (!date || !time || !/^\d{2}:\d{2}$/.test(time)) return null;
|
|
const gregorian = date.toDate();
|
|
const [hours, minutes] = time.split(":").map(Number);
|
|
gregorian.setHours(hours, minutes, 0, 0);
|
|
return gregorian.toISOString();
|
|
}
|
|
|
|
export default function AdminDateTimeField({
|
|
label,
|
|
value,
|
|
onChange,
|
|
required,
|
|
disabled,
|
|
}: AdminDateTimeFieldProps) {
|
|
const initial = React.useMemo(() => splitDateTime(value), [value]);
|
|
const [date, setDate] = React.useState<DateObject | null>(initial.date);
|
|
const [time, setTime] = React.useState(initial.time);
|
|
|
|
React.useEffect(() => {
|
|
setDate(initial.date);
|
|
setTime(initial.time);
|
|
}, [initial.date, initial.time]);
|
|
|
|
const emitChange = (nextDate: DateObject | null, nextTime: string) => {
|
|
onChange(combineDateTime(nextDate, nextTime));
|
|
};
|
|
|
|
return (
|
|
<div className="space-y-2">
|
|
<Label>
|
|
{label}
|
|
{required ? <span className="text-destructive"> *</span> : null}
|
|
</Label>
|
|
<div className="grid gap-2 sm:grid-cols-[1fr_120px]">
|
|
<DatePicker
|
|
value={date}
|
|
onChange={(next) => {
|
|
const nextDate = next instanceof DateObject ? next : null;
|
|
setDate(nextDate);
|
|
emitChange(nextDate, time);
|
|
}}
|
|
calendar={persian}
|
|
locale={persian_fa}
|
|
calendarPosition="bottom-right"
|
|
disabled={disabled}
|
|
inputClass="h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm text-right ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
|
|
placeholder="تاریخ"
|
|
containerClassName="w-full"
|
|
/>
|
|
<Input
|
|
dir="ltr"
|
|
type="time"
|
|
value={time}
|
|
disabled={disabled}
|
|
onChange={(event) => {
|
|
setTime(event.target.value);
|
|
emitChange(date, event.target.value);
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|