"use client"; import { Bell, BellOff, CheckCheck, ExternalLink, Filter, Info, type Megaphone, Rocket, Wrench, X, Zap, } from "lucide-react"; import Link from "next/link"; import { useState } from "react"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from "@/components/ui/card"; import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import { Separator } from "@/components/ui/separator"; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; import type { AnnouncementCategory } from "@/contracts/types/announcement.types"; import { type AnnouncementWithState, useAnnouncements } from "@/hooks/use-announcements"; import { formatRelativeDate } from "@/lib/format-date"; // --------------------------------------------------------------------------- // Category configuration // --------------------------------------------------------------------------- const categoryConfig: Record< AnnouncementCategory, { label: string; icon: typeof Megaphone; color: string; badgeVariant: "default" | "secondary" | "destructive" | "outline"; } > = { feature: { label: "Feature", icon: Rocket, color: "text-emerald-500", badgeVariant: "default", }, update: { label: "Update", icon: Zap, color: "text-blue-500", badgeVariant: "secondary", }, maintenance: { label: "Maintenance", icon: Wrench, color: "text-amber-500", badgeVariant: "outline", }, info: { label: "Info", icon: Info, color: "text-muted-foreground", badgeVariant: "secondary", }, }; // --------------------------------------------------------------------------- // Announcement card // --------------------------------------------------------------------------- function AnnouncementCard({ announcement, onMarkRead, onDismiss, }: { announcement: AnnouncementWithState; onMarkRead: (id: string) => void; onDismiss: (id: string) => void; }) { const config = categoryConfig[announcement.category]; const Icon = config.icon; return (
{announcement.title} {config.label} {announcement.isImportant && ( Important )} {!announcement.isRead && ( )}
{formatRelativeDate(announcement.date)}
{/* Actions */}
{!announcement.isRead && ( Mark as read )} Dismiss

{announcement.description}

{announcement.link && ( )}
); } // --------------------------------------------------------------------------- // Empty state // --------------------------------------------------------------------------- function EmptyState({ hasFilters }: { hasFilters: boolean }) { return (
{hasFilters ? ( ) : ( )}

{hasFilters ? "No matching announcements" : "No announcements"}

{hasFilters ? "Try adjusting your filters to see more announcements." : "You're all caught up! New announcements will appear here."}

); } // --------------------------------------------------------------------------- // Page // --------------------------------------------------------------------------- export default function AnnouncementsPage() { const [activeCategories, setActiveCategories] = useState([]); const [showOnlyUnread, setShowOnlyUnread] = useState(false); const { announcements, unreadCount, markRead, markAllRead, dismiss } = useAnnouncements({ includeDismissed: false, }); // Apply local filters const filteredAnnouncements = announcements.filter((a) => { if (activeCategories.length > 0 && !activeCategories.includes(a.category)) return false; if (showOnlyUnread && a.isRead) return false; return true; }); const hasActiveFilters = activeCategories.length > 0 || showOnlyUnread; const toggleCategory = (cat: AnnouncementCategory) => { setActiveCategories((prev) => prev.includes(cat) ? prev.filter((c) => c !== cat) : [...prev, cat] ); }; return (
{/* Header */}

Announcements

{/* Content */}
{/* Toolbar */}
{/* Category filter dropdown */} Categories {(Object.keys(categoryConfig) as AnnouncementCategory[]).map((cat) => { const cfg = categoryConfig[cat]; const CatIcon = cfg.icon; return ( toggleCategory(cat)} > {cfg.label} ); })} setShowOnlyUnread((v) => !v)} > Unread only {hasActiveFilters && ( )}
{/* Mark all read */} {unreadCount > 0 && ( )}
{/* Announcement list */} {filteredAnnouncements.length === 0 ? ( ) : (
{filteredAnnouncements.map((announcement) => ( ))}
)}
); }