fix: cockpit/flowpilot bugs and redesign view toggle placement
- Fix return type annotation on unified_chat_service.send_chat_message (6→7 tuple) - Fix stale closure in CockpitPage handleStepComplete auto-advance logic - Fix IncidentHeader copy link hardcoding /assistant/ path (now uses current URL) - Wire psaTicketId from session data through to CockpitPage incident header - Fix FlowPilotAsks showing only first question — add chevron navigation for all - Redesign ViewToggle: add icons (MessageSquare/LayoutDashboard) and subtitles - Move view toggle from chatbar toolbar to standalone row above input on dashboard - Standardize toggle placement across dashboard, FlowPilot, and Cockpit pages Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,50 +1,69 @@
|
||||
import { useNavigate } from 'react-router-dom'
|
||||
import { MessageSquare, LayoutDashboard } from 'lucide-react'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useFeatureFlag } from '@/hooks/useFeatureFlag'
|
||||
import { useUserPreferencesStore } from '@/store/userPreferencesStore'
|
||||
|
||||
type FlowPilotView = 'flowpilot' | 'cockpit'
|
||||
|
||||
const VIEW_OPTIONS: { key: FlowPilotView; label: string; icon: typeof MessageSquare; subtitle: string }[] = [
|
||||
{ key: 'flowpilot', label: 'FlowPilot', icon: MessageSquare, subtitle: 'Chat-first AI troubleshooting' },
|
||||
{ key: 'cockpit', label: 'Cockpit', icon: LayoutDashboard, subtitle: 'Steps, evidence & triage board' },
|
||||
]
|
||||
|
||||
interface ViewToggleProps {
|
||||
currentView: 'flowpilot' | 'cockpit'
|
||||
sessionId: string
|
||||
/** Which view is currently active — drives highlight state */
|
||||
currentView: FlowPilotView
|
||||
/** Session ID for navigation (session pages only). Omit for preference-only mode (dashboard). */
|
||||
sessionId?: string
|
||||
/** Show the subtitle below the toggle. Default true for standalone, false for inline. */
|
||||
showSubtitle?: boolean
|
||||
}
|
||||
|
||||
export function ViewToggle({ currentView, sessionId }: ViewToggleProps) {
|
||||
export function ViewToggle({ currentView, sessionId, showSubtitle = true }: ViewToggleProps) {
|
||||
const navigate = useNavigate()
|
||||
const hasCockpit = useFeatureFlag('flowpilot_cockpit')
|
||||
const setPreferredView = useUserPreferencesStore(s => s.setPreferredFlowPilotView)
|
||||
|
||||
if (!hasCockpit) return null
|
||||
|
||||
const handleSwitch = (view: 'flowpilot' | 'cockpit') => {
|
||||
const activeOption = VIEW_OPTIONS.find(o => o.key === currentView) ?? VIEW_OPTIONS[0]
|
||||
|
||||
const handleSwitch = (view: FlowPilotView) => {
|
||||
if (view === currentView) return
|
||||
const path = view === 'cockpit'
|
||||
? `/cockpit/${sessionId}`
|
||||
: `/assistant/${sessionId}`
|
||||
navigate(path)
|
||||
setPreferredView(view)
|
||||
if (sessionId) {
|
||||
const path = view === 'cockpit'
|
||||
? `/cockpit/${sessionId}`
|
||||
: `/assistant/${sessionId}`
|
||||
navigate(path)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex items-center rounded-lg border border-border bg-card p-0.5 text-xs">
|
||||
<button
|
||||
onClick={() => handleSwitch('flowpilot')}
|
||||
className={cn(
|
||||
'rounded-md px-2.5 py-1 font-medium transition-colors',
|
||||
currentView === 'flowpilot'
|
||||
? 'bg-elevated text-foreground'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
FlowPilot
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleSwitch('cockpit')}
|
||||
className={cn(
|
||||
'rounded-md px-2.5 py-1 font-medium transition-colors',
|
||||
currentView === 'cockpit'
|
||||
? 'bg-elevated text-foreground'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
Cockpit
|
||||
</button>
|
||||
<div className="flex flex-col items-start gap-1">
|
||||
<div className="flex items-center rounded-lg border border-border bg-card p-0.5 text-xs">
|
||||
{VIEW_OPTIONS.map(({ key, label, icon: Icon }) => (
|
||||
<button
|
||||
key={key}
|
||||
onClick={() => handleSwitch(key)}
|
||||
className={cn(
|
||||
'flex items-center gap-1.5 rounded-md px-2.5 py-1 font-medium transition-colors',
|
||||
currentView === key
|
||||
? 'bg-elevated text-foreground'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
)}
|
||||
>
|
||||
<Icon size={12} />
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{showSubtitle && (
|
||||
<span className="text-[10px] text-muted-foreground leading-none pl-0.5">
|
||||
{activeOption.subtitle}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user