summaryrefslogtreecommitdiff
path: root/lib/docu-list-rule/document-class/table
diff options
context:
space:
mode:
author0-Zz-ang <s1998319@gmail.com>2025-08-04 14:59:15 +0900
committer0-Zz-ang <s1998319@gmail.com>2025-08-04 14:59:15 +0900
commit59b5715ebb3e1fd7bd4eb02ce50399715734f865 (patch)
tree39ccd16482c1b90b6583ead73384822157254d88 /lib/docu-list-rule/document-class/table
parentf0213de0d2fb5fcb931b3ddaddcbb6581cab5d28 (diff)
(박서영) docu-list-rule detail sheet 컴포넌트 추가 및 검색 필터 기능 오류 수정
Diffstat (limited to 'lib/docu-list-rule/document-class/table')
-rw-r--r--lib/docu-list-rule/document-class/table/delete-document-class-dialog.tsx88
-rw-r--r--lib/docu-list-rule/document-class/table/delete-document-class-option-dialog.tsx2
-rw-r--r--lib/docu-list-rule/document-class/table/document-class-edit-sheet.tsx2
-rw-r--r--lib/docu-list-rule/document-class/table/document-class-option-add-dialog.tsx66
-rw-r--r--lib/docu-list-rule/document-class/table/document-class-option-edit-sheet.tsx84
-rw-r--r--lib/docu-list-rule/document-class/table/document-class-options-detail-sheet.tsx152
-rw-r--r--lib/docu-list-rule/document-class/table/document-class-options-table-columns.tsx15
-rw-r--r--lib/docu-list-rule/document-class/table/document-class-options-table-toolbar.tsx22
-rw-r--r--lib/docu-list-rule/document-class/table/document-class-options-table.tsx176
-rw-r--r--lib/docu-list-rule/document-class/table/document-class-table-columns.tsx8
-rw-r--r--lib/docu-list-rule/document-class/table/document-class-table-toolbar.tsx2
-rw-r--r--lib/docu-list-rule/document-class/table/document-class-table.tsx53
12 files changed, 343 insertions, 327 deletions
diff --git a/lib/docu-list-rule/document-class/table/delete-document-class-dialog.tsx b/lib/docu-list-rule/document-class/table/delete-document-class-dialog.tsx
index 677fe8ef..e81e4df6 100644
--- a/lib/docu-list-rule/document-class/table/delete-document-class-dialog.tsx
+++ b/lib/docu-list-rule/document-class/table/delete-document-class-dialog.tsx
@@ -28,8 +28,8 @@ import {
DrawerTrigger,
} from "@/components/ui/drawer"
-import { deleteDocumentClassCodeGroup } from "@/lib/docu-list-rule/document-class/service"
-import { documentClasses } from "@/db/schema/documentClasses"
+import { deleteDocumentClassCodeGroup, getDocumentClassOptionsCount } from "@/lib/docu-list-rule/document-class/service"
+import { documentClasses } from "@/db/schema"
interface DeleteDocumentClassDialogProps
extends React.ComponentPropsWithoutRef<typeof Dialog> {
@@ -45,8 +45,32 @@ export function DeleteDocumentClassDialog({
...props
}: DeleteDocumentClassDialogProps) {
const [isDeletePending, startDeleteTransition] = React.useTransition()
+ const [optionsCounts, setOptionsCounts] = React.useState<Record<number, number>>({})
+ const [isLoadingOptions, setIsLoadingOptions] = React.useState(false)
const isDesktop = useMediaQuery("(min-width: 640px)")
+ // Document Class들의 옵션 개수 조회
+ React.useEffect(() => {
+ const fetchOptionsCounts = async () => {
+ setIsLoadingOptions(true)
+ const counts: Record<number, number> = {}
+
+ for (const docClass of documentClasses) {
+ const result = await getDocumentClassOptionsCount(docClass.id)
+ if (result.success) {
+ counts[docClass.id] = result.count
+ }
+ }
+
+ setOptionsCounts(counts)
+ setIsLoadingOptions(false)
+ }
+
+ if (documentClasses.length > 0) {
+ fetchOptionsCounts()
+ }
+ }, [documentClasses])
+
function onDelete() {
startDeleteTransition(async () => {
try {
@@ -76,7 +100,7 @@ export function DeleteDocumentClassDialog({
<DialogTrigger asChild>
<Button variant="outline" size="sm">
<Trash className="mr-2 size-4" aria-hidden="true" />
- 삭제 ({documentClasses.length})
+ delete ({documentClasses.length})
</Button>
</DialogTrigger>
) : null}
@@ -87,6 +111,26 @@ export function DeleteDocumentClassDialog({
이 작업은 되돌릴 수 없습니다. 선택된{" "}
<span className="font-medium">{documentClasses.length}</span>
개의 Document Class를 서버에서 영구적으로 삭제합니다.
+ {isLoadingOptions ? (
+ <div className="mt-2 text-sm text-muted-foreground">
+ 옵션 정보를 확인하는 중...
+ </div>
+ ) : (
+ documentClasses.some(docClass => optionsCounts[docClass.id] > 0) && (
+ <div className="mt-2 text-sm text-orange-600">
+ ⚠️ 다음 Document Class들은 옵션을 가지고 있어 함께 삭제됩니다:
+ <ul className="mt-1 ml-4 list-disc">
+ {documentClasses
+ .filter(docClass => optionsCounts[docClass.id] > 0)
+ .map(docClass => (
+ <li key={docClass.id}>
+ {docClass.code} ({optionsCounts[docClass.id]}개 옵션)
+ </li>
+ ))}
+ </ul>
+ </div>
+ )
+ )}
</DialogDescription>
</DialogHeader>
<DialogFooter className="gap-2 sm:space-x-0">
@@ -123,15 +167,35 @@ export function DeleteDocumentClassDialog({
</Button>
</DrawerTrigger>
) : null}
- <DrawerContent>
- <DrawerHeader>
- <DrawerTitle>정말로 삭제하시겠습니까?</DrawerTitle>
- <DrawerDescription>
- 이 작업은 되돌릴 수 없습니다. 선택된{" "}
- <span className="font-medium">{documentClasses.length}</span>
- 개의 Document Class를 서버에서 영구적으로 삭제합니다.
- </DrawerDescription>
- </DrawerHeader>
+ <DrawerContent>
+ <DrawerHeader>
+ <DrawerTitle>정말로 삭제하시겠습니까?</DrawerTitle>
+ <DrawerDescription>
+ 이 작업은 되돌릴 수 없습니다. 선택된{" "}
+ <span className="font-medium">{documentClasses.length}</span>
+ 개의 Document Class를 서버에서 영구적으로 삭제합니다.
+ {isLoadingOptions ? (
+ <div className="mt-2 text-sm text-muted-foreground">
+ 옵션 정보를 확인하는 중...
+ </div>
+ ) : (
+ documentClasses.some(docClass => optionsCounts[docClass.id] > 0) && (
+ <div className="mt-2 text-sm text-orange-600">
+ ⚠️ 다음 Document Class들은 옵션을 가지고 있어 함께 삭제됩니다:
+ <ul className="mt-1 ml-4 list-disc">
+ {documentClasses
+ .filter(docClass => optionsCounts[docClass.id] > 0)
+ .map(docClass => (
+ <li key={docClass.id}>
+ {docClass.code} ({optionsCounts[docClass.id]}개 옵션)
+ </li>
+ ))}
+ </ul>
+ </div>
+ )
+ )}
+ </DrawerDescription>
+ </DrawerHeader>
<DrawerFooter className="gap-2 sm:space-x-0">
<DrawerClose asChild>
<Button variant="outline">취소</Button>
diff --git a/lib/docu-list-rule/document-class/table/delete-document-class-option-dialog.tsx b/lib/docu-list-rule/document-class/table/delete-document-class-option-dialog.tsx
index f0fcbc34..34ce239f 100644
--- a/lib/docu-list-rule/document-class/table/delete-document-class-option-dialog.tsx
+++ b/lib/docu-list-rule/document-class/table/delete-document-class-option-dialog.tsx
@@ -28,7 +28,7 @@ import {
} from "@/components/ui/drawer"
import { deleteDocumentClassOption } from "@/lib/docu-list-rule/document-class/service"
-import { documentClassOptions } from "@/db/schema/documentClasses"
+import { documentClassOptions } from "@/db/schema"
interface DeleteDocumentClassOptionDialogProps
extends React.ComponentPropsWithoutRef<typeof Dialog> {
diff --git a/lib/docu-list-rule/document-class/table/document-class-edit-sheet.tsx b/lib/docu-list-rule/document-class/table/document-class-edit-sheet.tsx
index 97729caa..5ad23b22 100644
--- a/lib/docu-list-rule/document-class/table/document-class-edit-sheet.tsx
+++ b/lib/docu-list-rule/document-class/table/document-class-edit-sheet.tsx
@@ -28,7 +28,7 @@ import {
import { Input } from "@/components/ui/input"
import { updateDocumentClassCodeGroup } from "@/lib/docu-list-rule/document-class/service"
-import { documentClasses } from "@/db/schema/documentClasses"
+import { documentClasses } from "@/db/schema"
const updateDocumentClassSchema = z.object({
value: z.string().min(1, "Value는 필수입니다."),
diff --git a/lib/docu-list-rule/document-class/table/document-class-option-add-dialog.tsx b/lib/docu-list-rule/document-class/table/document-class-option-add-dialog.tsx
index 5bfcbd33..93681c09 100644
--- a/lib/docu-list-rule/document-class/table/document-class-option-add-dialog.tsx
+++ b/lib/docu-list-rule/document-class/table/document-class-option-add-dialog.tsx
@@ -28,105 +28,93 @@ import {
import { Input } from "@/components/ui/input"
import { createDocumentClassOptionItem } from "@/lib/docu-list-rule/document-class/service"
-import { documentClasses } from "@/db/schema/documentClasses"
-const createDocumentClassOptionSchema = z.object({
- optionValue: z.string().min(1, "옵션 값은 필수입니다."),
+const createOptionSchema = z.object({
+ optionCode: z.string().min(1, "코드는 필수입니다."),
})
-type CreateDocumentClassOptionSchema = z.infer<typeof createDocumentClassOptionSchema>
+type CreateOptionSchema = z.infer<typeof createOptionSchema>
interface DocumentClassOptionAddDialogProps {
- selectedDocumentClass: typeof documentClasses.$inferSelect | null
+ documentClassId: number
onSuccess?: () => void
}
-export function DocumentClassOptionAddDialog({
- selectedDocumentClass,
- onSuccess,
-}: DocumentClassOptionAddDialogProps) {
+export function DocumentClassOptionAddDialog({ documentClassId, onSuccess }: DocumentClassOptionAddDialogProps) {
const [open, setOpen] = React.useState(false)
const [isPending, startTransition] = React.useTransition()
- const form = useForm<CreateDocumentClassOptionSchema>({
- resolver: zodResolver(createDocumentClassOptionSchema),
+ const form = useForm<CreateOptionSchema>({
+ resolver: zodResolver(createOptionSchema),
defaultValues: {
- optionValue: "",
+ optionCode: "",
},
- mode: "onChange"
})
- async function onSubmit(input: CreateDocumentClassOptionSchema) {
- if (!selectedDocumentClass) {
- toast.error("Document Class가 선택되지 않았습니다.")
- return
- }
-
+ const handleSubmit = (data: CreateOptionSchema) => {
startTransition(async () => {
try {
const result = await createDocumentClassOptionItem({
- documentClassId: selectedDocumentClass.id,
- optionValue: input.optionValue,
+ documentClassId,
+ optionCode: data.optionCode,
})
-
+
if (result.success) {
- toast.success("Document Class 옵션이 생성되었습니다.")
- form.reset()
+ toast.success("옵션이 성공적으로 추가되었습니다.")
setOpen(false)
+ form.reset()
onSuccess?.()
} else {
- toast.error(result.error || "생성에 실패했습니다.")
+ toast.error(`옵션 추가 실패: ${result.error}`)
}
} catch (error) {
console.error("Create error:", error)
- toast.error("Document Class 옵션 생성 중 오류가 발생했습니다.")
+ toast.error("옵션 추가 중 오류가 발생했습니다.")
}
})
}
const handleCancel = () => {
- form.reset()
setOpen(false)
+ form.reset()
}
return (
- <Dialog open={open && !!selectedDocumentClass} onOpenChange={setOpen}>
+ <Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
- <Button variant="outline" size="sm" disabled={!selectedDocumentClass}>
+ <Button variant="outline" size="sm">
<Plus className="mr-2 h-4 w-4" />
옵션 추가
</Button>
</DialogTrigger>
- <DialogContent className="sm:max-w-[425px]">
+ <DialogContent>
<DialogHeader>
- <DialogTitle>Document Class 옵션 추가</DialogTitle>
+ <DialogTitle>옵션 추가</DialogTitle>
<DialogDescription>
- {selectedDocumentClass?.description || "Document Class"}에 새로운 옵션을 추가합니다.
- <span className="text-red-500 mt-1 block text-sm">* 표시된 항목은 필수 입력사항입니다.</span>
+ 새로운 Document Class 옵션을 추가합니다.
</DialogDescription>
</DialogHeader>
<Form {...form}>
- <form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
+ <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
<FormField
control={form.control}
- name="optionValue"
+ name="optionCode"
render={({ field }) => (
<FormItem>
- <FormLabel>옵션 값 *</FormLabel>
+ <FormLabel>코드</FormLabel>
<FormControl>
- <Input {...field} placeholder="옵션 값을 입력하세요" />
+ <Input {...field} placeholder="옵션 코드" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
-
<DialogFooter>
<Button type="button" variant="outline" onClick={handleCancel}>
취소
</Button>
<Button type="submit" disabled={isPending || !form.formState.isValid}>
- {isPending ? "추가 중..." : "추가"}
+ 추가
</Button>
</DialogFooter>
</form>
diff --git a/lib/docu-list-rule/document-class/table/document-class-option-edit-sheet.tsx b/lib/docu-list-rule/document-class/table/document-class-option-edit-sheet.tsx
index 6f6e7a87..bc2318c6 100644
--- a/lib/docu-list-rule/document-class/table/document-class-option-edit-sheet.tsx
+++ b/lib/docu-list-rule/document-class/table/document-class-option-edit-sheet.tsx
@@ -5,12 +5,10 @@ import { zodResolver } from "@hookform/resolvers/zod"
import { useForm } from "react-hook-form"
import { toast } from "sonner"
import * as z from "zod"
-import { Loader } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
Sheet,
- SheetClose,
SheetContent,
SheetDescription,
SheetFooter,
@@ -26,14 +24,15 @@ import {
FormMessage,
} from "@/components/ui/form"
import { Input } from "@/components/ui/input"
+
import { updateDocumentClassOption } from "@/lib/docu-list-rule/document-class/service"
-import { documentClassOptions } from "@/db/schema/documentClasses"
+import { documentClassOptions } from "@/db/schema"
-const updateDocumentClassOptionSchema = z.object({
- optionValue: z.string().min(1, "옵션 값은 필수입니다."),
+const updateOptionSchema = z.object({
+ optionCode: z.string().min(1, "코드는 필수입니다."),
})
-type UpdateDocumentClassOptionSchema = z.infer<typeof updateDocumentClassOptionSchema>
+type UpdateOptionSchema = z.infer<typeof updateOptionSchema>
interface DocumentClassOptionEditSheetProps {
open: boolean
@@ -48,91 +47,82 @@ export function DocumentClassOptionEditSheet({
data,
onSuccess,
}: DocumentClassOptionEditSheetProps) {
- const [isUpdatePending, startUpdateTransition] = React.useTransition()
+ const [isPending, startTransition] = React.useTransition()
- const form = useForm<UpdateDocumentClassOptionSchema>({
- resolver: zodResolver(updateDocumentClassOptionSchema),
+ const form = useForm<UpdateOptionSchema>({
+ resolver: zodResolver(updateOptionSchema),
defaultValues: {
- optionValue: data?.optionValue || "",
+ optionCode: "",
},
- mode: "onChange"
})
React.useEffect(() => {
if (data) {
form.reset({
- optionValue: data.optionValue || "",
+ optionCode: data.optionCode || "",
})
}
}, [data, form])
- async function onSubmit(input: UpdateDocumentClassOptionSchema) {
+ const handleSubmit = (formData: UpdateOptionSchema) => {
if (!data) return
- startUpdateTransition(async () => {
+ startTransition(async () => {
try {
const result = await updateDocumentClassOption({
id: data.id,
- optionValue: input.optionValue,
+ optionCode: formData.optionCode,
})
-
+
if (result.success) {
- toast.success("Document Class 옵션이 성공적으로 수정되었습니다.")
- onSuccess?.()
+ toast.success("옵션이 성공적으로 수정되었습니다.")
onOpenChange(false)
+ onSuccess?.()
} else {
- toast.error(result.error || "수정에 실패했습니다.")
+ toast.error(`옵션 수정 실패: ${result.error}`)
}
} catch (error) {
console.error("Update error:", error)
- toast.error("Document Class 옵션 수정 중 오류가 발생했습니다.")
+ toast.error("옵션 수정 중 오류가 발생했습니다.")
}
})
}
+ const handleCancel = () => {
+ onOpenChange(false)
+ form.reset()
+ }
+
return (
<Sheet open={open} onOpenChange={onOpenChange}>
- <SheetContent className="flex flex-col gap-6 sm:max-w-md">
- <SheetHeader className="text-left">
- <SheetTitle>Document Class 옵션 수정</SheetTitle>
+ <SheetContent>
+ <SheetHeader>
+ <SheetTitle>옵션 수정</SheetTitle>
<SheetDescription>
- Document Class 옵션 정보를 수정하고 변경사항을 저장하세요
+ Document Class 옵션을 수정합니다.
</SheetDescription>
</SheetHeader>
<Form {...form}>
- <form onSubmit={form.handleSubmit(onSubmit)} className="flex flex-col gap-4">
+ <form onSubmit={form.handleSubmit(handleSubmit)} className="space-y-4">
<FormField
control={form.control}
- name="optionValue"
+ name="optionCode"
render={({ field }) => (
<FormItem>
- <FormLabel>옵션 값</FormLabel>
+ <FormLabel>코드</FormLabel>
<FormControl>
- <Input placeholder="옵션 값을 입력하세요" {...field} />
+ <Input {...field} placeholder="옵션 코드" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
-
-
- <SheetFooter className="gap-2 pt-2 sm:space-x-0">
- <SheetClose asChild>
- <Button type="button" variant="outline">
- 취소
- </Button>
- </SheetClose>
- <Button
- type="submit"
- disabled={isUpdatePending || !form.formState.isValid}
- >
- {isUpdatePending && (
- <Loader
- className="mr-2 size-4 animate-spin"
- aria-hidden="true"
- />
- )}
- 저장
+ <SheetFooter>
+ <Button type="button" variant="outline" onClick={handleCancel}>
+ 취소
+ </Button>
+ <Button type="submit" disabled={isPending || !form.formState.isValid}>
+ 수정
</Button>
</SheetFooter>
</form>
diff --git a/lib/docu-list-rule/document-class/table/document-class-options-detail-sheet.tsx b/lib/docu-list-rule/document-class/table/document-class-options-detail-sheet.tsx
new file mode 100644
index 00000000..50e79d89
--- /dev/null
+++ b/lib/docu-list-rule/document-class/table/document-class-options-detail-sheet.tsx
@@ -0,0 +1,152 @@
+"use client"
+
+import * as React from "react"
+import { useReactTable, getCoreRowModel, getSortedRowModel, getFilteredRowModel, getPaginationRowModel } from "@tanstack/react-table"
+import { DataTableDetail } from "@/components/data-table/data-table-detail"
+import { DataTableAdvancedToolbarDetail } from "@/components/data-table/data-table-advanced-toolbar-detail"
+import type { DataTableAdvancedFilterField, DataTableRowAction } from "@/types/table"
+import {
+ Sheet,
+ SheetContent,
+} from "@/components/ui/sheet"
+import { getDocumentClassSubOptions } from "@/lib/docu-list-rule/document-class/service"
+import { getColumns } from "@/lib/docu-list-rule/document-class/table/document-class-options-table-columns"
+import { DocumentClassOptionEditSheet } from "@/lib/docu-list-rule/document-class/table/document-class-option-edit-sheet"
+import { DeleteDocumentClassOptionDialog } from "@/lib/docu-list-rule/document-class/table/delete-document-class-option-dialog"
+import { DocumentClassOptionsTableToolbarActions } from "@/lib/docu-list-rule/document-class/table/document-class-options-table-toolbar"
+import { documentClasses, documentClassOptions } from "@/db/schema/docu-list-rule"
+
+type DocumentClassOption = typeof documentClassOptions.$inferSelect
+
+interface DocumentClassOptionsDetailSheetProps {
+ open: boolean
+ onOpenChange: (open: boolean) => void
+ documentClass: typeof documentClasses.$inferSelect | null
+ onSuccess?: () => void
+ promises?: Promise<[{ data: DocumentClassOption[]; pageCount: number }]>
+}
+
+export function DocumentClassOptionsDetailSheet({
+ open,
+ onOpenChange,
+ documentClass,
+ promises,
+}: DocumentClassOptionsDetailSheetProps) {
+ const [rowAction, setRowAction] = React.useState<DataTableRowAction<DocumentClassOption> | null>(null)
+ const [rawData, setRawData] = React.useState<{ data: DocumentClassOption[]; pageCount: number }>({ data: [], pageCount: 0 })
+
+ React.useEffect(() => {
+ if (promises) {
+ promises.then(([result]) => {
+ setRawData(result)
+ })
+ } else if (open && documentClass) {
+ // fallback: 클라이언트에서 직접 fetch (CSR)
+ (async () => {
+ try {
+ const result = await getDocumentClassSubOptions(documentClass.id)
+ if (result.success && result.data) {
+ setRawData({
+ data: result.data,
+ pageCount: 1
+ })
+ }
+ } catch (error) {
+ console.error("Error refreshing data:", error)
+ }
+ })()
+ }
+ }, [promises, open, documentClass])
+
+ const refreshData = React.useCallback(async () => {
+ if (!documentClass) return
+
+ try {
+ const result = await getDocumentClassSubOptions(documentClass.id, {
+ page: 1,
+ perPage: 10,
+ })
+ if (result.success && result.data) {
+ setRawData({
+ data: result.data,
+ pageCount: 1
+ })
+ }
+ } catch (error) {
+ console.error("Error refreshing data:", error)
+ }
+ }, [documentClass])
+
+ const columns = React.useMemo(() => getColumns({ setRowAction }), [setRowAction])
+
+ // 고급 필터 필드 설정
+ const advancedFilterFields: DataTableAdvancedFilterField<DocumentClassOption>[] = [
+ { id: "optionCode", label: "코드", type: "text" },
+ { id: "description", label: "설명", type: "text" },
+ { id: "createdAt", label: "생성일", type: "date" },
+ ]
+
+ const table = useReactTable({
+ data: rawData.data,
+ columns,
+ getCoreRowModel: getCoreRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getFilteredRowModel: getFilteredRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ initialState: {
+ sorting: [{ id: "optionCode", desc: false }],
+ pagination: {
+ pageSize: 10,
+ },
+ },
+ getRowId: (originalRow) => String(originalRow.id),
+ })
+
+ if (!documentClass) return null
+
+ return (
+ <Sheet open={open} onOpenChange={onOpenChange}>
+ <SheetContent className="flex flex-col gap-6 sm:max-w-4xl">
+ <div className="flex items-center justify-between">
+ <div>
+ <h3 className="text-lg font-medium">{documentClass.value} 옵션 관리</h3>
+ <p className="text-sm text-muted-foreground">
+ {documentClass.value}의 Document Class 옵션들을 관리합니다.
+ </p>
+ </div>
+ </div>
+
+ <DocumentClassOptionsTableToolbarActions
+ table={table}
+ documentClassId={documentClass.id}
+ onSuccess={refreshData}
+ />
+
+ <DataTableDetail table={table}>
+ <DataTableAdvancedToolbarDetail
+ table={table}
+ filterFields={advancedFilterFields}
+ />
+ </DataTableDetail>
+
+ <DeleteDocumentClassOptionDialog
+ open={rowAction?.type === "delete"}
+ onOpenChange={() => setRowAction(null)}
+ options={rowAction?.row.original ? [rowAction?.row.original] : []}
+ showTrigger={false}
+ onSuccess={() => {
+ rowAction?.row.toggleSelected(false)
+ refreshData()
+ }}
+ />
+
+ <DocumentClassOptionEditSheet
+ open={rowAction?.type === "update"}
+ onOpenChange={() => setRowAction(null)}
+ data={rowAction?.row.original ?? null}
+ onSuccess={refreshData}
+ />
+ </SheetContent>
+ </Sheet>
+ )
+} \ No newline at end of file
diff --git a/lib/docu-list-rule/document-class/table/document-class-options-table-columns.tsx b/lib/docu-list-rule/document-class/table/document-class-options-table-columns.tsx
index c04a7b37..c3bf440d 100644
--- a/lib/docu-list-rule/document-class/table/document-class-options-table-columns.tsx
+++ b/lib/docu-list-rule/document-class/table/document-class-options-table-columns.tsx
@@ -18,7 +18,7 @@ import {
} from "@/components/ui/dropdown-menu"
import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
-import { documentClassOptions } from "@/db/schema/documentClasses"
+import { documentClassOptions } from "@/db/schema/docu-list-rule"
interface GetColumnsProps {
setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<typeof documentClassOptions.$inferSelect> | null>>
@@ -114,19 +114,18 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<typeof
minSize: 80
},
{
- accessorKey: "optionValue",
+ accessorKey: "description",
enableResizing: true,
header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title="옵션 값" />
+ <DataTableColumnHeaderSimple column={column} title="설명" />
),
meta: {
- excelHeader: "옵션 값",
+ excelHeader: "설명",
type: "text",
},
- cell: ({ row }) => row.getValue("optionValue") ?? "",
- minSize: 80
+ cell: ({ row }) => row.getValue("description") ?? "",
+ minSize: 120
},
-
{
accessorKey: "createdAt",
enableResizing: true,
@@ -141,7 +140,7 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<typeof
const dateVal = row.getValue("createdAt") as Date
return formatDateTime(dateVal, "KR")
},
- minSize: 80
+ minSize: 100
}
]
diff --git a/lib/docu-list-rule/document-class/table/document-class-options-table-toolbar.tsx b/lib/docu-list-rule/document-class/table/document-class-options-table-toolbar.tsx
index 5044d90d..0cd44a4f 100644
--- a/lib/docu-list-rule/document-class/table/document-class-options-table-toolbar.tsx
+++ b/lib/docu-list-rule/document-class/table/document-class-options-table-toolbar.tsx
@@ -3,23 +3,23 @@
import * as React from "react"
import { type Table } from "@tanstack/react-table"
-import { DocumentClassOptionAddDialog } from "./document-class-option-add-dialog"
-import { DeleteDocumentClassOptionDialog } from "./delete-document-class-option-dialog"
-import { documentClasses, documentClassOptions } from "@/db/schema/documentClasses"
+import { DocumentClassOptionAddDialog } from "@/lib/docu-list-rule/document-class/table/document-class-option-add-dialog"
+import { DeleteDocumentClassOptionDialog } from "@/lib/docu-list-rule/document-class/table/delete-document-class-option-dialog"
+import { documentClassOptions } from "@/db/schema/docu-list-rule"
-interface DocumentClassOptionsTableToolbarActionsProps<TData> {
- table: Table<TData>
- selectedDocumentClass: typeof documentClasses.$inferSelect | null
+interface DocumentClassOptionsTableToolbarActionsProps {
+ table: Table<typeof documentClassOptions.$inferSelect>
+ documentClassId: number
onSuccess?: () => void
}
-export function DocumentClassOptionsTableToolbarActions<TData>({
+export function DocumentClassOptionsTableToolbarActions({
table,
- selectedDocumentClass,
+ documentClassId,
onSuccess,
-}: DocumentClassOptionsTableToolbarActionsProps<TData>) {
+}: DocumentClassOptionsTableToolbarActionsProps) {
const selectedRows = table.getFilteredSelectedRowModel().rows
- const selectedOptions = selectedRows.map((row) => row.original as typeof documentClassOptions.$inferSelect)
+ const selectedOptions = selectedRows.map((row) => row.original)
return (
<div className="flex items-center gap-2">
@@ -35,7 +35,7 @@ export function DocumentClassOptionsTableToolbarActions<TData>({
) : null}
<DocumentClassOptionAddDialog
- selectedDocumentClass={selectedDocumentClass}
+ documentClassId={documentClassId}
onSuccess={onSuccess}
/>
</div>
diff --git a/lib/docu-list-rule/document-class/table/document-class-options-table.tsx b/lib/docu-list-rule/document-class/table/document-class-options-table.tsx
deleted file mode 100644
index 644e3599..00000000
--- a/lib/docu-list-rule/document-class/table/document-class-options-table.tsx
+++ /dev/null
@@ -1,176 +0,0 @@
-"use client"
-
-import * as React from "react"
-import { useDataTable } from "@/hooks/use-data-table"
-import { DataTable } from "@/components/data-table/data-table"
-import { DataTableAdvancedToolbar } from "@/components/data-table/data-table-advanced-toolbar"
-import type { DataTableAdvancedFilterField, DataTableRowAction } from "@/types/table"
-
-import { getDocumentClassSubOptions } from "@/lib/docu-list-rule/document-class/service"
-import { getColumns } from "./document-class-options-table-columns"
-import { DocumentClassOptionEditSheet } from "./document-class-option-edit-sheet"
-import { DeleteDocumentClassOptionDialog } from "./delete-document-class-option-dialog"
-import { DocumentClassOptionsTableToolbarActions } from "./document-class-options-table-toolbar"
-import { documentClasses, documentClassOptions } from "@/db/schema/docu-list-rule"
-
-type DocumentClass = typeof documentClasses.$inferSelect
-
-interface DocumentClassOptionsTableProps {
- selectedDocumentClass: DocumentClass | null
- documentClasses: DocumentClass[]
- onSelectDocumentClass: (documentClass: DocumentClass) => void
-}
-
-export function DocumentClassOptionsTable({
- selectedDocumentClass,
- documentClasses,
- onSelectDocumentClass
-}: DocumentClassOptionsTableProps) {
- const [rowAction, setRowAction] = React.useState<DataTableRowAction<typeof documentClassOptions.$inferSelect> | null>(null)
-
- // 선택된 Document Class의 옵션 데이터 로드
- const [options, setOptions] = React.useState<typeof documentClassOptions.$inferSelect[]>([])
-
- // DB 등록 순서대로 정렬된 Document Classes
- const sortedDocumentClasses = React.useMemo(() => {
- return [...documentClasses].sort((a, b) => a.id - b.id)
- }, [documentClasses])
-
- const handleSuccess = React.useCallback(async () => {
- // 옵션 테이블 새로고침
- if (selectedDocumentClass) {
- try {
- const result = await getDocumentClassSubOptions(selectedDocumentClass.id)
- if (result.success && result.data) {
- setOptions(result.data)
- }
- } catch (error) {
- console.error("Error refreshing options:", error)
- }
- }
- }, [selectedDocumentClass])
-
- const columns = React.useMemo(() => getColumns({ setRowAction }), [setRowAction])
-
- // 고급 필터 필드 설정
- const advancedFilterFields: DataTableAdvancedFilterField<typeof documentClassOptions.$inferSelect>[] = [
- { id: "optionCode", label: "코드", type: "text" },
- { id: "optionValue", label: "옵션 값", type: "text" },
- { id: "createdAt", label: "생성일", type: "date" },
- ]
-
- const { table } = useDataTable({
- data: options,
- columns,
- pageCount: 1,
- enablePinning: true,
- enableAdvancedFilter: true,
- manualSorting: false,
- initialState: {
- sorting: [{ id: "id", desc: false }],
- columnPinning: { right: ["actions"] },
- },
- getRowId: (originalRow) => String(originalRow.id),
- shallow: false,
- clearOnDefault: true,
- })
-
- React.useEffect(() => {
- const loadOptions = async () => {
- if (!selectedDocumentClass) {
- setOptions([])
- return
- }
-
- try {
- const result = await getDocumentClassSubOptions(selectedDocumentClass.id)
- if (result.success && result.data) {
- setOptions(result.data)
- }
- } catch (error) {
- console.error("Error loading options:", error)
- setOptions([])
- }
- }
-
- loadOptions()
- }, [selectedDocumentClass])
-
- if (!selectedDocumentClass) {
- return (
- <div className="space-y-4">
- <div className="flex gap-2">
- {sortedDocumentClasses.map((documentClass) => (
- <button
- key={documentClass.id}
- onClick={() => onSelectDocumentClass(documentClass)}
- className={`px-4 py-2 text-sm font-medium rounded-md transition-colors ${
- selectedDocumentClass?.id === documentClass.id
- ? "bg-primary text-primary-foreground"
- : "bg-muted text-muted-foreground hover:bg-muted/80"
- }`}
- >
- {documentClass.value}
- </button>
- ))}
- </div>
- <div className="text-center text-muted-foreground py-4">
- Document Class를 선택하면 옵션을 관리할 수 있습니다.
- </div>
- </div>
- )
- }
-
- return (
- <>
- <div className="space-y-2">
- <div className="flex gap-2">
- {sortedDocumentClasses.map((documentClass) => (
- <button
- key={documentClass.id}
- onClick={() => onSelectDocumentClass(documentClass)}
- className={`px-4 py-2 text-sm font-medium rounded-md transition-colors ${
- selectedDocumentClass?.id === documentClass.id
- ? "bg-primary text-primary-foreground"
- : "bg-muted text-muted-foreground hover:bg-muted/80"
- }`}
- >
- {documentClass.value}
- </button>
- ))}
- </div>
-
- <DataTable table={table}>
- <DataTableAdvancedToolbar
- table={table}
- filterFields={advancedFilterFields}
- >
- <DocumentClassOptionsTableToolbarActions
- table={table}
- selectedDocumentClass={selectedDocumentClass}
- onSuccess={handleSuccess}
- />
- </DataTableAdvancedToolbar>
- </DataTable>
- </div>
-
- <DeleteDocumentClassOptionDialog
- open={rowAction?.type === "delete"}
- onOpenChange={() => setRowAction(null)}
- options={rowAction?.row.original ? [rowAction?.row.original] : []}
- showTrigger={false}
- onSuccess={() => {
- rowAction?.row.toggleSelected(false)
- handleSuccess()
- }}
- />
-
- <DocumentClassOptionEditSheet
- open={rowAction?.type === "update"}
- onOpenChange={() => setRowAction(null)}
- data={rowAction?.row.original ?? null}
- onSuccess={handleSuccess}
- />
- </>
- )
-} \ No newline at end of file
diff --git a/lib/docu-list-rule/document-class/table/document-class-table-columns.tsx b/lib/docu-list-rule/document-class/table/document-class-table-columns.tsx
index 6684d13a..8c391def 100644
--- a/lib/docu-list-rule/document-class/table/document-class-table-columns.tsx
+++ b/lib/docu-list-rule/document-class/table/document-class-table-columns.tsx
@@ -22,12 +22,13 @@ import { documentClasses } from "@/db/schema/docu-list-rule"
interface GetColumnsProps {
setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<typeof documentClasses.$inferSelect> | null>>
+ onDetail?: (documentClass: typeof documentClasses.$inferSelect) => void
}
/**
* tanstack table 컬럼 정의
*/
-export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<typeof documentClasses.$inferSelect>[] {
+export function getColumns({ setRowAction, onDetail }: GetColumnsProps): ColumnDef<typeof documentClasses.$inferSelect>[] {
// ----------------------------------------------------------------
// 1) select 컬럼 (체크박스)
// ----------------------------------------------------------------
@@ -77,6 +78,11 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<typeof
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-40">
<DropdownMenuItem
+ onSelect={() => onDetail?.(row.original)}
+ >
+ Detail
+ </DropdownMenuItem>
+ <DropdownMenuItem
onSelect={() => setRowAction({ row, type: "update" })}
>
Edit
diff --git a/lib/docu-list-rule/document-class/table/document-class-table-toolbar.tsx b/lib/docu-list-rule/document-class/table/document-class-table-toolbar.tsx
index 7bc28a06..9b43f43d 100644
--- a/lib/docu-list-rule/document-class/table/document-class-table-toolbar.tsx
+++ b/lib/docu-list-rule/document-class/table/document-class-table-toolbar.tsx
@@ -5,7 +5,7 @@ import { type Table } from "@tanstack/react-table"
import { DeleteDocumentClassDialog } from "./delete-document-class-dialog"
import { DocumentClassAddDialog } from "./document-class-add-dialog"
-import { documentClasses } from "@/db/schema/documentClasses"
+import { documentClasses } from "@/db/schema"
interface DocumentClassTableToolbarActionsProps {
table: Table<typeof documentClasses.$inferSelect>
diff --git a/lib/docu-list-rule/document-class/table/document-class-table.tsx b/lib/docu-list-rule/document-class/table/document-class-table.tsx
index e3daac8a..c66a1395 100644
--- a/lib/docu-list-rule/document-class/table/document-class-table.tsx
+++ b/lib/docu-list-rule/document-class/table/document-class-table.tsx
@@ -6,12 +6,11 @@ import { useDataTable } from "@/hooks/use-data-table"
import { DataTable } from "@/components/data-table/data-table"
import { DataTableAdvancedToolbar } from "@/components/data-table/data-table-advanced-toolbar"
import type { DataTableAdvancedFilterField, DataTableRowAction } from "@/types/table"
-import { getDocumentClassCodeGroups } from "@/lib/docu-list-rule/document-class/service"
-import { getColumns } from "./document-class-table-columns"
-import { DocumentClassEditSheet } from "./document-class-edit-sheet"
-import { DocumentClassOptionsTable } from "./document-class-options-table"
-import { DocumentClassTableToolbarActions } from "./document-class-table-toolbar"
-import { DeleteDocumentClassDialog } from "./delete-document-class-dialog"
+import { getColumns } from "@/lib/docu-list-rule/document-class/table/document-class-table-columns"
+import { DocumentClassEditSheet } from "@/lib/docu-list-rule/document-class/table/document-class-edit-sheet"
+import { DocumentClassOptionsDetailSheet } from "@/lib/docu-list-rule/document-class/table/document-class-options-detail-sheet"
+import { DocumentClassTableToolbarActions } from "@/lib/docu-list-rule/document-class/table/document-class-table-toolbar"
+import { DeleteDocumentClassDialog } from "@/lib/docu-list-rule/document-class/table/delete-document-class-dialog"
import { documentClasses } from "@/db/schema/docu-list-rule"
interface DocumentClassTableProps {
@@ -22,6 +21,7 @@ export function DocumentClassTable({ promises }: DocumentClassTableProps) {
const router = useRouter()
const rawData = React.use(promises!)
const [rowAction, setRowAction] = React.useState<DataTableRowAction<typeof documentClasses.$inferSelect> | null>(null)
+ const [isDetailSheetOpen, setIsDetailSheetOpen] = React.useState(false)
const [selectedDocumentClass, setSelectedDocumentClass] = React.useState<typeof documentClasses.$inferSelect | null>(null)
const refreshData = React.useCallback(() => {
@@ -29,7 +29,13 @@ export function DocumentClassTable({ promises }: DocumentClassTableProps) {
router.refresh()
}, [router])
- const columns = React.useMemo(() => getColumns({ setRowAction }), [setRowAction])
+ // Detail 버튼 클릭 핸들러
+ const handleDetail = React.useCallback((documentClass: typeof documentClasses.$inferSelect) => {
+ setSelectedDocumentClass(documentClass)
+ setIsDetailSheetOpen(true)
+ }, [])
+
+ const columns = React.useMemo(() => getColumns({ setRowAction, onDetail: handleDetail }), [setRowAction, handleDetail])
// 고급 필터 필드 설정
const advancedFilterFields: DataTableAdvancedFilterField<typeof documentClasses.$inferSelect>[] = [
@@ -40,14 +46,12 @@ export function DocumentClassTable({ promises }: DocumentClassTableProps) {
]
const { table } = useDataTable({
- data: rawData[0].data as any,
+ data: rawData[0].data as typeof documentClasses.$inferSelect[],
columns,
pageCount: rawData[0].pageCount,
enablePinning: true,
enableAdvancedFilter: true,
- manualSorting: false,
initialState: {
- sorting: [{ id: "createdAt", desc: true }],
columnPinning: { right: ["actions"] },
},
getRowId: (originalRow) => String(originalRow.id),
@@ -66,26 +70,15 @@ export function DocumentClassTable({ promises }: DocumentClassTableProps) {
</DataTableAdvancedToolbar>
</DataTable>
- {/* 구분선 */}
- <div className="border-t border-border my-6"></div>
-
- {/* Document Class 옵션 관리 제목 */}
- <div className="flex items-center justify-between space-y-2">
- <div>
- <div className="flex items-center gap-2">
- <h2 className="text-2xl font-bold tracking-tight">Document Class 옵션 관리</h2>
- </div>
- <p className="text-muted-foreground">
- Document Class 옵션들을 관리합니다.
- </p>
- </div>
- </div>
-
- {/* Document Class 옵션 테이블 */}
- <DocumentClassOptionsTable
- selectedDocumentClass={selectedDocumentClass}
- documentClasses={rawData[0].data || []}
- onSelectDocumentClass={setSelectedDocumentClass}
+ {/* Detail 시트 */}
+ <DocumentClassOptionsDetailSheet
+ open={isDetailSheetOpen}
+ onOpenChange={setIsDetailSheetOpen}
+ documentClass={selectedDocumentClass}
+ onSuccess={() => {
+ setIsDetailSheetOpen(false)
+ setSelectedDocumentClass(null)
+ }}
/>
<DeleteDocumentClassDialog