127 lines
4.9 KiB
TypeScript
127 lines
4.9 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import { AdminLayout } from '@/components/layout/AdminLayout';
|
|
import {
|
|
Card,
|
|
CardContent,
|
|
// CardDescription, // Not used in StatsCard, can be removed if not used elsewhere
|
|
CardHeader,
|
|
CardTitle,
|
|
} from '@/components/ui/card';
|
|
import { BarChart, Layers, FileCheck, GitBranch } from 'lucide-react';
|
|
import { supabase } from '@/lib/supabase'; // Import Supabase client
|
|
import { speciesApi } from '@/services/adminApi'; // Import speciesApi
|
|
|
|
interface StatsCardProps {
|
|
title: string;
|
|
value: number | string;
|
|
description: string;
|
|
icon: React.ReactNode;
|
|
}
|
|
|
|
export default function AdminDashboard() {
|
|
const [stats, setStats] = useState({
|
|
totalSpecies: 0,
|
|
totalTradeRecords: 0,
|
|
totalIucnAssessments: 0,
|
|
totalCitesListings: 0,
|
|
});
|
|
const [loadingStats, setLoadingStats] = useState(true);
|
|
|
|
useEffect(() => {
|
|
const fetchStats = async () => {
|
|
console.log('[Dashboard] Fetching stats...');
|
|
setLoadingStats(true);
|
|
try {
|
|
// Using Promise.all to fetch all counts concurrently
|
|
const [
|
|
speciesRes,
|
|
tradeRecordsRes,
|
|
iucnAssessmentsRes,
|
|
citesListingsRes,
|
|
] = await Promise.all([
|
|
speciesApi.getAll(1, 1), // Fetches total count for species from speciesApi
|
|
supabase.from('cites_trade_records').select('*', { count: 'exact', head: true }), // Corrected table name
|
|
supabase.from('iucn_assessments').select('*', { count: 'exact', head: true }),
|
|
supabase.from('cites_listings').select('*', { count: 'exact', head: true }),
|
|
]);
|
|
|
|
// Log errors if any, but still try to set counts for successful fetches
|
|
// speciesApi.getAll() throws on error, so speciesRes won't have an .error property here if successful.
|
|
// Errors from speciesApi.getAll() will be caught by the main try...catch block.
|
|
if (tradeRecordsRes.error) console.error('[Dashboard] Error fetching CITES trade records count:', tradeRecordsRes.error);
|
|
if (iucnAssessmentsRes.error) console.error('[Dashboard] Error fetching IUCN assessments count:', iucnAssessmentsRes.error);
|
|
if (citesListingsRes.error) console.error('[Dashboard] Error fetching CITES listings count:', citesListingsRes.error);
|
|
|
|
setStats({
|
|
totalSpecies: speciesRes.count || 0, // speciesRes.count is available if speciesApi.getAll() succeeded
|
|
totalTradeRecords: tradeRecordsRes.count || 0,
|
|
totalIucnAssessments: iucnAssessmentsRes.count || 0,
|
|
totalCitesListings: citesListingsRes.count || 0,
|
|
});
|
|
} catch (error) {
|
|
console.error('[Dashboard] Error in fetchStats Promise.all:', error);
|
|
// In case of a major error in Promise.all itself, stats will remain 0
|
|
} finally {
|
|
setLoadingStats(false);
|
|
console.log('[Dashboard] Finished fetching stats. Current stats:', stats); // Log current stats after fetch
|
|
}
|
|
};
|
|
|
|
fetchStats();
|
|
}, []); // Empty dependency array ensures this runs once on mount
|
|
|
|
return (
|
|
<AdminLayout>
|
|
<div className="mb-6">
|
|
<h1 className="text-3xl font-bold">Admin Dashboard</h1>
|
|
<p className="text-muted-foreground">Manage your Arctic species database</p>
|
|
</div>
|
|
|
|
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-4">
|
|
<StatsCard
|
|
title="Total Species"
|
|
value={loadingStats ? 'Loading...' : stats.totalSpecies}
|
|
description="Species in database"
|
|
icon={<Layers className="h-6 w-6 text-blue-500" />}
|
|
/>
|
|
<StatsCard
|
|
title="CITES Trade Records"
|
|
value={loadingStats ? 'Loading...' : stats.totalTradeRecords}
|
|
description="Trade records tracked"
|
|
icon={<BarChart className="h-6 w-6 text-green-500" />}
|
|
/>
|
|
<StatsCard
|
|
title="IUCN Assessments"
|
|
value={loadingStats ? 'Loading...' : stats.totalIucnAssessments}
|
|
description="Conservation status assessments"
|
|
icon={<FileCheck className="h-6 w-6 text-red-500" />}
|
|
/>
|
|
<StatsCard
|
|
title="CITES Listings"
|
|
value={loadingStats ? 'Loading...' : stats.totalCitesListings}
|
|
description="CITES appendix listings"
|
|
icon={<GitBranch className="h-6 w-6 text-purple-500" />}
|
|
/>
|
|
</div>
|
|
|
|
{/* TODO: Add loading indicator for the whole stats section if loadingStats is true */}
|
|
{/* Recent activity, quick links or other dashboard elements */}
|
|
</AdminLayout>
|
|
);
|
|
}
|
|
|
|
function StatsCard({ title, value, description, icon }: StatsCardProps) {
|
|
return (
|
|
<Card>
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
<CardTitle className="text-sm font-medium">{title}</CardTitle>
|
|
{icon}
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="text-2xl font-bold">{value}</div>
|
|
<p className="text-xs text-muted-foreground">{description}</p>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|