mirror of
https://github.com/Radiquum/AniX.git
synced 2025-09-04 05:25:36 +05:00
anix/feat: finish Filters Modal Component
This commit is contained in:
parent
2a2343fed3
commit
0f1c61b765
5 changed files with 281 additions and 13 deletions
|
@ -51,6 +51,7 @@ export const ENDPOINTS = {
|
|||
}
|
||||
},
|
||||
filter: `${API_PREFIX}/filter`,
|
||||
filterTypes: `${API_PREFIX}/type/all`,
|
||||
search: {
|
||||
profileList: `${API_PREFIX}/search/profile/list`,
|
||||
profileHistory: `${API_PREFIX}/search/history`,
|
||||
|
|
106
app/components/Discovery/Modal/FiltersAgeRatingModal.tsx
Normal file
106
app/components/Discovery/Modal/FiltersAgeRatingModal.tsx
Normal file
|
@ -0,0 +1,106 @@
|
|||
"use client";
|
||||
|
||||
import { FilterAgeRatingToString } from "#/api/utils";
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Label,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
} from "flowbite-react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
ageRatings: number[];
|
||||
setAgeRatings: (lists: number[]) => void;
|
||||
};
|
||||
|
||||
export const FiltersAgeRatingModal = ({
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
ageRatings,
|
||||
setAgeRatings,
|
||||
}: Props) => {
|
||||
const [newAgeRatings, setNewAgeRatings] = useState(ageRatings);
|
||||
|
||||
function toggleRating(number: number) {
|
||||
if (newAgeRatings.includes(number)) {
|
||||
setNewAgeRatings(newAgeRatings.filter((rating) => rating != number));
|
||||
} else {
|
||||
setNewAgeRatings([...newAgeRatings, number]);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setNewAgeRatings(ageRatings);
|
||||
}, [ageRatings]);
|
||||
|
||||
return (
|
||||
<Modal show={isOpen} onClose={() => setIsOpen(false)} dismissible>
|
||||
<ModalHeader>Выберите списки</ModalHeader>
|
||||
<ModalBody>
|
||||
{Object.entries(FilterAgeRatingToString).map(([key, value]) => {
|
||||
return (
|
||||
<div
|
||||
className="flex items-center gap-2"
|
||||
key={`filter-age-rating-${value}`}
|
||||
>
|
||||
<Checkbox
|
||||
id={`filter-age-rating-${value}`}
|
||||
onChange={() => toggleRating(Number(key))}
|
||||
checked={newAgeRatings.includes(Number(key))}
|
||||
color="blue"
|
||||
/>
|
||||
<Label htmlFor={`filter-age-rating-${value}`}>{value}</Label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setAgeRatings([]);
|
||||
setNewAgeRatings([]);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
color="red"
|
||||
>
|
||||
Сбросить
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setAgeRatings(newAgeRatings);
|
||||
setNewAgeRatings([]);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
color="blue"
|
||||
>
|
||||
Применить
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (
|
||||
newAgeRatings.length !=
|
||||
Object.keys(FilterAgeRatingToString).length
|
||||
) {
|
||||
setNewAgeRatings(
|
||||
Object.keys(FilterAgeRatingToString).map((key) => Number(key))
|
||||
);
|
||||
} else {
|
||||
setNewAgeRatings([]);
|
||||
}
|
||||
}}
|
||||
color="light"
|
||||
>
|
||||
{newAgeRatings.length >= Object.keys(FilterAgeRatingToString).length ?
|
||||
"Снять все"
|
||||
: "Выбрать все"}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
};
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
import {
|
||||
Filter,
|
||||
FilterAgeRatingToString,
|
||||
FilterCategoryIdToString,
|
||||
FilterCountry,
|
||||
FilterDefault,
|
||||
|
@ -14,6 +15,7 @@ import {
|
|||
FilterStatusIdToString,
|
||||
FilterStudio,
|
||||
FilterYear,
|
||||
tryCatchAPI,
|
||||
} from "#/api/utils";
|
||||
import {
|
||||
Button,
|
||||
|
@ -24,10 +26,14 @@ import {
|
|||
ModalFooter,
|
||||
ModalHeader,
|
||||
} from "flowbite-react";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { FiltersGenreModal } from "./FiltersGenreModal";
|
||||
import { useUserStore } from "#/store/auth";
|
||||
import { FiltersListExcludeModal } from "./FiltersListExcludeModal";
|
||||
import { ENDPOINTS } from "#/api/config";
|
||||
import { FiltersTypesModal } from "./FiltersTypesModal";
|
||||
import { FiltersAgeRatingModal } from "./FiltersAgeRatingModal";
|
||||
import { useRouter } from "next/navigation";
|
||||
|
||||
type ModalProps = {
|
||||
isOpen: boolean;
|
||||
|
@ -37,20 +43,46 @@ type ModalProps = {
|
|||
|
||||
export const FiltersModal = ({ isOpen, setIsOpen, filter }: ModalProps) => {
|
||||
const userStore = useUserStore();
|
||||
const router = useRouter();
|
||||
|
||||
const [newFilter, setNewFilter] = useState(filter || FilterDefault);
|
||||
const [isGenreModalOpen, setIsGenreModalOpen] = useState(false);
|
||||
const [isListExcludeModalOpen, setIsListExcludeModalOpen] = useState(false);
|
||||
const [isTypeModalOpen, setIsTypeModalOpen] = useState(false);
|
||||
const [isAgeRatingModalOpen, setIsAgeRatingModalOpen] = useState(false);
|
||||
|
||||
const [types, setTypes] = useState([]);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
setError(null);
|
||||
|
||||
const { data, error } = await tryCatchAPI(fetch(ENDPOINTS.filterTypes));
|
||||
|
||||
if (error) {
|
||||
setError(error);
|
||||
} else {
|
||||
setTypes(data.types);
|
||||
}
|
||||
};
|
||||
fetchData();
|
||||
}, []);
|
||||
|
||||
function saveGenres(genres, is_genres_exclude_mode_enabled) {
|
||||
setNewFilter({ ...newFilter, genres, is_genres_exclude_mode_enabled });
|
||||
}
|
||||
|
||||
function saveFilter() {
|
||||
const _filter = JSON.stringify(newFilter);
|
||||
router.push(`/discovery/filter?filter=${_filter}`);
|
||||
setIsOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
// show={isOpen}
|
||||
show={true}
|
||||
show={isOpen}
|
||||
onClose={() => setIsOpen(false)}
|
||||
size="4xl"
|
||||
dismissible
|
||||
|
@ -163,11 +195,15 @@ export const FiltersModal = ({ isOpen, setIsOpen, filter }: ModalProps) => {
|
|||
<Button
|
||||
color={"blue"}
|
||||
className="w-full min-h-10 h-fit"
|
||||
// onClick={() => setIsGenreModalOpen(true)}
|
||||
onClick={() => setIsTypeModalOpen(true)}
|
||||
>
|
||||
{/* {newFilter.genres.length > 0 ? */}
|
||||
{/* newFilter.genres.join(", ") */}
|
||||
{/* : "Неважно"} */}
|
||||
{error ?
|
||||
error.message
|
||||
: newFilter.types.length > 0 ?
|
||||
newFilter.types
|
||||
.map((type) => types.find((t) => t.id === type).name)
|
||||
.join(", ")
|
||||
: "Неважно"}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
|
@ -443,11 +479,13 @@ export const FiltersModal = ({ isOpen, setIsOpen, filter }: ModalProps) => {
|
|||
<Button
|
||||
color={"blue"}
|
||||
className="w-full min-h-10 h-fit"
|
||||
// onClick={() => setIsGenreModalOpen(true)}
|
||||
onClick={() => setIsAgeRatingModalOpen(true)}
|
||||
>
|
||||
{/* {newFilter.genres.length > 0 ?
|
||||
newFilter.genres.join(", ")
|
||||
: "Неважно"} */}
|
||||
{newFilter.age_ratings.length > 0 ?
|
||||
newFilter.age_ratings
|
||||
.map((age_rating) => FilterAgeRatingToString[age_rating])
|
||||
.join(", ")
|
||||
: "Неважно"}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
|
@ -476,7 +514,11 @@ export const FiltersModal = ({ isOpen, setIsOpen, filter }: ModalProps) => {
|
|||
</div>
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter></ModalFooter>
|
||||
<ModalFooter>
|
||||
<Button color="blue" onClick={saveFilter}>
|
||||
Применить
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
<FiltersGenreModal
|
||||
isOpen={isGenreModalOpen}
|
||||
|
@ -493,6 +535,21 @@ export const FiltersModal = ({ isOpen, setIsOpen, filter }: ModalProps) => {
|
|||
setNewFilter({ ...newFilter, profile_list_exclusions })
|
||||
}
|
||||
/>
|
||||
<FiltersTypesModal
|
||||
isOpen={isTypeModalOpen}
|
||||
setIsOpen={setIsTypeModalOpen}
|
||||
typesData={types}
|
||||
types={newFilter.types}
|
||||
setTypes={(types) => setNewFilter({ ...newFilter, types })}
|
||||
/>
|
||||
<FiltersAgeRatingModal
|
||||
isOpen={isAgeRatingModalOpen}
|
||||
setIsOpen={setIsAgeRatingModalOpen}
|
||||
ageRatings={newFilter.age_ratings}
|
||||
setAgeRatings={(age_ratings) =>
|
||||
setNewFilter({ ...newFilter, age_ratings })
|
||||
}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
100
app/components/Discovery/Modal/FiltersTypesModal.tsx
Normal file
100
app/components/Discovery/Modal/FiltersTypesModal.tsx
Normal file
|
@ -0,0 +1,100 @@
|
|||
"use client";
|
||||
|
||||
import {
|
||||
Button,
|
||||
Checkbox,
|
||||
Label,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalFooter,
|
||||
ModalHeader,
|
||||
} from "flowbite-react";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
setIsOpen: (isOpen: boolean) => void;
|
||||
typesData: any[];
|
||||
types: number[];
|
||||
setTypes: (types: number[]) => void;
|
||||
};
|
||||
|
||||
export const FiltersTypesModal = ({
|
||||
isOpen,
|
||||
setIsOpen,
|
||||
typesData,
|
||||
types,
|
||||
setTypes,
|
||||
}: Props) => {
|
||||
const [newTypes, setNewTypes] = useState(types);
|
||||
|
||||
function toggleType(number: number) {
|
||||
if (newTypes.includes(number)) {
|
||||
setNewTypes(newTypes.filter((list) => list != number));
|
||||
} else {
|
||||
setNewTypes([...newTypes, number]);
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
setNewTypes(types);
|
||||
}, [types]);
|
||||
|
||||
return (
|
||||
<Modal show={isOpen} onClose={() => setIsOpen(false)} dismissible>
|
||||
<ModalHeader>Выберите списки</ModalHeader>
|
||||
<ModalBody>
|
||||
{typesData.map((item) => {
|
||||
return (
|
||||
<div
|
||||
className="flex items-center gap-2"
|
||||
key={`filter-types-${item.name}`}
|
||||
>
|
||||
<Checkbox
|
||||
id={`filter-types-${item.name}`}
|
||||
onChange={() => toggleType(Number(item.id))}
|
||||
checked={newTypes.includes(Number(item.id))}
|
||||
color="blue"
|
||||
/>
|
||||
<Label htmlFor={`filter-types-${item.name}`}>{item.name}</Label>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setTypes([]);
|
||||
setNewTypes([]);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
color="red"
|
||||
>
|
||||
Сбросить
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
setTypes(newTypes);
|
||||
setNewTypes([]);
|
||||
setIsOpen(false);
|
||||
}}
|
||||
color="blue"
|
||||
>
|
||||
Применить
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => {
|
||||
if (newTypes.length != typesData.length) {
|
||||
setNewTypes(typesData.map((item) => Number(item.id)));
|
||||
} else {
|
||||
setNewTypes([]);
|
||||
}
|
||||
}}
|
||||
color="light"
|
||||
>
|
||||
{newTypes.length >= typesData.length ? "Снять все" : "Выбрать все"}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</Modal>
|
||||
);
|
||||
};
|
|
@ -45,7 +45,11 @@ export const DiscoverPage = () => {
|
|||
<span className="flex-shrink-0 inline-block w-8 h-8 mr-2 iconify mdi--collections-bookmark"></span>
|
||||
<span>Коллекции</span>
|
||||
</Button>
|
||||
<Button size="xl" color="green">
|
||||
<Button
|
||||
size="xl"
|
||||
color="green"
|
||||
onClick={() => setFiltersModalOpen(true)}
|
||||
>
|
||||
<span className="flex-shrink-0 inline-block w-8 h-8 mr-2 iconify mdi--mixer-settings"></span>
|
||||
<span>Фильтр</span>
|
||||
</Button>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue