diff options
| author | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-03-28 00:42:08 +0000 |
|---|---|---|
| committer | dujinkim <dujin.kim@dtsolution.co.kr> | 2025-03-28 00:42:08 +0000 |
| commit | b8e8328b1ffffb80bf4ebb776a4a24e5680fc5bc (patch) | |
| tree | bbb4d82cee5f3fbf107e0648dea9a8f66e2710c4 /lib/po/table/item-dialog.tsx | |
| parent | 34bbeb86c1a8d24b5f526710889b5e54d699cfd0 (diff) | |
테이블 칼럼 리사이즈 및 핀 충돌 해결
Diffstat (limited to 'lib/po/table/item-dialog.tsx')
| -rw-r--r-- | lib/po/table/item-dialog.tsx | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/lib/po/table/item-dialog.tsx b/lib/po/table/item-dialog.tsx new file mode 100644 index 00000000..a6690e75 --- /dev/null +++ b/lib/po/table/item-dialog.tsx @@ -0,0 +1,173 @@ +"use client" + +import * as React from "react" +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog" +import { Button } from "@/components/ui/button" +import { ScrollArea } from "@/components/ui/scroll-area" +import { Separator } from "@/components/ui/separator" +import { Badge } from "@/components/ui/badge" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table" +import { Package, Info, DollarSign, Tag, Clock, Hash } from "lucide-react" + +import type { ContractDetailParsed } from "@/db/schema/contract" + +interface ItemsDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + po: ContractDetailParsed | null +} + +export function ItemsDialog({ open, onOpenChange, po }: ItemsDialogProps) { + console.log(po) + + // Format currency with appropriate symbol + const formatCurrency = (value: number | null, currency?: string) => { + if (value === null) return '-'; + const currencySymbol = currency === 'USD' ? '$' : currency || ''; + return `${currencySymbol}${value.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}`; + }; + + // Format date to a readable format + const formatDate = (dateString: string) => { + const date = new Date(dateString); + return date.toLocaleString(); + }; + + return ( + <Dialog open={open} onOpenChange={onOpenChange}> + <DialogContent className="sm:max-w-[700px] max-h-[90vh]"> + <DialogHeader> + <DialogTitle className="flex items-center gap-2"> + <Package className="h-5 w-5" /> + Contract Items + </DialogTitle> + <DialogDescription> + {po + ? `Item list for contract #${po.contractNo} - ${po.contractName}` + : "No contract selected." + } + </DialogDescription> + </DialogHeader> + + {/* Main content */} + {po ? ( + po.items.length === 0 ? ( + <div className="text-sm text-muted-foreground flex items-center justify-center p-8"> + <Info className="mr-2 h-4 w-4" /> + No items found for this contract. + </div> + ) : ( + <div className="overflow-hidden"> + <div className="flex justify-between items-center my-2"> + <div className="text-sm font-medium"> + Total Items: <Badge variant="outline">{po.items.length}</Badge> + </div> + <div className="text-sm text-muted-foreground"> + Currency: <Badge variant="secondary">{po.currency || "Default"}</Badge> + </div> + </div> + + <ScrollArea className="h-[350px] pr-4 rounded-md border" style={{height:450}}> + <div className="p-4 space-y-4"> + {po.items.map((item) => ( + <Card key={item.id} className="shadow-sm hover:shadow-md transition-shadow duration-200"> + <CardHeader className="pb-2 bg-muted/30"> + <div className="flex justify-between items-center"> + <CardTitle className="text-base flex items-center gap-2"> + <Hash className="h-4 w-4" /> + Item #{item.itemId} + </CardTitle> + {item.quantity > 1 && ( + <Badge className="ml-2"> + Qty: {item.quantity} + </Badge> + )} + </div> + </CardHeader> + <CardContent className="pt-3 pb-4"> + {item.description && ( + <div className="mb-3 text-sm"> + <div className="font-medium mb-1 text-muted-foreground flex items-center"> + <Info className="mr-2 h-4 w-4" /> + Description + </div> + <div className="pl-6">{item.description}</div> + </div> + )} + + <Table> + <TableBody> + <TableRow> + <TableCell className="py-2 font-medium">Unit Price</TableCell> + <TableCell className="py-2 text-right"> + {item.unitPrice !== null ? ( + <div className="flex items-center justify-end gap-1"> + <DollarSign className="h-3 w-3 text-muted-foreground" /> + {formatCurrency(item.unitPrice, po.currency??"KRW")} + </div> + ) : "-"} + </TableCell> + </TableRow> + + {item.taxRate !== null && ( + <TableRow> + <TableCell className="py-2 font-medium">Tax Rate</TableCell> + <TableCell className="py-2 text-right">{item.taxRate}%</TableCell> + </TableRow> + )} + + {item.taxAmount !== null && ( + <TableRow> + <TableCell className="py-2 font-medium">Tax Amount</TableCell> + <TableCell className="py-2 text-right">{formatCurrency(item.taxAmount, po.currency??"KRW")}</TableCell> + </TableRow> + )} + + {item.totalLineAmount !== null && ( + <TableRow className="border-t border-t-primary/20"> + <TableCell className="py-2 font-medium">Total Amount</TableCell> + <TableCell className="py-2 text-right font-semibold"> + {formatCurrency(item.totalLineAmount, po.currency??"KRW")} + </TableCell> + </TableRow> + )} + </TableBody> + </Table> + + {item.remark && ( + <div className="mt-3 text-sm"> + <div className="font-medium mb-1 text-muted-foreground flex items-center"> + <Tag className="mr-2 h-4 w-4" /> + Remark + </div> + <div className="pl-6 italic text-muted-foreground">{item.remark}</div> + </div> + )} + + <div className="mt-3 pt-2 border-t text-xs text-muted-foreground flex items-center"> + <Clock className="mr-1 h-3 w-3" /> + Updated: {formatDate(item.updatedAt)} + </div> + </CardContent> + </Card> + ))} + </div> + </ScrollArea> + </div> + ) + ) : ( + <div className="text-sm text-muted-foreground flex items-center justify-center p-8"> + Please select a contract to see its items. + </div> + )} + + <DialogFooter className="mt-2"> + <Button variant="outline" onClick={() => onOpenChange(false)}> + Close + </Button> + </DialogFooter> + </DialogContent> + </Dialog> + ) +}
\ No newline at end of file |
