diff --git a/client/components/admin/AdminChangelogDigest.tsx b/client/components/admin/AdminChangelogDigest.tsx new file mode 100644 index 00000000..582d181a --- /dev/null +++ b/client/components/admin/AdminChangelogDigest.tsx @@ -0,0 +1,122 @@ +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { formatDistanceToNow } from "date-fns"; +import type { LucideIcon } from "lucide-react"; +import { ArrowUpRight, Bug, Plus, Shield } from "lucide-react"; + +interface ChangelogChange { + type: "added" | "improved" | "fixed" | "removed" | "security"; + description: string; + impact: "high" | "medium" | "low"; +} + +interface ChangelogEntry { + id: string; + version: string; + date: string; + title: string; + description: string; + changes: ChangelogChange[]; + author: string; +} + +interface AdminChangelogDigestProps { + entries: ChangelogEntry[]; + onViewChangelog: () => void; +} + +const changeIcon: Record = { + added: Plus, + improved: ArrowUpRight, + fixed: Bug, + removed: ArrowUpRight, + security: Shield, +}; + +const changeAccent: Record = { + added: "text-emerald-300", + improved: "text-sky-300", + fixed: "text-amber-300", + removed: "text-rose-300", + security: "text-purple-300", +}; + +export default function AdminChangelogDigest({ + entries, + onViewChangelog, +}: AdminChangelogDigestProps) { + return ( + + +
+
+ Latest changelog + + Recent platform improvements curated for the owner dashboard. + +
+ +
+
+ + {entries.length === 0 ? ( +

+ No changelog entries available. Publish a new release note to update + this feed. +

+ ) : ( + entries.map((entry) => { + const recentChanges = entry.changes.slice(0, 3); + return ( +
+
+
+

+ {entry.title} +

+

+ Version {entry.version} • {entry.author} +

+
+ + {formatDistanceToNow(new Date(entry.date), { addSuffix: true })} + +
+

+ {entry.description} +

+ {recentChanges.length ? ( +
    + {recentChanges.map((change, idx) => { + const ChangeIcon = changeIcon[change.type]; + return ( +
  • + + + + {change.type.charAt(0).toUpperCase() + change.type.slice(1)}: + {" "} + {change.description} + +
  • + ); + })} +
+ ) : null} +
+ ); + }) + )} +
+
+ ); +}