Cleanup
This commit is contained in:
@@ -24,7 +24,7 @@ import {
|
|||||||
CardTitle,
|
CardTitle,
|
||||||
} from "@/components/ui/card";
|
} from "@/components/ui/card";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Clipboard, Truck, Package, ArrowRight } from "lucide-react";
|
import { Clipboard, Truck, Package, ArrowRight, ChevronDown } from "lucide-react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
import {
|
||||||
@@ -38,6 +38,12 @@ import {
|
|||||||
AlertDialogTitle,
|
AlertDialogTitle,
|
||||||
AlertDialogTrigger,
|
AlertDialogTrigger,
|
||||||
} from "@/components/ui/alert-dialog";
|
} from "@/components/ui/alert-dialog";
|
||||||
|
import Layout from "@/components/layout/layout";
|
||||||
|
import {
|
||||||
|
Collapsible,
|
||||||
|
CollapsibleContent,
|
||||||
|
CollapsibleTrigger,
|
||||||
|
} from "@/components/ui/collapsible";
|
||||||
|
|
||||||
interface Order {
|
interface Order {
|
||||||
orderId: string;
|
orderId: string;
|
||||||
@@ -58,16 +64,20 @@ interface Order {
|
|||||||
|
|
||||||
const getStatusVariant = (status: string) => {
|
const getStatusVariant = (status: string) => {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
|
case 'acknowledged':
|
||||||
|
return 'secondary';
|
||||||
case 'paid':
|
case 'paid':
|
||||||
return 'paid';
|
return 'default';
|
||||||
case 'shipped':
|
case 'shipped':
|
||||||
return 'shipped';
|
return 'default';
|
||||||
case 'completed':
|
case 'completed':
|
||||||
return 'completed';
|
return 'default';
|
||||||
case 'cancelled':
|
case 'cancelled':
|
||||||
return 'destructive';
|
return 'destructive';
|
||||||
|
case 'unpaid':
|
||||||
|
return 'secondary';
|
||||||
default:
|
default:
|
||||||
return 'unpaid';
|
return 'secondary';
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -87,6 +97,7 @@ export default function OrderDetailsPage() {
|
|||||||
const [currentOrderNumber, setCurrentOrderNumber] = useState(0);
|
const [currentOrderNumber, setCurrentOrderNumber] = useState(0);
|
||||||
const [totalPages, setTotalPages] = useState(1);
|
const [totalPages, setTotalPages] = useState(1);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const [isAcknowledging, setIsAcknowledging] = useState(false);
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const params = useParams();
|
const params = useParams();
|
||||||
@@ -177,9 +188,9 @@ export default function OrderDetailsPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleDiscardOrder = async () => {
|
const handleMarkAsAcknowledged = async () => {
|
||||||
try {
|
try {
|
||||||
setIsDiscarding(true);
|
setIsAcknowledging(true);
|
||||||
const authToken = document.cookie.split("Authorization=")[1];
|
const authToken = document.cookie.split("Authorization=")[1];
|
||||||
const response = await fetchData(
|
const response = await fetchData(
|
||||||
`${process.env.NEXT_PUBLIC_API_URL}/orders/${orderId}`,
|
`${process.env.NEXT_PUBLIC_API_URL}/orders/${orderId}`,
|
||||||
@@ -189,19 +200,47 @@ export default function OrderDetailsPage() {
|
|||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
Authorization: `Bearer ${authToken}`,
|
Authorization: `Bearer ${authToken}`,
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ status: "cancelled" }),
|
body: JSON.stringify({ status: "acknowledged" }),
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response && response.message === "Order status updated successfully") {
|
if (response && response.message === "Order status updated successfully") {
|
||||||
setOrder((prevOrder) => prevOrder ? { ...prevOrder, status: "cancelled" } : null);
|
setOrder((prevOrder) => prevOrder ? { ...prevOrder, status: "acknowledged" } : null);
|
||||||
toast.success("Order discarded successfully!");
|
toast.success("Order marked as acknowledged!");
|
||||||
} else {
|
} else {
|
||||||
throw new Error(response.error || "Failed to discard order");
|
throw new Error(response.error || "Failed to mark order as acknowledged");
|
||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error("Failed to discard order:", error);
|
console.error("Failed to mark order as acknowledged:", error);
|
||||||
toast.error(error.message || "Failed to discard order");
|
toast.error(error.message || "Failed to mark order as acknowledged");
|
||||||
|
} finally {
|
||||||
|
setIsAcknowledging(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDiscardOrder = async () => {
|
||||||
|
try {
|
||||||
|
setIsDiscarding(true);
|
||||||
|
const authToken = document.cookie.split("Authorization=")[1];
|
||||||
|
const response = await fetchData(
|
||||||
|
`${process.env.NEXT_PUBLIC_API_URL}/orders/${orderId}`,
|
||||||
|
{
|
||||||
|
method: "DELETE",
|
||||||
|
headers: {
|
||||||
|
Authorization: `Bearer ${authToken}`,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response && response.message === "Order deleted successfully") {
|
||||||
|
toast.success("Order deleted successfully!");
|
||||||
|
router.push('/dashboard/orders');
|
||||||
|
} else {
|
||||||
|
throw new Error(response.error || "Failed to delete order");
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("Failed to delete order:", error);
|
||||||
|
toast.error(error.message || "Failed to delete order");
|
||||||
} finally {
|
} finally {
|
||||||
setIsDiscarding(false);
|
setIsDiscarding(false);
|
||||||
}
|
}
|
||||||
@@ -345,235 +384,281 @@ export default function OrderDetailsPage() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (loading)
|
if (loading)
|
||||||
return <div className="text-center py-10">Loading order details...</div>;
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className="text-center py-10">Loading order details...</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
if (error)
|
if (error)
|
||||||
return <div className="text-center text-red-500 py-10">Error: {error}</div>;
|
return (
|
||||||
|
<Layout>
|
||||||
|
<div className="text-center text-red-500 py-10">Error: {error}</div>
|
||||||
|
</Layout>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container mx-auto px-4 py-8">
|
<Layout>
|
||||||
<div className="space-y-6">
|
<div className="container mx-auto px-4 py-8">
|
||||||
<div className="flex justify-between items-center">
|
<div className="space-y-6">
|
||||||
<div className="flex items-center gap-4">
|
<div className="flex justify-between items-center">
|
||||||
<h1 className="text-3xl font-bold">Order Details: {order?.orderId}</h1>
|
<div className="flex items-center gap-4">
|
||||||
|
<h1 className="text-3xl font-bold">Order Details: {order?.orderId}</h1>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Badge
|
||||||
|
variant={getStatusVariant(order?.status || '')}
|
||||||
|
className={`${
|
||||||
|
order?.status === 'acknowledged'
|
||||||
|
? '!bg-purple-600 hover:!bg-purple-700 text-white'
|
||||||
|
: order?.status === 'paid'
|
||||||
|
? '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}
|
||||||
|
</Badge>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Order Navigation - Moved to top */}
|
||||||
|
<div className="flex justify-between items-center border-b pb-4">
|
||||||
|
<div className="w-[140px]">
|
||||||
|
{nextOrderId && (
|
||||||
|
<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>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>PGP Encrypted Address</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Securely encrypted delivery address
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<Textarea
|
||||||
|
value={order?.pgpAddress || ""}
|
||||||
|
readOnly
|
||||||
|
className="font-mono text-xs"
|
||||||
|
rows={10}
|
||||||
|
/>
|
||||||
|
</CardContent>
|
||||||
|
<CardFooter>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => copyToClipboard(order?.pgpAddress || "")}
|
||||||
|
>
|
||||||
|
<Clipboard className="w-4 h-4 mr-2" />
|
||||||
|
Copy to Clipboard
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
|
|
||||||
|
<Card>
|
||||||
|
<CardHeader>
|
||||||
|
<CardTitle>Shipping Information</CardTitle>
|
||||||
|
<CardDescription>
|
||||||
|
Shipping method and tracking details
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent className="space-y-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<Label>Shipping Method</Label>
|
||||||
|
<div className="text-sm text-gray-700 dark:text-gray-300 font-medium">
|
||||||
|
{order?.shippingMethod.type} - £
|
||||||
|
{order?.shippingMethod.price.toFixed(2)}
|
||||||
|
</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>
|
||||||
|
<CardFooter>
|
||||||
|
<Button
|
||||||
|
onClick={handleAddTracking}
|
||||||
|
disabled={isSending || !trackingNumber.trim()}
|
||||||
|
>
|
||||||
|
<Package className="w-4 h-4 mr-2" />
|
||||||
|
{isSending ? "Updating..." : "Update Tracking"}
|
||||||
|
</Button>
|
||||||
|
</CardFooter>
|
||||||
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<Badge variant={getStatusVariant(order?.status || '')}>
|
|
||||||
{order?.status}
|
|
||||||
</Badge>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="grid gap-6 md:grid-cols-2">
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardTitle>PGP Encrypted Address</CardTitle>
|
<CardTitle>Order Items</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>Item</TableHead>
|
||||||
rows={10}
|
<TableHead>Quantity</TableHead>
|
||||||
/>
|
<TableHead>Price</TableHead>
|
||||||
</CardContent>
|
<TableHead>Total</TableHead>
|
||||||
<CardFooter>
|
|
||||||
<Button
|
|
||||||
variant="outline"
|
|
||||||
onClick={() => copyToClipboard(order?.pgpAddress || "")}
|
|
||||||
>
|
|
||||||
<Clipboard className="w-4 h-4 mr-2" />
|
|
||||||
Copy to Clipboard
|
|
||||||
</Button>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Shipping Information</CardTitle>
|
|
||||||
<CardDescription>
|
|
||||||
Shipping method and tracking details
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent className="space-y-4">
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label>Shipping Method</Label>
|
|
||||||
<div className="text-sm text-gray-700 dark:text-gray-300 font-medium">
|
|
||||||
{order?.shippingMethod.type} - £
|
|
||||||
{order?.shippingMethod.price.toFixed(2)}
|
|
||||||
</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>
|
|
||||||
<CardFooter>
|
|
||||||
<Button
|
|
||||||
onClick={handleAddTracking}
|
|
||||||
disabled={isSending || !trackingNumber.trim()}
|
|
||||||
>
|
|
||||||
<Package className="w-4 h-4 mr-2" />
|
|
||||||
{isSending ? "Updating..." : "Update Tracking"}
|
|
||||||
</Button>
|
|
||||||
</CardFooter>
|
|
||||||
</Card>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Order Items</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<Table>
|
|
||||||
<TableHeader>
|
|
||||||
<TableRow>
|
|
||||||
<TableHead>Item</TableHead>
|
|
||||||
<TableHead>Quantity</TableHead>
|
|
||||||
<TableHead>Price</TableHead>
|
|
||||||
<TableHead>Total</TableHead>
|
|
||||||
</TableRow>
|
|
||||||
</TableHeader>
|
|
||||||
<TableBody>
|
|
||||||
{order?.products.map((product) => (
|
|
||||||
<TableRow key={product._id}>
|
|
||||||
<TableCell>
|
|
||||||
{productNames[product.productId] || "Loading..."}
|
|
||||||
</TableCell>
|
|
||||||
<TableCell>{product.quantity}</TableCell>
|
|
||||||
<TableCell>£{product.pricePerUnit.toFixed(2)}</TableCell>
|
|
||||||
<TableCell>£{product.totalItemPrice.toFixed(2)}</TableCell>
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
))}
|
</TableHeader>
|
||||||
<TableRow>
|
<TableBody>
|
||||||
<TableCell colSpan={3} className="font-bold text-right">
|
{order?.products.map((product) => (
|
||||||
Total:
|
<TableRow key={product._id}>
|
||||||
</TableCell>
|
<TableCell>
|
||||||
<TableCell className="font-bold">
|
{productNames[product.productId] || "Loading..."}
|
||||||
£{order?.totalPrice.toFixed(2)}
|
</TableCell>
|
||||||
</TableCell>
|
<TableCell>{product.quantity}</TableCell>
|
||||||
</TableRow>
|
<TableCell>£{product.pricePerUnit.toFixed(2)}</TableCell>
|
||||||
</TableBody>
|
<TableCell>£{product.totalItemPrice.toFixed(2)}</TableCell>
|
||||||
</Table>
|
</TableRow>
|
||||||
</CardContent>
|
))}
|
||||||
</Card>
|
<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 */}
|
{/* Add Crypto Transaction Details */}
|
||||||
<div className="rounded-lg border bg-card text-card-foreground shadow-sm">
|
<Collapsible>
|
||||||
<div className="p-6">
|
<div className="rounded-lg border bg-card text-card-foreground shadow-sm">
|
||||||
<h3 className="text-lg font-semibold mb-4">Crypto Transactions</h3>
|
<CollapsibleTrigger className="w-full">
|
||||||
{order?.txid && order.txid.length > 0 ? (
|
<div className="p-6 flex items-center justify-between">
|
||||||
<div className="space-y-3">
|
<h3 className="text-lg font-semibold">Crypto Transactions</h3>
|
||||||
{order.txid.slice(order.txid.length > 2 ? 1 : 0).map((txid: string, index: number) => (
|
<ChevronDown className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200 [&[data-state=open]]:rotate-180" />
|
||||||
<div
|
</div>
|
||||||
key={index}
|
</CollapsibleTrigger>
|
||||||
className="p-3 bg-muted rounded-md"
|
<CollapsibleContent>
|
||||||
>
|
<div className="px-6 pb-6">
|
||||||
<code className="text-sm">{txid}</code>
|
{order?.txid && order.txid.length > 0 ? (
|
||||||
</div>
|
<div className="space-y-3">
|
||||||
))}
|
{order.txid.slice(order.txid.length > 2 ? 1 : 0).map((txid: string, index: number) => (
|
||||||
</div>
|
<div
|
||||||
) : (
|
key={index}
|
||||||
<p className="text-muted-foreground">No crypto transactions associated with this order</p>
|
className="p-3 bg-muted rounded-md"
|
||||||
)}
|
>
|
||||||
</div>
|
<code className="text-sm break-all">{txid}</code>
|
||||||
</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 mt-4">
|
<div className="flex justify-between gap-4">
|
||||||
<div>
|
<div>
|
||||||
{order?.status !== "cancelled" && (
|
{order?.status !== "cancelled" && (
|
||||||
<AlertDialog>
|
<AlertDialog>
|
||||||
<AlertDialogTrigger asChild>
|
<AlertDialogTrigger asChild>
|
||||||
<Button variant="destructive" size="lg" disabled={isDiscarding}>
|
<Button variant="destructive" size="lg" disabled={isDiscarding}>
|
||||||
{isDiscarding ? "Discarding..." : "Discard Order"}
|
{isDiscarding ? "Deleting..." : "Delete Order"}
|
||||||
</Button>
|
</Button>
|
||||||
</AlertDialogTrigger>
|
</AlertDialogTrigger>
|
||||||
<AlertDialogContent>
|
<AlertDialogContent>
|
||||||
<AlertDialogHeader>
|
<AlertDialogHeader>
|
||||||
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
<AlertDialogTitle>Are you sure?</AlertDialogTitle>
|
||||||
<AlertDialogDescription>
|
<AlertDialogDescription>
|
||||||
This action will discard the order and cannot be undone.
|
This action will permanently delete the order and cannot be undone.
|
||||||
</AlertDialogDescription>
|
</AlertDialogDescription>
|
||||||
</AlertDialogHeader>
|
</AlertDialogHeader>
|
||||||
<AlertDialogFooter>
|
<AlertDialogFooter>
|
||||||
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
<AlertDialogCancel>Cancel</AlertDialogCancel>
|
||||||
<AlertDialogAction onClick={handleDiscardOrder}>
|
<AlertDialogAction onClick={handleDiscardOrder}>
|
||||||
Discard Order
|
Delete Order
|
||||||
</AlertDialogAction>
|
</AlertDialogAction>
|
||||||
</AlertDialogFooter>
|
</AlertDialogFooter>
|
||||||
</AlertDialogContent>
|
</AlertDialogContent>
|
||||||
</AlertDialog>
|
</AlertDialog>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-4">
|
<div className="flex gap-4">
|
||||||
{order?.status === "unpaid" && (
|
{(order?.status === "unpaid" || order?.status === "paid") && (
|
||||||
<Button
|
<Button
|
||||||
size="lg"
|
size="lg"
|
||||||
onClick={handleMarkAsPaid}
|
onClick={handleMarkAsAcknowledged}
|
||||||
disabled={isPaid}
|
disabled={isAcknowledging}
|
||||||
className="bg-emerald-600 hover:bg-emerald-700"
|
className="bg-purple-600 hover:bg-purple-700"
|
||||||
>
|
>
|
||||||
{isPaid ? "Order Marked as Paid" : "Mark Order as Paid"}
|
{isAcknowledging ? "Updating..." : "Mark as Acknowledged"}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{order?.status === "paid" && (
|
|
||||||
<Button
|
|
||||||
size="lg"
|
|
||||||
onClick={handleMarkAsShipped}
|
|
||||||
disabled={isMarkingShipped}
|
|
||||||
className="bg-blue-600 hover:bg-blue-700"
|
|
||||||
>
|
|
||||||
<Truck className="w-5 h-5 mr-2" />
|
|
||||||
{isMarkingShipped ? "Updating..." : "Mark as Shipped"}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Order Navigation */}
|
{(order?.status === "unpaid" || order?.status === "acknowledged") && (
|
||||||
<div className="flex justify-between items-center pt-8 mt-8 border-t">
|
<Button
|
||||||
<div className="w-[140px]">
|
size="lg"
|
||||||
{prevOrderId && (
|
onClick={handleMarkAsPaid}
|
||||||
<Button
|
disabled={isPaid}
|
||||||
variant="outline"
|
className="bg-emerald-600 hover:bg-emerald-700"
|
||||||
size="lg"
|
>
|
||||||
onClick={() => {
|
{isPaid ? "Order Marked as Paid" : "Mark Order as Paid"}
|
||||||
setLoading(true);
|
</Button>
|
||||||
router.push(`/dashboard/orders/${prevOrderId}`);
|
)}
|
||||||
}}
|
|
||||||
className="flex items-center"
|
{order?.status === "paid" && (
|
||||||
>
|
<Button
|
||||||
<span className="mr-2">←</span>
|
size="lg"
|
||||||
Newer
|
onClick={handleMarkAsShipped}
|
||||||
</Button>
|
disabled={isMarkingShipped}
|
||||||
)}
|
className="bg-blue-600 hover:bg-blue-700"
|
||||||
</div>
|
>
|
||||||
<div className="w-[140px] flex justify-end">
|
<Truck className="w-5 h-5 mr-2" />
|
||||||
{nextOrderId && (
|
{isMarkingShipped ? "Updating..." : "Mark as Shipped"}
|
||||||
<Button
|
</Button>
|
||||||
variant="outline"
|
)}
|
||||||
size="lg"
|
</div>
|
||||||
onClick={() => {
|
|
||||||
setLoading(true);
|
|
||||||
router.push(`/dashboard/orders/${nextOrderId}`);
|
|
||||||
}}
|
|
||||||
className="flex items-center"
|
|
||||||
>
|
|
||||||
Older
|
|
||||||
<span className="ml-2">→</span>
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, useCallback } from "react";
|
import { useState, useEffect, useCallback } from "react";
|
||||||
|
import React from "react";
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@@ -57,10 +58,11 @@ type SortableColumns = "orderId" | "totalPrice" | "status" | "orderDate";
|
|||||||
interface StatusConfig {
|
interface StatusConfig {
|
||||||
icon: React.ElementType;
|
icon: React.ElementType;
|
||||||
color: string;
|
color: string;
|
||||||
|
bgColor: string;
|
||||||
animate?: string;
|
animate?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
type OrderStatus = "paid" | "unpaid" | "confirming" | "shipped" | "completed" | "disputed" | "cancelled";
|
type OrderStatus = "paid" | "unpaid" | "acknowledged" | "shipped" | "completed" | "cancelled" | "confirming";
|
||||||
|
|
||||||
export default function OrderTable() {
|
export default function OrderTable() {
|
||||||
const [orders, setOrders] = useState<Order[]>([]);
|
const [orders, setOrders] = useState<Order[]>([]);
|
||||||
@@ -208,13 +210,42 @@ export default function OrderTable() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const statusConfig: Record<OrderStatus, StatusConfig> = {
|
const statusConfig: Record<OrderStatus, StatusConfig> = {
|
||||||
paid: { icon: CheckCircle2, color: "text-green-500" },
|
acknowledged: {
|
||||||
unpaid: { icon: XCircle, color: "text-red-500" },
|
icon: CheckCircle2,
|
||||||
confirming: { icon: Loader2, color: "text-yellow-500", animate: "animate-spin" },
|
color: "text-white",
|
||||||
shipped: { icon: Truck, color: "text-blue-500" },
|
bgColor: "bg-purple-600"
|
||||||
completed: { icon: CheckCircle2, color: "text-gray-500" },
|
},
|
||||||
disputed: { icon: XCircle, color: "text-orange-500" },
|
paid: {
|
||||||
cancelled: { icon: XCircle, color: "text-gray-400" }
|
icon: CheckCircle2,
|
||||||
|
color: "text-white",
|
||||||
|
bgColor: "bg-emerald-600"
|
||||||
|
},
|
||||||
|
unpaid: {
|
||||||
|
icon: XCircle,
|
||||||
|
color: "text-white",
|
||||||
|
bgColor: "bg-red-500"
|
||||||
|
},
|
||||||
|
confirming: {
|
||||||
|
icon: Loader2,
|
||||||
|
color: "text-white",
|
||||||
|
bgColor: "bg-yellow-500",
|
||||||
|
animate: "animate-spin"
|
||||||
|
},
|
||||||
|
shipped: {
|
||||||
|
icon: Truck,
|
||||||
|
color: "text-white",
|
||||||
|
bgColor: "bg-blue-600"
|
||||||
|
},
|
||||||
|
completed: {
|
||||||
|
icon: CheckCircle2,
|
||||||
|
color: "text-white",
|
||||||
|
bgColor: "bg-green-600"
|
||||||
|
},
|
||||||
|
cancelled: {
|
||||||
|
icon: XCircle,
|
||||||
|
color: "text-white",
|
||||||
|
bgColor: "bg-gray-500"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -317,8 +348,12 @@ export default function OrderTable() {
|
|||||||
<TableCell>#{order.orderId}</TableCell>
|
<TableCell>#{order.orderId}</TableCell>
|
||||||
<TableCell>£{order.totalPrice.toFixed(2)}</TableCell>
|
<TableCell>£{order.totalPrice.toFixed(2)}</TableCell>
|
||||||
<TableCell>
|
<TableCell>
|
||||||
<div className={`flex items-center ${statusConfig[order.status as OrderStatus]?.color || "text-red-500"}`}>
|
<div className={`inline-flex items-center gap-2 px-3 py-1 rounded-full text-sm ${
|
||||||
<StatusIcon className={`h-4 w-4 mr-2 ${statusConfig[order.status as OrderStatus]?.animate || ""}`} />
|
statusConfig[order.status as OrderStatus]?.bgColor || "bg-gray-500"
|
||||||
|
} ${statusConfig[order.status as OrderStatus]?.color || "text-white"}`}>
|
||||||
|
{React.createElement(statusConfig[order.status as OrderStatus]?.icon || XCircle, {
|
||||||
|
className: `h-4 w-4 ${statusConfig[order.status as OrderStatus]?.animate || ""}`
|
||||||
|
})}
|
||||||
{order.status.charAt(0).toUpperCase() + order.status.slice(1)}
|
{order.status.charAt(0).toUpperCase() + order.status.slice(1)}
|
||||||
</div>
|
</div>
|
||||||
</TableCell>
|
</TableCell>
|
||||||
|
|||||||
Reference in New Issue
Block a user