summaryrefslogtreecommitdiff
path: root/lib/incoterms
diff options
context:
space:
mode:
Diffstat (limited to 'lib/incoterms')
-rw-r--r--lib/incoterms/table/delete-incoterms-dialog.tsx154
-rw-r--r--lib/incoterms/table/incoterms-add-dialog.tsx12
-rw-r--r--lib/incoterms/table/incoterms-edit-sheet.tsx60
-rw-r--r--lib/incoterms/table/incoterms-table-columns.tsx206
-rw-r--r--lib/incoterms/table/incoterms-table-toolbar.tsx41
-rw-r--r--lib/incoterms/table/incoterms-table.tsx102
6 files changed, 444 insertions, 131 deletions
diff --git a/lib/incoterms/table/delete-incoterms-dialog.tsx b/lib/incoterms/table/delete-incoterms-dialog.tsx
new file mode 100644
index 00000000..8b91033c
--- /dev/null
+++ b/lib/incoterms/table/delete-incoterms-dialog.tsx
@@ -0,0 +1,154 @@
+"use client"
+
+import * as React from "react"
+import { type Row } from "@tanstack/react-table"
+import { Loader, Trash } from "lucide-react"
+import { toast } from "sonner"
+
+import { useMediaQuery } from "@/hooks/use-media-query"
+import { Button } from "@/components/ui/button"
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from "@/components/ui/dialog"
+import {
+ Drawer,
+ DrawerClose,
+ DrawerContent,
+ DrawerDescription,
+ DrawerFooter,
+ DrawerHeader,
+ DrawerTitle,
+ DrawerTrigger,
+} from "@/components/ui/drawer"
+
+import { deleteIncoterm } from "../service"
+import { incoterms } from "@/db/schema/procurementRFQ"
+
+interface DeleteIncotermsDialogProps
+ extends React.ComponentPropsWithoutRef<typeof Dialog> {
+ incoterms: Row<typeof incoterms.$inferSelect>["original"][]
+ showTrigger?: boolean
+ onSuccess?: () => void
+}
+
+export function DeleteIncotermsDialog({
+ incoterms,
+ showTrigger = true,
+ onSuccess,
+ ...props
+}: DeleteIncotermsDialogProps) {
+ const [isDeletePending, startDeleteTransition] = React.useTransition()
+ const isDesktop = useMediaQuery("(min-width: 640px)")
+
+ function onDelete() {
+ startDeleteTransition(async () => {
+ try {
+ // 각 인코텀즈를 순차적으로 삭제
+ for (const incoterm of incoterms) {
+ const result = await deleteIncoterm(incoterm.code)
+ if (!result.success) {
+ toast.error(`인코텀즈 ${incoterm.code} 삭제 실패: ${result.error}`)
+ return
+ }
+ }
+
+ props.onOpenChange?.(false)
+ toast.success("인코텀즈가 성공적으로 삭제되었습니다.")
+ onSuccess?.()
+ } catch (error) {
+ console.error("Delete error:", error)
+ toast.error("인코텀즈 삭제 중 오류가 발생했습니다.")
+ }
+ })
+ }
+
+ if (isDesktop) {
+ return (
+ <Dialog {...props}>
+ {showTrigger ? (
+ <DialogTrigger asChild>
+ <Button variant="outline" size="sm">
+ <Trash className="mr-2 size-4" aria-hidden="true" />
+ delete ({incoterms.length})
+ </Button>
+ </DialogTrigger>
+ ) : null}
+ <DialogContent>
+ <DialogHeader>
+ <DialogTitle>정말로 삭제하시겠습니까?</DialogTitle>
+ <DialogDescription>
+ 이 작업은 되돌릴 수 없습니다. 선택된{" "}
+ <span className="font-medium">{incoterms.length}</span>
+ 개의 인코텀즈를 서버에서 영구적으로 삭제합니다.
+ </DialogDescription>
+ </DialogHeader>
+ <DialogFooter className="gap-2 sm:space-x-0">
+ <DialogClose asChild>
+ <Button variant="outline">취소</Button>
+ </DialogClose>
+ <Button
+ aria-label="선택된 행 삭제"
+ variant="destructive"
+ onClick={onDelete}
+ disabled={isDeletePending}
+ >
+ {isDeletePending && (
+ <Loader
+ className="mr-2 size-4 animate-spin"
+ aria-hidden="true"
+ />
+ )}
+ 삭제
+ </Button>
+ </DialogFooter>
+ </DialogContent>
+ </Dialog>
+ )
+ }
+
+ return (
+ <Drawer {...props}>
+ {showTrigger ? (
+ <DrawerTrigger asChild>
+ <Button variant="outline" size="sm">
+ <Trash className="mr-2 size-4" aria-hidden="true" />
+ delete ({incoterms.length})
+ </Button>
+ </DrawerTrigger>
+ ) : null}
+ <DrawerContent>
+ <DrawerHeader>
+ <DrawerTitle>정말로 삭제하시겠습니까?</DrawerTitle>
+ <DrawerDescription>
+ 이 작업은 되돌릴 수 없습니다. 선택된{" "}
+ <span className="font-medium">{incoterms.length}</span>
+ 개의 인코텀즈를 서버에서 영구적으로 삭제합니다.
+ </DrawerDescription>
+ </DrawerHeader>
+ <DrawerFooter className="gap-2 sm:space-x-0">
+ <DrawerClose asChild>
+ <Button variant="outline">취소</Button>
+ </DrawerClose>
+ <Button
+ aria-label="선택된 행 삭제"
+ variant="destructive"
+ onClick={onDelete}
+ disabled={isDeletePending}
+ >
+ {isDeletePending && (
+ <Loader className="mr-2 size-4 animate-spin" aria-hidden="true" />
+ )}
+ delete
+ </Button>
+ </DrawerFooter>
+ </DrawerContent>
+ </Drawer>
+ )
+} \ No newline at end of file
diff --git a/lib/incoterms/table/incoterms-add-dialog.tsx b/lib/incoterms/table/incoterms-add-dialog.tsx
index ef378e1e..0f7384d6 100644
--- a/lib/incoterms/table/incoterms-add-dialog.tsx
+++ b/lib/incoterms/table/incoterms-add-dialog.tsx
@@ -3,7 +3,7 @@
import * as React from "react";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
-import { z } from "zod";
+import * as z from "zod";
import { Plus, Loader2 } from "lucide-react";
import { Button } from "@/components/ui/button";
@@ -70,7 +70,8 @@ export function IncotermsAddDialog({ onSuccess }: IncotermsAddDialogProps) {
try {
const result = await createIncoterm(data);
if (result.data) {
- toast.success("인코텀즈가 추가되었습니다.");
+ toast.success("인코텀즈가 성공적으로 추가되었습니다.");
+ form.reset();
setOpen(false);
if (onSuccess) {
onSuccess();
@@ -89,16 +90,17 @@ export function IncotermsAddDialog({ onSuccess }: IncotermsAddDialogProps) {
return (
<Dialog open={open} onOpenChange={handleOpenChange}>
<DialogTrigger asChild>
- <Button size="sm" variant="outline">
+ <Button variant="outline" size="sm">
<Plus className="mr-2 h-4 w-4" />
인코텀즈 추가
</Button>
</DialogTrigger>
<DialogContent className="max-w-md">
<DialogHeader>
- <DialogTitle>인코텀즈 추가</DialogTitle>
+ <DialogTitle>새 인코텀즈 추가</DialogTitle>
<DialogDescription>
새로운 인코텀즈를 추가합니다. 필수 정보를 입력해주세요.
+ <span className="text-red-500 mt-1 block text-sm">* 표시된 항목은 필수 입력사항입니다.</span>
</DialogDescription>
</DialogHeader>
@@ -153,7 +155,7 @@ export function IncotermsAddDialog({ onSuccess }: IncotermsAddDialogProps) {
disabled={isLoading}
>
{isLoading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
- {isLoading ? "생성 중..." : "인코텀즈 추가"}
+ {isLoading ? "생성 중..." : "추가"}
</Button>
</DialogFooter>
</DialogContent>
diff --git a/lib/incoterms/table/incoterms-edit-sheet.tsx b/lib/incoterms/table/incoterms-edit-sheet.tsx
index 9cd067c7..1ae6e902 100644
--- a/lib/incoterms/table/incoterms-edit-sheet.tsx
+++ b/lib/incoterms/table/incoterms-edit-sheet.tsx
@@ -5,6 +5,8 @@ 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 {
Form,
@@ -16,8 +18,10 @@ import {
} from "@/components/ui/form"
import {
Sheet,
+ SheetClose,
SheetContent,
SheetDescription,
+ SheetFooter,
SheetHeader,
SheetTitle,
} from "@/components/ui/sheet"
@@ -37,7 +41,7 @@ type UpdateIncotermSchema = z.infer<typeof updateIncotermSchema>
interface IncotermsEditSheetProps {
open: boolean
onOpenChange: (open: boolean) => void
- data: typeof incoterms.$inferSelect
+ data: typeof incoterms.$inferSelect | null
onSuccess: () => void
}
@@ -47,12 +51,14 @@ export function IncotermsEditSheet({
data,
onSuccess,
}: IncotermsEditSheetProps) {
+ const [isUpdatePending, startUpdateTransition] = React.useTransition()
+
const form = useForm<UpdateIncotermSchema>({
resolver: zodResolver(updateIncotermSchema),
defaultValues: {
- code: data.code,
- description: data.description,
- isActive: data.isActive,
+ code: data?.code ?? "",
+ description: data?.description ?? "",
+ isActive: data?.isActive ?? true,
},
mode: "onChange"
})
@@ -68,14 +74,19 @@ export function IncotermsEditSheet({
}, [data, form])
async function onSubmit(input: UpdateIncotermSchema) {
- try {
- await updateIncoterm(data.code, input)
- toast.success("수정이 완료되었습니다.")
- onSuccess()
- onOpenChange(false)
- } catch {
- toast.error("수정 중 오류가 발생했습니다.")
- }
+ if (!data) return
+
+ startUpdateTransition(async () => {
+ try {
+ await updateIncoterm(data.code, input)
+ toast.success("인코텀즈가 성공적으로 수정되었습니다.")
+ onSuccess()
+ onOpenChange(false)
+ } catch (error) {
+ console.error("Update error:", error)
+ toast.error("인코텀즈 수정 중 오류가 발생했습니다.")
+ }
+ })
}
return (
@@ -96,7 +107,7 @@ export function IncotermsEditSheet({
<FormItem>
<FormLabel>코드</FormLabel>
<FormControl>
- <Input {...field} disabled />
+ <Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
@@ -132,12 +143,25 @@ export function IncotermsEditSheet({
</FormItem>
)}
/>
- <div className="flex justify-end space-x-2">
- <Button type="button" variant="outline" onClick={() => onOpenChange(false)}>
- 취소
+ <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"
+ />
+ )}
+ 저장
</Button>
- <Button type="submit">저장</Button>
- </div>
+ </SheetFooter>
</form>
</Form>
</SheetContent>
diff --git a/lib/incoterms/table/incoterms-table-columns.tsx b/lib/incoterms/table/incoterms-table-columns.tsx
index 56a44e8b..91ce4482 100644
--- a/lib/incoterms/table/incoterms-table-columns.tsx
+++ b/lib/incoterms/table/incoterms-table-columns.tsx
@@ -1,76 +1,71 @@
-import type { ColumnDef, Row } from "@tanstack/react-table";
-import { Button } from "@/components/ui/button";
-import { Badge } from "@/components/ui/badge";
-import { Ellipsis } from "lucide-react";
+"use client"
+
+import * as React from "react"
+import { type DataTableRowAction } from "@/types/table"
+import { type ColumnDef } from "@tanstack/react-table"
+import { Ellipsis } from "lucide-react"
+
+import { formatDateTime } from "@/lib/utils"
+import { Badge } from "@/components/ui/badge"
+import { Button } from "@/components/ui/button"
+import { Checkbox } from "@/components/ui/checkbox"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
+ DropdownMenuShortcut,
DropdownMenuTrigger,
-} from "@/components/ui/dropdown-menu";
-import { incoterms } from "@/db/schema/procurementRFQ";
-import { toast } from "sonner";
-import { deleteIncoterm } from "../service";
+} from "@/components/ui/dropdown-menu"
-type Incoterm = typeof incoterms.$inferSelect;
+import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
+import { incoterms } from "@/db/schema/procurementRFQ"
interface GetColumnsProps {
- setRowAction: (action: { type: string; row: Row<Incoterm> }) => void;
- onSuccess: () => void;
+ setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<typeof incoterms.$inferSelect> | null>>
}
-const handleDelete = async (code: string, onSuccess: () => void) => {
- const result = await deleteIncoterm(code);
- if (result.success) {
- toast.success("삭제 완료");
- onSuccess();
- } else {
- toast.error(result.error || "삭제 실패");
+/**
+ * tanstack table 컬럼 정의
+ */
+export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<typeof incoterms.$inferSelect>[] {
+ // ----------------------------------------------------------------
+ // 1) select 컬럼 (체크박스)
+ // ----------------------------------------------------------------
+ const selectColumn: ColumnDef<typeof incoterms.$inferSelect> = {
+ id: "select",
+ header: ({ table }) => (
+ <Checkbox
+ checked={
+ table.getIsAllPageRowsSelected() ||
+ (table.getIsSomePageRowsSelected() && "indeterminate")
+ }
+ onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
+ aria-label="Select all"
+ className="translate-y-0.5"
+ />
+ ),
+ cell: ({ row }) => (
+ <Checkbox
+ checked={row.getIsSelected()}
+ onCheckedChange={(value) => row.toggleSelected(!!value)}
+ aria-label="Select row"
+ className="translate-y-0.5"
+ />
+ ),
+ maxSize: 30,
+ enableSorting: false,
+ enableHiding: false,
}
-};
-export function getColumns({ setRowAction, onSuccess }: GetColumnsProps): ColumnDef<Incoterm>[] {
- return [
- {
- id: "code",
- header: () => <div>코드</div>,
- cell: ({ row }) => <div>{row.original.code}</div>,
- enableSorting: true,
- enableHiding: false,
- },
- {
- id: "description",
- header: () => <div>설명</div>,
- cell: ({ row }) => <div>{row.original.description}</div>,
- enableSorting: true,
- enableHiding: false,
- },
- {
- id: "isActive",
- header: () => <div>상태</div>,
- cell: ({ row }) => (
- <Badge variant={row.original.isActive ? "default" : "secondary"}>
- {row.original.isActive ? "활성" : "비활성"}
- </Badge>
- ),
- enableSorting: true,
- enableHiding: false,
- },
- {
- id: "createdAt",
- header: () => <div>생성일</div>,
- cell: ({ row }) => {
- const value = row.original.createdAt;
- const date = value ? new Date(value) : null;
- return date ? date.toLocaleDateString() : "";
- },
- enableSorting: true,
- enableHiding: false,
- },
- {
- id: "actions",
- cell: ({ row }) => (
+ // ----------------------------------------------------------------
+ // 2) actions 컬럼 (Dropdown 메뉴)
+ // ----------------------------------------------------------------
+ const actionsColumn: ColumnDef<typeof incoterms.$inferSelect> = {
+ id: "actions",
+ enableHiding: false,
+ cell: function Cell({ row }) {
+ return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button
@@ -83,20 +78,99 @@ export function getColumns({ setRowAction, onSuccess }: GetColumnsProps): Column
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-40">
<DropdownMenuItem
- onSelect={() => setRowAction({ type: "edit", row })}
+ onSelect={() => setRowAction({ row, type: "update" })}
>
- 수정
+ Edit
</DropdownMenuItem>
+
<DropdownMenuSeparator />
<DropdownMenuItem
- onSelect={() => handleDelete(row.original.code, onSuccess)}
- className="text-destructive"
+ onSelect={() => setRowAction({ row, type: "delete" })}
>
- 삭제
+ Delete
+ <DropdownMenuShortcut>⌘⌫</DropdownMenuShortcut>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
+ )
+ },
+ maxSize: 30,
+ }
+
+ // ----------------------------------------------------------------
+ // 3) 데이터 컬럼들
+ // ----------------------------------------------------------------
+ const dataColumns: ColumnDef<typeof incoterms.$inferSelect>[] = [
+ {
+ accessorKey: "code",
+ enableResizing: true,
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="코드" />
+ ),
+ meta: {
+ excelHeader: "코드",
+ type: "text",
+ },
+ cell: ({ row }) => row.getValue("code") ?? "",
+ minSize: 80
+ },
+ {
+ accessorKey: "description",
+ enableResizing: true,
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="설명" />
),
+ meta: {
+ excelHeader: "설명",
+ type: "text",
+ },
+ cell: ({ row }) => row.getValue("description") ?? "",
+ minSize: 80
},
- ];
+ {
+ accessorKey: "isActive",
+ enableResizing: true,
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="상태" />
+ ),
+ meta: {
+ excelHeader: "상태",
+ type: "boolean",
+ },
+ cell: ({ row }) => {
+ const isActive = row.getValue("isActive") as boolean
+ return (
+ <Badge variant={isActive ? "default" : "secondary"}>
+ {isActive ? "활성" : "비활성"}
+ </Badge>
+ )
+ },
+ minSize: 80
+ },
+ {
+ accessorKey: "createdAt",
+ enableResizing: true,
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title="생성일" />
+ ),
+ meta: {
+ excelHeader: "생성일",
+ type: "date",
+ },
+ cell: ({ row }) => {
+ const dateVal = row.getValue("createdAt") as Date
+ return formatDateTime(dateVal)
+ },
+ minSize: 80
+ }
+ ]
+
+ // ----------------------------------------------------------------
+ // 4) 최종 컬럼 배열: select, dataColumns, actions
+ // ----------------------------------------------------------------
+ return [
+ selectColumn,
+ ...dataColumns,
+ actionsColumn,
+ ]
} \ No newline at end of file
diff --git a/lib/incoterms/table/incoterms-table-toolbar.tsx b/lib/incoterms/table/incoterms-table-toolbar.tsx
index b87982c9..698acf59 100644
--- a/lib/incoterms/table/incoterms-table-toolbar.tsx
+++ b/lib/incoterms/table/incoterms-table-toolbar.tsx
@@ -1,16 +1,53 @@
"use client";
import * as React from "react";
+import { type Table } from "@tanstack/react-table";
+import { Download } from "lucide-react";
+
+import { exportTableToExcel } from "@/lib/export";
+import { Button } from "@/components/ui/button";
+import { DeleteIncotermsDialog } from "./delete-incoterms-dialog";
import { IncotermsAddDialog } from "./incoterms-add-dialog";
+import { incoterms } from "@/db/schema/procurementRFQ";
-interface IncotermsTableToolbarProps {
+interface IncotermsTableToolbarActionsProps {
+ table: Table<typeof incoterms.$inferSelect>;
onSuccess?: () => void;
}
-export function IncotermsTableToolbar({ onSuccess }: IncotermsTableToolbarProps) {
+export function IncotermsTableToolbarActions({ table, onSuccess }: IncotermsTableToolbarActionsProps) {
return (
<div className="flex items-center gap-2">
+ {/** 1) 선택된 로우가 있으면 삭제 다이얼로그 */}
+ {table.getFilteredSelectedRowModel().rows.length > 0 ? (
+ <DeleteIncotermsDialog
+ incoterms={table
+ .getFilteredSelectedRowModel()
+ .rows.map((row) => row.original)}
+ onSuccess={() => {
+ table.toggleAllRowsSelected(false);
+ onSuccess?.();
+ }}
+ />
+ ) : null}
+
<IncotermsAddDialog onSuccess={onSuccess} />
+
+ {/** 3) Export 버튼 */}
+ <Button
+ variant="outline"
+ size="sm"
+ onClick={() =>
+ exportTableToExcel(table, {
+ filename: "incoterms-list",
+ excludeColumns: ["select", "actions"],
+ })
+ }
+ className="gap-2"
+ >
+ <Download className="size-4" aria-hidden="true" />
+ <span className="hidden sm:inline">Export</span>
+ </Button>
</div>
);
} \ No newline at end of file
diff --git a/lib/incoterms/table/incoterms-table.tsx b/lib/incoterms/table/incoterms-table.tsx
index c5b5bba4..c98de810 100644
--- a/lib/incoterms/table/incoterms-table.tsx
+++ b/lib/incoterms/table/incoterms-table.tsx
@@ -3,13 +3,16 @@ 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 { getIncoterms } from "../service";
import { getColumns } from "./incoterms-table-columns";
-import { incoterms } from "@/db/schema/procurementRFQ";
-import { IncotermsTableToolbar } from "./incoterms-table-toolbar";
-import { toast } from "sonner";
+import { DeleteIncotermsDialog } from "./delete-incoterms-dialog";
import { IncotermsEditSheet } from "./incoterms-edit-sheet";
-import { Row } from "@tanstack/react-table";
-import { getIncoterms } from "../service";
+import { IncotermsTableToolbarActions } from "./incoterms-table-toolbar";
+import { incoterms } from "@/db/schema/procurementRFQ";
interface IncotermsTableProps {
promises?: Promise<[{ data: typeof incoterms.$inferSelect[]; pageCount: number }] >;
@@ -17,8 +20,7 @@ interface IncotermsTableProps {
export function IncotermsTable({ promises }: IncotermsTableProps) {
const [rawData, setRawData] = React.useState<{ data: typeof incoterms.$inferSelect[]; pageCount: number }>({ data: [], pageCount: 0 });
- const [isEditSheetOpen, setIsEditSheetOpen] = React.useState(false);
- const [selectedRow, setSelectedRow] = React.useState<typeof incoterms.$inferSelect | null>(null);
+ const [rowAction, setRowAction] = React.useState<DataTableRowAction<typeof incoterms.$inferSelect> | null>(null);
React.useEffect(() => {
if (promises) {
@@ -44,7 +46,6 @@ export function IncotermsTable({ promises }: IncotermsTableProps) {
setRawData(result);
} catch (error) {
console.error("Error refreshing data:", error);
- toast.error("데이터를 불러오는 중 오류가 발생했습니다.");
}
})();
}
@@ -67,50 +68,71 @@ export function IncotermsTable({ promises }: IncotermsTableProps) {
setRawData(result);
} catch (error) {
console.error("Error refreshing data:", error);
- toast.error("데이터를 불러오는 중 오류가 발생했습니다.");
}
}, []);
- const handleRowAction = async (action: { type: string; row: Row<typeof incoterms.$inferSelect> }) => {
- if (action.type === "edit") {
- setSelectedRow(action.row.original);
- setIsEditSheetOpen(true);
- }
- };
+ // 컬럼 설정 - 외부 파일에서 가져옴
+ const columns = React.useMemo(
+ () => getColumns({ setRowAction }),
+ [setRowAction]
+ )
- const columns = React.useMemo(() => getColumns({ setRowAction: handleRowAction, onSuccess: refreshData }), [refreshData]);
+ // 고급 필터 필드 설정
+ const advancedFilterFields: DataTableAdvancedFilterField<typeof incoterms.$inferSelect>[] = [
+ { id: "code", label: "코드", type: "text" },
+ {
+ id: "isActive", label: "상태", type: "select", options: [
+ { label: "활성", value: "true" },
+ { label: "비활성", value: "false" },
+ ]
+ },
+ { id: "description", label: "설명", type: "text" },
+ { id: "createdAt", label: "생성일", type: "date" },
+ ];
const { table } = useDataTable({
- data: rawData.data,
- columns,
- pageCount: rawData.pageCount,
- filterFields: [],
- enablePinning: true,
- enableAdvancedFilter: true,
- initialState: {
- sorting: [{ id: "createdAt", desc: true }],
- columnPinning: { right: ["actions"] },
- },
- getRowId: (originalRow) => String(originalRow.code),
- shallow: false,
- clearOnDefault: true,
- });
+ data: rawData.data,
+ columns,
+ pageCount: rawData.pageCount,
+ enablePinning: true,
+ enableAdvancedFilter: true,
+ initialState: {
+ sorting: [{ id: "createdAt", desc: true }],
+ columnPinning: { right: ["actions"] },
+ },
+ getRowId: (originalRow) => String(originalRow.code),
+ shallow: false,
+ clearOnDefault: true,
+ })
return (
<>
<DataTable table={table}>
- <DataTableAdvancedToolbar table={table} filterFields={[]} shallow={false}>
- <IncotermsTableToolbar onSuccess={refreshData} />
+ <DataTableAdvancedToolbar
+ table={table}
+ filterFields={advancedFilterFields}
+ >
+ <IncotermsTableToolbarActions table={table} onSuccess={refreshData} />
</DataTableAdvancedToolbar>
</DataTable>
- {isEditSheetOpen && selectedRow && (
- <IncotermsEditSheet
- open={isEditSheetOpen}
- onOpenChange={setIsEditSheetOpen}
- data={selectedRow}
- onSuccess={refreshData}
- />
- )}
+
+ <DeleteIncotermsDialog
+ open={rowAction?.type === "delete"}
+ onOpenChange={() => setRowAction(null)}
+ incoterms={rowAction?.row.original ? [rowAction?.row.original] : []}
+ showTrigger={false}
+ onSuccess={() => {
+ rowAction?.row.toggleSelected(false)
+ refreshData()
+ }}
+ />
+
+ <IncotermsEditSheet
+ open={rowAction?.type === "update"}
+ onOpenChange={() => setRowAction(null)}
+ data={rowAction?.row.original ?? null}
+ onSuccess={refreshData}
+ />
</>
);
} \ No newline at end of file