update storefront page

This commit is contained in:
NotII
2025-04-21 15:32:18 +01:00
parent b4bae9ae18
commit 63c7d602e4

View File

@@ -93,6 +93,27 @@ const getStatusVariant = (status: string) => {
} }
}; };
const getStatusStyle = (status: string) => {
switch (status) {
case 'acknowledged':
return 'bg-purple-500/10 text-purple-500 border-purple-500/20';
case 'paid':
return 'bg-emerald-500/10 text-emerald-500 border-emerald-500/20';
case 'shipped':
return 'bg-blue-500/10 text-blue-500 border-blue-500/20';
case 'completed':
return 'bg-green-500/10 text-green-500 border-green-500/20';
case 'cancelled':
return 'bg-red-500/10 text-red-500 border-red-500/20';
case 'unpaid':
return 'bg-yellow-500/10 text-yellow-500 border-yellow-500/20';
case 'confirming':
return 'bg-orange-500/10 text-orange-500 border-orange-500/20';
default:
return 'bg-gray-500/10 text-gray-500 border-gray-500/20';
}
};
export default function OrderDetailsPage() { export default function OrderDetailsPage() {
const [order, setOrder] = useState<Order | null>(null); const [order, setOrder] = useState<Order | null>(null);
const [trackingNumber, setTrackingNumber] = useState(""); const [trackingNumber, setTrackingNumber] = useState("");
@@ -471,292 +492,255 @@ export default function OrderDetailsPage() {
return ( return (
<Layout> <Layout>
<div className="container mx-auto px-4 py-8"> <div className="space-y-6">
<div className="space-y-6"> {/* Header Section */}
<div className="flex justify-between items-center"> <div className="flex items-center justify-between">
<div className="flex items-center gap-4"> <div className="flex items-center gap-4">
<h1 className="text-3xl font-bold">Order Details: {order?.orderId}</h1> <h1 className="text-2xl font-semibold text-gray-900 dark:text-white flex items-center">
<Package className="mr-2 h-6 w-6" />
Order {order?.orderId}
</h1>
<div className={`px-3 py-1 rounded-full border ${getStatusStyle(order?.status || '')}`}>
{order?.status?.toUpperCase()}
</div> </div>
<div className="flex items-center gap-2"> </div>
<Badge <div className="flex items-center gap-2">
variant={getStatusVariant(order?.status || '')} {prevOrderId && (
className={`${ <Button
order?.status === 'acknowledged' variant="outline"
? '!bg-purple-600 hover:!bg-purple-700 text-white' size="sm"
: order?.status === 'paid' onClick={() => router.push(`/dashboard/orders/${prevOrderId}`)}
? 'bg-emerald-600 hover:bg-emerald-700 text-white'
: order?.status === 'shipped'
? 'bg-blue-600 hover:bg-blue-700 text-white'
: order?.status === 'completed'
? 'bg-green-600 hover:bg-green-700 text-white'
: ''
}`}
> >
{order?.status} Previous Order
</Badge> </Button>
</div> )}
{nextOrderId && (
<Button
variant="outline"
size="sm"
onClick={() => router.push(`/dashboard/orders/${nextOrderId}`)}
>
Next Order
</Button>
)}
</div> </div>
</div>
{/* Order Navigation - Moved to top */} <div className="grid grid-cols-3 gap-6">
<div className="flex justify-between items-center border-b pb-4"> {/* Left Column - Order Details */}
<div className="w-[140px]"> <div className="col-span-2 space-y-6">
{nextOrderId && ( {/* Products Card */}
<Button
variant="outline"
size="lg"
onClick={() => {
setLoading(true);
router.push(`/dashboard/orders/${nextOrderId}`);
}}
className="flex items-center"
>
<span className="mr-2"></span>
Older
</Button>
)}
</div>
<div className="text-center text-sm text-muted-foreground">
Navigate Orders
</div>
<div className="w-[140px] flex justify-end">
{prevOrderId && (
<Button
variant="outline"
size="lg"
onClick={() => {
setLoading(true);
router.push(`/dashboard/orders/${prevOrderId}`);
}}
className="flex items-center"
>
Newer
<span className="ml-2"></span>
</Button>
)}
</div>
</div>
<div className="grid gap-6 md:grid-cols-2">
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>PGP Encrypted Address</CardTitle> <CardTitle className="text-lg font-medium">Products</CardTitle>
<CardDescription>
Securely encrypted delivery address
</CardDescription>
</CardHeader> </CardHeader>
<CardContent> <CardContent>
<Textarea <Table>
value={order?.pgpAddress || ""} <TableHeader>
readOnly <TableRow>
className="font-mono text-xs" <TableHead>Product</TableHead>
rows={10} <TableHead className="text-right">Quantity</TableHead>
/> <TableHead className="text-right">Price/Unit</TableHead>
<TableHead className="text-right">Total</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{order?.products.map((product) => (
<TableRow key={product._id}>
<TableCell className="font-medium">
{productNames[product.productId] || "Loading..."}
</TableCell>
<TableCell className="text-right">{product.quantity}</TableCell>
<TableCell className="text-right">${product.pricePerUnit.toFixed(2)}</TableCell>
<TableCell className="text-right">${product.totalItemPrice.toFixed(2)}</TableCell>
</TableRow>
))}
<TableRow>
<TableCell colSpan={2} />
<TableCell className="text-right font-medium">Shipping ({order?.shippingMethod.type})</TableCell>
<TableCell className="text-right">${order?.shippingMethod.price.toFixed(2)}</TableCell>
</TableRow>
<TableRow>
<TableCell colSpan={2} />
<TableCell className="text-right font-bold">Total</TableCell>
<TableCell className="text-right font-bold">${order?.totalPrice.toFixed(2)}</TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent> </CardContent>
<CardFooter>
<Button
variant="outline"
onClick={() => copyToClipboard(order?.pgpAddress || "")}
>
<Clipboard className="w-4 h-4 mr-2" />
Copy to Clipboard
</Button>
</CardFooter>
</Card> </Card>
{/* Customer Details Card */}
<Card> <Card>
<CardHeader> <CardHeader>
<CardTitle>Shipping Information</CardTitle> <CardTitle className="text-lg font-medium">Customer Details</CardTitle>
<CardDescription>
Shipping method and tracking details
</CardDescription>
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<div className="space-y-2"> <div>
<Label>Shipping Method</Label> <Label className="text-sm font-medium text-zinc-500">PGP Address</Label>
<div className="text-sm text-gray-700 dark:text-gray-300 font-medium"> <div className="flex items-start gap-2 mt-1">
{order?.shippingMethod.type} - £ <Textarea
{order?.shippingMethod.price.toFixed(2)} value={order?.pgpAddress || ""}
readOnly
className="font-mono text-sm bg-zinc-950 h-32 resize-none"
/>
<Button
variant="outline"
size="sm"
onClick={() => copyToClipboard(order?.pgpAddress || "")}
>
<Clipboard className="h-4 w-4" />
</Button>
</div> </div>
</div> </div>
<div className="space-y-2">
<Label htmlFor="tracking">Tracking Number</Label>
<Input
id="tracking"
value={trackingNumber}
onChange={(e) => setTrackingNumber(e.target.value)}
placeholder="Enter tracking number"
/>
</div>
</CardContent> </CardContent>
<CardFooter>
<Button
onClick={handleAddTracking}
disabled={isSending || !trackingNumber.trim()}
>
<Package className="w-4 h-4 mr-2" />
{isSending ? "Updating..." : "Update Tracking"}
</Button>
</CardFooter>
</Card> </Card>
</div> </div>
<Card> {/* Right Column - Actions and Status */}
<CardHeader> <div className="space-y-6">
<CardTitle>Order Items</CardTitle> {/* Order Actions Card */}
</CardHeader> <Card>
<CardContent> <CardHeader>
<Table> <CardTitle className="text-lg font-medium">Order Actions</CardTitle>
<TableHeader> </CardHeader>
<TableRow> <CardContent className="space-y-4">
<TableHead>Item</TableHead> {order?.status === "unpaid" && (
<TableHead>Quantity</TableHead> <Button
<TableHead>Price</TableHead> className="w-full"
<TableHead>Total</TableHead> onClick={handleMarkAsPaid}
</TableRow> disabled={isPaid}
</TableHeader> >
<TableBody> Mark as Paid
{order?.products.map((product) => ( </Button>
<TableRow key={product._id}> )}
<TableCell>
{productNames[product.productId] || "Unknown Product (Deleted)"}
</TableCell>
<TableCell>{product.quantity}</TableCell>
<TableCell>£{product.pricePerUnit.toFixed(2)}</TableCell>
<TableCell>£{product.totalItemPrice.toFixed(2)}</TableCell>
</TableRow>
))}
<TableRow>
<TableCell colSpan={3} className="font-bold text-right">
Total:
</TableCell>
<TableCell className="font-bold">
£{order?.totalPrice.toFixed(2)}
</TableCell>
</TableRow>
</TableBody>
</Table>
</CardContent>
</Card>
{/* Add Crypto Transaction Details */} {order?.status === "paid" && (
<Collapsible> <Button
<div className="rounded-lg border bg-card text-card-foreground shadow-sm"> className="w-full"
<CollapsibleTrigger className="w-full"> onClick={handleMarkAsAcknowledged}
<div className="p-6 flex items-center justify-between"> disabled={isAcknowledging}
<h3 className="text-lg font-semibold">Crypto Transactions</h3> >
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200 [&[data-state=open]]:rotate-180" /> {isAcknowledging ? "Processing..." : "Acknowledge Order"}
</div> </Button>
</CollapsibleTrigger> )}
<CollapsibleContent>
<div className="px-6 pb-6"> {order?.status === "acknowledged" && (
{order?.txid && order.txid.length > 0 ? ( <div className="space-y-4">
<div className="space-y-3"> <div className="space-y-2">
{order.txid.slice(order.txid.length > 2 ? 1 : 0).map((txid: string, index: number) => ( <Label htmlFor="tracking">Tracking Number</Label>
<div <div className="flex gap-2">
key={index} <Input
className="p-3 bg-muted rounded-md" id="tracking"
value={trackingNumber}
onChange={(e) => setTrackingNumber(e.target.value)}
placeholder="Enter tracking number"
/>
<Button
variant="outline"
onClick={handleAddTracking}
disabled={!trackingNumber || isSending}
> >
<code className="text-sm break-all">{txid}</code> <Truck className="h-4 w-4" />
</div> </Button>
))} </div>
</div> </div>
) : (
<p className="text-muted-foreground">No crypto transactions associated with this order</p>
)}
</div>
</CollapsibleContent>
</div>
</Collapsible>
<div className="flex justify-between gap-4">
<div>
{order?.status !== "cancelled" && (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="destructive" size="lg" disabled={isDiscarding}>
{isDiscarding ? "Deleting..." : "Delete Order"}
</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
<AlertDialogDescription>
This action will permanently delete the order and cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction onClick={handleDiscardOrder}>
Delete Order
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)}
</div>
<div className="flex gap-4">
{(order?.status === "unpaid") && (
<AlertDialog>
<AlertDialogTrigger asChild>
<Button <Button
variant="destructive" className="w-full"
size="lg" onClick={handleMarkAsShipped}
disabled={isCancelling} disabled={isMarkingShipped || !trackingNumber}
> >
{isCancelling ? "Cancelling..." : "Cancel Order"} {isMarkingShipped ? "Processing..." : "Mark as Shipped"}
</Button> </Button>
</AlertDialogTrigger> </div>
<AlertDialogContent> )}
<AlertDialogHeader>
<AlertDialogTitle>Cancel this order?</AlertDialogTitle>
<AlertDialogDescription>
This will mark the order as cancelled. This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>No, keep it</AlertDialogCancel>
<AlertDialogAction onClick={handleCancelOrder}>
Yes, cancel order
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)}
{(order?.status === "unpaid" || order?.status === "paid") && ( {order?.status !== "cancelled" && order?.status !== "completed" && (
<Button <AlertDialog>
size="lg" <AlertDialogTrigger asChild>
onClick={handleMarkAsAcknowledged} <Button variant="destructive" className="w-full">
disabled={isAcknowledging} Cancel Order
className="bg-purple-600 hover:bg-purple-700" </Button>
> </AlertDialogTrigger>
{isAcknowledging ? "Updating..." : "Mark as Acknowledged"} <AlertDialogContent>
</Button> <AlertDialogHeader>
)} <AlertDialogTitle>Cancel Order</AlertDialogTitle>
<AlertDialogDescription>
Are you sure you want to cancel this order? This action cannot be undone.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={handleCancelOrder}
className="bg-red-500 hover:bg-red-600"
>
Confirm Cancel
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
)}
{order?.status === "unpaid" && ( {(order?.status === "completed" || order?.status === "cancelled") && (
<Button <div className="text-center py-6 text-sm text-zinc-500">
size="lg" No actions available - Order is {order.status.toLowerCase()}
onClick={handleMarkAsPaid} </div>
disabled={isPaid} )}
className="bg-emerald-600 hover:bg-emerald-700" </CardContent>
> </Card>
{isPaid ? "Order Marked as Paid" : "Mark Order as Paid"}
</Button>
)}
{(order?.status === "paid" || order?.status === "acknowledged") && ( {/* Transaction Details Card */}
<Button {order?.txid && order.txid.length > 0 && (
size="lg" <Card>
onClick={handleMarkAsShipped} <CardHeader>
disabled={isMarkingShipped} <CardTitle className="text-lg font-medium">Transaction Details</CardTitle>
className="bg-blue-600 hover:bg-blue-700" </CardHeader>
> <CardContent className="space-y-2">
<Truck className="w-5 h-5 mr-2" /> {order.txid.map((tx, index) => (
{isMarkingShipped ? "Updating..." : "Mark as Shipped"} <div key={index} className="flex items-center gap-2">
</Button> <Input
)} value={tx}
</div> readOnly
className="font-mono text-sm bg-zinc-950"
/>
<Button
variant="outline"
size="sm"
onClick={() => copyToClipboard(tx)}
>
<Clipboard className="h-4 w-4" />
</Button>
</div>
))}
</CardContent>
</Card>
)}
{/* Tracking Information Card */}
{order?.trackingNumber && (
<Card>
<CardHeader>
<CardTitle className="text-lg font-medium">Tracking Information</CardTitle>
</CardHeader>
<CardContent className="space-y-2">
<div className="flex items-center gap-2">
<Input
value={order.trackingNumber}
readOnly
className="font-mono text-sm bg-zinc-950"
/>
<Button
variant="outline"
size="sm"
onClick={() => copyToClipboard(order.trackingNumber || "")}
>
<Clipboard className="h-4 w-4" />
</Button>
</div>
</CardContent>
</Card>
)}
</div> </div>
</div> </div>
</div> </div>