fix: move ViewToggle into header bars and remove subtitle

Eliminates the dedicated ViewToggle row on CockpitPage by merging
it into IncidentHeader's action group via extraActions prop. Removes
subtitle from ViewToggle component entirely — the icon + label is
self-explanatory. Cleans up showSubtitle prop from all call sites.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-04-04 01:48:09 +00:00
parent 47e71c4753
commit aca976a49a
5 changed files with 32 additions and 38 deletions

View File

@@ -12,6 +12,8 @@ interface IncidentHeaderProps {
onStatusUpdate?: () => void
onPause?: () => void
onClose?: () => void
/** Extra elements rendered in the action group (e.g. ViewToggle) */
extraActions?: React.ReactNode
}
interface EditPopoverProps {
@@ -168,6 +170,7 @@ export function IncidentHeader({
onStatusUpdate,
onPause,
onClose,
extraActions,
}: IncidentHeaderProps) {
return (
<div className="bg-card border-b border-default px-4 py-2 flex items-center gap-4 flex-wrap">
@@ -223,6 +226,7 @@ export function IncidentHeader({
</button>
)}
<OverflowMenu onPause={onPause} onClose={onClose} />
{extraActions}
</div>
</div>
)

View File

@@ -6,9 +6,9 @@ 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' },
const VIEW_OPTIONS: { key: FlowPilotView; label: string; icon: typeof MessageSquare }[] = [
{ key: 'flowpilot', label: 'FlowPilot', icon: MessageSquare },
{ key: 'cockpit', label: 'Cockpit', icon: LayoutDashboard },
]
interface ViewToggleProps {
@@ -16,19 +16,15 @@ interface ViewToggleProps {
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, showSubtitle = true }: ViewToggleProps) {
export function ViewToggle({ currentView, sessionId }: ViewToggleProps) {
const navigate = useNavigate()
const hasCockpit = useFeatureFlag('flowpilot_cockpit')
const setPreferredView = useUserPreferencesStore(s => s.setPreferredFlowPilotView)
if (!hasCockpit) return null
const activeOption = VIEW_OPTIONS.find(o => o.key === currentView) ?? VIEW_OPTIONS[0]
const handleSwitch = (view: FlowPilotView) => {
if (view === currentView) return
setPreferredView(view)
@@ -41,29 +37,22 @@ export function ViewToggle({ currentView, sessionId, showSubtitle = true }: View
}
return (
<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 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>
)
}

View File

@@ -255,7 +255,7 @@ export default function CockpitPage() {
</button>
<div className="flex-1" />
{session.activeChatId && (
<ViewToggle currentView="cockpit" sessionId={session.activeChatId} showSubtitle={false} />
<ViewToggle currentView="cockpit" sessionId={session.activeChatId} />
)}
<button
onClick={session.handleNewChat}
@@ -275,11 +275,12 @@ export default function CockpitPage() {
onResolve={() => session.setShowConclude(true)}
onStatusUpdate={session.messages.length >= 2 ? () => session.setShowStatusUpdate(true) : undefined}
onClose={() => session.setShowConclude(true)}
extraActions={
<div className="hidden sm:block">
<ViewToggle currentView="cockpit" sessionId={session.activeChatId} />
</div>
}
/>
{/* View toggle bar — desktop only (mobile has it in the header) */}
<div className="hidden sm:flex items-center justify-end px-4 py-1.5 border-b border-border/50">
<ViewToggle currentView="cockpit" sessionId={session.activeChatId} />
</div>
{/* Resizable work zone + conversation log split */}
<div ref={splitContainerRef} className="flex-1 flex flex-col min-h-0 relative">

View File

@@ -116,7 +116,7 @@ export default function FlowPilotPage() {
</button>
<div className="flex-1" />
{session.activeChatId && (
<ViewToggle currentView="flowpilot" sessionId={session.activeChatId} showSubtitle={false} />
<ViewToggle currentView="flowpilot" sessionId={session.activeChatId} />
)}
<button
onClick={session.handleNewChat}

View File

@@ -52,7 +52,7 @@ export function QuickStartPage() {
{/* View preference — standalone row above input */}
<div className="flex items-center gap-3 mb-5">
<span className="text-xs text-muted-foreground">Launch in</span>
<ViewToggle currentView={preferredView} showSubtitle={false} />
<ViewToggle currentView={preferredView} />
</div>
{/* Chat-style input */}