https://altly.madebydanny.uk
1import { useEffect, useState } from "react";
2import { StatsCard } from "@/components/StatsCard";
3import { supabase } from "@/integrations/supabase/client";
4
5export const Stats = () => {
6 const [stats, setStats] = useState({
7 imagesUploaded: 0,
8 happyUsers: 0,
9 avgResponseMs: 0,
10 });
11
12 useEffect(() => {
13 const fetchStats = async () => {
14 // Use secure stats function that only exposes aggregates
15 const { data, error } = await supabase.rpc('get_alt_text_stats');
16
17 if (error) {
18 console.error('Error fetching stats:', error);
19 return;
20 }
21
22 if (data && data.length > 0) {
23 const statsData = data[0];
24 setStats({
25 imagesUploaded: Number(statsData.total_images) || 0,
26 happyUsers: Number(statsData.happy_users) || 0,
27 avgResponseMs: Number(statsData.avg_response_ms) || 0,
28 });
29 }
30 };
31
32 fetchStats();
33
34 // Subscribe to real-time updates
35 const channel = supabase
36 .channel('stats-updates')
37 .on(
38 'postgres_changes',
39 {
40 event: 'INSERT',
41 schema: 'public',
42 table: 'alt_text_generations'
43 },
44 () => {
45 fetchStats();
46 }
47 )
48 .subscribe();
49
50 return () => {
51 supabase.removeChannel(channel);
52 };
53 }, []);
54
55 return (
56 <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-12 max-w-4xl mx-auto">
57 <StatsCard label="Images Uploaded" value={stats.imagesUploaded} />
58 <StatsCard label="Happy Users" value={stats.happyUsers} />
59 <StatsCard
60 label="Avg Response (ms)"
61 value={stats.avgResponseMs.toLocaleString()}
62 />
63 </div>
64 );
65};