From d2064931f5b36ce240f5cbfc54c6d0eee9e84cbd Mon Sep 17 00:00:00 2001 From: Raghav Ganesh Date: Sun, 19 Oct 2025 17:48:01 +0530 Subject: [PATCH 1/2] feat: add real-time activities tracked stat card - Add subscribeToUserSubmissionCount function in firestore.ts for real-time count - Add new stat card showing total activities tracked with user join date tooltip - Update dashboard grid from 3 to 4 columns for new card - Add tooltip support to StatCard component --- src/components/dashboard/Dashboard.tsx | 29 ++++++++++++++++++++++---- src/lib/firebase/firestore.ts | 14 +++++++++++++ 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 39a8614..eb1aa7c 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -10,7 +10,7 @@ import { import FootprintChart from "@/components/charts/FootprintChart"; import ComparisonSection from "@/components/dashboard/ComparisonSection"; import ShareButton from "@/components/ui/ShareButton"; -import { getUserFootprints } from "@/lib/firebase/firestore"; +import { getUserFootprints, subscribeToUserSubmissionCount } from "@/lib/firebase/firestore"; type SortOption = "newest" | "oldest" | "highest_impact" | "lowest_impact"; @@ -27,11 +27,12 @@ interface StatCardProps { change?: number; icon: string; color: string; + tooltip?: string; } -function StatCard({ title, value, change, icon, color }: StatCardProps) { +function StatCard({ title, value, change, icon, color, tooltip }: StatCardProps) { return ( -
+

{title}

@@ -98,6 +99,7 @@ export default function Dashboard({ null ); const [loading, setLoading] = useState(true); + const [submissionCount, setSubmissionCount] = useState(0); useEffect(() => { // Use prop data if available, otherwise fetch from database @@ -205,6 +207,13 @@ export default function Dashboard({ } }, [propDashboardData]); + // Real-time subscription to submission count + useEffect(() => { + if (!user) return; + const unsubscribe = subscribeToUserSubmissionCount(user.id, setSubmissionCount); + return () => unsubscribe(); + }, [user, activityHistory.length]); + if (loading) { return (
@@ -237,6 +246,11 @@ export default function Dashboard({ const equivalentIcons = ["🚗", "📱", "☕", "💡"]; + // Format join date for tooltip + const joinDateLabel = user?.createdAt + ? new Date(user.createdAt).toLocaleDateString() + : ''; + return (
@@ -258,7 +272,7 @@ export default function Dashboard({
{/* Stats Cards */} -
+
+
{/* Comparison Section */} diff --git a/src/lib/firebase/firestore.ts b/src/lib/firebase/firestore.ts index f9c6cb5..86f3c92 100644 --- a/src/lib/firebase/firestore.ts +++ b/src/lib/firebase/firestore.ts @@ -12,6 +12,7 @@ import { limit, getDocs, Timestamp, + onSnapshot, } from 'firebase/firestore'; import { db } from './config'; import { Activity, CarbonFootprint, User, WeeklyGoal, Badge } from '@/types'; @@ -159,4 +160,17 @@ export const awardBadge = async (userId: string, badge: Omit void +) => { + const footprintsRef = collection(db, 'carbon_footprints'); + const q = query(footprintsRef, where('userId', '==', userId)); + const unsubscribe = onSnapshot(q, (snapshot) => { + onCount(snapshot.size); + }); + return unsubscribe; }; \ No newline at end of file From 2f1bffff10ab5f3e141306272aed4160c88f2530 Mon Sep 17 00:00:00 2001 From: Raghav Ganesh Date: Sat, 25 Oct 2025 00:31:52 +0530 Subject: [PATCH 2/2] refactor: to track activites collection --- src/components/dashboard/Dashboard.tsx | 10 +++++----- src/lib/firebase/firestore.ts | 13 +++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index f669094..3020ce3 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -10,7 +10,7 @@ import { import FootprintChart from "@/components/charts/FootprintChart"; import ComparisonSection from "@/components/dashboard/ComparisonSection"; import ShareButton from "@/components/ui/ShareButton"; -import { getUserFootprints, subscribeToUserSubmissionCount } from "@/lib/firebase/firestore"; +import { getUserFootprints, subscribeToUserActivitiesCount } from "@/lib/firebase/firestore"; import { exportToCSV, ActivityHistoryEntry } from "@/utils/exportCSV"; import { ArrowDownTrayIcon } from "@heroicons/react/24/outline"; @@ -101,7 +101,7 @@ export default function Dashboard({ null ); const [loading, setLoading] = useState(true); - const [submissionCount, setSubmissionCount] = useState(0); + const [activitiesCount, setActivitiesCount] = useState(0); const [exportStatus, setExportStatus] = useState<{ show: boolean; success: boolean; @@ -214,10 +214,10 @@ export default function Dashboard({ } }, [propDashboardData]); - // Real-time subscription to submission count + // Real-time subscription to activities count useEffect(() => { if (!user) return; - const unsubscribe = subscribeToUserSubmissionCount(user.id, setSubmissionCount); + const unsubscribe = subscribeToUserActivitiesCount(user.id, setActivitiesCount); return () => unsubscribe(); }, [user, activityHistory.length]); // Handle CSV export @@ -314,7 +314,7 @@ export default function Dashboard({ /> void +) => { + const activitiesRef = collection(db, 'activities'); + const q = query(activitiesRef, where('userId', '==', userId)); + const unsubscribe = onSnapshot(q, (snapshot) => { + onCount(snapshot.size); + }); + return unsubscribe; }; \ No newline at end of file