feat: TitlesFilterPanel component
All checks were successful
Build and Deploy Go App / build (push) Successful in 6m16s
Build and Deploy Go App / deploy (push) Successful in 37s

This commit is contained in:
nihonium 2025-12-04 05:52:31 +03:00
parent ab29c33f5b
commit 4dd60f3b19
Signed by: nihonium
GPG key ID: 0251623741027CFC
3 changed files with 142 additions and 6 deletions

View file

@ -0,0 +1,122 @@
import { useState } from "react";
import type { TitleStatus, ReleaseSeason } from "../../api";
import { ChevronDownIcon, ChevronUpIcon } from "@heroicons/react/24/solid";
export type TitlesFilter = {
extSearch: boolean;
status: TitleStatus | "";
rating: number | "";
releaseYear: number | "";
releaseSeason: ReleaseSeason | "";
};
type TitlesFilterPanelProps = {
filters: TitlesFilter;
setFilters: (filters: TitlesFilter) => void;
};
const STATUS_OPTIONS: (TitleStatus | "")[] = ["", "planned", "finished", "ongoing"];
const SEASON_OPTIONS: (ReleaseSeason | "")[] = ["", "winter", "spring", "summer", "fall"];
const RATING_OPTIONS = ["", 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
export function TitlesFilterPanel({ filters, setFilters }: TitlesFilterPanelProps) {
const [open, setOpen] = useState(false);
const handleChange = (field: keyof TitlesFilter, value: any) => {
setFilters({ ...filters, [field]: value });
};
return (
<div className="w-full flex justify-center my-4">
<div className="bg-white shadow rounded-lg w-full max-w-3xl p-4">
{/* Заголовок панели */}
<div
className="flex justify-between items-center cursor-pointer"
onClick={() => setOpen((prev) => !prev)}
>
<h3 className="text-lg font-medium">Filters</h3>
{open ? <ChevronUpIcon className="w-5 h-5" /> : <ChevronDownIcon className="w-5 h-5" />}
</div>
{/* Контент панели */}
{open && (
<div className="mt-4 grid grid-cols-2 sm:grid-cols-3 gap-4">
{/* Extended Search */}
<div className="flex items-center gap-2">
<input
type="checkbox"
id="extSearch"
checked={filters.extSearch}
onChange={(e) => handleChange("extSearch", e.target.checked)}
className="w-4 h-4"
/>
<label htmlFor="extSearch" className="text-sm">
Extended Search
</label>
</div>
{/* Status */}
<div className="flex flex-col">
<label htmlFor="status" className="text-sm mb-1">Status</label>
<select
id="status"
value={filters.status}
onChange={(e) => handleChange("status", e.target.value || "")}
className="border rounded px-2 py-1"
>
{STATUS_OPTIONS.map((s) => (
<option key={s || "all"} value={s}>{s || "All"}</option>
))}
</select>
</div>
{/* Rating */}
<div className="flex flex-col">
<label htmlFor="rating" className="text-sm mb-1">Rating</label>
<select
id="rating"
value={filters.rating}
onChange={(e) => handleChange("rating", e.target.value ? Number(e.target.value) : "")}
className="border rounded px-2 py-1"
>
{RATING_OPTIONS.map((r) => (
<option key={r} value={r}>{r || "All"}</option>
))}
</select>
</div>
{/* Release Year */}
<div className="flex flex-col">
<label htmlFor="releaseYear" className="text-sm mb-1">Release Year</label>
<input
type="number"
id="releaseYear"
value={filters.releaseYear || ""}
onChange={(e) =>
handleChange("releaseYear", e.target.value ? Number(e.target.value) : "")
}
className="border rounded px-2 py-1"
placeholder="Any"
/>
</div>
{/* Release Season */}
<div className="flex flex-col">
<label htmlFor="releaseSeason" className="text-sm mb-1">Release Season</label>
<select
id="releaseSeason"
value={filters.releaseSeason}
onChange={(e) => handleChange("releaseSeason", e.target.value || "")}
className="border rounded px-2 py-1"
>
{SEASON_OPTIONS.map((s) => (
<option key={s || "all"} value={s}>{s || "All"}</option>
))}
</select>
</div>
</div>
)}
</div>
</div>
);
}