diff --git a/frontend/src/components/widgets.jsx b/frontend/src/components/widgets.jsx
index 5c4e6ad..7b79e88 100644
--- a/frontend/src/components/widgets.jsx
+++ b/frontend/src/components/widgets.jsx
@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
+import { useNavigate } from 'react-router-dom';
import { Bar, Line, Doughnut } from 'react-chartjs-2';
import { AnalyticsApi } from '../api/client.js';
import {
@@ -15,9 +16,10 @@ const fmtBytes = (b) => {
return `${n.toFixed(1)} ${u[i]}`;
};
-export function StatCard({ label, value }) {
+export function StatCard({ label, value, to }) {
+ const navigate = useNavigate();
return (
-
+
navigate(to) : undefined} title={to ? `View ${label}` : undefined}>
{value}
{label}
@@ -37,6 +39,7 @@ export function HealthWidget({ health }) {
}
export function TopSendersWidget({ senders }) {
+ const navigate = useNavigate();
if (!senders) return
;
return (
@@ -44,7 +47,12 @@ export function TopSendersWidget({ senders }) {
{senders.map((s) => (
-
+
navigate(`/app/search?q=${encodeURIComponent(`from:${s.address}`)}`)}
+ title={`Search emails from ${s.address}`}
+ >
| {s.displayName || s.address} |
{s.emailCount} |
@@ -95,8 +103,21 @@ export function HeatmapWidget({ heatmap }) {
const DOW = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
+// Maps EmailCategory enum name → folder slug or search query
+const CAT_SLUG = {
+ Finance: '/app/folder/finance',
+ Social: '/app/folder/social',
+ Notification: '/app/folder/automated',
+ Promotional: '/app/folder/shopping',
+ Spam: '/app/folder/spam',
+ Newsletter: '/app/search?q=newsletter',
+ Personal: '/app/search?q=is:unread',
+ Unknown: '/app/folder/allmail',
+};
+
export function CategoryHeatmapWidget() {
const [cells, setCells] = useState(null);
+ const navigate = useNavigate();
useEffect(() => { AnalyticsApi.categoryHeatmap().then(setCells).catch(() => setCells([])); }, []);
if (!cells) return ;
@@ -115,15 +136,22 @@ export function CategoryHeatmapWidget() {
{DOW.map((d) => {d})}
- {categories.map((cat) => (
-
- {cat}
- {DOW.map((_, dow) => {
- const v = grid[`${cat}-${dow}`] || 0;
- return {v || ''};
- })}
-
- ))}
+ {categories.map((cat) => {
+ const dest = CAT_SLUG[cat];
+ return (
+
+ navigate(dest) : undefined}
+ >{cat}
+ {DOW.map((_, dow) => {
+ const v = grid[`${cat}-${dow}`] || 0;
+ return {v || ''};
+ })}
+
+ );
+ })}
);
diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx
index 7d7aa09..83ffeda 100644
--- a/frontend/src/pages/Dashboard.jsx
+++ b/frontend/src/pages/Dashboard.jsx
@@ -89,8 +89,8 @@ export default function Dashboard() {
const render = (key) => {
switch (key) {
case 'inbox-health': return ;
- case 'total-emails': return ;
- case 'unread-emails': return ;
+ case 'total-emails': return ;
+ case 'unread-emails': return ;
case 'storage': return ;
case 'top-senders': return ;
case 'volume': return ;
diff --git a/frontend/src/styles.css b/frontend/src/styles.css
index 111da29..e6d95c0 100644
--- a/frontend/src/styles.css
+++ b/frontend/src/styles.css
@@ -111,6 +111,12 @@ button:disabled { opacity: 0.5; cursor: default; }
.grid-item { background: var(--panel); border: 1px solid #2c3550; border-radius: 10px; overflow: hidden; }
.widget { padding: 14px; height: 100%; display: flex; flex-direction: column; }
.widget h3 { margin: 0 0 10px; font-size: 14px; cursor: move; }
+.widget--link { cursor: pointer; }
+.widget--link:hover { border-color: var(--accent); }
+.mini-row--link { cursor: pointer; }
+.mini-row--link:hover td { color: var(--accent); }
+.chm-label--link { cursor: pointer; text-decoration: underline dotted; }
+.chm-label--link:hover { color: var(--accent); }
.widget canvas { flex: 1; min-height: 0; }
.stat { align-items: flex-start; justify-content: center; }