fix(ui): thème, textes et lisibilité restants de la revue
Les boutons suivent la couleur d’apparence, les libellés trop petits ou trop techniques sont clarifiés, et le catalogue des fournisseurs se met à jour tout seul. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -12,6 +12,8 @@ interface HeatmapDay {
|
||||
interface RevisionHeatmapProps {
|
||||
data: HeatmapDay[]
|
||||
className?: string
|
||||
/** Révisions de flashcards, ou notes modifiées sur le tableau de bord. */
|
||||
kind?: 'reviews' | 'edits'
|
||||
}
|
||||
|
||||
function intensityClass(count: number, max: number): string {
|
||||
@@ -28,19 +30,25 @@ function resolveDateLocale(langCode: string): string {
|
||||
return langCode
|
||||
}
|
||||
|
||||
export function RevisionHeatmap({ data, className }: RevisionHeatmapProps) {
|
||||
export function RevisionHeatmap({ data, className, kind = 'reviews' }: RevisionHeatmapProps) {
|
||||
const { t, language } = useLanguage()
|
||||
const [hovered, setHovered] = useState<{ label: string; count: number; date: string } | null>(null)
|
||||
const [selected, setSelected] = useState<{ label: string; count: number; date: string } | null>(null)
|
||||
|
||||
const dateLocale = resolveDateLocale(language ?? 'en')
|
||||
const prefix = kind === 'edits' ? 'homeDashboard.activityHeatmap' : 'flashcards.heatmap'
|
||||
|
||||
const dayLabel = (count: number) => {
|
||||
if (count <= 0) return t(`${prefix}DayNone`)
|
||||
if (count === 1) return t(`${prefix}DayOne`)
|
||||
return t(`${prefix}Day`, { count })
|
||||
}
|
||||
|
||||
const { cells, maxCount, totalReviews, monthLabels } = useMemo(() => {
|
||||
const map = new Map(data.map((d) => [d.date, d.count]))
|
||||
const now = new Date()
|
||||
// todayUTC est le début de la journée courante à minuit UTC
|
||||
const todayUTC = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate()))
|
||||
|
||||
|
||||
const cells: { date: string; count: number; label: string }[] = []
|
||||
const monthLabels: { index: number; label: string }[] = []
|
||||
let lastMonth = -1
|
||||
@@ -78,22 +86,22 @@ export function RevisionHeatmap({ data, className }: RevisionHeatmapProps) {
|
||||
|
||||
return (
|
||||
<div className={cn('space-y-2', className)}>
|
||||
{/* En-tête */}
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-[10px] font-bold uppercase tracking-widest text-concrete">
|
||||
{t('flashcards.heatmapTitle')}
|
||||
<p className="text-[13px] font-semibold uppercase tracking-wider text-concrete">
|
||||
{t(`${prefix}Title`)}
|
||||
</p>
|
||||
<span className="text-[10px] text-concrete/60">
|
||||
{totalReviews > 0 ? `${totalReviews} révisions · 90 jours` : t('flashcards.heatmapLast90')}
|
||||
<span className="text-[13px] text-concrete/70">
|
||||
{totalReviews > 0
|
||||
? t(`${prefix}Total`, { count: totalReviews })
|
||||
: t(kind === 'edits' ? 'homeDashboard.activityHeatmapLast90' : 'flashcards.heatmapLast90')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Labels de mois au-dessus de la grille */}
|
||||
<div className="relative h-4">
|
||||
<div className="relative h-6">
|
||||
{monthLabels.map((m) => (
|
||||
<span
|
||||
key={m.label + m.index}
|
||||
className="absolute text-[9px] text-concrete/60 font-medium translate-y-0.5"
|
||||
className="absolute text-[13px] text-concrete/70 font-medium leading-tight"
|
||||
style={{ left: pct(m.index) }}
|
||||
>
|
||||
{m.label}
|
||||
@@ -101,14 +109,11 @@ export function RevisionHeatmap({ data, className }: RevisionHeatmapProps) {
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Grille pleine largeur */}
|
||||
<div className="grid grid-cols-[repeat(15,minmax(0,1fr))] gap-1 sm:grid-cols-[repeat(18,minmax(0,1fr))]">
|
||||
{cells.map((cell) => {
|
||||
const isHovered = hovered?.date === cell.date
|
||||
const isSelected = selected?.date === cell.date
|
||||
const reviewText = cell.count > 0
|
||||
? `${cell.count} révision${cell.count > 1 ? 's' : ''}`
|
||||
: 'Aucune révision'
|
||||
const reviewText = dayLabel(cell.count)
|
||||
|
||||
return (
|
||||
<button
|
||||
@@ -134,47 +139,29 @@ export function RevisionHeatmap({ data, className }: RevisionHeatmapProps) {
|
||||
})}
|
||||
</div>
|
||||
|
||||
{/* Info au survol / clic */}
|
||||
<div className="h-6 flex items-center justify-between text-[11px] border-b border-border/20 pb-1">
|
||||
<div className="min-h-7 flex items-center text-[13px] border-b border-border/20 pb-1">
|
||||
{activeInfo ? (
|
||||
<p className="flex items-center gap-1.5 animate-fadeIn">
|
||||
<span className="font-semibold text-foreground">
|
||||
{activeInfo.count > 0
|
||||
? `${activeInfo.count} révision${activeInfo.count > 1 ? 's' : ''}`
|
||||
: 'Aucune révision'}
|
||||
{dayLabel(activeInfo.count)}
|
||||
</span>
|
||||
<span className="text-concrete">· {activeInfo.label}</span>
|
||||
{selected?.date === activeInfo.date && !hovered && (
|
||||
<span className="text-[9px] bg-brand-accent/10 text-brand-accent px-1.5 py-0.2 rounded-full font-medium">
|
||||
sélectionné
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-[10px] text-concrete/40 italic">
|
||||
Survolez ou cliquez sur un carré pour voir le détail
|
||||
<p className="text-[13px] text-concrete/50 italic">
|
||||
{t(`${prefix}Hint`)}
|
||||
</p>
|
||||
)}
|
||||
{selected && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setSelected(null)}
|
||||
className="text-[10px] text-brand-accent hover:text-brand-accent/80 hover:underline transition-colors"
|
||||
>
|
||||
Effacer la sélection
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Légende */}
|
||||
<div className="flex items-center gap-2 pt-0.5">
|
||||
<span className="text-[9px] text-concrete/50">Moins</span>
|
||||
<span className="text-[13px] text-concrete/60">{t('flashcards.heatmapLess')}</span>
|
||||
<div className="flex gap-0.5">
|
||||
{['bg-black/[0.06] dark:bg-white/[0.08]', 'bg-brand-accent/20', 'bg-brand-accent/40', 'bg-brand-accent/70', 'bg-brand-accent'].map((cls, i) => (
|
||||
<div key={i} className={cn('w-3 h-3 rounded-[3px]', cls)} />
|
||||
))}
|
||||
</div>
|
||||
<span className="text-[9px] text-concrete/50">Plus</span>
|
||||
<span className="text-[13px] text-concrete/60">{t('flashcards.heatmapMore')}</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user