From 36d20ff3591742394fa96b9fc1a948c8c13b1960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A2=85=ED=9D=AC?= Date: Tue, 17 Mar 2026 22:46:44 +0900 Subject: [PATCH 1/6] feat(fe): make generator, checker, validator page --- .../create/_components/CheckerPage.tsx | 14 +- .../create/_components/FileUploadSection.tsx | 165 ++++++++++++++++++ .../create/_components/GeneratorPage.tsx | 25 ++- .../create/_components/ValidatorPage.tsx | 14 +- apps/frontend/public/icons/trashcan2-gray.svg | 3 + apps/frontend/public/icons/upload-blue.svg | 3 + 6 files changed, 209 insertions(+), 15 deletions(-) create mode 100644 apps/frontend/app/(client)/(main)/(create)/problem/create/_components/FileUploadSection.tsx create mode 100644 apps/frontend/public/icons/trashcan2-gray.svg create mode 100644 apps/frontend/public/icons/upload-blue.svg diff --git a/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/CheckerPage.tsx b/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/CheckerPage.tsx index 2d84d471c2..ad9123e2a8 100644 --- a/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/CheckerPage.tsx +++ b/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/CheckerPage.tsx @@ -1,3 +1,15 @@ +import { FileUploadSection } from './FileUploadSection' + export function CheckerPage() { - return
This is Checker page
+ return ( + + ) } diff --git a/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/FileUploadSection.tsx b/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/FileUploadSection.tsx new file mode 100644 index 0000000000..3be2474409 --- /dev/null +++ b/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/FileUploadSection.tsx @@ -0,0 +1,165 @@ +import infoIcon from '@/public/icons/file-info-gray.svg' +import fileIcon from '@/public/icons/file_gray.svg' +import trashcanIcon from '@/public/icons/trashcan2-gray.svg' +import uploadIcon from '@/public/icons/upload-blue.svg' +import Image from 'next/image' +import type { ChangeEvent } from 'react' +import { useState, useRef } from 'react' + +interface UploadedFile { + id: string + name: string + size: string +} + +interface FileUploadSectionProps { + title: string + description: string + emptyMessages: string[] + accept: string +} + +const FORMAT_EXAMPLES = [ + 'Generator : generator.cpp / generator.py', + 'Validator : validator.cpp (testlib 등)', + 'Checker : checker.cpp (특수 채점)' +] + +export function FileUploadSection({ + title, + description, + emptyMessages, + accept +}: FileUploadSectionProps) { + const [files, setFiles] = useState([]) + const fileInputRef = useRef(null) + + const handleFileChange = (e: ChangeEvent) => { + const selectedFiles = e.target.files + if (!selectedFiles) { + return + } + + const allowedExtensions = accept.split(',').map((ext) => ext.trim()) + + const newFiles: UploadedFile[] = Array.from(selectedFiles) + .filter((file) => + allowedExtensions.some((ext) => file.name.endsWith(ext)) + ) + .map((file) => { + const uniqueId = `${file.name}-${file.size}-${Date.now()}-${Math.random().toString(36).substring(2, 7)}` + + return { + id: uniqueId, + name: file.name, + size: `${(file.size / 1024).toFixed(1)}KB` + } + }) + + if (newFiles.length === 0 && selectedFiles.length > 0) { + alert(`${accept} 파일만 업로드 가능합니다.`) + return + } + + setFiles((prev) => [...prev, ...newFiles]) + if (fileInputRef.current) { + fileInputRef.current.value = '' + } + } + + const handleDelete = (id: string) => { + setFiles((prev) => prev.filter((file) => file.id !== id)) + } + + return ( +
+
+
+

{title}

+

+ {description} +

+
+ + +
+ +
+ {files.length === 0 ? ( +
+ info + {emptyMessages.map((msg, idx) => ( +

+ {msg} +

+ ))} +
+ ) : ( +
+ {files.map((file) => ( +
+
+ + file + +
+

{file.name}

+

+ {file.size} +

+
+
+ +
+ ))} +
+ )} +
+ +

포맷 예시

+
+ {FORMAT_EXAMPLES.map((example, index) => ( +
+ +

{example}

+
+ ))} +
+
+ ) +} diff --git a/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/GeneratorPage.tsx b/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/GeneratorPage.tsx index 62f8c54ae2..fd4eb18f2f 100644 --- a/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/GeneratorPage.tsx +++ b/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/GeneratorPage.tsx @@ -1,16 +1,15 @@ -import { useSuspenseQuery } from '@tanstack/react-query' +import { FileUploadSection } from './FileUploadSection' export function GeneratorPage() { - // 스켈레톤 확인을 위한 더미코드 - useSuspenseQuery({ - queryKey: ['SongJunGyu'], - queryFn: () => - new Promise((resolve) => { - setTimeout(() => { - resolve('') - }, 5000) - }) - }) - - return
This is Generator page
+ return ( + + ) } diff --git a/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/ValidatorPage.tsx b/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/ValidatorPage.tsx index b11e40a173..717993e4ab 100644 --- a/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/ValidatorPage.tsx +++ b/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/ValidatorPage.tsx @@ -1,3 +1,15 @@ +import { FileUploadSection } from './FileUploadSection' + export function ValidatorPage() { - return
This is Validator page
+ return ( + + ) } diff --git a/apps/frontend/public/icons/trashcan2-gray.svg b/apps/frontend/public/icons/trashcan2-gray.svg new file mode 100644 index 0000000000..1f28f0e650 --- /dev/null +++ b/apps/frontend/public/icons/trashcan2-gray.svg @@ -0,0 +1,3 @@ + + + diff --git a/apps/frontend/public/icons/upload-blue.svg b/apps/frontend/public/icons/upload-blue.svg new file mode 100644 index 0000000000..9d65cec1af --- /dev/null +++ b/apps/frontend/public/icons/upload-blue.svg @@ -0,0 +1,3 @@ + + + From 03beac4faf376ed6160301e9a654ca70ff5a15cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=98=A4=EC=A2=85=ED=9D=AC?= Date: Wed, 18 Mar 2026 13:11:14 +0900 Subject: [PATCH 2/6] chore(fe): apply feedback and change border styles --- .../problem/create/_components/FileUploadSection.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/FileUploadSection.tsx b/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/FileUploadSection.tsx index 3be2474409..84476a76dc 100644 --- a/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/FileUploadSection.tsx +++ b/apps/frontend/app/(client)/(main)/(create)/problem/create/_components/FileUploadSection.tsx @@ -72,8 +72,8 @@ export function FileUploadSection({ } return ( -
-
+
+

{title}

@@ -136,7 +136,7 @@ export function FileUploadSection({