summaryrefslogtreecommitdiff
path: root/lib/po/table/po-table-columns.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/po/table/po-table-columns.tsx')
-rw-r--r--lib/po/table/po-table-columns.tsx300
1 files changed, 187 insertions, 113 deletions
diff --git a/lib/po/table/po-table-columns.tsx b/lib/po/table/po-table-columns.tsx
index a13b2acf..6834e543 100644
--- a/lib/po/table/po-table-columns.tsx
+++ b/lib/po/table/po-table-columns.tsx
@@ -3,7 +3,7 @@
import * as React from "react"
import { type DataTableRowAction } from "@/types/table"
import { type ColumnDef } from "@tanstack/react-table"
-import { InfoIcon, PenIcon } from "lucide-react"
+import { InfoIcon, FileTextIcon, SendIcon, FileSignatureIcon, MoreHorizontalIcon, ExternalLinkIcon } from "lucide-react"
import { formatDate } from "@/lib/utils"
import { Button } from "@/components/ui/button"
@@ -13,19 +13,28 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip"
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuItem,
+ DropdownMenuLabel,
+ DropdownMenuSeparator,
+ DropdownMenuTrigger,
+} from "@/components/ui/dropdown-menu"
+import { Badge } from "@/components/ui/badge"
import { DataTableColumnHeaderSimple } from "@/components/data-table/data-table-column-simple-header"
import { poColumnsConfig } from "@/config/poColumnsConfig"
-import { ContractDetail } from "@/db/schema/contract"
+import { ContractDetailParsed } from "@/db/schema/contract"
interface GetColumnsProps {
- setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<ContractDetail> | null>>
+ setRowAction: React.Dispatch<React.SetStateAction<DataTableRowAction<ContractDetailParsed> | null>>
}
/**
* tanstack table column definitions with nested headers
*/
-export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<ContractDetail>[] {
+export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<ContractDetailParsed>[] {
// ----------------------------------------------------------------
// 1) select column (checkbox) - if needed
// ----------------------------------------------------------------
@@ -33,164 +42,229 @@ export function getColumns({ setRowAction }: GetColumnsProps): ColumnDef<Contrac
// ----------------------------------------------------------------
// 2) actions column (buttons for item info and signature request)
// ----------------------------------------------------------------
- const actionsColumn: ColumnDef<ContractDetail> = {
+ const actionsColumn: ColumnDef<ContractDetailParsed> = {
id: "actions",
enableHiding: false,
+ header: () => <div className="text-center">Actions</div>,
cell: function Cell({ row }) {
// Check if this contract already has a signature envelope
const hasSignature = row.original.hasSignature;
-
+ const contract = row.original;
+
return (
- <div className="flex items-center space-x-1">
- {/* Item Info Button */}
+ <div className="flex gap-2">
+ {/* Items Button - Visually distinct with badge showing count */}
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
- variant="ghost"
- size="icon"
+ variant="outline"
+ size="sm"
+ className="flex items-center gap-1 h-8 px-2"
onClick={() => setRowAction({ row, type: "items" })}
>
- <InfoIcon className="h-4 w-4" aria-hidden="true" />
+ <FileTextIcon className="h-3.5 w-3.5" aria-hidden="true" />
+ Items
+ {contract.items && contract.items.length > 0 && (
+ <Badge variant="secondary" className="ml-1 text-xs h-5 px-1.5">
+ {contract.items.length}
+ </Badge>
+ )}
</Button>
</TooltipTrigger>
<TooltipContent>
- View Item Info
+ View contract items and details
</TooltipContent>
</Tooltip>
</TooltipProvider>
-
- {/* Signature Request Button - only show if no signature exists */}
- {!hasSignature && (
+
+ {/* Signature related actions */}
+ {hasSignature ? (
+ <TooltipProvider>
+ <Tooltip>
+ <TooltipTrigger asChild>
+ <Button
+ variant="outline"
+ size="sm"
+ className="h-8 px-2"
+ onClick={() => setRowAction({ row, type: "esign-detail" })}
+ >
+ <FileSignatureIcon className="h-3.5 w-3.5 mr-1" aria-hidden="true" />
+ View Signatures
+ </Button>
+ </TooltipTrigger>
+ <TooltipContent>
+ View signature status and details
+ </TooltipContent>
+ </Tooltip>
+ </TooltipProvider>
+ ) : (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Button
- variant="ghost"
- size="icon"
+ variant="outline"
+ size="sm"
+ className="h-8 px-2 text-blue-600 border-blue-200 hover:bg-blue-50"
onClick={() => setRowAction({ row, type: "signature" })}
>
- <PenIcon className="h-4 w-4" aria-hidden="true" />
+ <SendIcon className="h-3.5 w-3.5 mr-1" aria-hidden="true" />
+ Request Signatures
</Button>
</TooltipTrigger>
<TooltipContent>
- Request Electronic Signature
+ Send electronic signature requests
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
+
+ {/* Alternative: Dropdown menu for more actions */}
+ {/*
+ <DropdownMenu>
+ <DropdownMenuTrigger asChild>
+ <Button variant="ghost" size="icon">
+ <MoreHorizontalIcon className="h-4 w-4" />
+ </Button>
+ </DropdownMenuTrigger>
+ <DropdownMenuContent align="end">
+ <DropdownMenuLabel>Actions</DropdownMenuLabel>
+ <DropdownMenuItem onClick={() => setRowAction({ row, type: "items" })}>
+ <FileTextIcon className="h-4 w-4 mr-2" />
+ View Items
+ </DropdownMenuItem>
+ <DropdownMenuSeparator />
+ {hasSignature ? (
+ <DropdownMenuItem onClick={() => setRowAction({ row, type: "esign-detail" })}>
+ <FileSignatureIcon className="h-4 w-4 mr-2" />
+ View Signatures
+ </DropdownMenuItem>
+ ) : (
+ <DropdownMenuItem onClick={() => setRowAction({ row, type: "signature" })}>
+ <SendIcon className="h-4 w-4 mr-2" />
+ Request Signatures
+ </DropdownMenuItem>
+ )}
+ </DropdownMenuContent>
+ </DropdownMenu>
+ */}
</div>
);
},
- size: 80, // Increased width to accommodate both buttons
+ size: 340, // Adjusted for multiple buttons
+ minSize:280
};
// ----------------------------------------------------------------
// 3) Regular columns grouped by group name
// ----------------------------------------------------------------
- // 3-1) groupMap: { [groupName]: ColumnDef<ContractDetail>[] }
- const groupMap: Record<string, ColumnDef<ContractDetail>[]> = {};
-
- // (1) JSON config를 읽어서 ColumnDef를 생성하는 부분 (일부 발췌)
-poColumnsConfig.forEach((cfg) => {
- const groupName = cfg.group || "_noGroup"
- if (!groupMap[groupName]) {
- groupMap[groupName] = []
- }
-
- let childCol: ColumnDef<ContractDetail>
-
- if (cfg.type === "custom" && cfg.customType === "esignStatus") {
- // ========================================
- // (2) 전자서명 전용 커스텀 컬럼
- // ========================================
- childCol = {
- id: cfg.id,
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title={cfg.label} />
- ),
- // 여기서 row.original.envelopes 등 활용하여 최신 전자서명 상태 표시
- cell: ({ row }) => {
- const data = row.original
- if (!data.envelopes || data.envelopes.length === 0) {
+ // 3-1) groupMap: { [groupName]: ColumnDef<ContractDetailParsed>[] }
+ const groupMap: Record<string, ColumnDef<ContractDetailParsed>[]> = {};
+
+ // (1) JSON config를 읽어서 ColumnDef를 생성하는 부분 (일부 발췌)
+ poColumnsConfig.forEach((cfg) => {
+ const groupName = cfg.group || "_noGroup"
+ if (!groupMap[groupName]) {
+ groupMap[groupName] = []
+ }
+
+ let childCol: ColumnDef<ContractDetailParsed>
+
+ if (cfg.type === "custom") {
+ // ========================================
+ // (2) 전자서명 전용 커스텀 컬럼
+ // ========================================
+ childCol = {
+ id: cfg.id,
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title={cfg.label} />
+ ),
+ // 여기서 row.original.envelopes 등 활용하여 최신 전자서명 상태 표시
+ cell: ({ row }) => {
+ const data = row.original
+ if (!data.envelopes || data.envelopes.length === 0) {
+ return (
+ <div className="text-sm text-gray-500">
+ No E-Sign
+ </div>
+ )
+ }
+
+ // envelopes가 여러 개 있으면 최신(가장 최근 updatedAt) 가져오기
+ const sorted = [...data.envelopes].sort((a, b) => {
+ const dateA = new Date(a.updatedAt)
+ const dateB = new Date(b.updatedAt)
+ return dateB.getTime() - dateA.getTime()
+ })
+ const latest = sorted[0]
+
+ const status = latest.envelopeStatus;
+
+ if (!status) {
+ // status가 null이면, 기본 색상이나 별도 표시
+ return <span className="text-gray-500">No Status</span>
+ }
+
+ const colorMap: Record<string, string> = {
+ completed: "text-green-600",
+ sent: "text-blue-600",
+ voided: "text-red-600",
+ };
+
+ const colorClass = colorMap[status] ?? "text-gray-700";
+
return (
- <div className="text-sm text-gray-500">
- No E-Sign
+ <div className="flex items-center">
+ <span
+ onClick={() => setRowAction({ row, type: "esign-detail" })}
+ className={`${colorClass} cursor-pointer flex items-center`}
+ >
+ {status}
+ <ExternalLinkIcon className="ml-1 h-3 w-3" />
+ </span>
</div>
)
- }
-
- // envelopes가 여러 개 있으면 최신(가장 최근 updatedAt) 가져오기
- const sorted = [...data.envelopes].sort((a, b) => {
- const dateA = new Date(a.updatedAt)
- const dateB = new Date(b.updatedAt)
- return dateB.getTime() - dateA.getTime()
- })
- const latest = sorted[0]
-
- // 상태에 따라 다른 UI 색상/아이콘
- const status = latest.envelopeStatus // "sent", "completed", ...
- const colorMap: Record<string, string> = {
- completed: "text-green-600",
- sent: "text-blue-600",
- voided: "text-red-600",
+ },
+ meta: {
+ excelHeader: cfg.excelHeader,
+ group: cfg.group,
+ type: cfg.type,
+ },
+ }
+ } else {
+ // ========================================
+ // (3) 일반 컬럼 (type: text/date/number 등)
+ // ========================================
+ childCol = {
+ accessorKey: cfg.id,
+ enableResizing: true,
+ header: ({ column }) => (
+ <DataTableColumnHeaderSimple column={column} title={cfg.label} />
+ ),
+ meta: {
+ excelHeader: cfg.excelHeader,
+ group: cfg.group,
+ type: cfg.type,
+ },
+ cell: ({ row, cell }) => {
+ // 날짜 포맷, 숫자 포맷 등 처리
+ if (cfg.type === "date") {
+ const dateVal = cell.getValue() as Date
+ return formatDate(dateVal)
+ }
// ...
- }
- const colorClass = colorMap[status] || "text-gray-700"
-
- return (
- <Button
- onClick={() => {
- // 다이얼로그 열기 등
- // 예: setRowAction({ row, type: "esign-detail" })
- setRowAction({ row, type: "esign-detail" })
- }}
- className={`underline underline-offset-2 ${colorClass}`}
- >
- {status}
- </Button>
- )
- },
- meta: {
- excelHeader: cfg.excelHeader,
- group: cfg.group,
- type: cfg.type,
- },
- }
- } else {
- // ========================================
- // (3) 일반 컬럼 (type: text/date/number 등)
- // ========================================
- childCol = {
- accessorKey: cfg.id,
- enableResizing: true,
- header: ({ column }) => (
- <DataTableColumnHeaderSimple column={column} title={cfg.label} />
- ),
- meta: {
- excelHeader: cfg.excelHeader,
- group: cfg.group,
- type: cfg.type,
- },
- cell: ({ row, cell }) => {
- // 날짜 포맷, 숫자 포맷 등 처리
- if (cfg.type === "date") {
- const dateVal = cell.getValue() as Date
- return formatDate(dateVal)
- }
- // ...
- return row.getValue(cfg.id) ?? ""
- },
+ return row.getValue(cfg.id) ?? ""
+ },
+ }
}
- }
- groupMap[groupName].push(childCol)
-})
+ groupMap[groupName].push(childCol)
+ })
// ----------------------------------------------------------------
// 3-2) Create actual parent columns (groups) from the groupMap
// ----------------------------------------------------------------
- const nestedColumns: ColumnDef<ContractDetail>[] = [];
+ const nestedColumns: ColumnDef<ContractDetailParsed>[] = [];
// Order can be fixed by pre-defining group order or sorting
// Here we just use Object.entries order