fix(dashboard): audit Second Brain — pistes, erreur briefing, dark, crédits
Some checks failed
CI / Lint, Unit Tests & Build (push) Failing after 1m16s
CI / Deploy production (on server) (push) Has been skipped

Corrige loadPaths sans argument après Echo/IA, bannière si briefing en échec,
checklist connexion inversée, skeletons dark, libellé usage = solde unique.
This commit is contained in:
Antigravity
2026-07-16 21:08:48 +00:00
parent a77f3ffb6e
commit e8ee53c815
6 changed files with 99 additions and 50 deletions

View File

@@ -58,7 +58,7 @@ export function DashboardInboxWidget({
compact
>
{loading ? (
<div className="h-10 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-10 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
) : (
<button
type="button"
@@ -94,7 +94,7 @@ export function DashboardRevisionWidget({
compact
>
{loading ? (
<div className="h-10 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-10 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
) : (
<button
type="button"
@@ -135,7 +135,7 @@ export function DashboardStatsWidget({
>
{loading ? (
<div className="grid grid-cols-3 gap-2">
{[0, 1, 2].map(i => <div key={i} className="h-12 rounded-lg bg-stone-50 animate-pulse" />)}
{[0, 1, 2].map(i => <div key={i} className="h-12 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />)}
</div>
) : (
<button type="button" onClick={onOpen} className="w-full text-start">
@@ -176,8 +176,8 @@ export function DashboardAgentActivityWidget({
>
{loading ? (
<div className="space-y-2">
<div className="h-8 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-8 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-8 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>
) : actions.length === 0 ? (
<p className="text-[10px] text-concrete italic py-2">{t('homeDashboard.widgetAgentActivityEmpty')}</p>
@@ -222,7 +222,7 @@ export function DashboardGmailWidget({
compact
>
{loading ? (
<div className="h-10 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-10 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
) : connected ? (
<button
type="button"
@@ -264,7 +264,7 @@ export function DashboardPinnedWidget({
>
{loading ? (
<div className="space-y-2">
<div className="h-8 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-8 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
</div>
) : notes.length === 0 ? (
<p className="text-[10px] text-concrete italic py-2">{t('homeDashboard.widgetPinnedEmpty')}</p>

View File

@@ -30,7 +30,7 @@ export function DashboardDailyReview({
>
{loading ? (
<div className="space-y-2">
{[0, 1, 2].map(i => <div key={i} className="h-8 rounded-lg bg-stone-50 animate-pulse" />)}
{[0, 1, 2].map(i => <div key={i} className="h-8 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />)}
</div>
) : (
<ul className="space-y-1.5">
@@ -79,7 +79,7 @@ export function DashboardOpenLoops({
compact
>
{loading ? (
<div className="h-16 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-16 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
) : loops.length === 0 ? (
<p className="text-[10px] text-concrete italic py-2">{t('homeDashboard.openLoopsEmpty')}</p>
) : (
@@ -122,7 +122,7 @@ export function DashboardDailyNoteWidget({
compact
>
{loading ? (
<div className="h-10 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-10 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
) : (
<button
type="button"
@@ -158,7 +158,7 @@ export function DashboardLinkSuggestions({
>
{loading ? (
<div className="space-y-2">
<div className="h-12 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-12 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
</div>
) : paths.length === 0 ? (
<p className="text-[10px] text-concrete italic">{t('homeDashboard.linkSuggestionsEmpty')}</p>
@@ -216,7 +216,7 @@ export function DashboardBridgesWidget({
title={t('homeDashboard.widgets.bridges')}
>
{loading ? (
<div className="h-20 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-20 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
) : suggestions.length === 0 ? (
<p className="text-[10px] text-concrete italic">{t('homeDashboard.bridgesEmpty')}</p>
) : (

View File

@@ -246,13 +246,19 @@ export function DashboardView({ onNoteSelect }: DashboardViewProps) {
const [echoRefreshing, setEchoRefreshing] = useState(false)
const [dismissingInsightId, setDismissingInsightId] = useState<string | null>(null)
const [actingBridgeSuggestionKey, setActingBridgeSuggestionKey] = useState<string | null>(null)
const [briefingError, setBriefingError] = useState(false)
const loadBriefing = useCallback(async () => {
try {
const res = await fetch('/api/briefing', { cache: 'no-store' })
if (res.ok) setData(await res.json())
if (res.ok) {
setData(await res.json())
setBriefingError(false)
} else {
setBriefingError(true)
}
} catch {
/* état dégradé */
setBriefingError(true)
}
}, [])
@@ -292,6 +298,36 @@ export function DashboardView({ onNoteSelect }: DashboardViewProps) {
}
}, [])
/** Recharge briefing puis enrichit les pistes (évite loadPaths() sans argument). */
const reloadBriefingAndPaths = useCallback(async () => {
try {
const res = await fetch('/api/briefing', { cache: 'no-store' })
if (!res.ok) {
setBriefingError(true)
return
}
const briefing = await res.json()
setData(briefing)
setBriefingError(false)
setPaths(buildFastPathsFromBriefing({
recentNotes: (briefing.recentNotes ?? []).map((n: BriefingNote) => ({
id: n.id,
title: n.title,
content: n.content,
notebookId: n.notebookId,
})),
inboxCount: briefing.inboxCount ?? 0,
dueFlashcards: briefing.dueFlashcards ?? 0,
insights: briefing.insights ?? [],
bridgeSuggestions: briefing.bridgeSuggestions ?? [],
agentSuggestions: briefing.agentSuggestions ?? [],
}))
await loadPaths(briefing)
} catch {
setBriefingError(true)
}
}, [loadPaths])
const loadSentiment = useCallback(async () => {
try {
const res = await fetch('/api/briefing/sentiment', { cache: 'no-store' })
@@ -486,7 +522,7 @@ export function DashboardView({ onNoteSelect }: DashboardViewProps) {
if (res.status === 403 && json.error === 'ai_consent_required') { await requestAiConsent(); return }
if (!res.ok) throw new Error(json.error)
if (json.insight) {
await Promise.all([loadBriefing(), loadPaths()])
await reloadBriefingAndPaths()
emitAiUsageChanged()
toast.success(t('homeDashboard.echoFound'))
} else {
@@ -497,12 +533,12 @@ export function DashboardView({ onNoteSelect }: DashboardViewProps) {
} finally {
setEchoRefreshing(false)
}
}, [requestAiConsent, loadBriefing, loadPaths, t])
}, [requestAiConsent, reloadBriefingAndPaths, t])
const handleEnableAi = useCallback(async () => {
await requestAiConsent()
await Promise.all([loadBriefing(), loadPaths()])
}, [requestAiConsent, loadBriefing, loadPaths])
await reloadBriefingAndPaths()
}, [requestAiConsent, reloadBriefingAndPaths])
const handleDismissBridgeSuggestion = useCallback(async (s: BridgeSuggestionItem) => {
const key = `${s.clusterAId}-${s.clusterBId}`
@@ -672,7 +708,8 @@ export function DashboardView({ onNoteSelect }: DashboardViewProps) {
{
key: 'connection',
label: t('homeDashboard.dailyReviewConnection'),
done: pathsList.some(p => p.type === 'connect' || p.type === 'resurface'),
// Coché quand il n'y a plus de piste de connexion à traiter
done: !pathsList.some(p => p.type === 'connect' || p.type === 'resurface'),
onClick: () => {
const p = pathsList.find(x => x.type === 'connect' || x.type === 'resurface')
if (p) handlePathAction(p)
@@ -853,8 +890,8 @@ export function DashboardView({ onNoteSelect }: DashboardViewProps) {
/>
{briefingLoading ? (
<div className="space-y-2">
<div className="h-10 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-10 rounded-lg bg-stone-50 animate-pulse" />
<div className="h-10 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
<div className="h-10 rounded-lg bg-stone-50 dark:bg-zinc-950/40 animate-pulse" />
</div>
) : reminders.length === 0 ? (
<p className="text-[11px] text-concrete italic py-1">{t('homeDashboard.allCaughtUp')}</p>
@@ -1021,6 +1058,20 @@ export function DashboardView({ onNoteSelect }: DashboardViewProps) {
<div className="absolute inset-0 bg-[linear-gradient(to_right,#80808005_1px,transparent_1px),linear-gradient(to_bottom,#80808005_1px,transparent_1px)] bg-[size:28px_28px] pointer-events-none z-0" />
<div className="w-full px-4 sm:px-6 lg:px-8 xl:px-10 2xl:px-12 py-6 sm:py-8 relative z-10">
{briefingError && !data && (
<div className="mb-4 rounded-xl border border-rose-500/30 bg-rose-500/5 px-4 py-3 flex flex-col sm:flex-row sm:items-center sm:justify-between gap-3">
<p className="text-sm text-ink dark:text-dark-ink">
{t('homeDashboard.briefingLoadError')}
</p>
<button
type="button"
onClick={() => { void reloadBriefingAndPaths() }}
className="shrink-0 text-[10px] font-mono font-bold uppercase tracking-wider px-3 py-2 rounded-lg bg-ink text-white dark:bg-white dark:text-black"
>
{t('homeDashboard.briefingRetry')}
</button>
</div>
)}
{/* ── En-tête : orientation en 2 secondes ── */}
<header className="mb-5">
<div className="flex flex-col sm:flex-row sm:items-end sm:justify-between gap-2 pb-4 border-b border-border/20">