Skip to content
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import { clientApi } from '@/apis/client';
import { ENDPOINTS } from '@/consts/api';
import type { ApiResponseT } from '@/types/api';

import type {
PostCreateTournamentRequestT,
PostCreateTournamentResponseT,
} from '../_types/tournament';
} from '@/types/tournament';

export const postCreateTournament = async (body: PostCreateTournamentRequestT) => {
const { data } = await clientApi.post<ApiResponseT<PostCreateTournamentResponseT>>(
Expand Down
24 changes: 0 additions & 24 deletions apps/web/src/app/archive/_common/_components/ArchivePageLayout.tsx

This file was deleted.

This file was deleted.

25 changes: 25 additions & 0 deletions apps/web/src/app/archive/tournament/_components/TournamentFab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { AddIconFill } from '@/assets/icons';
import Button from '@/components/button';
import CreateTournamentDialogContent from '@/components/common/create-tournament-dialog';
import { Dialog, DialogTrigger } from '@/components/dialog';

function TournamentFab() {
return (
<Dialog>
<DialogTrigger asChild>
<Button
variant="primary"
size="xl"
icon="only"
aria-label="토너먼트 만들기"
className="fixed right-[max(20px,calc(50%-480px/2+20px))] bottom-[103px] z-30"
>
<AddIconFill width={30} height={30} />
</Button>
</DialogTrigger>
<CreateTournamentDialogContent />
</Dialog>
);
}

export default TournamentFab;

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ function TournamentHistoryList({ statuses }: Props) {
);

return (
<main className="hide-scrollbar flex flex-1 flex-col gap-3 overflow-y-auto pb-24">
<>
{tournamentListData.map(tournament => (
<TournamentCard
key={tournament.tournamentId}
Expand All @@ -32,7 +32,7 @@ function TournamentHistoryList({ statuses }: Props) {
participantCount={tournament.participantProfileImages.length}
/>
))}
</main>
</>
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
'use client';

import { Suspense, useState } from 'react';

import Spacing from '@/components/spacing';

import { STATUS_BY_TAB, type TournamentStatusTabT } from '../_consts/tournamentTab';
import TournamentHistoryList from './TournamentHistoryList';
import TournamentHistorySkeleton from './TournamentHistorySkeleton';
import TournamentStatusTab from './TournamentStatusTab';

type Props = {
initialTab: TournamentStatusTabT;
};

function TournamentHistorySection({ initialTab }: Props) {
const [activeTab, setActiveTab] = useState<TournamentStatusTabT>(initialTab);

/** 서버 왕복 없이 주소만 동기화 */
const handleTabChange = (tab: TournamentStatusTabT) => {
setActiveTab(tab);
window.history.replaceState(null, '', `?tab=${tab}`);
};

return (
<>
<div className="sticky top-0 z-20 flex w-full flex-col bg-bg-layer-basement pt-padding-top">
<h1 className="heading-1-bold text-text-neutral-primary">내 토너먼트</h1>
<Spacing size={16} />
<TournamentStatusTab activeTab={activeTab} onTabChange={handleTabChange} />
</div>
<main className="hide-scrollbar flex flex-1 flex-col gap-4 overflow-y-auto pt-6 pb-24">
<Suspense key={activeTab} fallback={<TournamentHistorySkeleton />}>
<TournamentHistoryList statuses={STATUS_BY_TAB[activeTab]} />
</Suspense>
</main>
</>
);
}

export default TournamentHistorySection;
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import TournamentCardSkeleton from '@/components/tournament-card/TournamentCardSkeleton';

const SKELETON_COUNT = 4;

function TournamentHistorySkeleton() {
return (
<>
{Array.from({ length: SKELETON_COUNT }).map((_, index) => (
<TournamentCardSkeleton key={index} />
))}
</>
);
}

export default TournamentHistorySkeleton;
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
'use client';

import type { ComponentType, SVGProps } from 'react';

import { BasketIconOutline, ReciptIconOutline } from '@/assets/icons';
import { cn } from '@/utils/cn';

export type TournamentStatusTabT = 'in-progress' | 'completed';
import type { TournamentStatusTabT } from '../_consts/tournamentTab';

type Props = {
activeTab: TournamentStatusTabT;
onTabChange: (tab: TournamentStatusTabT) => void;
};

const TABS: { value: TournamentStatusTabT; label: string }[] = [
{ value: 'in-progress', label: '진행 중' },
{ value: 'completed', label: '완료' },
const TABS: {
value: TournamentStatusTabT;
label: string;
Icon: ComponentType<SVGProps<SVGSVGElement>>;
}[] = [
{ value: 'ongoing', label: '진행 중', Icon: BasketIconOutline },
{ value: 'completed', label: '완료', Icon: ReciptIconOutline },
];

function TournamentStatusTab({ activeTab, onTabChange }: Props) {
return (
<div className="flex w-full border-b border-border-neutral-muted">
{TABS.map(tab => (
{TABS.map(({ value, label, Icon }) => (
<button
key={tab.value}
key={value}
type="button"
onClick={() => onTabChange(tab.value)}
onClick={() => onTabChange(value)}
className={cn(
'relative flex h-11 flex-1 cursor-pointer items-center justify-center body-1-semibold transition-colors',
activeTab === tab.value ? 'text-text-neutral-primary' : 'text-text-neutral-tertiary'
'relative flex h-11 flex-1 cursor-pointer items-center justify-center gap-1 body-1-semibold transition-colors',
activeTab === value ? 'text-text-neutral-primary' : 'text-text-neutral-tertiary'
)}
>
{tab.label}
{activeTab === tab.value && (
<Icon className="size-5" />
{label}
{activeTab === value && (
<span className="absolute inset-x-0 bottom-0 h-0.5 bg-text-neutral-primary" />
)}
</button>
Expand Down
13 changes: 13 additions & 0 deletions apps/web/src/app/archive/tournament/_consts/tournamentTab.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { TournamentStatusT } from '@/types/tournament';

export type TournamentStatusTabT = 'ongoing' | 'completed';

/**
* 탭 → 조회할 토너먼트 상태 매핑
* - 진행 중(ongoing) 탭: 미완료(담는 중 + 플레이 중) 토너먼트
* - 완료(completed) 탭: 완료된 토너먼트
*/
export const STATUS_BY_TAB: Record<TournamentStatusTabT, TournamentStatusT[]> = {
ongoing: ['PENDING', 'IN_PROGRESS'],
completed: ['COMPLETED'],
};
33 changes: 28 additions & 5 deletions apps/web/src/app/archive/tournament/page.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,36 @@
import { HydrationBoundary, dehydrate } from '@tanstack/react-query';

import { getTournamentList } from '@/apis/getTournamentList';
import BottomTabBar from '@/components/bottom-tab-bar';
import { getQueryClient } from '@/utils/queryClient';

import TournamentFab from './_components/TournamentFab';
import TournamentHistorySection from './_components/TournamentHistorySection';
import { STATUS_BY_TAB, type TournamentStatusTabT } from './_consts/tournamentTab';

type Props = {
searchParams: Promise<{ tab?: string }>;
};

async function ArchiveTournamentPage({ searchParams }: Props) {
const { tab } = await searchParams;
const initialTab: TournamentStatusTabT = tab === 'completed' ? 'completed' : 'ongoing';

import TournamentHistoryContent from './_components/TournamentHistoryContent';
const queryClient = getQueryClient();
const statuses = STATUS_BY_TAB[initialTab];
await queryClient.prefetchQuery({
queryKey: ['tournamentList', statuses],
queryFn: () => getTournamentList(statuses),
});

async function ArchiveTournamentPage() {
return (
<>
<TournamentHistoryContent />
<div className="flex min-h-dvh flex-col bg-bg-layer-basement px-5">
<HydrationBoundary state={dehydrate(queryClient)}>
<TournamentHistorySection initialTab={initialTab} />
</HydrationBoundary>
<TournamentFab />
<BottomTabBar />
</>
</div>
);
}

Expand Down
80 changes: 0 additions & 80 deletions apps/web/src/app/home/_components/CreateTournamentDialog.tsx

This file was deleted.

Loading
Loading