fix(dashboard): audit Second Brain — pistes, erreur briefing, dark, crédits
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:
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user