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:
@@ -55,18 +55,18 @@ export function EscalateModal({ open, onClose, onEscalate, isProcessing, hasPsaT
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-col-reverse gap-2 sm:flex-row">
|
||||
<button
|
||||
onClick={handleClose}
|
||||
disabled={isProcessing}
|
||||
className="flex-1 rounded-lg bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-4 py-2.5 text-sm font-medium text-foreground hover:border-[rgba(255,255,255,0.12)] transition-colors disabled:opacity-50"
|
||||
className="flex-1 rounded-lg bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-4 py-2.5 min-h-[44px] text-sm font-medium text-foreground hover:border-[rgba(255,255,255,0.12)] transition-colors disabled:opacity-50"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
disabled={!reason.trim() || reason.trim().length < 5 || isProcessing}
|
||||
className="flex-1 flex items-center justify-center gap-2 rounded-lg bg-amber-500/90 px-4 py-2.5 text-sm font-semibold text-[#101114] hover:bg-amber-500 active:scale-[0.97] disabled:opacity-40 transition-all"
|
||||
className="flex-1 flex items-center justify-center gap-2 rounded-lg bg-amber-500/90 px-4 py-2.5 min-h-[44px] text-sm font-semibold text-[#101114] hover:bg-amber-500 active:scale-[0.97] disabled:opacity-40 transition-all"
|
||||
>
|
||||
{isProcessing ? (
|
||||
<Loader2 size={14} className="animate-spin" />
|
||||
|
||||
@@ -93,7 +93,7 @@ export function EscalationQueue({ onPickup }: EscalationQueueProps) {
|
||||
</div>
|
||||
|
||||
{sessions.map((session) => (
|
||||
<div key={session.id} className="glass-card p-4 space-y-3">
|
||||
<div key={session.id} className="glass-card p-3 sm:p-4 space-y-3">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-foreground">
|
||||
{session.problem_summary || 'Untitled session'}
|
||||
@@ -129,7 +129,7 @@ export function EscalationQueue({ onPickup }: EscalationQueueProps) {
|
||||
|
||||
<button
|
||||
onClick={() => handlePickup(session.id)}
|
||||
className="w-full rounded-lg bg-gradient-brand px-4 py-2 text-sm font-semibold text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] transition-all"
|
||||
className="w-full min-h-[44px] rounded-lg bg-gradient-brand px-4 py-2 text-sm font-semibold text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] transition-all"
|
||||
>
|
||||
Pick Up Session
|
||||
</button>
|
||||
|
||||
@@ -53,30 +53,32 @@ export function FlowPilotActionBar({
|
||||
<>
|
||||
{/* Bottom bar */}
|
||||
<div
|
||||
className="flex items-center gap-3 border-t px-5 py-3"
|
||||
className="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3 border-t px-3 py-3 sm:px-5"
|
||||
style={{ borderColor: 'var(--glass-border)', background: 'rgba(16, 17, 20, 0.8)', backdropFilter: 'blur(12px)' }}
|
||||
>
|
||||
<button
|
||||
onClick={() => { setShowResolve(true); setShowEscalate(false) }}
|
||||
disabled={!canResolve || isProcessing}
|
||||
className="flex items-center gap-2 rounded-lg bg-emerald-500/10 border border-emerald-500/20 px-4 py-2 text-sm font-medium text-emerald-400 hover:bg-emerald-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
||||
>
|
||||
<CheckCircle2 size={16} />
|
||||
Resolve
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowEscalate(true)}
|
||||
disabled={!canEscalate || isProcessing}
|
||||
className="flex items-center gap-2 rounded-lg bg-amber-500/10 border border-amber-500/20 px-4 py-2 text-sm font-medium text-amber-400 hover:bg-amber-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
||||
>
|
||||
<ArrowUpRight size={16} />
|
||||
Escalate
|
||||
</button>
|
||||
<div className="flex gap-2 sm:gap-3">
|
||||
<button
|
||||
onClick={() => { setShowResolve(true); setShowEscalate(false) }}
|
||||
disabled={!canResolve || isProcessing}
|
||||
className="flex flex-1 sm:flex-initial items-center justify-center gap-2 rounded-lg bg-emerald-500/10 border border-emerald-500/20 px-4 py-2 min-h-[44px] text-sm font-medium text-emerald-400 hover:bg-emerald-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
||||
>
|
||||
<CheckCircle2 size={16} />
|
||||
Resolve
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShowEscalate(true)}
|
||||
disabled={!canEscalate || isProcessing}
|
||||
className="flex flex-1 sm:flex-initial items-center justify-center gap-2 rounded-lg bg-amber-500/10 border border-amber-500/20 px-4 py-2 min-h-[44px] text-sm font-medium text-amber-400 hover:bg-amber-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
||||
>
|
||||
<ArrowUpRight size={16} />
|
||||
Escalate
|
||||
</button>
|
||||
</div>
|
||||
{onPause && (
|
||||
<button
|
||||
onClick={handlePause}
|
||||
disabled={isProcessing || submitting}
|
||||
className="flex items-center gap-2 rounded-lg bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-4 py-2 text-sm font-medium text-muted-foreground hover:text-foreground hover:border-[rgba(255,255,255,0.12)] disabled:opacity-40 disabled:pointer-events-none transition-colors ml-auto"
|
||||
className="flex items-center justify-center gap-2 rounded-lg bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-4 py-2 min-h-[44px] text-sm font-medium text-muted-foreground hover:text-foreground hover:border-[rgba(255,255,255,0.12)] disabled:opacity-40 disabled:pointer-events-none transition-colors sm:ml-auto"
|
||||
>
|
||||
<Pause size={16} />
|
||||
Pause
|
||||
@@ -86,8 +88,8 @@ export function FlowPilotActionBar({
|
||||
|
||||
{/* Resolve modal */}
|
||||
{showResolve && (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
|
||||
<div className="glass-card-static w-full max-w-lg p-6">
|
||||
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/60 backdrop-blur-sm">
|
||||
<div className="glass-card-static w-full max-w-full sm:max-w-lg mx-0 sm:mx-4 p-4 sm:p-6 rounded-t-2xl sm:rounded-2xl">
|
||||
<h3 className="font-heading text-lg font-semibold text-foreground mb-1">Resolve Session</h3>
|
||||
<p className="text-sm text-muted-foreground mb-4">Summarize what fixed the issue. This will be included in the auto-generated documentation.</p>
|
||||
<textarea
|
||||
@@ -98,17 +100,17 @@ export function FlowPilotActionBar({
|
||||
rows={4}
|
||||
autoFocus
|
||||
/>
|
||||
<div className="mt-4 flex justify-end gap-2">
|
||||
<div className="mt-4 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
||||
<button
|
||||
onClick={() => setShowResolve(false)}
|
||||
className="rounded-lg px-4 py-2 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="rounded-lg px-4 py-2 min-h-[44px] text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={handleResolve}
|
||||
disabled={resolutionSummary.length < 5 || submitting}
|
||||
className="rounded-lg bg-emerald-500/20 border border-emerald-500/30 px-4 py-2 text-sm font-medium text-emerald-400 hover:bg-emerald-500/30 disabled:opacity-50 transition-colors"
|
||||
className="rounded-lg bg-emerald-500/20 border border-emerald-500/30 px-4 py-2 min-h-[44px] text-sm font-medium text-emerald-400 hover:bg-emerald-500/30 disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{submitting ? 'Resolving...' : 'Resolve Session'}
|
||||
</button>
|
||||
|
||||
@@ -109,10 +109,10 @@ export function FlowPilotIntake({ onSubmit, isLoading }: FlowPilotIntakeProps) {
|
||||
: 'Start Session'
|
||||
|
||||
return (
|
||||
<div className="flex items-start justify-center pt-[10vh]">
|
||||
<div className="flex items-start justify-center px-3 sm:px-4 pt-[6vh] sm:pt-[10vh]">
|
||||
<div className="w-full max-w-2xl">
|
||||
<div className="text-center mb-6">
|
||||
<h1 className="font-heading text-2xl font-bold tracking-tight text-foreground">
|
||||
<div className="text-center mb-4 sm:mb-6">
|
||||
<h1 className="font-heading text-xl sm:text-2xl font-bold tracking-tight text-foreground">
|
||||
What are you troubleshooting?
|
||||
</h1>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
@@ -120,7 +120,7 @@ export function FlowPilotIntake({ onSubmit, isLoading }: FlowPilotIntakeProps) {
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="glass-card-static p-5 space-y-4">
|
||||
<div className="glass-card-static p-3 sm:p-5 space-y-4">
|
||||
{/* Selected ticket card */}
|
||||
{selectedTicket && selectedTicketId && (
|
||||
<div className="rounded-xl border border-primary/20 bg-primary/5 p-4">
|
||||
@@ -171,6 +171,12 @@ export function FlowPilotIntake({ onSubmit, isLoading }: FlowPilotIntakeProps) {
|
||||
<textarea
|
||||
value={text}
|
||||
onChange={(e) => setText(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) {
|
||||
e.preventDefault()
|
||||
if (text.trim() || logContent.trim()) handleSubmit()
|
||||
}
|
||||
}}
|
||||
placeholder="e.g. User can't access shared drive after password reset, getting 'Access Denied' in Event Viewer..."
|
||||
className="w-full rounded-lg border border-border bg-card px-4 py-3 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none resize-none"
|
||||
rows={5}
|
||||
@@ -230,14 +236,14 @@ export function FlowPilotIntake({ onSubmit, isLoading }: FlowPilotIntakeProps) {
|
||||
)}
|
||||
|
||||
{/* Submit */}
|
||||
<div className="flex items-center justify-between">
|
||||
<p className="text-[0.6875rem] text-[#5a6170]">
|
||||
<div className="flex flex-col-reverse gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p className="text-[0.6875rem] text-[#5a6170] text-center sm:text-left">
|
||||
FlowPilot will analyze your input and guide you through diagnosis
|
||||
</p>
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
disabled={!hasContent}
|
||||
className="rounded-lg bg-gradient-brand px-5 py-2.5 text-sm font-semibold text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] disabled:opacity-40 disabled:shadow-none transition-all whitespace-nowrap"
|
||||
className="w-full sm:w-auto min-h-[44px] rounded-lg bg-gradient-brand px-5 py-2.5 text-sm font-semibold text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] disabled:opacity-40 disabled:shadow-none transition-all whitespace-nowrap"
|
||||
>
|
||||
{submitLabel}
|
||||
</button>
|
||||
|
||||
@@ -28,7 +28,7 @@ export function FlowPilotOptions({ options, onSelect, disabled }: FlowPilotOptio
|
||||
onClick={() => handleSelect(option.value)}
|
||||
disabled={disabled}
|
||||
className={cn(
|
||||
'group relative rounded-xl border p-4 text-left transition-all',
|
||||
'group relative rounded-xl border p-3 sm:p-4 text-left transition-all min-h-[44px]',
|
||||
'hover:border-[rgba(6,182,212,0.3)] hover:shadow-[0_0_20px_rgba(6,182,212,0.08)]',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/40',
|
||||
isSelected
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -70,7 +70,7 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
return (
|
||||
<button
|
||||
onClick={() => setIsCollapsed(false)}
|
||||
className="w-full text-left glass-card-static p-4 opacity-70 hover:opacity-90 transition-opacity"
|
||||
className="w-full text-left glass-card-static p-3 sm:p-4 opacity-70 hover:opacity-90 transition-opacity"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<Icon size={16} className="shrink-0 text-muted-foreground" />
|
||||
@@ -84,7 +84,7 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
// Expanded completed step
|
||||
if (!isCurrentStep && !isCollapsed) {
|
||||
return (
|
||||
<div className="glass-card-static p-4 opacity-80">
|
||||
<div className="glass-card-static p-3 sm:p-4 opacity-80">
|
||||
<button
|
||||
onClick={() => setIsCollapsed(true)}
|
||||
className="mb-2 flex w-full items-center justify-between text-left"
|
||||
@@ -106,7 +106,7 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'glass-card-static p-5',
|
||||
'glass-card-static p-3 sm:p-4 lg:p-5',
|
||||
isResolutionSuggestion && 'border-emerald-500/30'
|
||||
)}
|
||||
>
|
||||
@@ -140,16 +140,16 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
<div className="space-y-3">
|
||||
{/* Resolution suggestion buttons */}
|
||||
{isResolutionSuggestion && (
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-col gap-2 sm:flex-row">
|
||||
<button
|
||||
onClick={() => handleResolutionResponse(true)}
|
||||
className="flex-1 rounded-lg bg-emerald-500/10 border border-emerald-500/20 px-4 py-2.5 text-sm font-medium text-emerald-400 hover:bg-emerald-500/20 transition-colors"
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-emerald-500/10 border border-emerald-500/20 px-4 py-2.5 text-sm font-medium text-emerald-400 hover:bg-emerald-500/20 transition-colors"
|
||||
>
|
||||
Yes, this is resolved
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleResolutionResponse(false)}
|
||||
className="flex-1 rounded-lg bg-card/50 border border-border px-4 py-2.5 text-sm font-medium text-foreground hover:bg-card transition-colors"
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-card/50 border border-border px-4 py-2.5 text-sm font-medium text-foreground hover:bg-card transition-colors"
|
||||
>
|
||||
No, keep investigating
|
||||
</button>
|
||||
@@ -178,16 +178,16 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
|
||||
{/* Action step buttons */}
|
||||
{!isResolutionSuggestion && step.step_type === 'action' && (content.action_type as string) !== 'script_generation' && (
|
||||
<div className="flex gap-2">
|
||||
<div className="flex flex-col gap-2 sm:flex-row">
|
||||
<button
|
||||
onClick={() => handleActionComplete(true)}
|
||||
className="flex-1 rounded-lg bg-primary/10 border border-primary/20 px-4 py-2.5 text-sm font-medium text-primary hover:bg-primary/20 transition-colors"
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-primary/10 border border-primary/20 px-4 py-2.5 text-sm font-medium text-primary hover:bg-primary/20 transition-colors"
|
||||
>
|
||||
I've completed this action
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleActionComplete(false)}
|
||||
className="flex-1 rounded-lg bg-card/50 border border-border px-4 py-2.5 text-sm font-medium text-foreground hover:bg-card transition-colors"
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-card/50 border border-border px-4 py-2.5 text-sm font-medium text-foreground hover:bg-card transition-colors"
|
||||
>
|
||||
This didn't work
|
||||
</button>
|
||||
|
||||
@@ -63,7 +63,7 @@ export function InSessionScriptGenerator({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-3 rounded-xl border border-primary/20 bg-primary/5 p-4 space-y-3">
|
||||
<div className="mt-3 rounded-xl border border-primary/20 bg-primary/5 p-3 sm:p-4 space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<Terminal size={14} className="text-primary" />
|
||||
<span className="font-label text-[0.625rem] uppercase tracking-wider text-primary">
|
||||
@@ -106,7 +106,7 @@ export function InSessionScriptGenerator({
|
||||
<button
|
||||
onClick={handleGenerate}
|
||||
disabled={isGenerating}
|
||||
className="w-full flex items-center justify-center gap-2 rounded-lg bg-gradient-brand px-4 py-2 text-sm font-semibold text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] disabled:opacity-40 transition-all"
|
||||
className="w-full min-h-[44px] flex items-center justify-center gap-2 rounded-lg bg-gradient-brand px-4 py-2 text-sm font-semibold text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] disabled:opacity-40 transition-all"
|
||||
>
|
||||
{isGenerating ? (
|
||||
<Loader2 size={14} className="animate-spin" />
|
||||
@@ -138,16 +138,16 @@ export function InSessionScriptGenerator({
|
||||
</div>
|
||||
|
||||
{/* Continue buttons */}
|
||||
<div className="flex gap-2 pt-1">
|
||||
<div className="flex flex-col gap-2 pt-1 sm:flex-row">
|
||||
<button
|
||||
onClick={() => handleContinue(true)}
|
||||
className="flex-1 rounded-lg bg-primary/10 border border-primary/20 px-4 py-2 text-sm font-medium text-primary hover:bg-primary/20 transition-colors"
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-primary/10 border border-primary/20 px-4 py-2 text-sm font-medium text-primary hover:bg-primary/20 transition-colors"
|
||||
>
|
||||
Script worked — continue
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleContinue(false)}
|
||||
className="flex-1 rounded-lg bg-card/50 border border-border px-4 py-2 text-sm font-medium text-foreground hover:bg-card transition-colors"
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-card/50 border border-border px-4 py-2 text-sm font-medium text-foreground hover:bg-card transition-colors"
|
||||
>
|
||||
Didn't resolve it
|
||||
</button>
|
||||
|
||||
@@ -47,11 +47,11 @@ export function SessionDocView({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<div className="space-y-4 sm:space-y-5">
|
||||
{/* PSA Push Status */}
|
||||
{currentPushStatus && currentPushStatus !== 'no_psa' && (
|
||||
<div
|
||||
className={`rounded-xl border px-4 py-3 flex items-center gap-3 ${
|
||||
className={`rounded-xl border px-3 py-3 sm:px-4 flex flex-wrap items-center gap-2 sm:gap-3 ${
|
||||
currentPushStatus === 'sent'
|
||||
? 'border-emerald-400/20 bg-emerald-400/5'
|
||||
: currentPushStatus === 'pending_retry'
|
||||
@@ -107,7 +107,7 @@ export function SessionDocView({
|
||||
)}
|
||||
|
||||
{/* Header */}
|
||||
<div className="glass-card-static p-5">
|
||||
<div className="glass-card-static p-3 sm:p-4 lg:p-5">
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
|
||||
<FileText size={16} />
|
||||
@@ -155,7 +155,7 @@ export function SessionDocView({
|
||||
)}
|
||||
|
||||
{/* Intake summary */}
|
||||
<div className="glass-card-static p-4">
|
||||
<div className="glass-card-static p-3 sm:p-4">
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-muted-foreground mb-2">
|
||||
Original intake
|
||||
</h4>
|
||||
@@ -168,7 +168,7 @@ export function SessionDocView({
|
||||
Diagnostic trail
|
||||
</h4>
|
||||
{documentation.diagnostic_steps.map((step) => (
|
||||
<div key={step.step_number} className="glass-card-static p-4">
|
||||
<div key={step.step_number} className="glass-card-static p-3 sm:p-4">
|
||||
<div className="flex items-start gap-3">
|
||||
<span className="font-label flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-card text-[0.625rem] text-muted-foreground border border-border">
|
||||
{step.step_number}
|
||||
@@ -189,14 +189,14 @@ export function SessionDocView({
|
||||
|
||||
{/* Rating */}
|
||||
{onRate && (
|
||||
<div className="glass-card-static p-4 text-center">
|
||||
<div className="glass-card-static p-3 sm:p-4 text-center">
|
||||
<p className="text-sm text-muted-foreground mb-2">How helpful was this session?</p>
|
||||
<div className="flex items-center justify-center gap-1">
|
||||
{[1, 2, 3, 4, 5].map((star) => (
|
||||
<button
|
||||
key={star}
|
||||
onClick={() => onRate(star)}
|
||||
className="p-1 transition-colors"
|
||||
className="p-2 sm:p-1 transition-colors"
|
||||
>
|
||||
<Star
|
||||
size={20}
|
||||
|
||||
@@ -65,12 +65,12 @@ export default function FlowPilotAnalyticsPage() {
|
||||
return (
|
||||
<div className="container mx-auto px-4 py-6 sm:px-6 sm:py-8 space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<span title="FlowPilot Analytics">
|
||||
<BarChart3 size={24} className="text-foreground" />
|
||||
</span>
|
||||
<h1 className="text-2xl font-bold font-heading text-foreground">FlowPilot Analytics</h1>
|
||||
<h1 className="text-xl sm:text-2xl font-bold font-heading text-foreground">FlowPilot Analytics</h1>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<Link
|
||||
@@ -128,7 +128,7 @@ export default function FlowPilotAnalyticsPage() {
|
||||
{/* Second row — Charts */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{/* MTTR Trend */}
|
||||
<div className="glass-card-static p-5">
|
||||
<div className="glass-card-static p-3 sm:p-5">
|
||||
<h3 className="font-heading text-sm font-semibold text-foreground mb-4">
|
||||
MTTR Trend
|
||||
</h3>
|
||||
@@ -173,7 +173,7 @@ export default function FlowPilotAnalyticsPage() {
|
||||
</div>
|
||||
|
||||
{/* Domain Breakdown */}
|
||||
<div className="glass-card-static p-5">
|
||||
<div className="glass-card-static p-3 sm:p-5">
|
||||
<h3 className="font-heading text-sm font-semibold text-foreground mb-4">
|
||||
Sessions by Domain
|
||||
</h3>
|
||||
@@ -207,7 +207,7 @@ export default function FlowPilotAnalyticsPage() {
|
||||
{/* Third row — Confidence + Knowledge Coverage */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{/* Confidence Breakdown */}
|
||||
<div className="glass-card-static p-5">
|
||||
<div className="glass-card-static p-3 sm:p-5">
|
||||
<h3 className="font-heading text-sm font-semibold text-foreground mb-4">
|
||||
Confidence Tiers
|
||||
</h3>
|
||||
@@ -237,7 +237,7 @@ export default function FlowPilotAnalyticsPage() {
|
||||
</div>
|
||||
|
||||
{/* Knowledge Coverage */}
|
||||
<div className="glass-card-static p-5">
|
||||
<div className="glass-card-static p-3 sm:p-5">
|
||||
<h3 className="font-heading text-sm font-semibold text-foreground mb-4">
|
||||
Knowledge Coverage
|
||||
</h3>
|
||||
@@ -305,10 +305,10 @@ function MetricCard({
|
||||
iconColor: string
|
||||
}) {
|
||||
return (
|
||||
<div className="glass-card-static p-4">
|
||||
<div className="glass-card-static p-3 sm:p-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<span
|
||||
className="flex h-8 w-8 items-center justify-center rounded-lg"
|
||||
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg"
|
||||
style={{ background: `${iconColor}15` }}
|
||||
>
|
||||
<Icon size={16} style={{ color: iconColor }} />
|
||||
|
||||
@@ -85,11 +85,11 @@ export default function ReviewQueuePage() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="flex h-full">
|
||||
<div className="flex h-full flex-col lg:flex-row overflow-hidden">
|
||||
{/* Left panel — Proposal list */}
|
||||
<div className="w-[380px] shrink-0 border-r overflow-y-auto" style={{ borderColor: 'var(--glass-border)' }}>
|
||||
<div className="w-full shrink-0 border-b lg:border-b-0 lg:border-r lg:w-[380px] overflow-y-auto" style={{ borderColor: 'var(--glass-border)' }}>
|
||||
{/* Header */}
|
||||
<div className="sticky top-0 z-10 p-4 space-y-3" style={{ background: 'rgba(16, 17, 20, 0.95)', backdropFilter: 'blur(12px)' }}>
|
||||
<div className="sticky top-0 z-10 p-3 sm:p-4 space-y-3" style={{ background: 'rgba(16, 17, 20, 0.95)', backdropFilter: 'blur(12px)' }}>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Lightbulb size={16} className="text-amber-400" />
|
||||
@@ -119,12 +119,12 @@ export default function ReviewQueuePage() {
|
||||
)}
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="flex gap-1">
|
||||
<div className="flex gap-1 overflow-x-auto">
|
||||
{STATUS_TABS.map((tab) => (
|
||||
<button
|
||||
key={tab.key}
|
||||
onClick={() => setActiveTab(tab.key)}
|
||||
className={`rounded-lg px-2.5 py-1 text-xs font-medium transition-colors ${
|
||||
className={`shrink-0 rounded-lg px-2.5 py-1 text-xs font-medium transition-colors ${
|
||||
activeTab === tab.key
|
||||
? 'bg-primary/10 text-primary'
|
||||
: 'text-muted-foreground hover:text-foreground'
|
||||
@@ -177,7 +177,7 @@ export default function ReviewQueuePage() {
|
||||
</div>
|
||||
|
||||
{/* Right panel — Detail */}
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<div className={`flex-1 overflow-y-auto ${!detail && !isLoadingDetail ? 'hidden lg:flex' : ''}`}>
|
||||
{isLoadingDetail ? (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<Loader2 size={20} className="animate-spin text-muted-foreground" />
|
||||
|
||||
Reference in New Issue
Block a user