fix: clear cockpit state on session switch and add loading placeholders

- Reset triageMeta, psaTicketId, steps, and completedSteps when
  activeChatId changes (prevents stale triage data from previous session
  showing while AI processes the new one)
- Split the activeChatId and activeActions reset effects so triage
  only resets on session switch, not on every new action set
- Add loading placeholders to cockpit work zone: spinner + "analyzing"
  text in steps panel, "questions will appear here" in right panel
- Add centered "Starting session..." loader to FlowPilot page when
  loading with no messages yet (prefill creation period)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-04-03 05:56:29 +00:00
parent b03f84aecf
commit 91e3f80707
2 changed files with 55 additions and 21 deletions

View File

@@ -87,11 +87,23 @@ export default function CockpitPage() {
prevMessageCountRef.current = session.messages.length prevMessageCountRef.current = session.messages.length
}, [session.messages.length, showOnboarding]) }, [session.messages.length, showOnboarding])
// Reset local step UI when switching cases or when a new action set arrives. // Reset all cockpit-local state when switching cases.
// Triage data gets repopulated via onSessionLoadedRef after the async fetch.
useEffect(() => { useEffect(() => {
setActiveStepIndex(0) setActiveStepIndex(0)
setCompletedSteps(new Set()) setCompletedSteps(new Set())
}, [session.activeChatId, session.activeActions]) setTriageMeta({
client_name: null, asset_name: null, issue_category: null,
triage_hypothesis: null, evidence_items: [],
})
setPsaTicketId(null)
}, [session.activeChatId])
// Reset step UI when a new action set arrives from AI.
useEffect(() => {
setActiveStepIndex(0)
setCompletedSteps(new Set())
}, [session.activeActions])
// ── Triage handlers ── // ── Triage handlers ──
@@ -306,28 +318,44 @@ export default function CockpitPage() {
<div className="flex min-h-0 overflow-hidden" style={{ height: `${workZonePct}%` }}> <div className="flex min-h-0 overflow-hidden" style={{ height: `${workZonePct}%` }}>
{/* Left: Steps panel */} {/* Left: Steps panel */}
<div className="flex-[55] min-w-0 p-3 overflow-y-auto border-r border-default"> <div className="flex-[55] min-w-0 p-3 overflow-y-auto border-r border-default">
<StepsPanel {session.loading && session.activeActions.length === 0 ? (
actions={session.activeActions} <div className="flex flex-col items-center justify-center h-full text-center gap-2">
activeIndex={activeStepIndex} <Loader2 size={20} className="animate-spin text-accent" />
completedSteps={completedSteps} <p className="text-xs text-muted-foreground">FlowPilot is analyzing the issue...</p>
onStepComplete={handleStepComplete} </div>
onStepSelect={handleStepSelect} ) : (
/> <StepsPanel
actions={session.activeActions}
activeIndex={activeStepIndex}
completedSteps={completedSteps}
onStepComplete={handleStepComplete}
onStepSelect={handleStepSelect}
/>
)}
</div> </div>
{/* Right: FlowPilot Asks + What We Know */} {/* Right: FlowPilot Asks + What We Know */}
<div className="flex-[45] min-w-0 p-3 overflow-y-auto flex flex-col gap-3"> <div className="flex-[45] min-w-0 p-3 overflow-y-auto flex flex-col gap-3">
<FlowPilotAsks {session.loading && session.activeQuestions.length === 0 && triageMeta.evidence_items.length === 0 ? (
questions={session.activeQuestions} <div className="flex flex-col items-center justify-center h-full text-center gap-2">
onAnswer={(answer) => { <Sparkles size={18} className="text-muted" />
void session.sendMessage(answer, { clearComposer: false }) <p className="text-xs text-muted-foreground">Questions and evidence will appear here</p>
}} </div>
loading={session.loading} ) : (
/> <>
<WhatWeKnow <FlowPilotAsks
items={triageMeta.evidence_items} questions={session.activeQuestions}
onAdd={handleEvidenceAdd} onAnswer={(answer) => {
onEdit={handleEvidenceEdit} void session.sendMessage(answer, { clearComposer: false })
/> }}
loading={session.loading}
/>
<WhatWeKnow
items={triageMeta.evidence_items}
onAdd={handleEvidenceAdd}
onEdit={handleEvidenceEdit}
/>
</>
)}
</div> </div>
</div> </div>

View File

@@ -149,6 +149,12 @@ export default function FlowPilotPage() {
</p> </p>
</div> </div>
)} )}
{session.messages.length === 0 && session.loading && (
<div className="flex flex-col items-center justify-center h-full text-center gap-3">
<Loader2 size={24} className="animate-spin text-primary" />
<p className="text-sm text-muted-foreground">Starting session...</p>
</div>
)}
{session.messages.map((msg, i) => ( {session.messages.map((msg, i) => (
<ChatMessage <ChatMessage
key={i} key={i}