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 && (
+
+
+
+ )
+ },
+ {
+ 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 {