Skip to content

Commit

Permalink
feature: add studios filter
Browse files Browse the repository at this point in the history
  • Loading branch information
olexh committed May 11, 2024
1 parent 34392c3 commit 5c4cef7
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 312 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { FC } from 'react';

import Image from 'next/image';
import Link from 'next/link';

import P from '@/components/typography/p';
import { Label } from '@/components/ui/label';
Expand All @@ -11,7 +12,7 @@ import {
} from '@/components/ui/tooltip';

interface Props {
companies: API.Company[];
companies: API.CompanyWithType[];
}

const Studio: FC<Props> = ({ companies }) => {
Expand All @@ -34,20 +35,28 @@ const Studio: FC<Props> = ({ companies }) => {
{studio.company.image ? (
<Tooltip delayDuration={0}>
<TooltipTrigger>
<Image
src={studio.company.image}
alt="studio"
width={100}
height={50}
className="w-16 rounded-md object-cover"
/>
<Link
href={`/anime?studios=${studio.company.slug}`}
>
<Image
src={studio.company.image}
alt="studio"
width={100}
height={50}
className="w-16 rounded-md object-cover"
/>
</Link>
</TooltipTrigger>
<TooltipContent>
<P className="text-sm">{studio.company.name}</P>
</TooltipContent>
</Tooltip>
) : (
<Label>{studio.company.name}</Label>
<Label>
<Link href={`/anime?studios=${studio.company.slug}`}>
{studio.company.name}
</Link>
</Label>
)}
</div>
</div>
Expand Down
112 changes: 68 additions & 44 deletions components/filters/anime-filters.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import clsx from 'clsx';
import * as React from 'react';
import { FC, Fragment, useEffect, useState } from 'react';
import { FC, useEffect, useState } from 'react';
import AntDesignClearOutlined from '~icons/ant-design/clear-outlined';
import MaterialSymbolsSortRounded from '~icons/material-symbols/sort-rounded';

Expand All @@ -17,16 +17,19 @@ import { ScrollArea } from '@/components/ui/scroll-area';
import {
Select,
SelectContent,
SelectEmpty,
SelectGroup,
SelectItem,
SelectList,
SelectSearch,
SelectSeparator,
SelectTrigger,
SelectValue,
groupOptions,
renderSelectOptions,
} from '@/components/ui/select';
import { Slider } from '@/components/ui/slider';
import getAnimeGenres from '@/services/api/anime/getAnimeGenres';
import useCompanies from '@/services/hooks/companies/useCompanies';
import {
AGE_RATING,
GENRE_TYPES,
Expand Down Expand Up @@ -88,26 +91,32 @@ const AnimeFilters: FC<Props> = ({ className, type }) => {
const ageRatings = searchParams.getAll('ratings');
const years = searchParams.getAll('years');
const genres = searchParams.getAll('genres');
const studios = searchParams.getAll('studios');
const lang = searchParams.get('only_translated');
const order = searchParams.get('order');
const sort = searchParams.get('sort') || 'score';

const sortOptions = type === 'anime' ? SORT_ANIME : SORT_WATCHLIST;

const { data: genresList } = useQuery({
queryKey: ['animeGenres'],
queryFn: () => getAnimeGenres(),
select: (data) =>
data.list.reduce<Record<API.GenreType, API.Genre[]>>(
(acc, genre) => {
if (!acc[genre.type]) {
acc[genre.type] = [];
}
acc[genre.type].push(genre);
return acc;
},
{} as Record<API.GenreType, API.Genre[]>,
groupOptions(
data.list.map((genre) => ({
value: genre.slug,
label: genre.name_ua,
group: GENRE_TYPES[genre.type].title_ua,
})),
),
});

const [studioSearch, setStudioSearch] = useState<string>();
const { data: studioList, isFetching: isStudioListFetching } = useCompanies({
type: 'studio',
query: studioSearch,
});

const [selectingYears, setSelectingYears] = useState<string[]>(
years.length > 0 ? years : YEARS.map((y) => String(y)),
);
Expand Down Expand Up @@ -137,6 +146,15 @@ const AnimeFilters: FC<Props> = ({ className, type }) => {
router.replace(`${pathname}?${query}`);
};

const handleStudioSearch = (keyword: string) => {
if(keyword.length < 3) {
setStudioSearch(undefined);
return;
}

setStudioSearch(keyword);
}

useEffect(() => {
if (JSON.stringify(selectingYears) !== JSON.stringify(years)) {
setSelectingYears(
Expand Down Expand Up @@ -166,15 +184,12 @@ const AnimeFilters: FC<Props> = ({ className, type }) => {
}
>
<SelectTrigger className="flex-1">
<SelectValue placeholder="Виберіть тип сортування..." />
<SelectValue placeholder="Виберіть сортування..." />
</SelectTrigger>
<SelectContent>
<SelectList>
<SelectGroup>
{(type === 'anime'
? SORT_ANIME
: SORT_WATCHLIST
).map((item) => (
{sortOptions.map((item) => (
<SelectItem
key={item.value}
value={item.value}
Expand Down Expand Up @@ -219,33 +234,8 @@ const AnimeFilters: FC<Props> = ({ className, type }) => {
<SelectContent>
<SelectSearch placeholder="Назва жанру..." />
<SelectList>
{genresList &&
(
Object.keys(
genresList,
) as API.GenreType[]
).map((type, index) => (
<Fragment key={type}>
{index !== 0 && <SelectSeparator />}
<SelectGroup
heading={
GENRE_TYPES[type].title_ua
}
>
{genresList[type].map(
(genre) => (
<SelectItem
key={genre.slug}
value={genre.slug}
>
{genre.name_ua ||
genre.name_en}
</SelectItem>
),
)}
</SelectGroup>
</Fragment>
))}
{genresList && renderSelectOptions(genresList)}
<SelectEmpty>Жанрів не знайдено</SelectEmpty>
</SelectList>
</SelectContent>
</Select>
Expand Down Expand Up @@ -301,6 +291,41 @@ const AnimeFilters: FC<Props> = ({ className, type }) => {
property="ratings"
onParamChange={handleChangeParam}
/>
<div className="flex w-full flex-col gap-4">
<Label className="text-muted-foreground">Студія</Label>
<Select
multiple
value={studios}
onValueChange={(value) =>
handleChangeParam('studios', value)
}
onSearch={handleStudioSearch}
>
<SelectTrigger>
<SelectValue placeholder="Виберіть студію..." />
</SelectTrigger>
<SelectContent>
<SelectSearch placeholder="Назва студії..." />
<SelectList>
<SelectGroup>
{!isStudioListFetching && studioList?.list.map((studio) => (
<SelectItem
key={studio.slug}
value={studio.slug}
>
{studio.name}
</SelectItem>
))}
<SelectEmpty>
{isStudioListFetching
? 'Завантаження...'
: 'Студій не знайдено'}
</SelectEmpty>
</SelectGroup>
</SelectList>
</SelectContent>
</Select>
</div>
<div className="flex w-full flex-col gap-4">
<Label className="text-muted-foreground">Рік виходу</Label>
<div className="flex items-center gap-4">
Expand Down Expand Up @@ -334,7 +359,6 @@ const AnimeFilters: FC<Props> = ({ className, type }) => {
>
<AntDesignClearOutlined /> Очистити
</Button>
; ;
</ScrollArea>
);
};
Expand Down
30 changes: 30 additions & 0 deletions services/api/companies/getCompanies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {
BaseFetchRequestProps,
fetchRequest,
} from '@/services/api/fetchRequest';

export interface Response extends API.WithPagination<API.Company> {}

export interface Params {
query?: string;
type: API.CompanyType;
}

export default async function req({
page = 1,
size = 15,
...props
}: BaseFetchRequestProps<Params>): Promise<Response> {
return fetchRequest<Response>({
...props,
path: `/companies`,
method: 'post',
page,
size,
config: {
next: {
revalidate: 60,
},
},
});
}
3 changes: 3 additions & 0 deletions services/hooks/anime/useAnimeCatalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const useAnimeCatalog = ({ page, iPage, ...props }: Props) => {
const ageRatings = props.rating || searchParams.getAll('ratings');
const years = props.years || searchParams.getAll('years');
const genres = props.genres || searchParams.getAll('genres');
const studios = props.studios || searchParams.getAll('studios');
const lang = props.only_translated || searchParams.get('only_translated');
const sort = searchParams.get('sort') || 'score';
const order = searchParams.get('order') || 'desc';
Expand All @@ -42,6 +43,7 @@ const useAnimeCatalog = ({ page, iPage, ...props }: Props) => {
years,
lang,
genres,
studios,
sort,
order,
},
Expand All @@ -65,6 +67,7 @@ const useAnimeCatalog = ({ page, iPage, ...props }: Props) => {
...(sort === 'score' ? ['scored_by:desc'] : []),
],
genres,
studios,
only_translated: Boolean(lang),
},
page: Number(pageParam),
Expand Down
15 changes: 15 additions & 0 deletions services/hooks/companies/useCompanies.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { useQuery } from '@tanstack/react-query';

import getCompanies, { Params } from '@/services/api/companies/getCompanies';

const useCompanies = (params: Params) => {
return useQuery({
queryKey: ['companies', { ...params }],
queryFn: () =>
getCompanies({
params,
}),
});
};

export default useCompanies;
Loading

0 comments on commit 5c4cef7

Please sign in to comment.