diff --git a/app/(pages)/edit/components/content/components/details.tsx b/app/(pages)/edit/components/content/components/details.tsx index 9ea9c3a1..17c644da 100644 --- a/app/(pages)/edit/components/content/components/details.tsx +++ b/app/(pages)/edit/components/content/components/details.tsx @@ -27,15 +27,15 @@ const Details = ({ content }: Props) => {

{title_en || '-'}

{title_ja || '-'}

diff --git a/app/(pages)/schedule/components/schedule-list.tsx b/app/(pages)/schedule/components/schedule-list.tsx index 0fce2f7c..bcb90106 100644 --- a/app/(pages)/schedule/components/schedule-list.tsx +++ b/app/(pages)/schedule/components/schedule-list.tsx @@ -4,19 +4,20 @@ import { getUnixTime, startOfDay } from 'date-fns'; import format from 'date-fns/format'; import * as React from 'react'; +import FiltersNotFound from '@/components/filters/components/filters-not-found'; import SubHeader from '@/components/sub-header'; import { Button } from '@/components/ui/button'; import useAnimeSchedule from '@/services/hooks/stats/useAnimeSchedule'; import ScheduleItem from './ui/schedule-item'; + const ScheduleList = () => { const { list, hasNextPage, isFetchingNextPage, fetchNextPage, ref } = - useAnimeSchedule({ status: 'ongoing' }); - - if (!list || list.length === 0) return null; + useAnimeSchedule(); + - const sortedList = list.reduce( + const sortedList = list?.reduce( (acc: Record, item) => { const day = getUnixTime(startOfDay(item.airing_at * 1000)); if (!(day in acc)) { @@ -31,22 +32,36 @@ const ScheduleList = () => { return (
- {Object.keys(sortedList).map((day) => ( -
- -
- {sortedList[day].map((item) => ( - - ))} + {sortedList && Object.keys(sortedList).map((day) => { + const formattedDay = format( + Number(day) * 1000, + 'eeee ,d MMM', + ).split(','); + + return ( +
+ + {formattedDay[0]} + + {formattedDay[1]} + + + } + /> +
+ {sortedList[day].map((item) => ( + + ))} +
-
- ))} + ); + })} {hasNextPage && ( )} + {(!list || list.length === 0) && }
); }; diff --git a/app/(pages)/schedule/page.tsx b/app/(pages)/schedule/page.tsx index 9294ed39..c550496e 100644 --- a/app/(pages)/schedule/page.tsx +++ b/app/(pages)/schedule/page.tsx @@ -4,12 +4,17 @@ import * as React from 'react'; import { dehydrate } from '@tanstack/query-core'; import { HydrationBoundary } from '@tanstack/react-query'; -import ScheduleList from '@/app/(pages)/schedule/components/schedule-list'; +import SubHeader from '@/components/sub-header'; import getAnimeSchedule from '@/services/api/stats/getAnimeSchedule'; import { getCookie } from '@/utils/actions'; import _generateMetadata from '@/utils/generateMetadata'; +import getCurrentSeason from '@/utils/getCurrentSeason'; import getQueryClient from '@/utils/getQueryClient'; +import ScheduleFilters from '../../../components/filters/schedule-filters'; +import ScheduleList from './components/schedule-list'; + + export async function generateMetadata(): Promise { return _generateMetadata({ title: { @@ -19,21 +24,32 @@ export async function generateMetadata(): Promise { }); } -const ScheduleListPage = async () => { +const ScheduleListPage = async ({ + searchParams, +}: { + searchParams: Record; +}) => { const queryClient = getQueryClient(); const auth = await getCookie('auth'); + const only_watch = searchParams.only_watch || undefined; + const season = (searchParams.season as API.Season) || getCurrentSeason()!; + const year = searchParams.year || String(new Date().getFullYear()); + const status = + searchParams.status && searchParams.status.length > 0 + ? searchParams.status + : ['ongoing']; + await queryClient.prefetchInfiniteQuery({ initialPageParam: 1, - queryKey: [ - 'animeSchedule', - { season: undefined, status: 'ongoing', auth }, - ], + queryKey: ['animeSchedule', { season, status, auth, year, only_watch }], queryFn: ({ pageParam = 1 }) => getAnimeSchedule({ - status: 'ongoing', + status, page: pageParam, auth, + only_watch, + airing_season: [season, year], }), }); @@ -41,7 +57,13 @@ const ScheduleListPage = async () => { return ( - +
+
+ + +
+ +
); }; diff --git a/app/(pages)/u/[username]/components/favorites/_components/collections.tsx b/app/(pages)/u/[username]/components/favorites/_components/collections.tsx index 7bcb70ae..41f4ddf4 100644 --- a/app/(pages)/u/[username]/components/favorites/_components/collections.tsx +++ b/app/(pages)/u/[username]/components/favorites/_components/collections.tsx @@ -35,6 +35,8 @@ const Component = ({ extended }: Props) => { } const filteredData = (extended ? list : list?.slice(0, 6)) || []; + const poster = (content: API.Anime | API.Character | API.Person) => + 'poster' in content ? content.poster : content.image; return ( <> @@ -50,7 +52,7 @@ const Component = ({ extended }: Props) => { { const searchParams = useSearchParams(); const pathname = usePathname(); @@ -36,7 +37,7 @@ const Component = () => { value: watchStatus, }))} value={watchStatus} - toggleProps={{ variant: 'ghost' }} + toggleProps={{ variant: 'outline' }} onChange={(value) => { const query = createQueryString( 'status', @@ -49,14 +50,14 @@ const Component = () => { !Array.isArray(option) && option && (
-
+
{createElement( WATCH_STATUS[option.value as API.WatchStatus] .icon!, )}
-

{option.label}

+
{option.label}
{pagination && (
); diff --git a/components/ui/popover.tsx b/components/ui/popover.tsx index 955e3cda..5c7afca8 100644 --- a/components/ui/popover.tsx +++ b/components/ui/popover.tsx @@ -6,6 +6,7 @@ import * as PopoverPrimitive from '@radix-ui/react-popover'; import { cn } from '@/utils/utils'; + const Popover = PopoverPrimitive.Root; const PopoverAnchor = PopoverPrimitive.Anchor; @@ -18,19 +19,28 @@ const PopoverArrow = PopoverPrimitive.Arrow; const PopoverContent = React.forwardRef< React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, align = 'center', sideOffset = 4, ...props }, ref) => ( - -)); + React.ComponentPropsWithoutRef & { + container?: HTMLElement | null; + } +>( + ( + { className, align = 'center', container, sideOffset = 4, ...props }, + ref, + ) => ( + + + + ), +); PopoverContent.displayName = PopoverPrimitive.Content.displayName; export { diff --git a/services/api/stats/getAnimeSchedule.ts b/services/api/stats/getAnimeSchedule.ts index 2418ba11..6f2d2d48 100644 --- a/services/api/stats/getAnimeSchedule.ts +++ b/services/api/stats/getAnimeSchedule.ts @@ -5,20 +5,27 @@ export interface Response extends API.WithPagination {} export default async function req({ airing_season, status, + only_watch, page = 1, size = 15, auth, }: { - airing_season?: API.Season[]; - status?: API.Status; + airing_season?: [API.Season, string]; + status?: API.Status[]; page?: number; size?: number; + only_watch?: boolean; auth?: string; }): Promise { return fetchRequest({ path: `/schedule/anime`, method: 'post', - params: { airing_season, status }, + params: { + airing_season, + status, + rating: ['g', 'pg', 'pg_13', 'r', 'r_plus'], + only_watch, + }, page, size, auth, diff --git a/services/hooks/stats/useAnimeSchedule.ts b/services/hooks/stats/useAnimeSchedule.ts index e924fcdb..ca944442 100644 --- a/services/hooks/stats/useAnimeSchedule.ts +++ b/services/hooks/stats/useAnimeSchedule.ts @@ -1,25 +1,34 @@ -import getAnimeCharacters from '@/services/api/anime/getAnimeCharacters'; +import { useSearchParams } from 'next/navigation'; + import getAnimeSchedule from '@/services/api/stats/getAnimeSchedule'; import useAuth from '@/services/hooks/auth/useAuth'; import useInfiniteList from '@/services/hooks/useInfiniteList'; +import getCurrentSeason from '@/utils/getCurrentSeason'; + +const useAnimeSchedule = () => { + const searchParams = useSearchParams(); + + const only_watch = searchParams.get('only_watch') ? Boolean(searchParams.get('only_watch')) : undefined; + const season = + (searchParams.get('season') as API.Season) || getCurrentSeason()!; + const year = + searchParams.get('year') || String(new Date().getFullYear()); + const status = + (searchParams.getAll('status').length > 0 + ? searchParams.getAll('status') + : ['ongoing']) as API.Status[]; -const useAnimeSchedule = ({ - season, - status, -}: { - season?: API.Season[]; - status?: API.Status; -}) => { const { auth } = useAuth(); return useInfiniteList({ - queryKey: ['animeSchedule', { season, status, auth }], + queryKey: ['animeSchedule', { season, status, auth, year, only_watch }], queryFn: ({ pageParam = 1 }) => getAnimeSchedule({ - airing_season: season, + airing_season: [season, year], status, page: pageParam, auth, + only_watch: only_watch, }), }); }; diff --git a/types/hikka.d.ts b/types/hikka.d.ts index 3e6806a9..989def81 100644 --- a/types/hikka.d.ts +++ b/types/hikka.d.ts @@ -15,6 +15,7 @@ declare global { icon?: (props: any) => ReactElement | ReactNode; color?: string; description?: string; + params?: Record; } >; @@ -62,6 +63,12 @@ declare global { description_ua: string; }; + type PersonEditParams = { + name_ua: string; + name_en: string; + name_native: string; + }; + type TextNotification = { type: NotificationType; icon: ReactNode; diff --git a/utils/constants.ts b/utils/constants.ts index e0662510..4927bdce 100644 --- a/utils/constants.ts +++ b/utils/constants.ts @@ -48,18 +48,30 @@ export const SEASON: Hikka.FilterProperty = { winter: { title_ua: 'Зима', title_en: 'Winter', + params: { + months: [1, 2, 3], + } }, spring: { title_ua: 'Весна', title_en: 'Spring', + params: { + months: [4, 5, 6], + } }, summer: { title_ua: 'Літо', title_en: 'Summer', + params: { + months: [7, 8, 9], + } }, fall: { title_ua: 'Осінь', title_en: 'Fall', + params: { + months: [10, 11, 12], + } }, }; @@ -578,7 +590,9 @@ export const ANIME_EDIT_GROUPS: Record = { }; export const EDIT_PARAMS: Record< - keyof Hikka.AnimeEditParams | keyof Hikka.CharacterEditParams, + | keyof Hikka.AnimeEditParams + | keyof Hikka.CharacterEditParams + | keyof Hikka.PersonEditParams, string > = { name_ua: 'Імʼя UA', @@ -591,6 +605,7 @@ export const EDIT_PARAMS: Record< title_ja: 'Назва JA', synopsis_ua: 'Опис UA', synopsis_en: 'Опис EN', + name_native: 'Рідне імʼя', }; export const CHARACTER_EDIT_PARAMS: Record = { @@ -656,8 +671,8 @@ export const PERSON_EDIT_PARAMS: Record = { }, { slug: 'name_native', - title: 'Нативною', - placeholder: 'Введіть нативне імʼя', + title: 'Рідною', + placeholder: 'Введіть рідне імʼя', type: 'input', }, ], diff --git a/utils/getCurrentSeason.ts b/utils/getCurrentSeason.ts new file mode 100644 index 00000000..a8de4792 --- /dev/null +++ b/utils/getCurrentSeason.ts @@ -0,0 +1,8 @@ +import { SEASON } from '@/utils/constants'; + +export default function getCurrentSeason() { + const currentMonth = new Date().getMonth() + 1; + return (Object.keys(SEASON) as Array).find((s) => + SEASON[s].params!.months.includes(currentMonth), + ); +} \ No newline at end of file