summaryrefslogtreecommitdiff
path: root/lib/docu-list-rule/document-class
diff options
context:
space:
mode:
Diffstat (limited to 'lib/docu-list-rule/document-class')
-rw-r--r--lib/docu-list-rule/document-class/service.ts167
-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
-rw-r--r--lib/docu-list-rule/document-class/validation.ts2
14 files changed, 474 insertions, 365 deletions
diff --git a/lib/docu-list-rule/document-class/service.ts b/lib/docu-list-rule/document-class/service.ts
index 99d85ea5..a1bb14a7 100644
--- a/lib/docu-list-rule/document-class/service.ts
+++ b/lib/docu-list-rule/document-class/service.ts
@@ -3,7 +3,7 @@
import { revalidatePath } from "next/cache"
import db from "@/db/db"
import { documentClasses, documentClassOptions, codeGroups } from "@/db/schema/docu-list-rule"
-import { eq, desc, asc, sql } from "drizzle-orm"
+import { eq, desc, asc, sql, and } from "drizzle-orm"
// Document Class 목록 조회 (A Class, B Class 등)
export async function getDocumentClassCodeGroups(input: {
@@ -69,7 +69,7 @@ export async function getDocumentClassCodeGroups(input: {
}
// 정렬 (안전한 필드 체크 적용)
- let orderBy = sql`${documentClasses.createdAt} DESC`
+ let orderBy = sql`${documentClasses.code} ASC`
if (sort && sort.length > 0) {
const sortField = sort[0]
// 안전성 체크: 필드가 실제 테이블에 존재하는지 확인
@@ -274,8 +274,34 @@ export async function updateDocumentClassCodeGroup(input: {
}
// Document Class 삭제
+// Document Class의 옵션 개수 조회
+export async function getDocumentClassOptionsCount(documentClassId: number) {
+ try {
+ const result = await db
+ .select({ count: sql<number>`count(*)` })
+ .from(documentClassOptions)
+ .where(eq(documentClassOptions.documentClassId, documentClassId))
+
+ return {
+ success: true,
+ count: result[0]?.count || 0
+ }
+ } catch (error) {
+ console.error("Error getting document class options count:", error)
+ return {
+ success: false,
+ count: 0
+ }
+ }
+}
+
export async function deleteDocumentClassCodeGroup(id: number) {
try {
+ // 먼저 해당 Document Class의 옵션들을 삭제
+ await db
+ .delete(documentClassOptions)
+ .where(eq(documentClassOptions.documentClassId, id))
+
// 삭제할 Document Class의 codeGroupId 확인
const documentClassToDelete = await db
.select({ codeGroupId: documentClasses.codeGroupId })
@@ -310,7 +336,7 @@ export async function deleteDocumentClassCodeGroup(id: number) {
return {
success: true,
data: deletedDocumentClass,
- message: "Document Class deleted successfully"
+ message: "Document Class and its options deleted successfully"
}
} catch (error) {
console.error("Error deleting document class:", error)
@@ -322,22 +348,57 @@ export async function deleteDocumentClassCodeGroup(id: number) {
}
// Document Class 옵션 목록 조회
-export async function getDocumentClassSubOptions(documentClassId: number) {
+export async function getDocumentClassSubOptions(documentClassId: number, input?: {
+ page?: number
+ perPage?: number
+ search?: string
+ sort?: Array<{ id: string; desc: boolean }>
+ filters?: Array<{ id: string; value: string }>
+ joinOperator?: "and" | "or"
+}) {
try {
+ const { page = 1, perPage = 1000, sort, search } = input || {}
+ const offset = (page - 1) * perPage
+
+ // 기본 조건: documentClassId
+ let whereConditions = eq(documentClassOptions.documentClassId, documentClassId)
+
+ // 검색 조건
+ if (search) {
+ const searchTerm = `%${search}%`
+ whereConditions = sql`${whereConditions} AND (
+ ${documentClassOptions.optionCode} ILIKE ${searchTerm} OR
+ ${documentClassOptions.description} ILIKE ${searchTerm}
+ )`
+ }
+
+ // 정렬 (안전한 필드 체크 적용)
+ let orderBy = sql`${documentClassOptions.optionCode} ASC`
+ if (sort && sort.length > 0) {
+ const sortField = sort[0]
+ // 안전성 체크: 필드가 실제 테이블에 존재하는지 확인
+ if (sortField && sortField.id && typeof sortField.id === "string" && sortField.id in documentClassOptions) {
+ const direction = sortField.desc ? sql`DESC` : sql`ASC`
+ const col = documentClassOptions[sortField.id as keyof typeof documentClassOptions]
+ orderBy = sql`${col} ${direction}`
+ }
+ }
+
const data = await db
.select({
id: documentClassOptions.id,
documentClassId: documentClassOptions.documentClassId,
- optionValue: documentClassOptions.optionValue,
+ description: documentClassOptions.description,
optionCode: documentClassOptions.optionCode,
- sortOrder: documentClassOptions.sortOrder,
isActive: documentClassOptions.isActive,
createdAt: documentClassOptions.createdAt,
updatedAt: documentClassOptions.updatedAt,
})
.from(documentClassOptions)
- .where(eq(documentClassOptions.documentClassId, documentClassId))
- .orderBy(asc(documentClassOptions.sortOrder), asc(documentClassOptions.optionValue))
+ .where(whereConditions)
+ .orderBy(orderBy)
+ .limit(perPage)
+ .offset(offset)
return {
success: true,
@@ -356,7 +417,7 @@ export async function getDocumentClassSubOptions(documentClassId: number) {
// Document Class 옵션 생성
export async function createDocumentClassOptionItem(input: {
documentClassId: number
- optionValue: string
+ optionCode: string
}) {
try {
// Document Class 정보 조회하여 Value 가져오기
@@ -373,30 +434,27 @@ export async function createDocumentClassOptionItem(input: {
}
}
- // Value에서 클래스명 추출 (예: "A Class" → "A")
- const classValue = documentClass[0].value
- const className = classValue.split(' ')[0] // "A Class"에서 "A" 추출
- // 자동으로 optionCode 생성 (예: "A_OP_01", "A_OP_02" 등)
- const existingOptions = await db
- .select({ optionCode: documentClassOptions.optionCode })
+
+ // 사용자가 입력한 코드를 그대로 사용
+ const userOptionCode = input.optionCode.toUpperCase().trim()
+
+ // 같은 Document Class 내에서 코드 중복 체크
+ const existingOption = await db
+ .select({ id: documentClassOptions.id })
.from(documentClassOptions)
- .where(eq(documentClassOptions.documentClassId, input.documentClassId))
- .orderBy(desc(documentClassOptions.optionCode))
-
- let newOptionCode = `${className}_OP_01`
- if (existingOptions.length > 0) {
- const lastOption = existingOptions[0]
- if (lastOption.optionCode) {
- // "A_OP_01" 형태에서 숫자 추출
- const match = lastOption.optionCode.match(/_OP_(\d+)$/)
- if (match) {
- const lastNumber = parseInt(match[1]) || 0
- newOptionCode = `${className}_OP_${String(lastNumber + 1).padStart(2, '0')}`
- } else {
- // 기존 형식이 다른 경우 01부터 시작
- newOptionCode = `${className}_OP_01`
- }
+ .where(
+ and(
+ eq(documentClassOptions.documentClassId, input.documentClassId),
+ eq(documentClassOptions.optionCode, userOptionCode)
+ )
+ )
+ .limit(1)
+
+ if (existingOption.length > 0) {
+ return {
+ success: false,
+ error: "이미 존재하는 코드입니다."
}
}
@@ -404,9 +462,8 @@ export async function createDocumentClassOptionItem(input: {
.insert(documentClassOptions)
.values({
documentClassId: input.documentClassId,
- optionValue: input.optionValue,
- optionCode: newOptionCode,
- sortOrder: 0,
+ description: userOptionCode, // 코드값을 description에도 자동 설정
+ optionCode: userOptionCode,
isActive: true,
})
.returning({ id: documentClassOptions.id })
@@ -430,13 +487,49 @@ export async function createDocumentClassOptionItem(input: {
// Document Class 옵션 수정
export async function updateDocumentClassOption(input: {
id: number
- optionValue: string
+ optionCode: string
}) {
try {
+ const userOptionCode = input.optionCode.toUpperCase().trim()
+
+ // 기존 옵션 조회하여 documentClassId 가져오기
+ const currentOption = await db
+ .select({ documentClassId: documentClassOptions.documentClassId })
+ .from(documentClassOptions)
+ .where(eq(documentClassOptions.id, input.id))
+ .limit(1)
+
+ if (!currentOption.length) {
+ return {
+ success: false,
+ error: "옵션을 찾을 수 없습니다."
+ }
+ }
+
+ // 같은 Document Class 내에서 코드 중복 체크 (자신 제외)
+ const existingOption = await db
+ .select({ id: documentClassOptions.id })
+ .from(documentClassOptions)
+ .where(
+ and(
+ eq(documentClassOptions.documentClassId, currentOption[0].documentClassId),
+ eq(documentClassOptions.optionCode, userOptionCode)
+ )
+ )
+ .limit(1)
+
+ if (existingOption.length > 0 && existingOption[0].id !== input.id) {
+ return {
+ success: false,
+ error: "이미 존재하는 코드입니다."
+ }
+ }
+
const [updatedOption] = await db
.update(documentClassOptions)
.set({
- optionValue: input.optionValue,
+ description: userOptionCode, // 코드값을 description에도 자동 설정
+ optionCode: userOptionCode,
updatedAt: new Date(),
})
.where(eq(documentClassOptions.id, input.id))
@@ -480,4 +573,4 @@ export async function deleteDocumentClassOption(id: number) {
error: "Failed to delete document class option"
}
}
-} \ No newline at end of file
+} \ No newline at end of file
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
diff --git a/lib/docu-list-rule/document-class/validation.ts b/lib/docu-list-rule/document-class/validation.ts
index 0600e8fb..78f87484 100644
--- a/lib/docu-list-rule/document-class/validation.ts
+++ b/lib/docu-list-rule/document-class/validation.ts
@@ -1,5 +1,5 @@
import { createSearchParamsCache } from "nuqs/server";
-import { parseAsInteger, parseAsString, parseAsArrayOf, parseAsStringEnum } from "nuqs/server";
+import { parseAsInteger, parseAsString, parseAsStringEnum } from "nuqs/server";
import { getSortingStateParser, getFiltersStateParser } from "@/lib/parsers";
export const searchParamsDocumentClassCache = createSearchParamsCache({