fix(responsive): audit and fix FlowPilot + analytics components for mobile/tablet

Add responsive Tailwind classes across 11 FlowPilot-related components:
- FlowPilotSession: collapsible mobile sidebar summary, responsive padding
- FlowPilotActionBar: stacked buttons on mobile, responsive resolve modal
- FlowPilotIntake: responsive submit area, padding, heading sizes
- FlowPilotStepCard: responsive padding (p-3/p-4/p-5), stacked action buttons
- FlowPilotOptions: responsive padding, 44px touch targets
- SessionDocView: responsive card padding, touch-friendly star ratings
- EscalateModal: stacked buttons on mobile with min touch targets
- EscalationQueue: responsive card padding, touch targets
- InSessionScriptGenerator: responsive padding, stacked continue buttons
- ReviewQueuePage: responsive two-panel layout (stacked on mobile)
- FlowPilotAnalyticsPage: responsive header, chart card padding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-19 20:19:52 +00:00
parent 967a2b2c59
commit 1f4a8a6389
11 changed files with 143 additions and 76 deletions

View File

@@ -1,5 +1,5 @@
import { useEffect, useRef, useState } from 'react'
import { Network, Clock, Hash, Play, Ticket } from 'lucide-react'
import { Network, Clock, Hash, Play, Ticket, ChevronDown, ChevronUp } from 'lucide-react'
import type {
AISessionDetail,
AISessionStepResponse,
@@ -60,6 +60,7 @@ export function FlowPilotSession({
const scrollRef = useRef<HTMLDivElement>(null)
const [showTicketPicker, setShowTicketPicker] = useState(false)
const [linkingTicket, setLinkingTicket] = useState(false)
const [showMobileSidebar, setShowMobileSidebar] = useState(false)
const handleLinkTicket = async (ticketId: string, _ticket: PSATicketInfo) => {
if (!session.psa_connection_id && !session.ticket_data) {
@@ -112,7 +113,7 @@ export function FlowPilotSession({
if (isCompleted && documentation) {
return (
<div className="flex h-full flex-col">
<div className="flex-1 overflow-y-auto p-6">
<div className="flex-1 overflow-y-auto p-3 sm:p-4 lg:p-6">
<SessionDocView
documentation={documentation}
onRate={onRate}
@@ -130,10 +131,68 @@ export function FlowPilotSession({
return (
<div className="flex h-full flex-col">
{/* Mobile sidebar summary (collapsible) */}
<div className="lg:hidden border-b" style={{ borderColor: 'var(--glass-border)' }}>
<button
onClick={() => setShowMobileSidebar(!showMobileSidebar)}
className="flex w-full items-center justify-between px-3 py-2 sm:px-4"
>
<div className="flex items-center gap-3 text-xs text-muted-foreground overflow-x-auto">
{session.problem_domain && (
<span className="font-label rounded-md bg-primary/10 px-1.5 py-0.5 text-[0.5625rem] uppercase tracking-wider text-primary shrink-0">
{session.problem_domain}
</span>
)}
<span className="flex items-center gap-1 shrink-0">
<Hash size={10} />
{session.step_count} steps
</span>
<ConfidenceIndicator
tier={session.confidence_tier}
score={currentStep?.confidence_score ?? 0}
/>
</div>
{showMobileSidebar ? <ChevronUp size={14} className="text-muted-foreground shrink-0" /> : <ChevronDown size={14} className="text-muted-foreground shrink-0" />}
</button>
{showMobileSidebar && (
<div className="px-3 pb-3 sm:px-4 space-y-3">
{session.psa_ticket_id ? (
<SessionTicketCard
ticketId={session.psa_ticket_id}
ticketData={session.ticket_data as Record<string, unknown> | null}
/>
) : session.status === 'active' ? (
<button
onClick={() => setShowTicketPicker(true)}
disabled={linkingTicket}
className="w-full flex items-center gap-2 rounded-xl border border-dashed border-border px-3 py-2.5 text-xs text-muted-foreground hover:text-foreground hover:border-primary/30 transition-colors disabled:opacity-50 min-h-[44px]"
>
<Ticket size={14} />
{linkingTicket ? 'Linking...' : 'Link Ticket'}
</button>
) : null}
{session.problem_summary && (
<div>
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">Problem</h4>
<p className="text-sm text-foreground">{session.problem_summary}</p>
</div>
)}
{session.matched_flow_id && (
<div className="flex items-center gap-2">
<Network size={14} className="text-muted-foreground" />
<span className="text-xs text-foreground">
{session.match_score ? `${Math.round(session.match_score * 100)}% match` : 'Match found'}
</span>
</div>
)}
</div>
)}
</div>
{/* Main content area: conversation + sidebar */}
<div className="flex flex-1 min-h-0">
{/* Conversation column */}
<div ref={scrollRef} className="flex-1 overflow-y-auto p-6">
<div ref={scrollRef} className="flex-1 overflow-y-auto p-3 sm:p-4 lg:p-6">
<div className="mx-auto max-w-2xl space-y-3">
{allSteps.map((step) => (
<FlowPilotStepCard
@@ -148,7 +207,7 @@ export function FlowPilotSession({
</div>
</div>
{/* Sidebar */}
{/* Sidebar — desktop only */}
<div
className="hidden w-72 shrink-0 overflow-y-auto border-l p-4 lg:block"
style={{ borderColor: 'var(--glass-border)' }}
@@ -252,7 +311,7 @@ export function FlowPilotSession({
{/* Paused banner */}
{session.status === 'paused' && onResume && (
<div
className="flex items-center justify-between border-t px-5 py-3"
className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between border-t px-3 py-3 sm:px-5"
style={{ borderColor: 'var(--glass-border)', background: 'rgba(16, 17, 20, 0.8)', backdropFilter: 'blur(12px)' }}
>
<span className="text-sm text-muted-foreground">Session paused</span>