From 18d78bd91211fede94bf83802068b4bbfa0c6598 Mon Sep 17 00:00:00 2001 From: shriyaagg Date: Sun, 19 Oct 2025 16:42:25 +0530 Subject: [PATCH 1/2] added impact statistics in dashboard --- src/components/dashboard/Dashboard.tsx | 3 +- src/components/impactStats.tsx | 71 ++++++++++++++++++++++++++ src/constants/impactStats.ts | 28 ++++++++++ src/hooks/useCountUp.ts | 38 ++++++++++++++ 4 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 src/components/impactStats.tsx create mode 100644 src/constants/impactStats.ts create mode 100644 src/hooks/useCountUp.ts diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index 39a8614..bd7b41c 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -11,6 +11,7 @@ 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 ImpactStats from '../impactStats'; type SortOption = "newest" | "oldest" | "highest_impact" | "lowest_impact"; @@ -321,7 +322,7 @@ export default function Dashboard({ )} - + {/* Recent Activities / Activity History (UPDATED SECTION) */} {activityHistory.length > 0 && (
diff --git a/src/components/impactStats.tsx b/src/components/impactStats.tsx new file mode 100644 index 0000000..dfba719 --- /dev/null +++ b/src/components/impactStats.tsx @@ -0,0 +1,71 @@ +"use client"; + +import React from 'react'; +import { IMPACT_STATS, INSPIRATIONAL_TAGLINE, ImpactStat } from '../constants/impactStats'; +import useCountUp from '../hooks/useCountUp'; + +const StatCard: React.FC<{ stat: ImpactStat }> = ({ stat }) => { + const duration = stat.value > 1000 ? 2500 : 2000; + + const animatedValue = useCountUp(stat.value, duration, 0); + + let displayContent: string; + let unit: string = ''; + let isFinal: boolean = animatedValue >= stat.value * 0.99; + + if (stat.id === 2) { + displayContent = animatedValue.toFixed(1); + unit = ' tons'; + } else { + const roundedValue = Math.round(animatedValue); + displayContent = roundedValue.toLocaleString(); + + if (stat.display.includes('+') && isFinal) { + displayContent += '+'; + } + } + + const finalDisplay = `${displayContent}${unit}`; + + return ( +
+

+ {finalDisplay} +

+

+ {stat.suffix} +

+
+ ); +}; + +const ImpactStats: React.FC = () => { + return ( +
+

+ 🌱 + {INSPIRATIONAL_TAGLINE} +

+ +
+ {IMPACT_STATS.map(stat => ( + + ))} +
+
+ ); +}; + +export default ImpactStats; \ No newline at end of file diff --git a/src/constants/impactStats.ts b/src/constants/impactStats.ts new file mode 100644 index 0000000..270fead --- /dev/null +++ b/src/constants/impactStats.ts @@ -0,0 +1,28 @@ +export type ImpactStat = { + id: number; + value: number; + display: string; + suffix: string; +}; +export const IMPACT_STATS = [ + { + id: 1, + value: 10500, + display: "10,000+", + suffix: "activities tracked", + }, + { + id: 2, + value: 2500, + display: "2.5", + suffix: "tons CO2 awareness raised", + }, + { + id: 3, + value: 530, + display: "500+", + suffix: "eco-warriors joined", + }, +]; + +export const INSPIRATIONAL_TAGLINE = "Every action counts. Join the collective impact."; \ No newline at end of file diff --git a/src/hooks/useCountUp.ts b/src/hooks/useCountUp.ts new file mode 100644 index 0000000..ebf96fb --- /dev/null +++ b/src/hooks/useCountUp.ts @@ -0,0 +1,38 @@ +import { useState, useEffect, useRef } from 'react'; + +const useCountUp = (endValue: number, duration: number = 2000, startValue: number = 0): number => { + const [count, setCount] = useState(startValue); + const startTime = useRef(null); + + useEffect(() => { + + setCount(startValue); + startTime.current = null; + + const animateCount = (timestamp: number) => { + if (!startTime.current) { + startTime.current = timestamp; + } + + const progress = timestamp - startTime.current; + const easingProgress = Math.min(1, progress / duration); + + const easedProgress = 1 - Math.pow(1 - easingProgress, 3); + const nextValue = startValue + (endValue - startValue) * easedProgress; + + setCount(nextValue); + if (progress < duration) { + requestAnimationFrame(animateCount); + } else { + setCount(endValue); + } + }; + + const frameId = requestAnimationFrame(animateCount); + + return () => cancelAnimationFrame(frameId); + }, [endValue, duration, startValue]); + return Math.round(count * 100) / 100; +}; + +export default useCountUp; \ No newline at end of file From beeeff77180d18f2397adf7767cb4576b91f3f5b Mon Sep 17 00:00:00 2001 From: shriyaagg Date: Tue, 21 Oct 2025 16:24:46 +0530 Subject: [PATCH 2/2] Fix: Addressed review comments for ImpactStats component --- src/components/dashboard/Dashboard.tsx | 3 ++- src/components/impactStats.tsx | 19 ++++++-------- src/constants/impactStats.ts | 34 ++++++++++++++------------ 3 files changed, 29 insertions(+), 27 deletions(-) diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx index bd7b41c..6fe7bef 100644 --- a/src/components/dashboard/Dashboard.tsx +++ b/src/components/dashboard/Dashboard.tsx @@ -11,7 +11,7 @@ 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 ImpactStats from '../impactStats'; +import ImpactStats from '../ImpactStats'; type SortOption = "newest" | "oldest" | "highest_impact" | "lowest_impact"; @@ -322,6 +322,7 @@ export default function Dashboard({
)} + {/* Recent Activities / Activity History (UPDATED SECTION) */} {activityHistory.length > 0 && ( diff --git a/src/components/impactStats.tsx b/src/components/impactStats.tsx index dfba719..cd9a49c 100644 --- a/src/components/impactStats.tsx +++ b/src/components/impactStats.tsx @@ -5,27 +5,24 @@ import { IMPACT_STATS, INSPIRATIONAL_TAGLINE, ImpactStat } from '../constants/im import useCountUp from '../hooks/useCountUp'; const StatCard: React.FC<{ stat: ImpactStat }> = ({ stat }) => { - const duration = stat.value > 1000 ? 2500 : 2000; + const duration = stat.value > 1000 ? 2500 : 3000; const animatedValue = useCountUp(stat.value, duration, 0); let displayContent: string; let unit: string = ''; - let isFinal: boolean = animatedValue >= stat.value * 0.99; - - if (stat.id === 2) { - displayContent = animatedValue.toFixed(1); + + if (stat.decimalPlaces !== undefined) { + displayContent = animatedValue.toFixed(stat.decimalPlaces); unit = ' tons'; } else { const roundedValue = Math.round(animatedValue); displayContent = roundedValue.toLocaleString(); - - if (stat.display.includes('+') && isFinal) { - displayContent += '+'; - } } - const finalDisplay = `${displayContent}${unit}`; + const plusSign = stat.showPlus ? '+' : ''; + + const finalDisplay = `${plusSign}${displayContent}${unit}`; return (
{ ); }; -export default ImpactStats; \ No newline at end of file +export default ImpactStats; diff --git a/src/constants/impactStats.ts b/src/constants/impactStats.ts index 270fead..4b76773 100644 --- a/src/constants/impactStats.ts +++ b/src/constants/impactStats.ts @@ -1,28 +1,32 @@ -export type ImpactStat = { +export interface ImpactStat { id: number; - value: number; - display: string; suffix: string; -}; -export const IMPACT_STATS = [ + value: number; + decimalPlaces?: number; + showPlus?: boolean; +} + +export const INSPIRATIONAL_TAGLINE = "Every action counts. Join the collective impact."; + +export const IMPACT_STATS: ImpactStat[] = [ { id: 1, - value: 10500, - display: "10,000+", - suffix: "activities tracked", + suffix: 'activities tracked', + value: 10500, + showPlus: true, // Show '+' for users }, { id: 2, - value: 2500, - display: "2.5", - suffix: "tons CO2 awareness raised", + suffix: 'Tons of CO2 awareness raised', + value: 2.5, + decimalPlaces: 1, + showPlus: true, }, { id: 3, - value: 530, - display: "500+", - suffix: "eco-warriors joined", + suffix: 'eco-warriors joined', + value: 530, + showPlus:true, }, ]; -export const INSPIRATIONAL_TAGLINE = "Every action counts. Join the collective impact."; \ No newline at end of file