From 4dd60f3b190ffa8b2ca91ade49d83c1b36ea295d Mon Sep 17 00:00:00 2001 From: nihonium Date: Thu, 4 Dec 2025 05:52:31 +0300 Subject: [PATCH] feat: TitlesFilterPanel component --- .../src/api/services/DefaultService.ts | 3 + .../TitlesFilterPanel/TitlesFilterPanel.tsx | 122 ++++++++++++++++++ .../src/pages/TitlesPage/TitlesPage.tsx | 23 +++- 3 files changed, 142 insertions(+), 6 deletions(-) create mode 100644 modules/frontend/src/components/TitlesFilterPanel/TitlesFilterPanel.tsx diff --git a/modules/frontend/src/api/services/DefaultService.ts b/modules/frontend/src/api/services/DefaultService.ts index 218b461..6898c46 100644 --- a/modules/frontend/src/api/services/DefaultService.ts +++ b/modules/frontend/src/api/services/DefaultService.ts @@ -20,6 +20,7 @@ export class DefaultService { * @param cursor * @param sort * @param sortForward + * @param extSearch * @param word * @param status List of title statuses to filter * @param rating @@ -35,6 +36,7 @@ export class DefaultService { cursor?: string, sort?: TitleSort, sortForward: boolean = true, + extSearch: boolean = false, word?: string, status?: Array, rating?: number, @@ -57,6 +59,7 @@ export class DefaultService { 'cursor': cursor, 'sort': sort, 'sort_forward': sortForward, + 'ext_search': extSearch, 'word': word, 'status': status, 'rating': rating, diff --git a/modules/frontend/src/components/TitlesFilterPanel/TitlesFilterPanel.tsx b/modules/frontend/src/components/TitlesFilterPanel/TitlesFilterPanel.tsx new file mode 100644 index 0000000..3cfef69 --- /dev/null +++ b/modules/frontend/src/components/TitlesFilterPanel/TitlesFilterPanel.tsx @@ -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 ( +
+
+ {/* Заголовок панели */} +
setOpen((prev) => !prev)} + > +

Filters

+ {open ? : } +
+ + {/* Контент панели */} + {open && ( +
+ {/* Extended Search */} +
+ handleChange("extSearch", e.target.checked)} + className="w-4 h-4" + /> + +
+ + {/* Status */} +
+ + +
+ + {/* Rating */} +
+ + +
+ + {/* Release Year */} +
+ + + handleChange("releaseYear", e.target.value ? Number(e.target.value) : "") + } + className="border rounded px-2 py-1" + placeholder="Any" + /> +
+ + {/* Release Season */} +
+ + +
+
+ )} +
+
+ ); +} diff --git a/modules/frontend/src/pages/TitlesPage/TitlesPage.tsx b/modules/frontend/src/pages/TitlesPage/TitlesPage.tsx index c9911b9..ed55d8d 100644 --- a/modules/frontend/src/pages/TitlesPage/TitlesPage.tsx +++ b/modules/frontend/src/pages/TitlesPage/TitlesPage.tsx @@ -8,6 +8,7 @@ import { TitleCardHorizontal } from "../../components/cards/TitleCardHorizontal" import type { CursorObj, Title, TitleSort } from "../../api"; import { LayoutSwitch } from "../../components/LayoutSwitch/LayoutSwitch"; import { Link } from "react-router-dom"; +import { type TitlesFilter, TitlesFilterPanel } from "../../components/TitlesFilterPanel/TitlesFilterPanel"; const PAGE_SIZE = 10; @@ -22,6 +23,14 @@ export default function TitlesPage() { const [sortForward, setSortForward] = useState(true); const [layout, setLayout] = useState<"square" | "horizontal">("square"); + const [filters, setFilters] = useState({ + extSearch: false, + status: "", + rating: "", + releaseYear: "", + releaseSeason: "", + }); + const fetchPage = async (cursorObj: CursorObj | null) => { const cursorStr = cursorObj ? btoa(JSON.stringify(cursorObj)).replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '') : ""; @@ -30,13 +39,14 @@ export default function TitlesPage() { cursorStr, sort, sortForward, + filters.extSearch, search.trim() || undefined, - undefined, - undefined, - undefined, - undefined, + filters.status ? [filters.status] : undefined, + filters.rating || undefined, + filters.releaseYear || undefined, + filters.releaseSeason || undefined, + PAGE_SIZE, PAGE_SIZE, - undefined, "all" ); @@ -73,7 +83,7 @@ export default function TitlesPage() { }; initLoad(); - }, [search, sort, sortForward]); + }, [search, sort, sortForward, filters]); const handleLoadMore = async () => { @@ -121,6 +131,7 @@ const handleLoadMore = async () => { setSortForward={setSortForward} /> + {loading &&
Loading...
}