Skip to content

Commit

Permalink
feat(fe): redesign contest table (#1930)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
youznn authored Aug 14, 2024
1 parent 61ebacf commit d0aa829
Show file tree
Hide file tree
Showing 12 changed files with 258 additions and 46 deletions.
Original file line number Diff line number Diff line change
@@ -1,38 +1,45 @@
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) => {
contest.status = 'finished'
})

return (
<>
<p className="text-xl font-bold md:text-2xl">Finished</p>
{/* TODO: Add search bar */}
<DataTable
data={ContestData.data}
columns={columns}
headerStyle={{
title: 'text-left w-2/5 md:w-3/6',
startTime: 'w-1/5 md:w-1/6',
endTime: 'w-1/5 md:w-1/6',
participants: 'w-1/5 md:w-1/6',
status: 'w-1/4 md:w-1/6'
}}
linked
emptyMessage="No finished contests found."
/>
</>
<DataTable
data={ContestData.data}
columns={columns}
headerStyle={{
title: 'text-left w-2/5 md:w-1/3',
registered: 'w-1/5 md:w-1/6',
participants: 'w-1/5 md:w-1/6',
totalScore: 'w-1/5 md:w-1/6',
period: 'w-1/5 md:w-1/4'
}}
linked
/>
)
}
Original file line number Diff line number Diff line change
@@ -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<Contest>[] = [
{
header: 'Title',
accessorKey: 'title',
cell: ({ row }) => (
<p className="overflow-hidden text-ellipsis whitespace-nowrap text-left text-sm md:text-base">
{row.original.title}
</p>
)
},
{
header: 'Registered',
accessorKey: 'registered',
cell: ({ row }) =>
row.original.isRegistered && (
<div className="flex items-center justify-center">
<Image src={CheckIcon} alt="check" height={24} />
</div>
)
},
{
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')
}
]
Original file line number Diff line number Diff line change
@@ -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 (
<>
<DataTable
data={data}
columns={columns}
headerStyle={{
title: 'text-left w-2/5 md:w-1/3',
status: 'w-1/5 md:w-1/6',
participants: 'w-1/5 md:w-1/6',
totalScore: 'w-1/5 md:w-1/6',
period: 'w-1/5 md:w-1/4'
}}
linked
/>
</>
)
}
Original file line number Diff line number Diff line change
@@ -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<Contest>[] = [
{
Expand All @@ -18,29 +18,24 @@ export const columns: ColumnDef<Contest>[] = [
{
header: 'Status',
accessorKey: 'status',
cell: ({ row }) => (
<Badge type={row.original.status}>
<p>
{row.original.status.startsWith('registered')
? 'registered'
: row.original.status}
</p>
</Badge>
)
cell: ({ row }) => <StatusBadge variant={row.original.status} />
},
{
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')
}
]
37 changes: 37 additions & 0 deletions apps/frontend/app/(main)/contest/_components/TableSwitchButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { cn } from '@/lib/utils'
import Link from 'next/link'

export default function TableSwitchButton({
registered
}: {
registered: boolean
}) {
return (
<div className="flex items-center">
<Link
href="/contest"
className={cn(
'w-fit p-6 text-xl font-medium text-[#333333]/30 hover:text-[#333333]/50 md:text-2xl',
!registered
? 'text-primary-light hover:text-primary-light border-primary-light border-b-2 font-bold'
: ''
)}
scroll={false}
>
Finished
</Link>
<Link
href="/contest?registered=true"
className={cn(
'w-fit p-6 text-xl font-medium text-[#333333]/30 hover:text-[#333333]/50 md:text-2xl',
registered
? 'text-primary-light hover:text-primary-light border-primary-light border-b-2 font-bold'
: ''
)}
scroll={false}
>
Registered
</Link>
</div>
)
}
46 changes: 42 additions & 4 deletions apps/frontend/app/(main)/contest/page.tsx
Original file line number Diff line number Diff line change
@@ -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 (
Expand Down Expand Up @@ -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 (
<>
<div className="mb-12 flex flex-col gap-12">
Expand All @@ -61,9 +79,29 @@ export default async function Contest() {
/>
</Suspense>
</div>
<Suspense fallback={<FinishedContestTableFallback />}>
<FinishedContestTable />
</Suspense>
<div className="flex-col">
<h1 className="mb-6 text-2xl font-bold text-gray-700">
List of Contests
</h1>
<Suspense fallback={<FinishedContestTableFallback />}>
{session ? (
<TableSwitchButton registered={registered} />
) : (
<p className="text-primary-light border-primary-light w-fit border-b-2 p-6 text-2xl font-bold md:text-2xl">
Finished
</p>
)}
<Separator className="mb-3" />
<div className="flex justify-end">
<SearchBar className="w-60" />
</div>
{session && registered ? (
<RegisteredContestTable search={search} />
) : (
<FinishedContestTable search={search} session={session} />
)}
</Suspense>
</div>
</>
)
}
5 changes: 4 additions & 1 deletion apps/frontend/components/SearchBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
)
}

Expand Down
3 changes: 3 additions & 0 deletions apps/frontend/public/check_blue.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/frontend/public/finished.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions apps/frontend/public/ongoing.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions apps/frontend/public/upcoming.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions apps/frontend/types/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ export interface Contest {
enableCopyPaste: boolean
status: ContestStatus
participants: number
isRegistered: boolean
}

export interface ContestAnnouncement {
Expand Down

0 comments on commit d0aa829

Please sign in to comment.