Files
ember-market-frontend/components/dashboard/recent-activity.tsx
g 318927cd0c
Some checks failed
Build Frontend / build (push) Failing after 7s
Add modular dashboard widgets and layout editor
Introduces a modular dashboard system with draggable, configurable widgets including revenue, low stock, recent customers, and pending chats. Adds a dashboard editor for layout customization, widget visibility, and settings. Refactors dashboard content to use the new widget system and improves UI consistency and interactivity.
2026-01-12 10:39:50 +00:00

120 lines
5.2 KiB
TypeScript

"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 { RelativeTime } from "@/components/ui/relative-time"
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=10&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">
<RelativeTime date={item.orderDate} />
</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>
)
}