diff --git a/src/components/dashboard/Dashboard.tsx b/src/components/dashboard/Dashboard.tsx
index 39a8614..6fe7bef 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,8 @@ 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..cd9a49c
--- /dev/null
+++ b/src/components/impactStats.tsx
@@ -0,0 +1,68 @@
+"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 : 3000;
+
+ const animatedValue = useCountUp(stat.value, duration, 0);
+
+ let displayContent: string;
+ let unit: string = '';
+
+ if (stat.decimalPlaces !== undefined) {
+ displayContent = animatedValue.toFixed(stat.decimalPlaces);
+ unit = ' tons';
+ } else {
+ const roundedValue = Math.round(animatedValue);
+ displayContent = roundedValue.toLocaleString();
+ }
+
+ const plusSign = stat.showPlus ? '+' : '';
+
+ const finalDisplay = `${plusSign}${displayContent}${unit}`;
+
+ return (
+
+
+ {finalDisplay}
+
+
+ {stat.suffix}
+
+
+ );
+};
+
+const ImpactStats: React.FC = () => {
+ return (
+
+
+ 🌱
+ {INSPIRATIONAL_TAGLINE}
+
+
+
+ {IMPACT_STATS.map(stat => (
+
+ ))}
+
+
+ );
+};
+
+export default ImpactStats;
diff --git a/src/constants/impactStats.ts b/src/constants/impactStats.ts
new file mode 100644
index 0000000..4b76773
--- /dev/null
+++ b/src/constants/impactStats.ts
@@ -0,0 +1,32 @@
+export interface ImpactStat {
+ id: number;
+ suffix: string;
+ value: number;
+ decimalPlaces?: number;
+ showPlus?: boolean;
+}
+
+export const INSPIRATIONAL_TAGLINE = "Every action counts. Join the collective impact.";
+
+export const IMPACT_STATS: ImpactStat[] = [
+ {
+ id: 1,
+ suffix: 'activities tracked',
+ value: 10500,
+ showPlus: true, // Show '+' for users
+ },
+ {
+ id: 2,
+ suffix: 'Tons of CO2 awareness raised',
+ value: 2.5,
+ decimalPlaces: 1,
+ showPlus: true,
+ },
+ {
+ id: 3,
+ suffix: 'eco-warriors joined',
+ value: 530,
+ showPlus:true,
+ },
+];
+
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