Skip to content

Commit

Permalink
Merge branch 'main' into t490-eslint-migration
Browse files Browse the repository at this point in the history
  • Loading branch information
eunnbi committed Jan 7, 2025
2 parents 4085fa8 + fad701a commit 722e7c9
Show file tree
Hide file tree
Showing 10 changed files with 468 additions and 329 deletions.
81 changes: 46 additions & 35 deletions apps/frontend/app/(client)/(main)/_components/ContestCards.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,55 @@
import { Button } from '@/components/shadcn/button'
import { fetcher } from '@/libs/utils'
import type { Contest } from '@/types/type'
import type { Route } from 'next'
import Link from 'next/link'
import ContestCard from './ContestCard'

export default async function ContestCards() {
const contests = await getContests()

return (
contests.length > 0 && (
<div className="flex w-full flex-col gap-6">
<div className="flex items-center justify-between text-gray-700">
<p className="text-2xl font-bold">Contest 🏆</p>
<Link href={'/contest'}>
<Button variant="ghost" className="h-8 px-3">
See More
</Button>
</Link>
</div>
<div className="flex justify-start gap-5 md:hidden">
{contests.slice(0, 2).map((contest) => {
return (
<Link
key={contest.id}
href={`/contest/${contest.id}` as Route}
className="inline-block w-1/2"
>
<ContestCard contest={contest} />
</Link>
)
})}
</div>
<div className="hidden justify-start gap-5 md:flex">
{contests.map((contest) => {
return (
<Link
key={contest.id}
href={`/contest/${contest.id}` as Route}
className="inline-block w-1/3"
>
<ContestCard contest={contest} />
</Link>
)
})}
</div>
</div>
)
)
}

const getContests = async () => {
const data: {
ongoing: Contest[]
Expand All @@ -20,38 +66,3 @@ const getContests = async () => {

return contests.slice(0, 3)
}

export default async function ContestCards() {
const contests = await getContests()

return (
<>
<div className="flex justify-start gap-5 md:hidden">
{contests.slice(0, 2).map((contest) => {
return (
<Link
key={contest.id}
href={`/contest/${contest.id}` as Route}
className="inline-block w-1/2"
>
<ContestCard contest={contest} />
</Link>
)
})}
</div>
<div className="hidden justify-start gap-5 md:flex">
{contests.map((contest) => {
return (
<Link
key={contest.id}
href={`/contest/${contest.id}` as Route}
className="inline-block w-1/3"
>
<ContestCard contest={contest} />
</Link>
)
})}
</div>
</>
)
}
55 changes: 33 additions & 22 deletions apps/frontend/app/(client)/(main)/_components/ProblemCards.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { Button } from '@/components/shadcn/button'
import Link from 'next/link'
import { getProblemList } from '../../_libs/apis/problem'
import ProblemCard from './ProblemCard'
Expand All @@ -9,29 +10,39 @@ export default async function ProblemCards() {
})

return (
<>
<div className="flex justify-start gap-5 md:hidden">
{problems.slice(0, 2).map((problem) => (
<Link
key={problem.id}
href={`/problem/${problem.id}`}
className="inline-block w-1/2"
>
<ProblemCard problem={problem} />
problems.length > 0 && (
<div className="flex w-full flex-col gap-6">
<div className="flex items-center justify-between text-gray-700">
<p className="text-2xl font-bold">Problem ✨</p>
<Link href={'/problem'}>
<Button variant="ghost" className="h-8 px-3">
See More
</Button>
</Link>
))}
</div>
<div className="flex justify-start gap-5 md:hidden">
{problems.slice(0, 2).map((problem) => (
<Link
key={problem.id}
href={`/problem/${problem.id}`}
className="inline-block w-1/2"
>
<ProblemCard problem={problem} />
</Link>
))}
</div>
<div className="hidden justify-start gap-5 md:flex">
{problems.map((problem) => (
<Link
key={problem.id}
href={`/problem/${problem.id}`}
className="inline-block w-1/3"
>
<ProblemCard problem={problem} />
</Link>
))}
</div>
</div>
<div className="hidden justify-start gap-5 md:flex">
{problems.map((problem) => (
<Link
key={problem.id}
href={`/problem/${problem.id}`}
className="inline-block w-1/3"
>
<ProblemCard problem={problem} />
</Link>
))}
</div>
</>
)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import { convertToLetter, dateFormatter } from '@/libs/utils'
import type { ContestProblem } from '@/types/type'
import { ErrorBoundary } from '@suspensive/react'
import type { ColumnDef } from '@tanstack/react-table'
import MySubmission from './MySubmission'
import { Suspense } from 'react'
import { MySubmissionFallback, MySubmission } from './MySubmission'

export const columns: ColumnDef<ContestProblem>[] = [
{
Expand All @@ -30,7 +32,11 @@ export const columns: ColumnDef<ContestProblem>[] = [
cell: ({ row }) =>
row.original.submissionTime && (
<div className="flex items-center justify-center">
<MySubmission problem={row.original} />
<ErrorBoundary fallback={null}>
<Suspense fallback={<MySubmissionFallback />}>
<MySubmission problem={row.original} />
</Suspense>
</ErrorBoundary>
</div>
)
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { submissionQueries } from '@/app/(client)/_libs/queries/submission'
import FetchErrorFallback from '@/components/FetchErrorFallback'
import {
Dialog,
DialogTrigger,
Expand All @@ -10,94 +12,86 @@ import {
TooltipProvider,
TooltipTrigger
} from '@/components/shadcn/tooltip'
import { fetcherWithAuth } from '@/libs/utils'
import seeSubmissionIcon from '@/public/icons/see-submission.svg'
import type { SubmissionDetail, Submission, ContestProblem } from '@/types/type'
import type { ContestProblem } from '@/types/type'
import * as TooltipPrimitive from '@radix-ui/react-tooltip'
import { ErrorBoundary } from '@suspensive/react'
import { useSuspenseQuery } from '@tanstack/react-query'
import Image from 'next/image'
import { useParams } from 'next/navigation'
import { useEffect, useState } from 'react'
import SubmissionDetailContent from './SubmissionDetailContent'

interface SubmissionsResponse {
data: Submission[]
total: number
}
import { useState, Suspense } from 'react'
import {
SubmissionDetailContent,
SubmissionDetailContentFallback
} from './SubmissionDetailContent'

export default function MySubmission({ problem }: { problem: ContestProblem }) {
export function MySubmission({ problem }: { problem: ContestProblem }) {
const [isTooltipOpen, setIsTooltipOpen] = useState(false)
const [submission, setSubmission] = useState<SubmissionDetail | null>(null)
const [submissionId, setSubmissionId] = useState<number | null>(null)
const { contestId } = useParams()
const { contestId: contestIdString } = useParams()
const contestId = Number(contestIdString)

useEffect(() => {
const getSubmission = async () => {
const submissions: SubmissionsResponse = await fetcherWithAuth
.get(`contest/${contestId}/submission`, {
searchParams: {
take: 1,
problemId: problem.id
}
})
.json()
const firstSubmission = submissions.data[0]
setSubmissionId(firstSubmission.id)
const { data: latestSubmissionData } = useSuspenseQuery(
submissionQueries.list({
contestId,
problemId: problem.id,
take: 1
})
)

const submission: SubmissionDetail = await fetcherWithAuth
.get(
`submission/${firstSubmission.id}?problemId=${problem.id}&contestId=${contestId}`
)
.json()
setSubmission(submission)
}
getSubmission()
}, [contestId, problem.id])
const latestSubmission = latestSubmissionData?.data?.[0]
const latestSubmissionId = latestSubmission?.id ?? 0

if (!submission || !submissionId) {
return <Skeleton className="size-[25px]" />
if (!latestSubmissionId) {
return null
}

return (
<>
<Dialog onOpenChange={() => setIsTooltipOpen(false)}>
<TooltipProvider>
<Tooltip>
<DialogTrigger asChild>
<TooltipTrigger asChild>
<Image
src={seeSubmissionIcon}
width={20}
height={20}
alt={'See submission'}
onClick={(e) => {
e.stopPropagation()
setIsTooltipOpen(true)
}}
onMouseEnter={() => setIsTooltipOpen(true)}
onMouseLeave={() => setIsTooltipOpen(false)}
/>
</TooltipTrigger>
</DialogTrigger>
{isTooltipOpen && (
<TooltipContent className="mr-4 bg-white">
<p className="text-xs text-neutral-900">
Click to check your latest submission.
</p>
<TooltipPrimitive.Arrow className="fill-white" />
</TooltipContent>
)}
</Tooltip>
</TooltipProvider>
<div onClick={(e) => e.stopPropagation()}>
<DialogContent className="max-h-[620px] max-w-[800px] justify-center">
<SubmissionDetailContent
submission={submission}
submissionId={submissionId}
problem={problem}
/>
</DialogContent>
</div>
</Dialog>
</>
<Dialog onOpenChange={() => setIsTooltipOpen(false)}>
<TooltipProvider>
<Tooltip>
<DialogTrigger asChild>
<TooltipTrigger asChild>
<Image
src={seeSubmissionIcon}
width={20}
height={20}
alt={'See submission'}
onClick={(e) => {
e.stopPropagation()
setIsTooltipOpen(true)
}}
onMouseEnter={() => setIsTooltipOpen(true)}
onMouseLeave={() => setIsTooltipOpen(false)}
/>
</TooltipTrigger>
</DialogTrigger>
{isTooltipOpen && (
<TooltipContent className="mr-4 bg-white">
<p className="text-xs text-neutral-900">
Click to check your latest submission.
</p>
<TooltipPrimitive.Arrow className="fill-white" />
</TooltipContent>
)}
</Tooltip>
</TooltipProvider>
<div onClick={(e) => e.stopPropagation()}>
<DialogContent className="max-h-[620px] max-w-[800px] justify-center">
<ErrorBoundary fallback={FetchErrorFallback}>
<Suspense fallback={<SubmissionDetailContentFallback />}>
<SubmissionDetailContent
contestId={contestId}
submissionId={latestSubmissionId}
problem={problem}
/>
</Suspense>
</ErrorBoundary>
</DialogContent>
</div>
</Dialog>
)
}

export function MySubmissionFallback() {
return <Skeleton className="size-[25px]" />
}
Loading

0 comments on commit 722e7c9

Please sign in to comment.