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
1 change: 1 addition & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

89 changes: 65 additions & 24 deletions src/components/dashboard/Dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 { ActivityIcon, getActivityIconConfig } from "@/utils/activityIcons";

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

Expand Down Expand Up @@ -38,12 +39,13 @@ function StatCard({ title, value, change, icon, color }: StatCardProps) {
<p className={`text-2xl font-bold ${color}`}>{value}</p>
{change !== undefined && (
<p
className={`text-sm mt-1 ${change > 0
className={`text-sm mt-1 ${
change > 0
? "text-red-600"
: change < 0
? "text-green-600"
: "text-gray-600"
}`}
? "text-green-600"
: "text-gray-600"
}`}
>
{change > 0 ? "β†—" : change < 0 ? "β†˜" : "β†’"}{" "}
{Math.abs(change).toFixed(1)}% from last week
Expand Down Expand Up @@ -322,14 +324,14 @@ export default function Dashboard({
</div>
)}

{/* Recent Activities / Activity History (UPDATED SECTION) */}
{/* Recent Activities / Activity History (UPDATED WITH ICONS) */}
{activityHistory.length > 0 && (
<div className="bg-white rounded-xl shadow-lg p-8">
<div className="flex justify-between items-center mb-6">
<h3 className="text-2xl font-bold text-gray-900">
Activity History
</h3>
{/* NEW: Sort Dropdown UI */}
{/* Sort Dropdown UI */}
<div className="flex items-center space-x-2">
<span className="text-sm font-medium text-gray-700">Sort by:</span>
<div className="relative">
Expand All @@ -344,38 +346,77 @@ export default function Dashboard({
</option>
))}
</select>
{/* Custom chevron/sort icon */}
<div className="pointer-events-none absolute inset-y-0 right-0 flex items-center px-2 text-gray-700">
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M3 4h18M3 8h18m-6 4h6m-6 4h6M3 16h6m-6 4h6"></path></svg>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M3 4h18M3 8h18m-6 4h6m-6 4h6M3 16h6m-6 4h6"></path>
</svg>
</div>
</div>
</div>
</div>

{/* Sorted Activity List */}
{/* Sorted Activity List with Icons */}
<div className="space-y-4">
{activityHistory.map((entry: any) => (
<div key={entry.id} className="bg-gray-50 rounded-lg p-4">
<div className="flex items-center justify-between mb-2">
<span className="text-sm text-gray-600">
{/* Using toLocaleString for better date/time display */}
{entry.timestamp.toLocaleString()}
</span>
<span className="font-bold text-green-600">
+{formatCO2Amount(entry.result.totalCO2)}
<div
key={entry.id}
className="bg-gradient-to-r from-gray-50 to-white rounded-lg p-5 border border-gray-200 hover:shadow-md transition-shadow"
>
<div className="flex items-center justify-between mb-3">
<span className="text-sm text-gray-600 font-medium">
{entry.timestamp.toLocaleString('en-US', {
weekday: 'short',
month: 'short',
day: 'numeric',
hour: '2-digit',
minute: '2-digit'
})}
</span>
<div className="flex items-center space-x-2">
<span className="text-xs text-gray-500">Total COβ‚‚:</span>
<span className="font-bold text-green-600 text-lg">
+{formatCO2Amount(entry.result.totalCO2)}
</span>
</div>
</div>

<div className="flex flex-wrap gap-2">
{Object.entries(entry.activities).map(
([activity, value]) =>
(value as number) > 0 ? (
<span
([activity, value]) => {
if ((value as number) <= 0) return null;

// Map activity input names to ActivityType
const activityType =
activity === 'streamingHours' ? 'streaming' :
activity === 'codingHours' ? 'coding' :
activity === 'videoCallHours' ? 'video_calls' :
activity === 'cloudStorageGB' ? 'cloud_storage' :
activity === 'gamingHours' ? 'gaming' :
activity === 'socialMediaHours' ? 'social_media' :
'emails';

const config = getActivityIconConfig(activityType as ActivityType);

return (
<div
key={activity}
className="text-xs bg-blue-100 text-blue-700 px-2 py-1 rounded-full"
className="flex items-center space-x-2 bg-white px-3 py-2 rounded-full border-2 hover:shadow-sm transition-all"
style={{
borderColor: config.borderColor.replace('border-', '#').replace('200', 'e5e7eb')
}}
>
{activity}: {value as number}
</span>
) : null
<ActivityIcon
activity={activityType as ActivityType}
size={16}
/>
<span className="text-sm font-medium text-gray-700">
{activity.replace(/Hours|GB/, '')}: {value as number}
{activity.includes('Hours') ? 'h' :
activity.includes('GB') ? 'GB' : ''}
</span>
</div>
);
}
)}
</div>
</div>
Expand Down
Loading