import React from 'react';
import { useNavigate } from 'react-router-dom';
import {
Calendar,
AlertTriangle,
Download,
Eye,
Clock,
Plus,
HardDrive,
Image,
Archive,
Heart
} from 'lucide-react';
import { differenceInDays, parseISO } from 'date-fns';
import { useTranslation } from 'react-i18next';
import { useLocalizedDate } from '../../hooks/useLocalizedDate';
import { Button, Card, Loading } from '../../components/common';
import { useQuery } from '@tanstack/react-query';
import { eventsService } from '../../services/events.service';
import { adminService } from '../../services/admin.service';
interface StatCard {
title: string;
value: string | number;
change?: string;
icon: React.ComponentType<{ className?: string }>;
color: string;
}
export const AdminDashboard: React.FC = () => {
const { t } = useTranslation();
const navigate = useNavigate();
const { format, formatDistanceToNow } = useLocalizedDate();
// Fetch dashboard statistics
const { data: dashboardStats, isLoading: statsLoading } = useQuery({
queryKey: ['admin-dashboard-stats'],
queryFn: () => adminService.getDashboardStats(),
});
// Fetch recent activity
const { data: recentActivity } = useQuery({
queryKey: ['admin-recent-activity'],
queryFn: () => adminService.getRecentActivity(10),
});
// Fetch system health
const { data: systemHealth } = useQuery({
queryKey: ['admin-system-health'],
queryFn: () => adminService.getSystemHealth(),
refetchInterval: 30000, // Refresh every 30 seconds
});
// Fetch events data for expiring events
const { data: eventsData, isLoading: eventsLoading } = useQuery({
queryKey: ['admin-events-summary'],
queryFn: () => eventsService.getEvents(1, 100),
});
const isLoading = statsLoading || eventsLoading;
if (isLoading) {
return (
);
}
// Calculate expiring events
const activeEvents = eventsData?.events.filter(e => e.is_active && !e.is_archived) || [];
const expiringEvents = activeEvents.filter(e => {
const days = differenceInDays(parseISO(e.expires_at), new Date());
return days <= 7 && days > 0;
});
// Format numbers for display
const formatNumber = (num: number): string => {
if (num >= 1000000) return `${(num / 1000000).toFixed(1)}M`;
if (num >= 1000) return `${(num / 1000).toFixed(1)}K`;
return num.toString();
};
// Build statistics cards - always show 8 cards in 2x4 grid
const stats: StatCard[] = [
{
title: t('admin.activeEvents'),
value: dashboardStats?.activeEvents || 0,
icon: Calendar,
color: 'text-green-600',
},
{
title: t('admin.expiringSoon'),
value: dashboardStats?.expiringEvents || 0,
change: t('admin.next7Days'),
icon: AlertTriangle,
color: 'text-orange-600',
},
{
title: t('admin.totalPhotos'),
value: formatNumber(dashboardStats?.totalPhotos || 0),
icon: Image,
color: 'text-blue-600',
},
{
title: t('admin.storageUsed'),
value: adminService.formatBytes(dashboardStats?.storageUsed || 0),
icon: HardDrive,
color: 'text-purple-600',
},
{
title: t('admin.totalViews'),
value: formatNumber(dashboardStats?.totalViews || 0),
change: dashboardStats?.viewsTrend ? t('admin.percentFromLastWeek', { percent: `${dashboardStats.viewsTrend > 0 ? '+' : ''}${dashboardStats.viewsTrend}` }) : undefined,
icon: Eye,
color: 'text-indigo-600',
},
{
title: t('admin.downloads'),
value: formatNumber(dashboardStats?.totalDownloads || 0),
change: dashboardStats?.downloadsTrend ? t('admin.percentFromLastWeek', { percent: `${dashboardStats.downloadsTrend > 0 ? '+' : ''}${dashboardStats.downloadsTrend}` }) : undefined,
icon: Download,
color: 'text-pink-600',
},
{
title: t('admin.archivedEvents'),
value: dashboardStats?.archivedEvents || 0,
icon: Archive,
color: 'text-gray-600',
},
{
title: t('admin.systemHealth'),
value: systemHealth ? t(`admin.health.${systemHealth.overall}`) : t('admin.health.checking'),
icon: Heart,
color: systemHealth?.overall === 'healthy' ? 'text-green-600' : systemHealth?.overall === 'warning' ? 'text-yellow-600' : 'text-red-600',
},
];
return (
{/* Page Header */}
{t('navigation.dashboard')}
{t('admin.dashboardSubtitle')}
}
onClick={() => navigate('/admin/events/new')}
>
{t('events.createEvent')}
{/* Statistics Grid */}
{stats.map((stat) => (
{stat.title}
{stat.value}
{stat.change && (
{stat.change}
)}
))}
{/* Main Content Grid */}
{/* Expiring Events */}
{t('admin.eventsExpiringSoon')}
{expiringEvents.length === 0 ? (
{t('admin.noEventsExpiring')}
) : (
{expiringEvents.slice(0, 5).map((event) => {
const daysLeft = differenceInDays(parseISO(event.expires_at), new Date());
return (
navigate(`/admin/events/${event.id}`)}
>
{event.event_name}
{format(parseISO(event.event_date), 'PP')}
{t('admin.daysLeft', { count: daysLeft })}
{t('gallery.expires')} {format(parseISO(event.expires_at), 'PP')}
);
})}
)}
{expiringEvents.length > 5 && (
)}
{/* Recent Activity */}
{t('admin.recentActivity')}
{!recentActivity || recentActivity.length === 0 ? (
{t('admin.noRecentActivity')}
) : (
recentActivity.slice(0, 5).map((activity) => {
// Get color based on activity type
const getActivityColor = (type: string) => {
const colors: Record
= {
'event_created': 'bg-green-500',
'photos_uploaded': 'bg-blue-500',
'event_archived': 'bg-purple-500',
'archive_restored': 'bg-indigo-500',
'archive_deleted': 'bg-red-500',
'bulk_download': 'bg-blue-500',
'email_config_updated': 'bg-yellow-500',
'branding_updated': 'bg-pink-500',
'theme_updated': 'bg-purple-500',
'gallery_password_entry': 'bg-gray-500',
};
return colors[type] || 'bg-gray-500';
};
// Format activity message with translations
const getActivityMessage = (): string => {
const translationKey = `admin.activities.${activity.type}`;
const params: Record = {
eventName: activity.eventName || t('common.unknown'),
count: activity.metadata?.count || 0,
template: activity.metadata?.template_key || '',
categoryName: activity.metadata?.category_name || ''
};
// Translate; if key missing i18n returns the key string itself
const translated = t(translationKey, params) as string;
if (!translated || translated === translationKey) {
// Fallback: format a readable English message
return adminService.formatActivityMessage(activity);
}
return translated;
};
return (
{getActivityMessage()}
{activity.actorName}
{formatDistanceToNow(parseISO(activity.createdAt), { addSuffix: true })}
);
})
)}
);
};
AdminDashboard.displayName = 'AdminDashboard';