Skip to content

Commit

Permalink
feat(fe): refresh problems dropdown list after submit (#2212)
Browse files Browse the repository at this point in the history
* feat(fe): refresh problems dropdown list after submit

* fix(fe): make dropdown component

* chore(fe): fix code

* feat(fe): add submission result store

* fix(fe): use tanstack query

* fix(fe): use tanstack query

* fix(fe): move code

* fix(fe): resolve infinite rendering problem

* fix(fe): resolve review

* chore(fe): apply code review

---------

Co-authored-by: Kohminchae <[email protected]>
Co-authored-by: Kohminchae <[email protected]>
  • Loading branch information
3 people authored Nov 22, 2024
1 parent be860a0 commit 9e7bebb
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 54 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
'use client'

import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from '@/components/shadcn/dropdown-menu'
import { cn, convertToLetter, fetcherWithAuth } from '@/libs/utils'
import checkIcon from '@/public/icons/check-green.svg'
import type { ContestProblem, ProblemDetail } from '@/types/type'
import { useQuery } from '@tanstack/react-query'
import Image from 'next/image'
import Link from 'next/link'
import { FaSortDown } from 'react-icons/fa'

interface ContestProblemsResponse {
data: ContestProblem[]
total: number
}

interface ContestProblemDropdownProps {
problem: ProblemDetail
problemId: number
contestId: number
}

export default function ContestProblemDropdown({
problem,
problemId,
contestId
}: ContestProblemDropdownProps) {
const { data: contestProblems } = useQuery<
ContestProblemsResponse | undefined
>({
queryKey: ['contest', contestId, 'problems'],
queryFn: () =>
fetcherWithAuth.get(`contest/${contestId}/problem?take=20`).json()
})

return (
<DropdownMenu>
<DropdownMenuTrigger className="flex gap-1 text-lg text-white outline-none">
<h1>{`${convertToLetter(contestProblems?.data.find((item) => item.id === Number(problemId))?.order as number)}. ${problem.title}`}</h1>
<FaSortDown />
</DropdownMenuTrigger>
<DropdownMenuContent className="border-slate-700 bg-slate-900">
{contestProblems?.data.map((p) => (
<Link key={p.id} href={`/contest/${contestId}/problem/${p.id}`}>
<DropdownMenuItem
className={cn(
'flex justify-between text-white hover:cursor-pointer focus:bg-slate-800 focus:text-white',
problem.id === p.id &&
'text-primary-light focus:text-primary-light'
)}
>
{`${convertToLetter(p.order)}. ${p.title}`}
{p.submissionTime && (
<div className="flex items-center justify-center pl-2">
<Image src={checkIcon} alt="check" width={16} height={16} />
</div>
)}
</DropdownMenuItem>
</Link>
))}
</DropdownMenuContent>
</DropdownMenu>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import type {
Submission,
Template
} from '@/types/type'
import { useQueryClient } from '@tanstack/react-query'
import JSConfetti from 'js-confetti'
import { Save } from 'lucide-react'
import type { Route } from 'next'
Expand Down Expand Up @@ -83,6 +84,8 @@ export default function Editor({
const whereToPush = useRef('')
const isModalConfrimed = useRef(false)

const queryClient = useQueryClient()

useInterval(
async () => {
const res = await fetcherWithAuth(`submission/${submissionId}`, {
Expand Down Expand Up @@ -186,6 +189,9 @@ export default function Editor({
storeCodeToLocalStorage(code)
const submission: Submission = await res.json()
setSubmissionId(submission.id)
queryClient.refetchQueries({
queryKey: ['contest', contestId, 'problems']
})
} else {
setIsSubmitting(false)
if (res.status === 401) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import ContestStatusTimeDiff from '@/components/ContestStatusTimeDiff'
import HeaderAuthPanel from '@/components/auth/HeaderAuthPanel'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger
} from '@/components/shadcn/dropdown-menu'
import { auth } from '@/libs/auth'
import { cn, convertToLetter, fetcher, fetcherWithAuth } from '@/libs/utils'
import checkIcon from '@/public/icons/check-green.svg'
import { fetcher, fetcherWithAuth } from '@/libs/utils'
import codedangLogo from '@/public/logos/codedang-editor.svg'
import type { Contest, ContestProblem, ProblemDetail } from '@/types/type'
import type { Contest, ProblemDetail } from '@/types/type'
import type { Route } from 'next'
import Image from 'next/image'
import Link from 'next/link'
import { redirect } from 'next/navigation'
import { FaSortDown } from 'react-icons/fa'
import ContestProblemDropdown from './ContestProblemDropdown'
import EditorMainResizablePanel from './EditorResizablePanel'

interface EditorLayoutProps {
Expand All @@ -24,25 +17,16 @@ interface EditorLayoutProps {
children: React.ReactNode
}

interface ContestProblemProps {
data: ContestProblem[]
total: number
}

export default async function EditorLayout({
contestId,
problemId,
children
}: EditorLayoutProps) {
let problems: ContestProblemProps | undefined
let contest: Contest | undefined
let problem: ProblemDetail

if (contestId) {
// for getting contest info and problems list
problems = await fetcherWithAuth
.get(`contest/${contestId}/problem?take=20`)
.json()
const res = await fetcherWithAuth(
`contest/${contestId}/problem/${problemId}`
)
Expand Down Expand Up @@ -72,46 +56,17 @@ export default async function EditorLayout({
<div className="flex items-center gap-1 font-medium">
{contest ? <>Contest</> : <Link href="/problem">Problem</Link>}
<p className="mx-2"> / </p>
{contest ? (
{contest && contestId ? (
<>
<Link href={`/contest/${contestId}` as Route}>
{contest.title}
</Link>
<p className="mx-2"> / </p>
<DropdownMenu>
<DropdownMenuTrigger className="flex gap-1 text-lg text-white outline-none">
<h1>{`${convertToLetter(problems?.data.find((item) => item.id === Number(problemId))?.order as number)}. ${problem.title}`}</h1>
<FaSortDown />
</DropdownMenuTrigger>
<DropdownMenuContent className="border-slate-700 bg-slate-900">
{problems?.data.map((p: ContestProblem) => (
<Link
key={p.id}
href={`/contest/${contestId}/problem/${p.id}` as Route}
>
<DropdownMenuItem
className={cn(
'flex justify-between text-white hover:cursor-pointer focus:bg-slate-800 focus:text-white',
problem.id === p.id &&
'text-primary-light focus:text-primary-light'
)}
>
{`${convertToLetter(p.order)}. ${p.title}`}
{p.submissionTime && (
<div className="flex items-center justify-center pl-2">
<Image
src={checkIcon}
alt="check"
width={16}
height={16}
/>
</div>
)}
</DropdownMenuItem>
</Link>
))}
</DropdownMenuContent>
</DropdownMenu>
<ContestProblemDropdown
problem={problem}
problemId={problemId}
contestId={contestId}
/>
</>
) : (
<h1 className="w-[1024px] overflow-hidden text-ellipsis whitespace-nowrap text-lg font-medium text-white">{`#${problem.id}. ${problem.title}`}</h1>
Expand Down
1 change: 1 addition & 0 deletions apps/frontend/stores/editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const useLanguageStore = (problemId: number, contestId?: number) => {
)
)
}

interface CodeState {
code: string
setCode: (code: string) => void
Expand Down

0 comments on commit 9e7bebb

Please sign in to comment.