import React, { useState, useEffect, useRef } from 'react'; import { createPortal } from 'react-dom'; import { Calendar, Tag, ChevronRight, X, User } from "lucide-react"; import { colors } from "../../theme/colors"; import { API_BASE_URL } from '../../config/api'; import branding from '../../branding'; const AnnouncementModal = ({ announcement, onClose }) => { const dialogRef = useRef(null); useEffect(() => { const previousOverflow = document.body.style.overflow; const previousFocus = document.activeElement; const handleKeyDown = (event) => { if (event.key === 'Escape') onClose(); if (event.key !== 'Tab' || !dialogRef.current) return; const controls = dialogRef.current.querySelectorAll('button, a[href], input, select, textarea, [tabindex]:not([tabindex="-1"])'); if (!controls.length) return; const first = controls[0]; const last = controls[controls.length - 1]; if (event.shiftKey && document.activeElement === first) { event.preventDefault(); last.focus(); } else if (!event.shiftKey && document.activeElement === last) { event.preventDefault(); first.focus(); } }; document.body.style.overflow = 'hidden'; document.addEventListener('keydown', handleKeyDown); dialogRef.current?.focus(); return () => { document.body.style.overflow = previousOverflow; document.removeEventListener('keydown', handleKeyDown); previousFocus?.focus?.(); }; }, [onClose]); if (!announcement) return null; const getTagColor = (tag) => { const lowerTag = tag.toLowerCase(); if (lowerTag.includes('update') || lowerTag.includes('feature')) return colors.green; if (lowerTag.includes('alert') || lowerTag.includes('maintenance') || lowerTag.includes('downtime')) return colors.red; if (lowerTag.includes('technical') || lowerTag.includes('info')) return colors.blue; return colors.yellow; }; const formatDate = (dateString) => { try { const date = new Date(dateString); return date.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch (e) { return dateString; } }; return createPortal(
{/* Header */}
{announcement.tags && announcement.tags.map((tag, idx) => { const tagColor = getTagColor(tag); return ( {tag} ); })}

{announcement.title}

{/* Content */}
{formatDate(announcement.created_at)}
Posted by {announcement.author_name || 'Administrator'}
{announcement.content}
{/* Footer */}
, document.body ); }; const AnnouncementsSection = () => { const [announcements, setAnnouncements] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [selectedAnnouncement, setSelectedAnnouncement] = useState(null); const [showAll, setShowAll] = useState(false); useEffect(() => { const fetchAnnouncements = async () => { try { const response = await fetch(`${API_BASE_URL}/api/announcements?limit=20`); if (!response.ok) { throw new Error('Failed to fetch announcements'); } const data = await response.json(); if (data.success) { setAnnouncements(data.announcements); } else { throw new Error(data.error || 'Unknown error'); } } catch (err) { console.error('Error fetching announcements:', err); setError('Could not load latest news.'); } finally { setIsLoading(false); } }; fetchAnnouncements(); }, []); const formatDate = (dateString) => { try { const date = new Date(dateString); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); } catch (e) { return dateString; } }; const displayedAnnouncements = showAll ? announcements : announcements.slice(0, 3); if (isLoading) { return (

Updates

Latest announcements.

{[1, 2, 3].map((item) =>
)}
); } if (error || announcements.length === 0) return null; return (

Platform updates

Latest announcements.

Service changes and notes from the {branding.name} team.

{displayedAnnouncements.map((item) => ( ))}
{announcements.length > 3 && ( )} {/* Modal Overlay */} {selectedAnnouncement && ( setSelectedAnnouncement(null)} /> )}
); }; export default AnnouncementsSection;