Enhance dashboard UI and add order timeline
All checks were successful
Build Frontend / build (push) Successful in 1m12s
All checks were successful
Build Frontend / build (push) Successful in 1m12s
Refactored dashboard pages for improved layout and visual consistency using Card components, motion animations, and updated color schemes. Added an OrderTimeline component to the order details page to visualize order lifecycle. Improved customer management page with better sorting, searching, and a detailed customer dialog. Updated storefront settings page with a modernized layout and clearer sectioning.
This commit is contained in:
119
components/dashboard/recent-activity.tsx
Normal file
119
components/dashboard/recent-activity.tsx
Normal file
@@ -0,0 +1,119 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import { motion } from "framer-motion"
|
||||
import { ShoppingBag, CreditCard, Truck, MessageSquare, AlertCircle } from "lucide-react"
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
|
||||
import { clientFetch } from "@/lib/api"
|
||||
import { Skeleton } from "@/components/ui/skeleton"
|
||||
import { formatDistanceToNow } from "date-fns"
|
||||
import Link from "next/link"
|
||||
|
||||
interface ActivityItem {
|
||||
_id: string;
|
||||
orderId: string;
|
||||
status: string;
|
||||
totalPrice: number;
|
||||
orderDate: string;
|
||||
username?: string;
|
||||
}
|
||||
|
||||
export default function RecentActivity() {
|
||||
const [activities, setActivities] = useState<ActivityItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
useEffect(() => {
|
||||
async function fetchRecentOrders() {
|
||||
try {
|
||||
const data = await clientFetch("/orders?limit=5&sortBy=orderDate&sortOrder=desc");
|
||||
setActivities(data.orders || []);
|
||||
} catch (error) {
|
||||
console.error("Failed to fetch recent activity:", error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
|
||||
fetchRecentOrders();
|
||||
}, []);
|
||||
|
||||
const getStatusIcon = (status: string) => {
|
||||
switch (status) {
|
||||
case "paid": return <CreditCard className="h-4 w-4" />;
|
||||
case "shipped": return <Truck className="h-4 w-4" />;
|
||||
case "unpaid": return <ShoppingBag className="h-4 w-4" />;
|
||||
default: return <AlertCircle className="h-4 w-4" />;
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case "paid": return "bg-emerald-500/10 text-emerald-500";
|
||||
case "shipped": return "bg-blue-500/10 text-blue-500";
|
||||
case "unpaid": return "bg-amber-500/10 text-amber-500";
|
||||
default: return "bg-gray-500/10 text-gray-500";
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className="h-full">
|
||||
<CardHeader>
|
||||
<CardTitle>Recent Activity</CardTitle>
|
||||
<CardDescription>Latest updates from your store</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{loading ? (
|
||||
<div className="space-y-4">
|
||||
{[1, 2, 3, 4, 5].map((i) => (
|
||||
<div key={i} className="flex items-center gap-4">
|
||||
<Skeleton className="h-8 w-8 rounded-full" />
|
||||
<div className="space-y-2 flex-1">
|
||||
<Skeleton className="h-4 w-3/4" />
|
||||
<Skeleton className="h-3 w-1/4" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : activities.length === 0 ? (
|
||||
<div className="py-8 text-center text-muted-foreground">
|
||||
No recent activity
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-6">
|
||||
{activities.map((item, index) => (
|
||||
<motion.div
|
||||
key={item._id}
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ delay: index * 0.1 }}
|
||||
className="flex items-start gap-4 relative"
|
||||
>
|
||||
{index !== activities.length - 1 && (
|
||||
<div className="absolute left-[15px] top-8 bottom-[-24px] w-[2px] bg-border/50" />
|
||||
)}
|
||||
<div className={`mt-1 p-2 rounded-full z-10 ${getStatusColor(item.status)}`}>
|
||||
{getStatusIcon(item.status)}
|
||||
</div>
|
||||
<div className="flex-1 space-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
<Link href={`/dashboard/orders/${item._id}`} className="font-medium hover:underline">
|
||||
Order #{item.orderId}
|
||||
</Link>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{formatDistanceToNow(new Date(item.orderDate), { addSuffix: true })}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{item.status === "paid" ? "Payment received" :
|
||||
item.status === "shipped" ? "Order marked as shipped" :
|
||||
`Order status: ${item.status}`} for £{item.totalPrice.toFixed(2)}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user