Skip to content

Commit

Permalink
fix(fe): change index based number to letter (#1533)
Browse files Browse the repository at this point in the history
* refactor: change converToLetter logic

* fix: change number base to letter base in dropdown

* fix: change number base to letter base in description

* fix: add contest feature in problem tab
  • Loading branch information
dayongkr authored Mar 2, 2024
1 parent 52516de commit 9a1dd39
Show file tree
Hide file tree
Showing 8 changed files with 70 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export default async function layout({
params,
children
}: {
params: { problemId: string; contestId: string }
params: { problemId: number; contestId: number }
children: React.ReactNode
}) {
const { problemId, contestId } = params
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
import { EditorDescription } from '@/components/EditorDescription'
import { fetcher } from '@/lib/utils'
import type { ProblemDetail } from '@/types/type'
import { fetcher, fetcherWithAuth } from '@/lib/utils'
import type { ContestProblem, ProblemDetail } from '@/types/type'

export default async function DescriptionPage({
params
}: {
params: { problemId: number }
params: { problemId: number; contestId: number }
}) {
const { problemId } = params
const problem: ProblemDetail = await fetcher(`problem/${problemId}`).json()

return <EditorDescription problem={problem} />
const contestProblems: { problems: ContestProblem[] } = await fetcherWithAuth(
`contest/${params.contestId}/problem`
).json()
return (
<EditorDescription
problem={problem}
contestProblems={contestProblems.problems}
/>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import EditorLayout from '@/components/EditorLayout'

export default async function layout({
params,
children
}: {
params: { problemId: number; contestId: number }
children: React.ReactNode
}) {
const { problemId, contestId } = params

return (
<EditorLayout problemId={problemId} contestId={contestId}>
{children}
</EditorLayout>
)
}
3 changes: 3 additions & 0 deletions frontend-client/components/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ interface DataTableProps<TData, TValue> {
* tailwindcss class name for each header
* @param name
* name of the table, used for routing
* @param linked
* if true, each row is linked to the detail page
* @example
* ```tsx
* // page.tsx
Expand All @@ -50,6 +52,7 @@ interface DataTableProps<TData, TValue> {
* createTime: 'w-1/4 md:w-1/6'
* }}
* name="notice"
* linked # for routing
* />
* ```
* ```tsx
Expand Down
13 changes: 10 additions & 3 deletions frontend-client/components/EditorDescription.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
AccordionTrigger
} from '@/components/ui/accordion'
import { Badge } from '@/components/ui/badge'
import type { ProblemDetail } from '@/types/type'
import { convertToLetter } from '@/lib/utils'
import type { ContestProblem, ProblemDetail } from '@/types/type'
import { motion } from 'framer-motion'
import { sanitize } from 'isomorphic-dompurify'
import { CheckCircle, Lightbulb, Tag } from 'lucide-react'
Expand Down Expand Up @@ -42,13 +43,19 @@ const useCopy = () => {
return { copiedID, copy }
}

export function EditorDescription({ problem }: { problem: ProblemDetail }) {
export function EditorDescription({
problem,
contestProblems
}: {
problem: ProblemDetail
contestProblems?: ContestProblem[]
}) {
const { copiedID, copy } = useCopy()

return (
<div className="dark flex h-full flex-col gap-8 p-6 text-lg">
<div>
<h1 className="mb-3 text-xl font-bold">{`#${problem.id}. ${problem.title}`}</h1>
<h1 className="mb-3 text-xl font-bold">{`#${contestProblems ? convertToLetter(contestProblems.find((item) => item.id === problem.id)?.order as number) : problem.id}. ${problem.title}`}</h1>
<div
className="prose prose-invert max-w-full text-sm leading-relaxed text-slate-300"
dangerouslySetInnerHTML={{ __html: sanitize(problem.description) }}
Expand Down
34 changes: 18 additions & 16 deletions frontend-client/components/EditorLayout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import EditorResizablePanel from '@/components/EditorResizablePanel'
import { fetcher } from '@/lib/utils'
import { convertToLetter, fetcher, fetcherWithAuth } from '@/lib/utils'
import codedangLogo from '@/public/codedang-editor.svg'
import type { Contest, ContestProblem, ProblemDetail } from '@/types/type'
import type { Route } from 'next'
Expand All @@ -15,8 +15,8 @@ import {
} from './ui/dropdown-menu'

interface EditorLayoutProps {
contestId?: string
problemId: string
contestId?: number
problemId: number
children: React.ReactNode
}

Expand All @@ -25,18 +25,18 @@ export default async function EditorLayout({
problemId,
children
}: EditorLayoutProps) {
const problems: { problems: ContestProblem[] } | null = contestId
? await fetcher('problem', {
searchParams: {
contestId
}
}).json()
: null
let problems: { problems: ContestProblem[] } | undefined
let contest: Contest | undefined

if (contestId) {
// for getting contest info and problems list
problems = await fetcherWithAuth.get(`contest/${contestId}/problem`).json()
contest = await fetcher(`contest/${contestId}`).json()
}

// for getting problem detail
const problem: ProblemDetail = await fetcher(`problem/${problemId}`).json()

const contest: Contest | null = contestId
? await fetcher(`contest/${contestId}`).json()
: null
return (
<div className="grid-rows-editor grid h-dvh w-full min-w-[1000px] overflow-x-auto bg-slate-800 text-white">
<header className="flex justify-between bg-slate-900 px-4">
Expand All @@ -56,14 +56,14 @@ export default async function EditorLayout({
{contest ? (
<DropdownMenu>
<DropdownMenuTrigger className="flex gap-1 text-lg font-bold text-white outline-none">
<h1>{`${problems?.problems.find((item) => item.id === Number(problemId))?.order}. ${problem.title}`}</h1>
<h1>{`${convertToLetter(problems?.problems.find((item) => item.id === Number(problemId))?.order as number)}. ${problem.title}`}</h1>
<FaSortDown />
</DropdownMenuTrigger>
<DropdownMenuContent className="border-slate-700 bg-slate-900">
{problems?.problems.map((p: ContestProblem) => (
<Link key={p.id} href={`${p.id}` as Route}>
<DropdownMenuItem className="text-white hover:cursor-pointer focus:bg-slate-800 focus:text-white">
{`${p.order}. ${p.title}`}
{`${convertToLetter(p.order)}. ${p.title}`}
</DropdownMenuItem>
</Link>
))}
Expand All @@ -75,7 +75,9 @@ export default async function EditorLayout({
</div>
</div>
</header>
<EditorResizablePanel problem={problem}>{children}</EditorResizablePanel>
<EditorResizablePanel problem={problem} contestId={contestId}>
{children}
</EditorResizablePanel>
</div>
)
}
11 changes: 8 additions & 3 deletions frontend-client/components/EditorResizablePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,12 @@ import EditorHeader from './EditorHeader'
interface ProblemEditorProps {
problem: ProblemDetail
children: React.ReactNode
contestId?: number
}

export default function EditorMainResizablePanel({
problem,
contestId,
children
}: ProblemEditorProps) {
// get programming language from localStorage for default value
Expand All @@ -34,6 +36,7 @@ export default function EditorMainResizablePanel({
)
const { code, setCode, setLanguage, language } = useEditorStore()
const pathname = usePathname()
const base = contestId ? `/contest/${contestId}` : ''

useEffect(() => {
if (!language) {
Expand All @@ -58,21 +61,23 @@ export default function EditorMainResizablePanel({
<div className="flex h-full w-full items-center border-b border-slate-700 bg-slate-800 px-6">
<Tabs
value={
pathname.startsWith(`/problem/${problem.id}/submission`)
pathname.startsWith(`${base}/problem/${problem.id}/submission`)
? 'Submission'
: 'Description'
}
>
<TabsList className="bg-slate-900">
<Link href={`/problem/${problem.id}` as Route}>
<Link href={`${base}/problem/${problem.id}` as Route}>
<TabsTrigger
value="Description"
className="data-[state=active]:text-primary-light data-[state=active]:bg-slate-700"
>
Description
</TabsTrigger>
</Link>
<Link href={`/problem/${problem.id}/submission` as Route}>
<Link
href={`${base}/problem/${problem.id}/submission` as Route}
>
<TabsTrigger
value="Submission"
className="data-[state=active]:text-primary-light data-[state=active]:bg-slate-700"
Expand Down
7 changes: 1 addition & 6 deletions frontend-client/lib/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,5 @@ export const fetcherWithAuth = fetcher.extend({
})

export const convertToLetter = (n: number) => {
const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
const firstDigit = Math.floor(n / 26) - 1

return firstDigit >= 0
? alphabet[firstDigit] + alphabet[n % 26]
: alphabet[n % 26]
return String.fromCharCode(65 + n)
}

0 comments on commit 9a1dd39

Please sign in to comment.