Public Access
feat(stats): add stats page and pile trend
Show the stat groups on /stats, the pile trend on the dashboard card, one switch per group in the settings, and the shared groups on the public profile.
This commit is contained in:
1 parent
83bbc2730d
commit
9d1c7bb40d
31 files changed
+978
-11
No files matched your search
@@ -0,0 +1,44 @@
|
||||
import {useTranslation} from 'react-i18next';
|
||||
import {Inbox} from 'lucide-react';
|
||||
|
||||
import StatsSection from './StatsSection.jsx';
|
||||
import StatTile from './StatTile.jsx';
|
||||
import GameRow from './GameRow.jsx';
|
||||
import {formatPlaytime} from '../../utils/games.js';
|
||||
import {formatDecimal} from '../../utils/stats.js';
|
||||
|
||||
export default function PileStats({pile, linked = true}) {
|
||||
const {t, i18n} = useTranslation();
|
||||
const since = pile.oldestUnplayed
|
||||
? new Date(pile.oldestUnplayed.inLibrarySince).toLocaleDateString(i18n.language, {dateStyle: 'medium'})
|
||||
: null;
|
||||
return (
|
||||
<StatsSection title={t('STATS.PILE.TITLE')} icon={Inbox}>
|
||||
<div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
|
||||
<StatTile label={t('STATS.PILE.GAMES')} value={pile.backlogGames.toLocaleString(i18n.language)}/>
|
||||
<StatTile label={t('STATS.PILE.NORMAL')} value={formatPlaytime(pile.normalMinutes)}
|
||||
hint={t('STATS.PILE.KNOWN', {count: pile.gamesWithTimeToBeat})}/>
|
||||
<StatTile label={t('STATS.PILE.COMPLETIONIST')} value={formatPlaytime(pile.completionistMinutes)}/>
|
||||
</div>
|
||||
{pile.normalMinutes > 0 && (
|
||||
<ul className="space-y-1 text-sm">
|
||||
<li>
|
||||
{pile.paceWeeks == null
|
||||
? t('STATS.PILE.PACE_NONE')
|
||||
: t('STATS.PILE.PACE', {
|
||||
weeks: formatDecimal(pile.paceWeeks, i18n.language),
|
||||
perWeek: formatPlaytime(pile.minutesPerWeek),
|
||||
})}
|
||||
</li>
|
||||
<li>{t('STATS.PILE.WORK_YEARS', {years: formatDecimal(pile.workYears, i18n.language, 2)})}</li>
|
||||
</ul>
|
||||
)}
|
||||
{pile.oldestUnplayed && (
|
||||
<div className="space-y-2">
|
||||
<span className="text-sm font-medium">{t('STATS.PILE.OLDEST')}</span>
|
||||
<GameRow game={pile.oldestUnplayed} value={t('STATS.PILE.SINCE', {date: since})} linked={linked}/>
|
||||
</div>
|
||||
)}
|
||||
</StatsSection>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user