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; }; }