Skip to content
This repository has been archived by the owner on Dec 15, 2024. It is now read-only.

Commit

Permalink
re-implement dates filter
Browse files Browse the repository at this point in the history
  • Loading branch information
evermake committed Nov 23, 2024
1 parent 5f40b73 commit 3d44fcc
Showing 1 changed file with 98 additions and 63 deletions.
161 changes: 98 additions & 63 deletions frontend/src/components/filters/DatesFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,84 +5,119 @@ import {
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover.tsx";

import { cn } from "@/lib/utils.ts";
import { format } from "date-fns";
import { cn, plainDatesForFilter } from "@/lib/utils.ts";
import { ru } from "date-fns/locale/ru";
import { Calendar as CalendarIcon } from "lucide-react";
import { DateRange } from "react-day-picker";
import { FilterBaseProps } from "./common";
import { Filters } from "@/lib/types";
import { BaseFilter } from "./BaseFilter";
import { Temporal } from "temporal-polyfill";
import { useState } from "react";

export function DatesFilter(props: FilterBaseProps<Filters["date"]>) {
const { disabled, value, onChange, ...rest } = props;

const dateRange: DateRange | null = value ? {
from: value?.start_date ? new Date(value.start_date) : new Date(),
to: value?.end_date ? new Date(value.end_date) : undefined,
} : null
const valueStartDate = value?.start_date ? new Date(value.start_date) : null;
const valueStartPlain = valueStartDate ? dateToPlain(valueStartDate) : null;
const valueEndDate = value?.end_date ? new Date(value.end_date) : null;
const valueEndPlain = valueEndDate ? dateToPlain(valueEndDate) : null;

return (
<BaseFilter {...rest}>
<div>
<Popover>
<PopoverTrigger asChild>
<Button
id="date"
variant={"outline"}
className={cn(
"w-fit min-w-[300px] justify-start text-left font-normal",
!dateRange && "text-muted-foreground",
)}
>
<CalendarIcon />
{dateRange?.from ? (
dateRange.to ? (
<>
{format(dateRange.from, "dd.MM.yyyy", { locale: ru })} -{" "}
{format(dateRange.to, "dd.MM.yyyy", { locale: ru })}
</>
) : (
<>
После {format(dateRange.from, "dd.MM.yyyy", { locale: ru })}
</>
)
) : (
<span>Выберите даты</span>
)}
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
disabled={disabled}
initialFocus
mode="range"
defaultMonth={dateRange?.from}
selected={dateRange ?? undefined}
onSelect={(range) => {
onChange(
range
? {
start_date: range.from
? encodeDate(range.from)
: undefined,
end_date: range.to ? encodeDate(range.to) : undefined,
}
: null,
);
}}
numberOfMonths={2}
locale={ru}
/>
</PopoverContent>
</Popover>
<div className="flex items-center gap-2">
<DatePicker
value={valueStartPlain}
onChange={(v) => onChange(plainDatesForFilter(v, valueEndPlain))}
placeholder="Не раньше"
/>
<span></span>
<DatePicker
value={valueEndPlain}
onChange={(v) => onChange(plainDatesForFilter(valueStartPlain, v))}
placeholder="Не позже"
/>
</div>
</BaseFilter>
);
}

// Returns date in format "YYYY-MM-DD".
function encodeDate(d: Date): string {
return d.toISOString().split("T")[0];
function DatePicker({
value,
onChange,
placeholder,
}: {
value: Temporal.PlainDate | null;
onChange: (v: Temporal.PlainDate | null) => void;
placeholder: string;
}) {
const selectedDate = value ? plainToDate(value) : undefined;
const [open, setOpen] = useState(false);

return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<Button
id="date"
variant={"outline"}
className={cn(
"justify-start text-left font-normal flex-auto",
!value && "text-muted-foreground",
)}
>
<CalendarIcon />
<span>
{value
? plainToDate(value).toLocaleString("ru-RU", {
year: "numeric",
month: "2-digit",
day: "2-digit",
})
: placeholder}
</span>
</Button>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="center">
<Calendar
mode="single"
defaultMonth={selectedDate}
selected={selectedDate}
onSelect={(newDate) => {
onChange(newDate ? dateToPlain(newDate) : null);
}}
numberOfMonths={1}
locale={ru}
/>
<div className="flex justify-end gap-2 px-4 pb-4">
<Button
variant="default"
onClick={() => {
onChange(Temporal.Now.plainDateISO());
setOpen(false);
}}
>
Сегодня
</Button>
<Button
variant="outline"
onClick={() => {
onChange(null);
setOpen(false);
}}
>
Сбросить
</Button>
</div>
</PopoverContent>
</Popover>
);
}

const plainToDate = (d: Temporal.PlainDate) =>
new Date(d.year, d.month - 1, d.day);

const dateToPlain = (d: Date) =>
Temporal.PlainDate.from({
year: d.getFullYear(),
month: d.getMonth() + 1,
day: d.getDate(),
});

0 comments on commit 3d44fcc

Please sign in to comment.