Public Access
feat(stats): add platform statistics and landing blocks
Add PlatformStats component to stats page. Extract YearBars from FinishedStats for reuse on landing. Add three blocks to landing: games per platform, top genres, finished per year. Update translations.
This commit is contained in:
1 parent
b129f6a6e4
commit
22633a9e45
12 files changed
+143
-18
No files matched your search
@@ -3,26 +3,32 @@ import {Flag} from 'lucide-react';
|
||||
|
||||
import StatsSection from './StatsSection.jsx';
|
||||
|
||||
export function YearBars({perYear}) {
|
||||
const max = Math.max(1, ...perYear.map((y) => y.games));
|
||||
return (
|
||||
<ul className="space-y-2">
|
||||
{perYear.map((y) => (
|
||||
<li key={y.year} className="flex items-center gap-3 text-sm">
|
||||
<span className="w-12 tabular-nums">{y.year}</span>
|
||||
<div className="h-2 flex-1 rounded-full bg-muted">
|
||||
<div className="h-full rounded-full bg-primary"
|
||||
style={{width: `${(y.games / max) * 100}%`}}/>
|
||||
</div>
|
||||
<span className="w-8 text-right tabular-nums">{y.games}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
|
||||
export default function FinishedStats({finished}) {
|
||||
const {t} = useTranslation();
|
||||
const max = Math.max(1, ...finished.perYear.map((y) => y.games));
|
||||
return (
|
||||
<StatsSection title={t('STATS.FINISHED.TITLE')} icon={Flag}>
|
||||
{finished.perYear.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">{t('STATS.FINISHED.EMPTY')}</p>
|
||||
) : (
|
||||
<ul className="space-y-2">
|
||||
{finished.perYear.map((y) => (
|
||||
<li key={y.year} className="flex items-center gap-3 text-sm">
|
||||
<span className="w-12 tabular-nums">{y.year}</span>
|
||||
<div className="h-2 flex-1 rounded-full bg-muted">
|
||||
<div className="h-full rounded-full bg-primary"
|
||||
style={{width: `${(y.games / max) * 100}%`}}/>
|
||||
</div>
|
||||
<span className="w-8 text-right tabular-nums">{y.games}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<YearBars perYear={finished.perYear}/>
|
||||
)}
|
||||
{finished.finishedBeforeTracking > 0 && (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {MonitorSmartphone} from 'lucide-react';
|
||||
|
||||
import StatsSection from './StatsSection.jsx';
|
||||
import {TagBars} from './TasteStats.jsx';
|
||||
|
||||
export default function PlatformStats({platforms}) {
|
||||
const {t} = useTranslation();
|
||||
return (
|
||||
<StatsSection title={t('STATS.PLATFORMS.TITLE')} icon={MonitorSmartphone}>
|
||||
{platforms.perPlatform.length === 0 ? (
|
||||
<p className="text-sm text-muted-foreground">{t('STATS.PLATFORMS.EMPTY')}</p>
|
||||
) : (
|
||||
<TagBars tags={platforms.perPlatform}/>
|
||||
)}
|
||||
</StatsSection>
|
||||
);
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import StatsSection from './StatsSection.jsx';
|
||||
import {formatPlaytime} from '../../utils/games.js';
|
||||
import {genreKey} from '../../utils/stats.js';
|
||||
|
||||
function TagBars({tags, label}) {
|
||||
export function TagBars({tags, label = (name) => name}) {
|
||||
const {t} = useTranslation();
|
||||
const max = Math.max(1, ...tags.map((tag) => tag.minutes));
|
||||
return (
|
||||
|
||||
@@ -5,6 +5,7 @@ import TopGamesStats from './TopGamesStats.jsx';
|
||||
import TasteStats from './TasteStats.jsx';
|
||||
import HabitsStats from './HabitsStats.jsx';
|
||||
import FinishedStats from './FinishedStats.jsx';
|
||||
import PlatformStats from './PlatformStats.jsx';
|
||||
|
||||
/**
|
||||
* One stat group as a tile; null while its data is not there. A function, not a component: a tile grid leaves out a
|
||||
@@ -22,6 +23,7 @@ export default function statGroupTile(id, stats, linked = true) {
|
||||
case 'STATS_TASTE': return stats?.taste ? <TasteStats taste={stats.taste}/> : null;
|
||||
case 'STATS_HABITS': return stats?.habits ? <HabitsStats habits={stats.habits}/> : null;
|
||||
case 'STATS_FINISHED': return stats?.finished ? <FinishedStats finished={stats.finished}/> : null;
|
||||
case 'STATS_PLATFORMS': return stats?.platforms ? <PlatformStats platforms={stats.platforms}/> : null;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user