From d0aa829e6e72e2664a5124570e35ef8579d2d303 Mon Sep 17 00:00:00 2001 From: YooJin Lee <113789141+youznn@users.noreply.github.com> Date: Wed, 14 Aug 2024 14:36:55 +0900 Subject: [PATCH] feat(fe): redesign contest table (#1930) * feat(fe): redesign finished contest table * feat(fe): add registered tab * chore(fe): redesign tab button * chore(fe): change font weight * feat(fe): add search bar * feat(fe): add status badge * feat(fe): add search bar * chore(fe): delete unused import * feat(fe): add isRegistered column * chore(fe): delete unused tags, change props * chore(fe): delete unused tags --- .../_components/FinishedContestTable.tsx | 51 +++++++------ .../_components/FinishedTableColumns.tsx | 47 ++++++++++++ .../_components/RegisteredContestTable.tsx | 71 +++++++++++++++++++ ...Columns.tsx => RegisteredTableColumns.tsx} | 33 ++++----- .../contest/_components/TableSwitchButton.tsx | 37 ++++++++++ apps/frontend/app/(main)/contest/page.tsx | 46 ++++++++++-- apps/frontend/components/SearchBar.tsx | 5 +- apps/frontend/public/check_blue.svg | 3 + apps/frontend/public/finished.svg | 3 + apps/frontend/public/ongoing.svg | 3 + apps/frontend/public/upcoming.svg | 4 ++ apps/frontend/types/type.ts | 1 + 12 files changed, 258 insertions(+), 46 deletions(-) create mode 100644 apps/frontend/app/(main)/contest/_components/FinishedTableColumns.tsx create mode 100644 apps/frontend/app/(main)/contest/_components/RegisteredContestTable.tsx rename apps/frontend/app/(main)/contest/_components/{Columns.tsx => RegisteredTableColumns.tsx} (54%) create mode 100644 apps/frontend/app/(main)/contest/_components/TableSwitchButton.tsx create mode 100644 apps/frontend/public/check_blue.svg create mode 100644 apps/frontend/public/finished.svg create mode 100644 apps/frontend/public/ongoing.svg create mode 100644 apps/frontend/public/upcoming.svg diff --git a/apps/frontend/app/(main)/contest/_components/FinishedContestTable.tsx b/apps/frontend/app/(main)/contest/_components/FinishedContestTable.tsx index aed1c570f5..d5f39d14ed 100644 --- a/apps/frontend/app/(main)/contest/_components/FinishedContestTable.tsx +++ b/apps/frontend/app/(main)/contest/_components/FinishedContestTable.tsx @@ -1,15 +1,27 @@ import DataTable from '@/components/DataTable' -import { fetcher } from '@/lib/utils' +import { fetcher, fetcherWithAuth } from '@/lib/utils' import type { Contest } from '@/types/type' -import { columns } from './Columns' +import type { Session } from 'next-auth' +import { columns } from './FinishedTableColumns' interface ContestProps { data: Contest[] } -export default async function FinishedContestTable() { - const ContestData: ContestProps = await fetcher - .get('contest/finished?take=51') +export default async function FinishedContestTable({ + search, + session +}: { + search: string + session: Session | null +}) { + const ContestData: ContestProps = await (session ? fetcherWithAuth : fetcher) + .get('contest/finished', { + searchParams: { + search, + take: '51' + } + }) .json() ContestData.data.forEach((contest) => { @@ -17,22 +29,17 @@ export default async function FinishedContestTable() { }) return ( - <> -

Finished

- {/* TODO: Add search bar */} - - + ) } diff --git a/apps/frontend/app/(main)/contest/_components/FinishedTableColumns.tsx b/apps/frontend/app/(main)/contest/_components/FinishedTableColumns.tsx new file mode 100644 index 0000000000..8ad0820aab --- /dev/null +++ b/apps/frontend/app/(main)/contest/_components/FinishedTableColumns.tsx @@ -0,0 +1,47 @@ +'use client' + +import { dateFormatter } from '@/lib/utils' +import CheckIcon from '@/public/check_blue.svg' +import type { Contest } from '@/types/type' +import type { ColumnDef } from '@tanstack/react-table' +import Image from 'next/image' + +export const columns: ColumnDef[] = [ + { + header: 'Title', + accessorKey: 'title', + cell: ({ row }) => ( +

+ {row.original.title} +

+ ) + }, + { + header: 'Registered', + accessorKey: 'registered', + cell: ({ row }) => + row.original.isRegistered && ( +
+ check +
+ ) + }, + { + header: 'Participants', + accessorKey: 'participants', + cell: ({ row }) => row.original.participants + }, + { + header: 'Total score', + accessorKey: 'totalScore', + cell: () => '000/000' + }, + { + header: 'Period', + accessorKey: 'period', + cell: ({ row }) => + dateFormatter(row.original.startTime, 'YYYY-MM-DD') + + ' ~ ' + + dateFormatter(row.original.endTime, 'YYYY-MM-DD') + } +] diff --git a/apps/frontend/app/(main)/contest/_components/RegisteredContestTable.tsx b/apps/frontend/app/(main)/contest/_components/RegisteredContestTable.tsx new file mode 100644 index 0000000000..02c3feeb6e --- /dev/null +++ b/apps/frontend/app/(main)/contest/_components/RegisteredContestTable.tsx @@ -0,0 +1,71 @@ +import DataTable from '@/components/DataTable' +import { fetcherWithAuth } from '@/lib/utils' +import type { Contest } from '@/types/type' +import { columns } from './RegisteredTableColumns' + +interface FinishedContestProps { + data: Contest[] +} + +const getOngoingUpcomingContests = async (search: string) => { + const data: { + registeredOngoing: Contest[] + registeredUpcoming: Contest[] + } = await fetcherWithAuth + .get('contest/registered-ongoing-upcoming', { + searchParams: { + search, + take: '51' + } + }) + .json() + data.registeredOngoing.forEach((contest) => { + contest.status = 'ongoing' + }) + data.registeredUpcoming.forEach((contest) => { + contest.status = 'upcoming' + }) + return data.registeredOngoing.concat(data.registeredUpcoming) +} + +const getFinishedContests = async (search: string) => { + const data = await getOngoingUpcomingContests(search) + + const FinishedData: FinishedContestProps = await fetcherWithAuth + .get('contest/registered-finished', { + searchParams: { + search, + take: '51' + } + }) + .json() + FinishedData.data.forEach((contest) => { + contest.status = 'finished' + }) + return data.concat(FinishedData.data) +} + +export default async function RegisteredContestTable({ + search +}: { + search: string +}) { + const data = await getFinishedContests(search) + + return ( + <> + + + ) +} diff --git a/apps/frontend/app/(main)/contest/_components/Columns.tsx b/apps/frontend/app/(main)/contest/_components/RegisteredTableColumns.tsx similarity index 54% rename from apps/frontend/app/(main)/contest/_components/Columns.tsx rename to apps/frontend/app/(main)/contest/_components/RegisteredTableColumns.tsx index c76bda7440..8a2bbb5f94 100644 --- a/apps/frontend/app/(main)/contest/_components/Columns.tsx +++ b/apps/frontend/app/(main)/contest/_components/RegisteredTableColumns.tsx @@ -1,9 +1,9 @@ 'use client' -import Badge from '@/app/(main)/_components/Badge' import { dateFormatter } from '@/lib/utils' import type { Contest } from '@/types/type' import type { ColumnDef } from '@tanstack/react-table' +import StatusBadge from '../../_components/StatusBadge' export const columns: ColumnDef[] = [ { @@ -18,29 +18,24 @@ export const columns: ColumnDef[] = [ { header: 'Status', accessorKey: 'status', - cell: ({ row }) => ( - -

- {row.original.status.startsWith('registered') - ? 'registered' - : row.original.status} -

-
- ) + cell: ({ row }) => }, { - header: 'Starts at', - accessorKey: 'startTime', - cell: ({ row }) => dateFormatter(row.original.startTime, 'YYYY-MM-DD') + header: 'Participants', + accessorKey: 'participants', + cell: ({ row }) => row.original.participants }, { - header: 'Ends at', - accessorKey: 'endTime', - cell: ({ row }) => dateFormatter(row.original.endTime, 'YYYY-MM-DD') + header: 'Total score', + accessorKey: 'totalScore', + cell: () => '000/000' }, { - header: 'Participants', - accessorKey: 'participants', - cell: ({ row }) => row.original.participants + header: 'Period', + accessorKey: 'period', + cell: ({ row }) => + dateFormatter(row.original.startTime, 'YYYY-MM-DD') + + ' ~ ' + + dateFormatter(row.original.endTime, 'YYYY-MM-DD') } ] diff --git a/apps/frontend/app/(main)/contest/_components/TableSwitchButton.tsx b/apps/frontend/app/(main)/contest/_components/TableSwitchButton.tsx new file mode 100644 index 0000000000..606d3cdc3f --- /dev/null +++ b/apps/frontend/app/(main)/contest/_components/TableSwitchButton.tsx @@ -0,0 +1,37 @@ +import { cn } from '@/lib/utils' +import Link from 'next/link' + +export default function TableSwitchButton({ + registered +}: { + registered: boolean +}) { + return ( +
+ + Finished + + + Registered + +
+ ) +} diff --git a/apps/frontend/app/(main)/contest/page.tsx b/apps/frontend/app/(main)/contest/page.tsx index 8f31b15227..b4b4606df3 100644 --- a/apps/frontend/app/(main)/contest/page.tsx +++ b/apps/frontend/app/(main)/contest/page.tsx @@ -1,8 +1,20 @@ +import SearchBar from '@/components/SearchBar' +import { Separator } from '@/components/ui/separator' import { Skeleton } from '@/components/ui/skeleton' import { auth } from '@/lib/auth' +import { redirect } from 'next/navigation' import { Suspense } from 'react' import ContestCardList from './_components/ContestCardList' import FinishedContestTable from './_components/FinishedContestTable' +import RegisteredContestTable from './_components/RegisteredContestTable' +import TableSwitchButton from './_components/TableSwitchButton' + +interface ContestProps { + searchParams: { + registered: string + search: string + } +} function ContestCardListFallback() { return ( @@ -41,8 +53,14 @@ function FinishedContestTableFallback() { ) } -export default async function Contest() { +export default async function Contest({ searchParams }: ContestProps) { const session = await auth() + const registered = searchParams.registered === 'true' ?? false + if (!session && registered) { + redirect('/contest') + } + const search = searchParams.search ?? '' + return ( <>
@@ -61,9 +79,29 @@ export default async function Contest() { />
- }> - - +
+

+ List of Contests +

+ }> + {session ? ( + + ) : ( +

+ Finished +

+ )} + +
+ +
+ {session && registered ? ( + + ) : ( + + )} +
+
) } diff --git a/apps/frontend/components/SearchBar.tsx b/apps/frontend/components/SearchBar.tsx index 0c5fce033c..7ee61b952f 100644 --- a/apps/frontend/components/SearchBar.tsx +++ b/apps/frontend/components/SearchBar.tsx @@ -32,7 +32,10 @@ export default function SearchBar({ className }: SearchBarProps) { } else newParam.delete('search') const newParamString = newParam.toString() router.push( - `${pathname}${newParamString ? '?' + newParamString : ''}` as Route + `${pathname}${newParamString ? '?' + newParamString : ''}` as Route, + { + scroll: false + } ) } diff --git a/apps/frontend/public/check_blue.svg b/apps/frontend/public/check_blue.svg new file mode 100644 index 0000000000..7f336fda7a --- /dev/null +++ b/apps/frontend/public/check_blue.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/frontend/public/finished.svg b/apps/frontend/public/finished.svg new file mode 100644 index 0000000000..3bdec593ac --- /dev/null +++ b/apps/frontend/public/finished.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/frontend/public/ongoing.svg b/apps/frontend/public/ongoing.svg new file mode 100644 index 0000000000..c27d6edf92 --- /dev/null +++ b/apps/frontend/public/ongoing.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/frontend/public/upcoming.svg b/apps/frontend/public/upcoming.svg new file mode 100644 index 0000000000..f282d13f11 --- /dev/null +++ b/apps/frontend/public/upcoming.svg @@ -0,0 +1,4 @@ + + + + diff --git a/apps/frontend/types/type.ts b/apps/frontend/types/type.ts index f8a1cc4ee1..2e45c306ef 100644 --- a/apps/frontend/types/type.ts +++ b/apps/frontend/types/type.ts @@ -84,6 +84,7 @@ export interface Contest { enableCopyPaste: boolean status: ContestStatus participants: number + isRegistered: boolean } export interface ContestAnnouncement {