Skip to content
Open
Changes from all 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
141 changes: 96 additions & 45 deletions apps/frontend/app/admin/course/_components/DuplicateCourseButton.tsx
Copy link
Contributor

Choose a reason for hiding this comment

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

courseNum이랑 semester 입력칸에 빈값 제출하면 어떻게 되나요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

지금은 기본 값으로 복제하려는 원본 코스의 정보를 넣어서 사용자가 일부러 빈 칸으로 만들지 않으면 그렇게 되지는 않지만, 빈 칸일 경우에 별도의 에러 메세지가 출력 되지 않습니다. 이 부분도 수정해서 빈 칸일 때 빨간 메세지가 뜨고, 복제 버튼이 비활성화되는 식으로 조치하도록 하겠습니다.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Button } from '@/components/shadcn/button'
import { DUPLICATE_COURSE } from '@/graphql/course/mutation'
import { useMutation } from '@apollo/client'
import { useState } from 'react'
import { useMemo } from 'react'
import { GoAlertFill } from 'react-icons/go'
import { IoCopy } from 'react-icons/io5'
import { toast } from 'sonner'
Expand All @@ -14,11 +15,18 @@ import { useDataTable } from '../../_components/table/context'
interface DuplicateCourseButtonProps {
onSuccess: () => void
}
type CourseRow = {
id: number
title: string
code: string
semester: string
studentCount: number
}

export function DuplicateCourseButton({
onSuccess
}: DuplicateCourseButtonProps) {
const { table } = useDataTable<{ id: number; title: string }>()
const { table } = useDataTable<CourseRow>()
const [duplicateCourse] = useMutation(DUPLICATE_COURSE)
const selectedCount = table.getSelectedRowModel().rows.length
const canDuplicate = selectedCount === 1
Expand All @@ -29,12 +37,33 @@ export function DuplicateCourseButton({
const [classNum, setClassNum] = useState('')

const handleDuplicateButtonClick = () => {
if (table.getSelectedRowModel().rows.length !== 1) {
const selectedRows = table.getSelectedRowModel().rows
if (selectedRows.length !== 1) {
return
}
const selectedCourse = selectedRows[0].original
console.log('selected Course: ', selectedCourse)
setCourseNum(selectedCourse.title ?? '')
setSemester(selectedCourse.semester ?? '')
setClassNum('')
setIsDialogOpen(true)
}

const classNumError = useMemo(() => {
if (classNum.trim() === ' ') {
Copy link
Contributor

Choose a reason for hiding this comment

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

이거 띄어쓰기 한칸 ' '이렇게 있는게 맞아요 아니면 '' 그냥 붙여쓰는게 맞아요? (진짜 몰라서 물어보는 거임...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

아 이 부분은 빈칸인지 확인하는 코드인데 중현님 말씀대로 붙여 쓰는게 맞을 것 같습니다.. 수정할게요!

return 'Class Number must be entered.'
Copy link
Contributor

Choose a reason for hiding this comment

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

근데 이렇게 되면 맨처음 입력하라는 팝업창(?)이 뜨게 되면 아무것도 입력되지 않은 빈상태일텐데 바로 에러 나는거 아니에요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

지금은 classNum 에 초기값이 아무것도 없어서 classNumError 가 활성화 되는 게 맞습니다. 근데, 이게 에러가 난다고 해서 터지거나 하는 게 아니라 단순하게 빨간 글씨로 경고 문구만 생기는 거라 괜찮을 것 같아요!

Copy link
Contributor

@Choi-Jung-Hyeon Choi-Jung-Hyeon Mar 25, 2026

Choose a reason for hiding this comment

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

근데 초기에 아무값 없는 상태에서 duplicate 버튼을 눌렀을때에만 경고가 떠야하는데, 지금 이 로직이면 classNum을 입력하는 팝업창이 뜨자마자 활성화되는거아니에요?? 아닐 수도 ㅋㅋ

Copy link
Contributor Author

Choose a reason for hiding this comment

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

아아 근데 활성화 되는건 맞는데
image
이런 식으로 빨간색 글씨만 뜨는 거라 괜찮지 않을까요..??

}
if (!/^\d+$/.test(classNum)) {
return 'Class Number must be an integer between 1 and 99.'
}
const parsedClassNum = Number(classNum)
if (parsedClassNum < 1 || parsedClassNum > 99) {
return 'Class Number must be an integer between 1 and 99.'
}

return ''
}, [classNum])

const handleDuplicateRows = async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

지금은 에러가 있어도 복제버튼을 누르면 백엔드 서버로 요청이 날아가지네요 뭔가 방어 코드를 추가해주는 것이 좋을 것 같아요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

이 부분은 제가 에러가 있으면 복제 버튼이 비활성화 되는 식으로 변경해보도록 하겠습니다. 좋은 지적 감사합니다!

const selectedRows = table.getSelectedRowModel().rows

Expand Down Expand Up @@ -98,53 +127,75 @@ export function DuplicateCourseButton({
onClick: handleDuplicateRows
}}
>
<ModalSection
title="Courses that will be Copied"
description="Make sure to review the courses that will be duplicated."
items={[
table
.getSelectedRowModel()
.rows.map((row) => row.original.title)
.join(', ')
]}
/>
<div className="flex h-[37px] w-full items-center gap-[6px] bg-[#FFEBEE] px-[18px] py-[8px]">
<GoAlertFill size={16} color="#FF3B2F" />
<span className="text-sm text-[#FF3B2F]">
Course Info, Assignments and Exercises will be duplicated.
</span>
</div>

<div className="flex flex-col gap-4">
<div className="flex flex-col gap-2">
<label className="text-sm font-medium">Course Number</label>
<input
value={courseNum}
onChange={(e) => setCourseNum(e.target.value)}
placeholder="Enter course number"
className="w-full rounded-md border px-3 py-2 text-sm"
<div className="max-h-[70vh] w-full min-w-0 overflow-y-auto overflow-x-hidden pr-1">
<div className="flex w-full min-w-0 flex-col gap-4">
<ModalSection
title="Courses that will be Copied"
description="Make sure to review the courses that will be duplicated."
items={[
table
.getSelectedRowModel()
.rows.map((row) => row.original.title)
.join(', ')
]}
/>
</div>

<div className="flex flex-col gap-2">
<label className="text-sm font-medium">Semester</label>
<input
value={semester}
onChange={(e) => setSemester(e.target.value)}
placeholder="Enter semester"
className="w-full rounded-md border px-3 py-2 text-sm"
/>
<div className="flex w-full min-w-0 items-start gap-[6px] rounded-md bg-[#FFEBEE] px-[18px] py-[8px]">
<GoAlertFill
size={16}
color="#FF3B2F"
className="mt-[2px] shrink-0"
/>
<span className="break-words text-sm text-[#FF3B2F]">
Course Info, Assignments and Exercises will be duplicated.
</span>
</div>

<div className="flex w-full min-w-0 flex-col gap-4">
<div className="flex w-full min-w-0 flex-col gap-2">
<label className="text-sm font-medium">Course Number</label>
<input
value={courseNum}
onChange={(e) => setCourseNum(e.target.value)}
placeholder="Enter course number"
className="box-border w-full min-w-0 rounded-md border border-gray-300 px-3 py-2 text-sm"
/>
</div>

<div className="flex w-full min-w-0 flex-col gap-2">
<label className="text-sm font-medium">Semester</label>
<input
value={semester}
onChange={(e) => setSemester(e.target.value)}
placeholder="Enter semester"
className="box-border w-full min-w-0 rounded-md border border-gray-300 px-3 py-2 text-sm"
/>
</div>

<div className="flex w-full min-w-0 flex-col gap-2">
<label className="text-sm font-medium">Class Number</label>
<input
value={classNum}
onChange={(e) => setClassNum(e.target.value)}
placeholder="Enter class number"
className={`box-border w-full min-w-0 rounded-md border px-3 py-2 text-sm ${
classNumError
? 'border-red-500 focus:outline-none focus:ring-1 focus:ring-red-500'
: 'border-gray-300'
}`}
/>

<p
className={`min-h-[20px] text-sm ${
classNumError ? 'text-red-500' : 'invisible'
}`}
>
{classNumError || 'placeholder'}
</p>
</div>
</div>
</div>
</div>
<div className="flex flex-col gap-2">
<label className="text-sm font-medium">Class Number</label>
<input
value={classNum}
onChange={(e) => setClassNum(e.target.value)}
placeholder="Enter class number"
className="w-full rounded-md border px-3 py-2 text-sm"
/>
</div>
</AlertModal>
)
}
Loading