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:
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user