Computes drill-down data for a specific filter dimension.
Filtered match results.
Type of drill-down:
Specific value to drill down into.
Reading history for chapter counts.
Drill-down data with detailed breakdown.
export function computeDrillDownData( matchResults: NormalizedMatchForStats[], type: "genre" | "format" | "status" | "date", value: string, readingHistory: ReadingHistory,): import("@/types/statistics").DrillDownData { let filtered: NormalizedMatchForStats[] = []; switch (type) { case "genre": filtered = matchResults.filter((match) => { const genres = match.selectedMatch?.genres ?? []; return genres.includes(value); }); break; case "format": filtered = matchResults.filter( (match) => match.selectedMatch?.format === value, ); break; case "status": filtered = matchResults.filter((match) => match.status === value); break; case "date": // For date drill-down, filter by specific date filtered = matchResults.filter((match) => { if (!match.matchDate) return false; const matchDateStr = getDateKey(match.matchDate.getTime()); return matchDateStr === value; }); break; } // Build detailed data const data = filtered.map((match) => { const mangaId = String(match.kenmeiManga.id); const mangaHistory = readingHistory.entries.filter( (entry) => String(entry.mangaId) === mangaId, ); // Sort by timestamp and get the latest entry to ensure correct chapter count const latestEntry = mangaHistory.length > 0 ? mangaHistory.toSorted((a, b) => a.timestamp - b.timestamp).at(-1) : null; const chapters = latestEntry?.chaptersRead ?? 0; // Compute confidence as selectedMatch.confidence or max across all AniList matches const selectedConfidence = match.selectedMatch?.confidence; const allConfidences = ( match.anilistMatches?.map((m) => m.confidence ?? 0) || [0] ).filter(Number.isFinite); const maxConfidence = allConfidences.length > 0 ? Math.max(...allConfidences) : 0; const confidence = selectedConfidence ?? maxConfidence; return { title: match.kenmeiManga.title, chapters, status: match.status, confidence, format: match.selectedMatch?.format, }; }); // Sort by chapters read (descending) and limit to top 100 data.sort((a, b) => b.chapters - a.chapters); const limitedData = data.slice(0, 100); return { type, value, data: limitedData, };} Copy
export function computeDrillDownData( matchResults: NormalizedMatchForStats[], type: "genre" | "format" | "status" | "date", value: string, readingHistory: ReadingHistory,): import("@/types/statistics").DrillDownData { let filtered: NormalizedMatchForStats[] = []; switch (type) { case "genre": filtered = matchResults.filter((match) => { const genres = match.selectedMatch?.genres ?? []; return genres.includes(value); }); break; case "format": filtered = matchResults.filter( (match) => match.selectedMatch?.format === value, ); break; case "status": filtered = matchResults.filter((match) => match.status === value); break; case "date": // For date drill-down, filter by specific date filtered = matchResults.filter((match) => { if (!match.matchDate) return false; const matchDateStr = getDateKey(match.matchDate.getTime()); return matchDateStr === value; }); break; } // Build detailed data const data = filtered.map((match) => { const mangaId = String(match.kenmeiManga.id); const mangaHistory = readingHistory.entries.filter( (entry) => String(entry.mangaId) === mangaId, ); // Sort by timestamp and get the latest entry to ensure correct chapter count const latestEntry = mangaHistory.length > 0 ? mangaHistory.toSorted((a, b) => a.timestamp - b.timestamp).at(-1) : null; const chapters = latestEntry?.chaptersRead ?? 0; // Compute confidence as selectedMatch.confidence or max across all AniList matches const selectedConfidence = match.selectedMatch?.confidence; const allConfidences = ( match.anilistMatches?.map((m) => m.confidence ?? 0) || [0] ).filter(Number.isFinite); const maxConfidence = allConfidences.length > 0 ? Math.max(...allConfidences) : 0; const confidence = selectedConfidence ?? maxConfidence; return { title: match.kenmeiManga.title, chapters, status: match.status, confidence, format: match.selectedMatch?.format, }; }); // Sort by chapters read (descending) and limit to top 100 data.sort((a, b) => b.chapters - a.chapters); const limitedData = data.slice(0, 100); return { type, value, data: limitedData, };}
Overload for status-based drill-down: value must be a MatchStatus.
Overload for non-status drill-downs: value is a string.
Computes drill-down data for a specific filter dimension.
Param: matchResults
Filtered match results.
Param: type
Type of drill-down:
Param: value
Specific value to drill down into.
Param: readingHistory
Reading history for chapter counts.
Returns
Drill-down data with detailed breakdown.
Source