Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
'use client'

import { cn } from '@/libs/utils'
import InfoIconGray from '@/public/icons/info-icon-gray.svg'
import { useRef, useState } from 'react'

interface FileUploadProps {
primaryText: string
secondaryText: string
multiple?: boolean
onFilesChange: (files: File[]) => void
className?: string
}

export function FileUpload({
primaryText,
secondaryText,
multiple = false,
onFilesChange,
className
}: FileUploadProps) {
const [files, setFiles] = useState<File[]>([])
const inputRef = useRef<HTMLInputElement | null>(null)

const handleFiles = (incoming: FileList | null) => {
if (!incoming) {
return
}
const next = Array.from(incoming)
setFiles(next)
onFilesChange(next)
}
Comment on lines +38 to +45

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

onDrop을 통해 여러 파일이 한꺼번에 들어올 수 있으므로, multiple 옵션이 false인 경우에는 첫 번째 파일만 선택되도록 로직을 보강하는 것이 안전합니다.

  const handleFiles = (incoming: FileList | null) => {
    if (!incoming) {
      return
    }
    let next = Array.from(incoming)
    if (!multiple && next.length > 0) {
      next = [next[0]]
    }
    setFiles(next)
    onFilesChange(next)
  }


return (
<div
onClick={() => inputRef.current?.click()}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

div 요소에 onClick 이벤트를 사용하여 버튼처럼 동작하게 할 때는 웹 접근성(A11y)을 위해 role="button"과 tabIndex={0}, 그리고 키보드 이벤트 핸들러(onKeyDown)를 추가하여 키보드 사용자가 상호작용할 수 있도록 개선하는 것이 좋습니다.

    <div
      role="button"
      tabIndex={0}
      onKeyDown={(e) => {
        if (e.key === 'Enter' || e.key === ' ') {
          e.preventDefault()
          inputRef.current?.click()
        }
      }}
      onClick={() => inputRef.current?.click()}

onDragOver={(e) => e.preventDefault()}
onDrop={(e) => {
e.preventDefault()
handleFiles(e.dataTransfer.files)
}}
className={cn(
'border-color-cool-neutral-80 bg-color-neutral-99 flex cursor-pointer flex-col items-center justify-center rounded-[12px] py-20',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TC 인풋 자동 생성처럼 border가 먼저 적용되면 피그마 디자인처럼 안 나오는 부분이 있어서 border는 빼고 컴포넌트화 한 뒤에, 사용할 때 border를 필요에 따라 추가해야 할 것 같아요!

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인 부탁드립니다!

className
)}
>
<input
ref={inputRef}
type="file"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

사용자가 올바른 형식의 파일만 선택할 수 있도록 accept 속성을 추가하는 것을 권장합니다. FileUploadProps에 accept 필드를 추가하여 외부에서 제어할 수 있게 하면 더 범용적인 컴포넌트가 됩니다.

multiple={multiple}
className="hidden"
onChange={(e) => handleFiles(e.target.files)}
/>
{files.length === 0 ? (
<div className="flex flex-col items-center gap-[10px]">
<InfoIconGray className="mb-2" height={24} width={24} />
<div className="text-body1_m_16 text-color-cool-neutral-50 whitespace-pre-wrap text-center">
<p>{primaryText}</p>
<p>{secondaryText}</p>
</div>
</div>
) : (
<ul className="flex flex-col items-center gap-1">
{files.map((f) => (
<li
key={f.name}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

f.name은 파일 이름이 중복될 경우 React의 key 중복 경고를 발생시킬 수 있습니다. 파일 이름과 크기 등을 조합하여 더 고유한 키를 생성하는 것이 좋습니다.

Suggested change
key={f.name}
key={f.name + "-" + f.size}

className="text-sub4_sb_14 text-color-cool-neutral-30"
>
{f.name}
</li>
))}
</ul>
)}
</div>
)
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,19 @@ import { Button } from '@/components/shadcn/button'
import { cn } from '@/libs/utils'
import ArrowRightNarrowIcon from '@/public/icons/arrow-right-narrow.svg'
import CheckCircleIcon from '@/public/icons/check-circle.svg'
import PenIcon from '@/public/icons/pen.svg'
import { ErrorBoundary, Suspense } from '@suspensive/react'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useState } from 'react'
import { AiFillFile } from 'react-icons/ai'
import { BsPeopleFill } from 'react-icons/bs'
import { FaBook } from 'react-icons/fa'
import { FaSquareCheck } from 'react-icons/fa6'
import { PiMagnifyingGlassFill, PiWrenchFill } from 'react-icons/pi'
import { CheckerPage } from './CheckerPage'
import { PiWrenchFill } from 'react-icons/pi'
import { CollaborationPage } from './CollaborationPage'
import { GeneratorPage } from './GeneratorPage'
import { ProblemCreateContentSkeleton } from './ProblemCreateSkeletons'
import { SolutionPage } from './SolutionPage'
import { StatementPage } from './StatementPage'
import { TcManagePage } from './TcManagePage'
import { TestsPage } from './TestsPage'
import { UploadButton } from './UploadButton'
import { ValidatorPage } from './ValidatorPage'

export function ProblemCreateContainer() {
// 스켈레톤 확인을 위한 더미코드
Expand Down Expand Up @@ -53,33 +48,12 @@ export function ProblemCreateContainer() {
subText: '입력 및 정답 (Input & Output)',
Component: TestsPage
},
{
Icon: PenIcon,
label: 'Solution',
text: '솔루션',
subText: '솔루션 업로드 및 테스트 검증',
Component: SolutionPage
},
{
Icon: PiWrenchFill,
label: 'Generator',
text: '테스트 생성',
subText: '테스트 입력 생성',
Component: GeneratorPage
},
{
Icon: PiMagnifyingGlassFill,
label: 'Validator',
text: '입력 검증',
subText: '입력 및 검증',
Component: ValidatorPage
},
{
Icon: FaSquareCheck,
label: 'Checker',
text: '특수 채점',
subText: '특수 채점 기능',
Component: CheckerPage
label: 'TcManage',
text: '테스트 케이스 관리',
subText: '생성 및 입력 검증, 특수 채점',
Component: TcManagePage
},
{
Icon: BsPeopleFill,
Expand Down Expand Up @@ -179,7 +153,7 @@ export function ProblemCreateContainer() {
height={15}
className={cn({
'scale-x-[-1]':
label === 'Generator' || label === 'Collaboration',
label === 'TcManage' || label === 'Collaboration',
'text-color-cool-neutral-40': curTab,
'text-color-cool-neutral-70': !curTab
})}
Expand Down
Loading
Loading