| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116 |
- 'use client';
- import { useCallback, useEffect, useMemo, useState } from 'react';
- import { useRouter, useSearchParams } from 'next/navigation';
- import { fetchApi } from '@/lib/utils/client';
- import useAuth from '@/hooks/useAuth';
- import { useMemberContext } from '@/contexts/memberProvider';
- import Loading from '@/app/component/Loading';
- import RankingTabs from './RankingTabs';
- import PeriodFilter from './PeriodFilter';
- import CategoryFilter from './CategoryFilter';
- import Podium from './Podium';
- import RankingList from './RankingList';
- import MyRankBar from './MyRankBar';
- import type { RankingResponse, RankingPeriod, RankingType } from '@/types/response/ranking';
- interface Props {
- type: 'comprehensive'|'creator'|'donor';
- basePath: string;
- }
- export default function RankingView({ type, basePath }: Props)
- {
- const router = useRouter();
- const searchParams = useSearchParams();
- const { isAuthenticated } = useAuth();
- const { member } = useMemberContext();
- const initialPeriod = (searchParams.get('period') as RankingPeriod) || 'month';
- const initialCategory = searchParams.get('category') || 'all';
- const [period, setPeriod] = useState<RankingPeriod>(initialPeriod);
- const [category, setCategory] = useState<string>(initialCategory);
- const [data, setData] = useState<RankingResponse|null>(null);
- const [loading, setLoading] = useState(true);
- const viewerMemberID = isAuthenticated ? member?.id ?? null : null;
- const loadData = useCallback(() => {
- setLoading(true);
- const qs = new URLSearchParams();
- qs.set('type', type);
- qs.set('period', period);
- qs.set('category', category);
- qs.set('page', '1');
- qs.set('perPage', '50');
- fetchApi<RankingResponse>(`/api/ranking?${qs.toString()}`, { silent: true }).then((res) => {
- setData(res.data ?? null);
- }).catch(() => {
- setData(null);
- }).finally(() => {
- setLoading(false);
- });
- }, [type, period, category]);
- useEffect(() => {
- loadData();
- }, [loadData]);
- useEffect(() => {
- const qs = new URLSearchParams();
- if (period !== 'month') {
- qs.set('period', period);
- }
- if (category !== 'all') {
- qs.set('category', category);
- }
- const query = qs.toString();
- router.replace(query ? `${basePath}?${query}` : basePath, { scroll: false });
- }, [period, category, basePath, router]);
- const asRankingType: RankingType = type;
- const snapshotLabel = useMemo(() => {
- if (!data?.snapshotAt) {
- return null;
- }
- const d = new Date(data.snapshotAt);
- return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} 기준`;
- }, [data?.snapshotAt]);
- return (
- <div className="ranking">
- <header className="ranking__header">
- <h1>순위</h1>
- </header>
- <RankingTabs active={type} />
- <div className="ranking__filters">
- <PeriodFilter value={period} onChange={setPeriod} />
- {type !== 'donor' ? <CategoryFilter value={category} onChange={setCategory} /> : null}
- </div>
- {loading ? (
- <Loading type={2} />
- ) : (
- <>
- {data && data.top3.length > 0 ? (
- <Podium top3={data.top3} type={asRankingType} />
- ) : null}
- {snapshotLabel ? <p className="ranking__list-note">{snapshotLabel}</p> : null}
- <RankingList entries={data?.list ?? []} type={asRankingType} viewerMemberID={viewerMemberID} />
- {data?.myRank ? <MyRankBar info={data.myRank} type={asRankingType} /> : null}
- </>
- )}
- </div>
- );
- }
|