import type { ReactElement } from "react"; import { Card, SectionHeader, Badge } from "@mosaic/ui"; import type { RecentActivity } from "@/lib/api/dashboard"; type BadgeVariantType = | "badge-amber" | "badge-red" | "badge-teal" | "badge-blue" | "badge-muted" | "badge-purple" | "badge-pulse"; interface ActivityDisplayItem { id: string; icon: string; iconBg: string; title: string; highlight: string; rest: string; timestamp: string; badge?: | { text: string; variant: BadgeVariantType; } | undefined; } export interface ActivityFeedProps { items?: RecentActivity[] | undefined; } /* ------------------------------------------------------------------ */ /* Mapping helpers */ /* ------------------------------------------------------------------ */ function getIconForAction(action: string): { icon: string; iconBg: string } { const lower = action.toLowerCase(); if (lower.includes("complet") || lower.includes("finish") || lower.includes("success")) { return { icon: "\u2713", iconBg: "rgba(20,184,166,0.15)" }; } if (lower.includes("fail") || lower.includes("error")) { return { icon: "\u2717", iconBg: "rgba(229,72,77,0.15)" }; } if (lower.includes("warn") || lower.includes("limit")) { return { icon: "\u26A0", iconBg: "rgba(245,158,11,0.15)" }; } if (lower.includes("start") || lower.includes("creat")) { return { icon: "\u2191", iconBg: "rgba(47,128,255,0.15)" }; } if (lower.includes("update") || lower.includes("modif")) { return { icon: "\u21BB", iconBg: "rgba(139,92,246,0.15)" }; } return { icon: "\u2022", iconBg: "rgba(100,116,139,0.15)" }; } function getBadgeForAction(action: string): ActivityDisplayItem["badge"] { const lower = action.toLowerCase(); if (lower.includes("fail") || lower.includes("error")) { return { text: "error", variant: "badge-red" }; } if (lower.includes("warn") || lower.includes("limit")) { return { text: "warn", variant: "badge-amber" }; } return undefined; } function formatRelativeTime(isoDate: string): string { const now = Date.now(); const then = new Date(isoDate).getTime(); const diffMs = now - then; if (Number.isNaN(diffMs) || diffMs < 0) return "just now"; const minutes = Math.floor(diffMs / 60_000); if (minutes < 1) return "just now"; if (minutes < 60) return `${String(minutes)}m ago`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${String(hours)}h ago`; const days = Math.floor(hours / 24); return `${String(days)}d ago`; } function mapActivityToDisplay(activity: RecentActivity): ActivityDisplayItem { const { icon, iconBg } = getIconForAction(activity.action); return { id: activity.id, icon, iconBg, title: "", highlight: activity.entityType, rest: ` ${activity.action} (${activity.entityId})`, timestamp: formatRelativeTime(activity.createdAt), badge: getBadgeForAction(activity.action), }; } /* ------------------------------------------------------------------ */ /* Components */ /* ------------------------------------------------------------------ */ interface ActivityItemRowProps { item: ActivityDisplayItem; } function ActivityItemRow({ item }: ActivityItemRowProps): ReactElement { return (
{item.icon}
{item.title} {item.highlight} {item.rest} {item.badge !== undefined && ( {item.badge.text} )}
{item.timestamp}
); } export function ActivityFeed({ items }: ActivityFeedProps): ReactElement { const displayItems = items ? items.map(mapActivityToDisplay) : []; return (
{displayItems.length > 0 ? ( displayItems.map((item) => ) ) : (
No recent activity
)}
); }