fix(quotas): unifier le décompte IA (BYOK, rollback) et combler les fuites
All checks were successful
CI / Lint, Unit Tests & Build (push) Successful in 6m55s
CI / Deploy production (on server) (push) Successful in 36s

Centralise la réserve via ai-quota, corrige admin unavailable (-1), brancher les routes sans quota et le host-pays brainstorm, avec usage-meter élargi, noms de clusters, MCP et ajustements dashboard/insights.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Antigravity
2026-07-15 20:42:25 +00:00
parent 30da592ba2
commit 4fe31ebc99
75 changed files with 2949 additions and 785 deletions

View File

@@ -294,6 +294,7 @@ export function DashboardActivityWidget({
loading: boolean
}) {
const { t } = useLanguage()
const hasActivity = data.some(d => d.count > 0)
return (
<DashboardWidgetShell
widgetId="activity"
@@ -301,11 +302,17 @@ export function DashboardActivityWidget({
title={t('homeDashboard.widgets.activity')}
>
{loading ? (
<div className="h-24 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-24 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
) : !hasActivity ? (
<div className="rounded-xl border border-dashed border-border/35 bg-stone-50/50 dark:bg-zinc-950/30 p-4 text-center">
<p className="text-[10px] text-concrete leading-relaxed">{t('homeDashboard.activityEmptyHint')}</p>
</div>
) : (
<RevisionHeatmap data={data} />
<>
<RevisionHeatmap data={data} />
<p className="text-[9px] text-concrete mt-2">{t('homeDashboard.widgetActivityHint')}</p>
</>
)}
<p className="text-[9px] text-concrete mt-2">{t('homeDashboard.widgetActivityHint')}</p>
</DashboardWidgetShell>
)
}
@@ -329,16 +336,20 @@ export function DashboardFlashcardsProgressWidget({
retentionRate,
streak,
totalCards,
dueCount = 0,
loading,
onOpen,
}: {
retentionRate: number
streak: number
totalCards: number
dueCount?: number
loading?: boolean
onOpen: () => void
}) {
const { t } = useLanguage()
const retention = Math.max(0, Math.min(100, retentionRate))
return (
<DashboardWidgetShell
widgetId="flashcards-progress"
@@ -347,12 +358,28 @@ export function DashboardFlashcardsProgressWidget({
compact
>
{loading ? (
<div className="h-14 rounded-lg bg-stone-50 animate-pulse" />
<div className="space-y-2">
<div className="h-12 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
<div className="h-8 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
</div>
) : totalCards === 0 && dueCount === 0 ? (
<div className="rounded-xl border border-dashed border-border/35 bg-stone-50/50 dark:bg-zinc-950/30 p-3 text-center">
<p className="text-[10px] text-concrete leading-relaxed mb-2">
{t('homeDashboard.flashEmptyHint')}
</p>
<button
type="button"
onClick={onOpen}
className="text-[9px] font-mono font-bold uppercase text-brand-accent hover:underline"
>
{t('homeDashboard.flashEmptyCta')}
</button>
</div>
) : (
<button type="button" onClick={onOpen} className="w-full text-start">
<div className="grid grid-cols-3 gap-2">
<button type="button" onClick={onOpen} className="w-full text-start group">
<div className="grid grid-cols-3 gap-2 mb-2.5">
<div className="text-center p-2 rounded-xl border border-border/20">
<p className="text-lg font-serif font-bold text-ink dark:text-dark-ink">{retentionRate}%</p>
<p className="text-lg font-serif font-bold text-ink dark:text-dark-ink">{retention}%</p>
<p className="text-[8px] font-mono uppercase text-concrete">{t('homeDashboard.flashRetention')}</p>
</div>
<div className="text-center p-2 rounded-xl border border-border/20">
@@ -360,10 +387,25 @@ export function DashboardFlashcardsProgressWidget({
<p className="text-[8px] font-mono uppercase text-concrete">{t('homeDashboard.flashStreak')}</p>
</div>
<div className="text-center p-2 rounded-xl border border-border/20">
<p className="text-lg font-serif font-bold text-ink dark:text-dark-ink">{totalCards}</p>
<p className="text-lg font-serif font-bold text-ink dark:text-dark-ink">{totalCards || dueCount}</p>
<p className="text-[8px] font-mono uppercase text-concrete">{t('homeDashboard.flashTotal')}</p>
</div>
</div>
<div className="h-1.5 rounded-full bg-stone-100 dark:bg-zinc-800 overflow-hidden mb-2">
<div
className="h-full rounded-full bg-brand-accent transition-all"
style={{ width: `${retention}%` }}
/>
</div>
{dueCount > 0 ? (
<p className="text-[10px] font-medium text-brand-accent group-hover:underline">
{t('homeDashboard.flashDueCta', { count: dueCount })}
</p>
) : (
<p className="text-[9px] text-concrete group-hover:text-brand-accent transition-colors">
{t('homeDashboard.flashOpenCta')}
</p>
)}
</button>
)}
</DashboardWidgetShell>