summaryrefslogtreecommitdiff
path: root/lib/gtc-contract/status
diff options
context:
space:
mode:
Diffstat (limited to 'lib/gtc-contract/status')
-rw-r--r--lib/gtc-contract/status/clone-gtc-document-dialog.tsx383
-rw-r--r--lib/gtc-contract/status/create-gtc-document-dialog.tsx98
-rw-r--r--lib/gtc-contract/status/gtc-contract-table.tsx13
-rw-r--r--lib/gtc-contract/status/gtc-documents-table-columns.tsx35
4 files changed, 493 insertions, 36 deletions
diff --git a/lib/gtc-contract/status/clone-gtc-document-dialog.tsx b/lib/gtc-contract/status/clone-gtc-document-dialog.tsx
new file mode 100644
index 00000000..1e56f2f7
--- /dev/null
+++ b/lib/gtc-contract/status/clone-gtc-document-dialog.tsx
@@ -0,0 +1,383 @@
+"use client"
+
+import * as React from "react"
+import { useForm } from "react-hook-form"
+import { zodResolver } from "@hookform/resolvers/zod"
+import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog"
+import { Button } from "@/components/ui/button"
+import { Textarea } from "@/components/ui/textarea"
+
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from "@/components/ui/form"
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@/components/ui/select"
+import {
+ Popover,
+ PopoverTrigger,
+ PopoverContent,
+} from "@/components/ui/popover"
+import {
+ Command,
+ CommandInput,
+ CommandList,
+ CommandGroup,
+ CommandItem,
+ CommandEmpty,
+} from "@/components/ui/command"
+import { Check, ChevronsUpDown, Loader, Copy } from "lucide-react"
+import { cn } from "@/lib/utils"
+import { toast } from "sonner"
+
+import { cloneGtcDocumentSchema, type CloneGtcDocumentSchema } from "@/lib/gtc-contract/validations"
+import { cloneGtcDocument, getAvailableProjectsForGtcExcluding, hasStandardGtcDocument } from "@/lib/gtc-contract/service"
+import { type ProjectForFilter } from "@/lib/gtc-contract/service"
+import { type GtcDocumentWithRelations } from "@/db/schema/gtc"
+import { useSession } from "next-auth/react"
+import { Input } from "@/components/ui/input"
+import { useRouter } from "next/navigation"
+
+interface CloneGtcDocumentDialogProps {
+ sourceDocument: GtcDocumentWithRelations
+ open?: boolean
+ onOpenChange?: (open: boolean) => void
+}
+
+export function CloneGtcDocumentDialog({
+ sourceDocument,
+ open: controlledOpen,
+ onOpenChange: controlledOnOpenChange
+}: CloneGtcDocumentDialogProps) {
+ const [internalOpen, setInternalOpen] = React.useState(false)
+ const [projects, setProjects] = React.useState<ProjectForFilter[]>([])
+ const [isClonePending, startCloneTransition] = React.useTransition()
+ const { data: session } = useSession()
+ const router = useRouter()
+ const [defaultType, setDefaultType] = React.useState<"standard" | "project">("standard")
+
+ const isControlled = controlledOpen !== undefined
+ const open = isControlled ? controlledOpen! : internalOpen
+ const setOpen = isControlled ? controlledOnOpenChange! : setInternalOpen
+
+ const currentUserId = React.useMemo(() => {
+ return session?.user?.id ? Number(session.user.id) : null
+ }, [session])
+
+
+
+
+ const form = useForm<CloneGtcDocumentSchema>({
+ resolver: zodResolver(cloneGtcDocumentSchema),
+ defaultValues: {
+ sourceDocumentId: sourceDocument.id,
+ type: sourceDocument.type,
+ projectId: sourceDocument.projectId,
+ title: sourceDocument.title || "",
+ editReason: "",
+ },
+ })
+
+ const resetForm = React.useCallback((type: "standard" | "project") => {
+ form.reset({
+ sourceDocumentId: sourceDocument.id,
+ type,
+ projectId: sourceDocument.projectId,
+ title: sourceDocument.title || "",
+ editReason: "",
+ })
+ }, [form, sourceDocument])
+
+ React.useEffect(() => {
+ if (open) {
+ // 표준 GTC 존재 여부와 사용 가능한 프로젝트 동시 조회
+ Promise.all([
+ hasStandardGtcDocument(),
+ getAvailableProjectsForGtcExcluding(sourceDocument.projectId || undefined)
+ ]).then(([hasStandard, availableProjects]) => {
+ const initialType = hasStandard ? "project" : "standard"
+ setDefaultType(initialType)
+ setProjects(availableProjects)
+
+ // 폼 기본값 설정: 원본 문서 타입을 우선으로 하되, 표준이 이미 있고 원본도 표준이면 프로젝트로 변경
+ const targetType = hasStandard && sourceDocument.type === "standard" ? "project" : sourceDocument.type
+ resetForm(targetType)
+ })
+ }
+ }, [open, sourceDocument.projectId, sourceDocument.type, resetForm])
+
+
+ const watchedType = form.watch("type")
+
+ React.useEffect(() => {
+ // 소스 문서가 변경되면 폼 기본값 업데이트 (다이얼로그가 열려있을 때만)
+ if (open) {
+ hasStandardGtcDocument().then((hasStandard) => {
+ const targetType = hasStandard && sourceDocument.type === "standard" ? "project" : sourceDocument.type
+ resetForm(targetType)
+ })
+ }
+ }, [sourceDocument, resetForm, open])
+
+ async function onSubmit(data: CloneGtcDocumentSchema) {
+ startCloneTransition(async () => {
+ if (!currentUserId) {
+ toast.error("로그인이 필요합니다")
+ return
+ }
+
+ try {
+ const result = await cloneGtcDocument({
+ ...data,
+ createdById: currentUserId
+ })
+
+ if (result.error) {
+ toast.error(`에러: ${result.error}`)
+ return
+ }
+
+ resetForm(sourceDocument.type)
+ setOpen(false)
+ router.refresh()
+
+ toast.success("GTC 문서가 복제되었습니다.")
+ } catch (error) {
+ toast.error("문서 복제 중 오류가 발생했습니다.")
+ }
+ })
+ }
+
+ function handleDialogOpenChange(nextOpen: boolean) {
+ if (!nextOpen) {
+ // 다이얼로그 닫을 때는 원본 문서 정보로 리셋
+ resetForm(sourceDocument.type)
+ }
+ setOpen(nextOpen)
+ }
+
+ const DialogWrapper = isControlled ? React.Fragment : Dialog
+
+ return (
+ <DialogWrapper>
+ {!isControlled && (
+ <DialogTrigger asChild>
+ <Button variant="outline" size="sm">
+ <Copy className="mr-2 h-4 w-4" />
+ 복제하기
+ </Button>
+ </DialogTrigger>
+ )}
+
+ <Dialog open={open} onOpenChange={handleDialogOpenChange}>
+ <DialogContent className="max-w-md">
+ <DialogHeader>
+ <DialogTitle>GTC 문서 복제</DialogTitle>
+ <DialogDescription>
+ 기존 문서를 복제하여 새로운 문서를 생성합니다. <br />
+ <span className="font-medium text-foreground">
+ 원본: {sourceDocument.title || `${sourceDocument.type === 'standard' ? '표준' : '프로젝트'} GTC v${sourceDocument.revision}`}
+ </span>
+ </DialogDescription>
+ </DialogHeader>
+
+ <Form {...form}>
+ <form onSubmit={form.handleSubmit(onSubmit)}>
+ <div className="space-y-4 py-4">
+ {/* 구분 (Type) */}
+ <FormField
+ control={form.control}
+ name="type"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>구분</FormLabel>
+ <FormControl>
+ <Select
+ onValueChange={(value) => {
+ field.onChange(value)
+ // 표준으로 변경시 프로젝트 ID 초기화
+ if (value === "standard") {
+ form.setValue("projectId", null)
+ }
+ }}
+ value={field.value}
+ >
+ <SelectTrigger>
+ <SelectValue placeholder="구분을 선택하세요" />
+ </SelectTrigger>
+ <SelectContent>
+ <SelectItem value="standard">표준</SelectItem>
+ <SelectItem value="project">프로젝트</SelectItem>
+ </SelectContent>
+ </Select>
+ </FormControl>
+ {defaultType === "project" && sourceDocument.type === "standard" && (
+ <FormDescription>
+ 표준 GTC 문서가 이미 존재합니다. 복제시에는 프로젝트 타입을 권장합니다.
+ </FormDescription>
+ )}
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 프로젝트 선택 (프로젝트 타입인 경우만) */}
+ {watchedType === "project" && (
+ <FormField
+ control={form.control}
+ name="projectId"
+ render={({ field }) => {
+ const selectedProject = projects.find(
+ (p) => p.id === field.value
+ )
+ const [popoverOpen, setPopoverOpen] = React.useState(false)
+
+ return (
+ <FormItem>
+ <FormLabel>프로젝트</FormLabel>
+ <FormControl>
+ <Popover
+ open={popoverOpen}
+ onOpenChange={setPopoverOpen}
+ modal={true}
+ >
+ <PopoverTrigger asChild>
+ <Button
+ variant="outline"
+ role="combobox"
+ aria-expanded={popoverOpen}
+ className="w-full justify-between"
+ >
+ {selectedProject
+ ? `${selectedProject.name} (${selectedProject.code})`
+ : "프로젝트를 선택하세요..."}
+ <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
+ </Button>
+ </PopoverTrigger>
+
+ <PopoverContent className="w-full p-0">
+ <Command>
+ <CommandInput
+ placeholder="프로젝트 검색..."
+ className="h-9"
+ />
+ <CommandList>
+ <CommandEmpty>
+ {projects.length === 0
+ ? "사용 가능한 프로젝트가 없습니다."
+ : "프로젝트를 찾을 수 없습니다."
+ }
+ </CommandEmpty>
+ <CommandGroup>
+ {projects.map((project) => {
+ const label = `${project.name} (${project.code})`
+ return (
+ <CommandItem
+ key={project.id}
+ value={label}
+ onSelect={() => {
+ field.onChange(project.id)
+ setPopoverOpen(false)
+ }}
+ >
+ {label}
+ <Check
+ className={cn(
+ "ml-auto h-4 w-4",
+ selectedProject?.id === project.id
+ ? "opacity-100"
+ : "opacity-0"
+ )}
+ />
+ </CommandItem>
+ )
+ })}
+ </CommandGroup>
+ </CommandList>
+ </Command>
+ </PopoverContent>
+ </Popover>
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )
+ }}
+ />
+ )}
+
+ <FormField
+ control={form.control}
+ name="title"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>GTC 제목 (선택사항)</FormLabel>
+ <FormControl>
+ <Input
+ placeholder="GTC 제목을 입력하세요..."
+ {...field}
+ />
+ </FormControl>
+ <FormDescription>
+ 워드의 제목으로 사용됩니다.
+ </FormDescription>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+
+ {/* 편집 사유 */}
+ <FormField
+ control={form.control}
+ name="editReason"
+ render={({ field }) => (
+ <FormItem>
+ <FormLabel>복제 사유 (선택사항)</FormLabel>
+ <FormControl>
+ <Textarea
+ placeholder="복제 사유를 입력하세요..."
+ {...field}
+ rows={3}
+ />
+ </FormControl>
+ <FormMessage />
+ </FormItem>
+ )}
+ />
+ </div>
+
+ <DialogFooter>
+ <Button
+ type="button"
+ variant="outline"
+ onClick={() => setOpen(false)}
+ disabled={isClonePending}
+ >
+ Cancel
+ </Button>
+ <Button type="submit" disabled={isClonePending}>
+ {isClonePending && (
+ <Loader
+ className="mr-2 size-4 animate-spin"
+ aria-hidden="true"
+ />
+ )}
+ 복제하기
+ </Button>
+ </DialogFooter>
+ </form>
+ </Form>
+ </DialogContent>
+ </Dialog>
+ </DialogWrapper>
+ )
+} \ No newline at end of file
diff --git a/lib/gtc-contract/status/create-gtc-document-dialog.tsx b/lib/gtc-contract/status/create-gtc-document-dialog.tsx
index 003e4d51..174bb8dd 100644
--- a/lib/gtc-contract/status/create-gtc-document-dialog.tsx
+++ b/lib/gtc-contract/status/create-gtc-document-dialog.tsx
@@ -41,28 +41,45 @@ import { cn } from "@/lib/utils"
import { toast } from "sonner"
import { createGtcDocumentSchema, type CreateGtcDocumentSchema } from "@/lib/gtc-contract/validations"
-import { createGtcDocument, getProjectsForSelect } from "@/lib/gtc-contract/service"
-import { type Project } from "@/db/schema/projects"
+import { createGtcDocument, getAvailableProjectsForGtc, hasStandardGtcDocument } from "@/lib/gtc-contract/service"
+import { type ProjectForFilter } from "@/lib/gtc-contract/service"
import { useSession } from "next-auth/react"
import { Input } from "@/components/ui/input"
-import { useRouter } from "next/navigation";
+import { useRouter } from "next/navigation"
export function CreateGtcDocumentDialog() {
const [open, setOpen] = React.useState(false)
- const [projects, setProjects] = React.useState<Project[]>([])
+ const [projects, setProjects] = React.useState<ProjectForFilter[]>([])
const [isCreatePending, startCreateTransition] = React.useTransition()
+ const [defaultType, setDefaultType] = React.useState<"standard" | "project">("standard")
const { data: session } = useSession()
- const router = useRouter();
+ const router = useRouter()
const currentUserId = React.useMemo(() => {
return session?.user?.id ? Number(session.user.id) : null;
- }, [session]);
-
+ }, [session])
React.useEffect(() => {
if (open) {
- getProjectsForSelect().then((res) => {
- setProjects(res)
+ // 표준 GTC 존재 여부와 사용 가능한 프로젝트 동시 조회
+ Promise.all([
+ hasStandardGtcDocument(),
+ getAvailableProjectsForGtc()
+ ]).then(([hasStandard, availableProjects]) => {
+ const initialType = hasStandard ? "project" : "standard"
+ setDefaultType(initialType)
+ setProjects(availableProjects)
+
+ // 폼 기본값 설정 (setTimeout으로 다음 틱에 실행)
+ setTimeout(() => {
+ form.reset({
+ type: initialType,
+ projectId: null,
+ title: "",
+ revision: 0,
+ editReason: "",
+ })
+ }, 0)
})
}
}, [open])
@@ -78,11 +95,21 @@ export function CreateGtcDocumentDialog() {
},
})
+ const resetForm = React.useCallback((type: "standard" | "project") => {
+ form.reset({
+ type,
+ projectId: null,
+ title: "",
+ revision: 0,
+ editReason: "",
+ })
+ }, [form])
+
+
const watchedType = form.watch("type")
async function onSubmit(data: CreateGtcDocumentSchema) {
startCreateTransition(async () => {
-
if (!currentUserId) {
toast.error("로그인이 필요합니다")
return
@@ -99,9 +126,9 @@ export function CreateGtcDocumentDialog() {
return
}
- form.reset()
+ resetForm(defaultType)
setOpen(false)
- router.refresh();
+ router.refresh()
toast.success("GTC 문서가 생성되었습니다.")
} catch (error) {
@@ -112,7 +139,7 @@ export function CreateGtcDocumentDialog() {
function handleDialogOpenChange(nextOpen: boolean) {
if (!nextOpen) {
- form.reset()
+ resetForm(defaultType)
}
setOpen(nextOpen)
}
@@ -122,15 +149,15 @@ export function CreateGtcDocumentDialog() {
<DialogTrigger asChild>
<Button variant="default" size="sm">
<Plus className="mr-2 h-4 w-4" />
- Add GTC Document
+ GTC 추가
</Button>
</DialogTrigger>
<DialogContent className="max-w-md">
<DialogHeader>
- <DialogTitle>Create New GTC Document</DialogTitle>
+ <DialogTitle>새 GTC 만들기</DialogTitle>
<DialogDescription>
- 새 GTC 문서 정보를 입력하고 <b>Create</b> 버튼을 누르세요.
+ 새 GTC 문서 정보를 입력하고 <b>생성</b> 버튼을 누르세요.
</DialogDescription>
</DialogHeader>
@@ -159,11 +186,26 @@ export function CreateGtcDocumentDialog() {
<SelectValue placeholder="구분을 선택하세요" />
</SelectTrigger>
<SelectContent>
- <SelectItem value="standard">표준</SelectItem>
- <SelectItem value="project">프로젝트</SelectItem>
+ {/* 기본값에 따라 순서 조정 */}
+ {defaultType === "project" ? (
+ <>
+ <SelectItem value="project">프로젝트</SelectItem>
+ <SelectItem value="standard">표준</SelectItem>
+ </>
+ ) : (
+ <>
+ <SelectItem value="standard">표준</SelectItem>
+ <SelectItem value="project">프로젝트</SelectItem>
+ </>
+ )}
</SelectContent>
</Select>
</FormControl>
+ {defaultType === "project" && (
+ <FormDescription>
+ 표준 GTC 문서가 이미 존재합니다. 새 문서는 프로젝트 타입을 권장합니다.
+ </FormDescription>
+ )}
<FormMessage />
</FormItem>
)}
@@ -198,7 +240,9 @@ export function CreateGtcDocumentDialog() {
>
{selectedProject
? `${selectedProject.name} (${selectedProject.code})`
- : "프로젝트를 선택하세요..."}
+ : projects.length === 0
+ ? "사용 가능한 프로젝트가 없습니다"
+ : "프로젝트를 선택하세요..."}
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</PopoverTrigger>
@@ -210,7 +254,12 @@ export function CreateGtcDocumentDialog() {
className="h-9"
/>
<CommandList>
- <CommandEmpty>프로젝트를 찾을 수 없습니다.</CommandEmpty>
+ <CommandEmpty>
+ {projects.length === 0
+ ? "모든 프로젝트에 이미 GTC 문서가 있습니다."
+ : "프로젝트를 찾을 수 없습니다."
+ }
+ </CommandEmpty>
<CommandGroup>
{projects.map((project) => {
const label = `${project.name} (${project.code})`
@@ -241,6 +290,9 @@ export function CreateGtcDocumentDialog() {
</PopoverContent>
</Popover>
</FormControl>
+ <FormDescription>
+ {projects.length === 0 && "이미 GTC 문서가 있는 프로젝트는 표시되지 않습니다."}
+ </FormDescription>
<FormMessage />
</FormItem>
)
@@ -256,7 +308,7 @@ export function CreateGtcDocumentDialog() {
<FormLabel>GTC 제목 (선택사항)</FormLabel>
<FormControl>
<Input
- placeholder="GTC 제목를 입력하세요..."
+ placeholder="GTC 제목을 입력하세요..."
{...field}
/>
</FormControl>
@@ -295,7 +347,7 @@ export function CreateGtcDocumentDialog() {
onClick={() => setOpen(false)}
disabled={isCreatePending}
>
- Cancel
+ 취소
</Button>
<Button type="submit" disabled={isCreatePending}>
{isCreatePending && (
@@ -304,7 +356,7 @@ export function CreateGtcDocumentDialog() {
aria-hidden="true"
/>
)}
- Create
+ 생성
</Button>
</DialogFooter>
</form>
diff --git a/lib/gtc-contract/status/gtc-contract-table.tsx b/lib/gtc-contract/status/gtc-contract-table.tsx
index 0fb637b6..ce3a2c7a 100644
--- a/lib/gtc-contract/status/gtc-contract-table.tsx
+++ b/lib/gtc-contract/status/gtc-contract-table.tsx
@@ -27,6 +27,7 @@ import { UpdateGtcDocumentSheet } from "./update-gtc-document-sheet"
import { CreateGtcDocumentDialog } from "./create-gtc-document-dialog"
import { CreateNewRevisionDialog } from "./create-new-revision-dialog"
import { useRouter } from "next/navigation"
+import { CloneGtcDocumentDialog } from "./clone-gtc-document-dialog"
interface GtcDocumentsTableProps {
promises: Promise<
@@ -42,6 +43,8 @@ export function GtcDocumentsTable({ promises }: GtcDocumentsTableProps) {
const [{ data, pageCount }, projects, users] = React.use(promises)
const router = useRouter()
+ console.log(data)
+
const [rowAction, setRowAction] =
React.useState<DataTableRowAction<GtcDocumentWithRelations> | null>(null)
@@ -169,6 +172,16 @@ export function GtcDocumentsTable({ promises }: GtcDocumentsTableProps) {
originalDocument={rowAction?.row.original ?? null}
/>
+
+ {/* 복제 다이얼로그 */}
+ {rowAction?.type === "clone" && (
+ <CloneGtcDocumentDialog
+ sourceDocument={rowAction.row.original}
+ open={true}
+ onOpenChange={() => setRowAction(null)}
+ />
+ )}
+
{/* <CreateGtcDocumentDialog /> */}
</>
)
diff --git a/lib/gtc-contract/status/gtc-documents-table-columns.tsx b/lib/gtc-contract/status/gtc-documents-table-columns.tsx
index cd02a3e5..89415284 100644
--- a/lib/gtc-contract/status/gtc-documents-table-columns.tsx
+++ b/lib/gtc-contract/status/gtc-documents-table-columns.tsx
@@ -24,7 +24,7 @@ import { type GtcDocumentWithRelations } from "@/db/schema/gtc"
interface GetColumnsProps {
setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<GtcDocumentWithRelations> | null>>
- router: AppRouterInstance // ← 추가
+ router: AppRouterInstance
}
/** GTC Documents 테이블 컬럼 정의 (그룹 헤더 제거) */
@@ -75,12 +75,13 @@ export function getColumns({ setRowAction, router }: GetColumnsProps): ColumnDef
accessorKey: "project",
header: ({ column }) => <DataTableColumnHeaderSimple column={column} title="프로젝트" />,
cell: ({ row }) => {
- const project = row.original.project
- if (!project) return <span className="text-muted-foreground">-</span>
+ const projectName = row.original.projectName
+ const projectCode = row.original.projectCode
+ if (!projectName) return <span className="text-muted-foreground">-</span>
return (
<div className="flex flex-col min-w-0">
- <span className="font-medium truncate">{project.name}</span>
- <span className="text-xs text-muted-foreground">{project.code}</span>
+ <span className="font-medium truncate">{projectName}</span>
+ <span className="text-xs text-muted-foreground">{projectCode}</span>
</div>
)
},
@@ -195,6 +196,10 @@ export function getColumns({ setRowAction, router }: GetColumnsProps): ColumnDef
setRowAction({ row, type: "createRevision" })
}
+ const handleClone = () => {
+ setRowAction({ row, type: "clone" })
+ }
+
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
@@ -206,26 +211,30 @@ export function getColumns({ setRowAction, router }: GetColumnsProps): ColumnDef
<Ellipsis className="size-4" aria-hidden />
</Button>
</DropdownMenuTrigger>
- <DropdownMenuContent align="end" className="w-48">
+ <DropdownMenuContent align="end" className="w-44">
<DropdownMenuItem onSelect={handleViewDetails}>
- <Eye className="mr-2 h-4 w-4" />
- 상세
+ {/* <Eye className="mr-2 h-4 w-4" /> */}
+ 상세보기
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onSelect={() => setRowAction({ row, type: "update" })}>
- 수정
+ 수정하기
</DropdownMenuItem>
<DropdownMenuItem onSelect={handleCreateNewRevision}>
- 새 리비전 생성
+ 리비전 생성하기
+ </DropdownMenuItem>
+
+ <DropdownMenuItem onSelect={handleClone}>
+ 복제하기
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem onSelect={() => setRowAction({ row, type: "delete" })}>
- 삭제
- <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
+ 삭제하기
+ {/* <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut> */}
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
@@ -241,4 +250,4 @@ export function getColumns({ setRowAction, router }: GetColumnsProps): ColumnDef
...auditColumns,
actionsColumn,
]
-}
+} \ No newline at end of file