summaryrefslogtreecommitdiff
path: root/lib/po/table/item-dialog.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/po/table/item-dialog.tsx')
-rw-r--r--lib/po/table/item-dialog.tsx173
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