Files
Momento/memento-note/components/flashcards/revision-heatmap.tsx
Antigravity 36336e6b0d
Some checks failed
CI / Lint, Test & Build (push) Failing after 32s
CI / Deploy production (on server) (push) Has been skipped
feat(flashcards): révision SM-2, génération IA et page /revision
Livre US-FLASHCARDS avec decks, session de révision, stats et migration Prisma. Finalise le Web Clipper (i18n 15 langues) et corrige les erreurs ESLint bloquant la CI.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 19:22:20 +00:00

70 lines
2.1 KiB
TypeScript

'use client'
import { useMemo } from 'react'
import { cn } from '@/lib/utils'
import { useLanguage } from '@/lib/i18n'
interface HeatmapDay {
date: string
count: number
}
interface RevisionHeatmapProps {
data: HeatmapDay[]
className?: string
}
function intensityClass(count: number, max: number): string {
if (count <= 0) return 'bg-black/[0.04] dark:bg-white/[0.06]'
const ratio = count / Math.max(max, 1)
if (ratio >= 0.75) return 'bg-brand-accent'
if (ratio >= 0.5) return 'bg-brand-accent/70'
if (ratio >= 0.25) return 'bg-brand-accent/40'
return 'bg-brand-accent/20'
}
export function RevisionHeatmap({ data, className }: RevisionHeatmapProps) {
const { t } = useLanguage()
const { cells, maxCount } = useMemo(() => {
const map = new Map(data.map((d) => [d.date, d.count]))
const today = new Date()
const cells: { date: string; count: number; label: string }[] = []
for (let i = 89; i >= 0; i--) {
const d = new Date(today)
d.setDate(d.getDate() - i)
const key = d.toISOString().slice(0, 10)
cells.push({
date: key,
count: map.get(key) || 0,
label: d.toLocaleDateString(undefined, { day: 'numeric', month: 'short' }),
})
}
const maxCount = Math.max(1, ...cells.map((c) => c.count))
return { cells, maxCount }
}, [data])
return (
<div className={cn('space-y-3', className)}>
<div className="flex items-center justify-between">
<p className="text-[10px] font-bold uppercase tracking-widest text-concrete">
{t('flashcards.heatmapTitle')}
</p>
<span className="text-[10px] text-concrete/60">{t('flashcards.heatmapLast90')}</span>
</div>
<div className="grid grid-cols-[repeat(15,minmax(0,1fr))] gap-1 sm:grid-cols-[repeat(18,minmax(0,1fr))]">
{cells.map((cell) => (
<div
key={cell.date}
title={`${cell.label}: ${cell.count}`}
className={cn(
'aspect-square rounded-[3px] transition-colors',
intensityClass(cell.count, maxCount),
)}
/>
))}
</div>
</div>
)
}