Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions src/app/tips/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@

'use client';

import React from 'react';
import { TIPS_DATABASE } from '@/constants/tips';
import Link from 'next/link';

const AllTipsPage = () => {
return (
<div className="min-h-screen bg-gray-100 dark:bg-gray-900 p-8">
<div className="max-w-4xl mx-auto">
<div className="flex justify-between items-center mb-8">
<h1 className="text-4xl font-bold text-gray-900 dark:text-white">All Tips</h1>
<Link href="/" className="text-blue-500 hover:underline">Back to Dashboard</Link>
</div>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
{TIPS_DATABASE.map(tip => (
<div key={tip.id} className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md">
<div className="flex items-center mb-4">
<span className="text-3xl mr-4">{tip.icon}</span>
<h3 className="text-xl font-bold dark:text-white">{tip.title}</h3>
</div>
<p className="text-gray-600 dark:text-gray-300">{tip.description}</p>
</div>
))}
</div>
</div>
</div>
);
};

export default AllTipsPage;
26 changes: 26 additions & 0 deletions src/components/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { getUserFootprints } from "@/lib/firebase/firestore";
import { exportToCSV, ActivityHistoryEntry } from "@/utils/exportCSV";
import { ArrowDownTrayIcon } from "@heroicons/react/24/outline";
import Spinner from "@/components/ui/Spinner";
import TipOfTheDay from "@/components/tips/TipOfTheDay";

type SortOption = "newest" | "oldest" | "highest_impact" | "lowest_impact";

Expand Down Expand Up @@ -289,6 +290,31 @@ export default function Dashboard({
</div>
</div>

{/* Tip of the Day */}
<TipOfTheDay />

{/* Stats Cards */}
<div className="grid md:grid-cols-3 gap-6">
<StatCard
title="Today's Footprint"
value={formatCO2Amount(dashboardData.todayFootprint)}
icon="📅"
color="text-blue-600"
/>
<StatCard
title="This Week"
value={formatCO2Amount(dashboardData.weeklyFootprint)}
icon="📊"
color="text-green-600"
/>
<StatCard
title="This Month"
value={formatCO2Amount(dashboardData.monthlyFootprint)}
icon="📈"
color="text-purple-600"
/>
</div>

{/* Stats Cards */}
<div className="grid md:grid-cols-3 gap-6">
<StatCard
Expand Down
48 changes: 48 additions & 0 deletions src/components/tips/TipOfTheDay.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@

import React, { useState, useEffect } from 'react';
import { getTodaysTip, getNextTip } from '@/utils/tipRotation';
import { Tip } from '@/types';

const TipOfTheDay: React.FC = () => {
const [currentTip, setCurrentTip] = useState<Tip | null>(null);

useEffect(() => {
setCurrentTip(getTodaysTip());
}, []);

const handleNextTip = () => {
if (currentTip) {
const nextTip = getNextTip(currentTip.id);
setCurrentTip(nextTip);
}
};

if (!currentTip) {
return null;
}

return (
<div className="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-md">
<h3 className="text-xl font-bold mb-4 flex items-center">
<span className="text-2xl mr-2">💡</span> Tip of the Day
</h3>
<div className="mb-4">
<h4 className="font-bold text-lg">{currentTip.title}</h4>
<p className="text-gray-600 dark:text-gray-300">{currentTip.description}</p>
</div>
<div className="flex justify-between items-center">
<button
onClick={handleNextTip}
className="bg-blue-500 hover:bg-blue-600 text-white font-bold py-2 px-4 rounded"
>
Show next tip
</button>
<a href="/tips" className="text-blue-500 hover:underline">
All Tips
</a>
</div>
</div>
);
};

export default TipOfTheDay;
82 changes: 82 additions & 0 deletions src/utils/tipRotation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@

import { Tip } from '@/types';
import { TIPS_DATABASE } from '@/constants/tips';

const VIEWED_TIPS_KEY = 'viewedTips';
const LAST_TIP_DATE_KEY = 'lastTipDate';

type ViewedTips = {
[id: string]: string; // { tipId: dateViewed }
};

const getPreviouslyViewedTips = (): ViewedTips => {
if (typeof window === 'undefined') {
return {};
}
const viewedTips = localStorage.getItem(VIEWED_TIPS_KEY);
return viewedTips ? JSON.parse(viewedTips) : {};
};

const saveViewedTip = (tipId: string) => {
if (typeof window === 'undefined') {
return;
}
const viewedTips = getPreviouslyViewedTips();
viewedTips[tipId] = new Date().toISOString();
localStorage.setItem(VIEWED_TIPS_KEY, JSON.stringify(viewedTips));
};

const isTipRecent = (dateViewed: string): boolean => {
const thirtyDaysAgo = new Date();
thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
return new Date(dateViewed) > thirtyDaysAgo;
};

const getUnseenTips = (): Tip[] => {
const viewedTips = getPreviouslyViewedTips();
const unseenTips = TIPS_DATABASE.filter(tip => {
const dateViewed = viewedTips[tip.id];
return !dateViewed || !isTipRecent(dateViewed);
});
return unseenTips.length > 0 ? unseenTips : TIPS_DATABASE; // Fallback to all tips if all have been seen
};

export const getTodaysTip = (): Tip => {
if (typeof window === 'undefined') {
return TIPS_DATABASE[0];
}
const lastTipDate = localStorage.getItem(LAST_TIP_DATE_KEY);
const today = new Date().toDateString();

if (lastTipDate === today) {
const lastTipId = localStorage.getItem('lastTipId');
const lastTip = TIPS_DATABASE.find(tip => tip.id === lastTipId);
if (lastTip) {
return lastTip;
}
}

const unseenTips = getUnseenTips();
const todaysTip = unseenTips[Math.floor(Math.random() * unseenTips.length)];

localStorage.setItem(LAST_TIP_DATE_KEY, today);
localStorage.setItem('lastTipId', todaysTip.id);
saveViewedTip(todaysTip.id);

return todaysTip;
};

export const getNextTip = (currentTipId: string): Tip => {
const unseenTips = getUnseenTips();
let nextTip = unseenTips[Math.floor(Math.random() * unseenTips.length)];

while (nextTip.id === currentTipId && unseenTips.length > 1) {
nextTip = unseenTips[Math.floor(Math.random() * unseenTips.length)];
}

saveViewedTip(nextTip.id);
localStorage.setItem('lastTipId', nextTip.id);


return nextTip;
};