update storefront page
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user