diff --git a/src/core/services/immersion-tracker/query.ts b/src/core/services/immersion-tracker/query.ts
index 2c0a989..19545c5 100644
--- a/src/core/services/immersion-tracker/query.ts
+++ b/src/core/services/immersion-tracker/query.ts
@@ -124,6 +124,8 @@ export function getQueryHints(db: DatabaseSync): {
activeSessions: number;
episodesToday: number;
activeAnimeCount: number;
+ totalEpisodesWatched: number;
+ totalAnimeCompleted: number;
} {
const sessions = db.prepare('SELECT COUNT(*) AS total FROM imm_sessions');
const active = db.prepare('SELECT COUNT(*) AS total FROM imm_sessions WHERE ended_at_ms IS NULL');
@@ -147,7 +149,23 @@ export function getQueryHints(db: DatabaseSync): {
AND s.started_at_ms >= ?
`).get(thirtyDaysAgoMs) as { count: number })?.count ?? 0;
- return { totalSessions, activeSessions, episodesToday, activeAnimeCount };
+ const totalEpisodesWatched = (db.prepare(`
+ SELECT COUNT(*) AS count FROM imm_videos WHERE watched = 1
+ `).get() as { count: number })?.count ?? 0;
+
+ const totalAnimeCompleted = (db.prepare(`
+ SELECT COUNT(*) AS count FROM (
+ SELECT a.anime_id
+ FROM imm_anime a
+ JOIN imm_videos v ON v.anime_id = a.anime_id
+ JOIN imm_media_art m ON m.video_id = v.video_id
+ WHERE m.episodes_total IS NOT NULL AND m.episodes_total > 0
+ GROUP BY a.anime_id
+ HAVING COUNT(DISTINCT CASE WHEN v.watched = 1 THEN v.video_id END) >= MAX(m.episodes_total)
+ )
+ `).get() as { count: number })?.count ?? 0;
+
+ return { totalSessions, activeSessions, episodesToday, activeAnimeCount, totalEpisodesWatched, totalAnimeCompleted };
}
export function getDailyRollups(db: DatabaseSync, limit = 60): ImmersionSessionRollupRow[] {
diff --git a/stats/src/components/overview/OverviewTab.tsx b/stats/src/components/overview/OverviewTab.tsx
index 112845e..3db8b10 100644
--- a/stats/src/components/overview/OverviewTab.tsx
+++ b/stats/src/components/overview/OverviewTab.tsx
@@ -40,7 +40,7 @@ export function OverviewTab() {
No tracked card-add events in the current immersion DB yet. New cards mined after this fix will show here.
)}
-
+
Total Sessions
@@ -71,6 +71,18 @@ export function OverviewTab() {
{formatNumber(summary.totalTrackedCards)}
+
+
Episodes Completed
+
+ {formatNumber(summary.totalEpisodesWatched)}
+
+
+
+
Anime Completed
+
+ {formatNumber(summary.totalAnimeCompleted)}
+
+
diff --git a/stats/src/components/trends/TrendsTab.tsx b/stats/src/components/trends/TrendsTab.tsx
index 276ad3c..60f1940 100644
--- a/stats/src/components/trends/TrendsTab.tsx
+++ b/stats/src/components/trends/TrendsTab.tsx
@@ -141,9 +141,6 @@ export function TrendsTab() {
type="line"
/>
- Efficiency
-
-
Anime
diff --git a/stats/src/lib/dashboard-data.test.ts b/stats/src/lib/dashboard-data.test.ts
index 59b067c..a206832 100644
--- a/stats/src/lib/dashboard-data.test.ts
+++ b/stats/src/lib/dashboard-data.test.ts
@@ -49,7 +49,7 @@ test('buildOverviewSummary aggregates tracked totals and recent windows', () =>
const overview: OverviewData = {
sessions,
rollups,
- hints: { totalSessions: 1, activeSessions: 0, episodesToday: 2, activeAnimeCount: 3 },
+ hints: { totalSessions: 1, activeSessions: 0, episodesToday: 2, activeAnimeCount: 3, totalEpisodesWatched: 5, totalAnimeCompleted: 1 },
};
const summary = buildOverviewSummary(overview, now);
diff --git a/stats/src/lib/dashboard-data.ts b/stats/src/lib/dashboard-data.ts
index cd6222b..bd57080 100644
--- a/stats/src/lib/dashboard-data.ts
+++ b/stats/src/lib/dashboard-data.ts
@@ -14,6 +14,8 @@ export interface OverviewSummary {
totalTrackedCards: number;
episodesToday: number;
activeAnimeCount: number;
+ totalEpisodesWatched: number;
+ totalAnimeCompleted: number;
averageSessionMinutes: number;
totalSessions: number;
activeDays: number;
@@ -146,6 +148,8 @@ export function buildOverviewSummary(
totalTrackedCards: Math.max(sessionCards, rollupCards),
episodesToday: overview.hints.episodesToday ?? 0,
activeAnimeCount: overview.hints.activeAnimeCount ?? 0,
+ totalEpisodesWatched: overview.hints.totalEpisodesWatched ?? 0,
+ totalAnimeCompleted: overview.hints.totalAnimeCompleted ?? 0,
averageSessionMinutes:
overview.sessions.length > 0
? Math.round(sumBy(overview.sessions, (session) => session.activeWatchedMs) / overview.sessions.length / 60_000)
diff --git a/stats/src/types/stats.ts b/stats/src/types/stats.ts
index 1bcd3cf..059cdda 100644
--- a/stats/src/types/stats.ts
+++ b/stats/src/types/stats.ts
@@ -91,6 +91,8 @@ export interface OverviewData {
activeSessions: number;
episodesToday: number;
activeAnimeCount: number;
+ totalEpisodesWatched: number;
+ totalAnimeCompleted: number;
};
}