fix: deduplicate actions, promote ViewToggle tab bar, standardize naming
Remove duplicate Update/Close actions from chat toolbars (FlowPilotPage, CockpitPage) — session lifecycle actions now live only in headers. Redesign ViewToggle as a persistent tab bar with bottom-border active indicator and ARIA attributes. Standardize all action naming: Resolve (emerald), Update (blue), Close (rose), Pause (muted). Fix IncidentHeader Resolve from orange to emerald. Delete unused FlowPilotActionBar component (227 lines). Update ConcludeSessionModal copy to use forward-facing action verbs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,8 +33,8 @@ interface ConcludeSessionModalProps {
|
|||||||
const OUTCOMES: { value: ConclusionOutcome; label: string; description: string; icon: typeof CheckCircle2; color: string; bg: string; border: string }[] = [
|
const OUTCOMES: { value: ConclusionOutcome; label: string; description: string; icon: typeof CheckCircle2; color: string; bg: string; border: string }[] = [
|
||||||
{
|
{
|
||||||
value: 'resolved',
|
value: 'resolved',
|
||||||
label: 'Resolved',
|
label: 'Resolve',
|
||||||
description: 'Issue has been fixed or answered',
|
description: 'Mark the issue as fixed',
|
||||||
icon: CheckCircle2,
|
icon: CheckCircle2,
|
||||||
color: 'text-emerald-400',
|
color: 'text-emerald-400',
|
||||||
bg: 'bg-emerald-400/10',
|
bg: 'bg-emerald-400/10',
|
||||||
@@ -43,7 +43,7 @@ const OUTCOMES: { value: ConclusionOutcome; label: string; description: string;
|
|||||||
{
|
{
|
||||||
value: 'escalated',
|
value: 'escalated',
|
||||||
label: 'Escalate',
|
label: 'Escalate',
|
||||||
description: 'Needs to be handed off or escalated',
|
description: 'Hand off to another engineer or team',
|
||||||
icon: ArrowUpRight,
|
icon: ArrowUpRight,
|
||||||
color: 'text-amber-400',
|
color: 'text-amber-400',
|
||||||
bg: 'bg-amber-400/10',
|
bg: 'bg-amber-400/10',
|
||||||
@@ -51,8 +51,8 @@ const OUTCOMES: { value: ConclusionOutcome; label: string; description: string;
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'paused',
|
value: 'paused',
|
||||||
label: 'Paused',
|
label: 'Pause',
|
||||||
description: 'Continuing later — saving progress',
|
description: 'Save progress and come back later',
|
||||||
icon: Pause,
|
icon: Pause,
|
||||||
color: 'text-blue-400',
|
color: 'text-blue-400',
|
||||||
bg: 'bg-blue-400/10',
|
bg: 'bg-blue-400/10',
|
||||||
@@ -155,7 +155,7 @@ export function ConcludeSessionModal({
|
|||||||
setSummary('')
|
setSummary('')
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
setError('Failed to conclude session. Please try again.')
|
setError('Failed to close case. Please try again.')
|
||||||
setGenerating(false)
|
setGenerating(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -296,7 +296,7 @@ export function ConcludeSessionModal({
|
|||||||
{step === 'select-outcome' && (
|
{step === 'select-outcome' && (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<p className="text-sm text-muted-foreground mb-4">
|
<p className="text-sm text-muted-foreground mb-4">
|
||||||
How did this session end?
|
What would you like to do?
|
||||||
</p>
|
</p>
|
||||||
{OUTCOMES.map(o => {
|
{OUTCOMES.map(o => {
|
||||||
const Icon = o.icon
|
const Icon = o.icon
|
||||||
@@ -526,12 +526,12 @@ export function ConcludeSessionModal({
|
|||||||
{generating ? (
|
{generating ? (
|
||||||
<>
|
<>
|
||||||
<Loader2 size={15} className="animate-spin" />
|
<Loader2 size={15} className="animate-spin" />
|
||||||
Generating...
|
Closing...
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<Sparkles size={15} />
|
<Sparkles size={15} />
|
||||||
Generate Summary
|
Close & Generate
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useState, useRef, useEffect } from 'react'
|
import { useState, useRef, useEffect } from 'react'
|
||||||
import { Pencil, X, Check, ExternalLink, Pause, XCircle, Link2, MoreHorizontal, FileText } from 'lucide-react'
|
import { Pencil, X, Check, CheckCircle2, ExternalLink, Pause, XCircle, Link2, MoreHorizontal, FileText } from 'lucide-react'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { toast } from '@/lib/toast'
|
import { toast } from '@/lib/toast'
|
||||||
import type { TriageMeta } from '@/types/ai-session'
|
import type { TriageMeta } from '@/types/ai-session'
|
||||||
@@ -12,8 +12,6 @@ interface IncidentHeaderProps {
|
|||||||
onStatusUpdate?: () => void
|
onStatusUpdate?: () => void
|
||||||
onPause?: () => void
|
onPause?: () => void
|
||||||
onClose?: () => void
|
onClose?: () => void
|
||||||
/** Extra elements rendered in the action group (e.g. ViewToggle) */
|
|
||||||
extraActions?: React.ReactNode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface EditPopoverProps {
|
interface EditPopoverProps {
|
||||||
@@ -129,14 +127,14 @@ function OverflowMenu({ onPause, onClose }: { onPause?: () => void; onClose?: ()
|
|||||||
{open && (
|
{open && (
|
||||||
<>
|
<>
|
||||||
<div className="fixed inset-0 z-40" onClick={() => setOpen(false)} />
|
<div className="fixed inset-0 z-40" onClick={() => setOpen(false)} />
|
||||||
<div className="absolute right-0 top-full mt-1 z-50 w-44 rounded-lg border border-border bg-card py-1 shadow-xl">
|
<div className="absolute right-0 top-full mt-1 z-50 w-40 rounded-lg border border-border bg-card py-1 shadow-xl">
|
||||||
{onPause && (
|
{onPause && (
|
||||||
<button
|
<button
|
||||||
onClick={() => { onPause(); setOpen(false) }}
|
onClick={() => { onPause(); setOpen(false) }}
|
||||||
className="w-full flex items-center gap-2 px-3 py-2 text-xs text-muted-foreground hover:text-foreground hover:bg-[rgba(255,255,255,0.06)] transition-colors text-left"
|
className="w-full flex items-center gap-2 px-3 py-2 text-xs text-muted-foreground hover:text-foreground hover:bg-[rgba(255,255,255,0.06)] transition-colors text-left"
|
||||||
>
|
>
|
||||||
<Pause size={13} />
|
<Pause size={13} />
|
||||||
Pause Case
|
Pause
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
@@ -152,7 +150,7 @@ function OverflowMenu({ onPause, onClose }: { onPause?: () => void; onClose?: ()
|
|||||||
className="w-full flex items-center gap-2 px-3 py-2 text-xs text-muted-foreground hover:text-rose-400 hover:bg-rose-500/10 transition-colors text-left"
|
className="w-full flex items-center gap-2 px-3 py-2 text-xs text-muted-foreground hover:text-rose-400 hover:bg-rose-500/10 transition-colors text-left"
|
||||||
>
|
>
|
||||||
<XCircle size={13} />
|
<XCircle size={13} />
|
||||||
Close Case
|
Close
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -170,7 +168,6 @@ export function IncidentHeader({
|
|||||||
onStatusUpdate,
|
onStatusUpdate,
|
||||||
onPause,
|
onPause,
|
||||||
onClose,
|
onClose,
|
||||||
extraActions,
|
|
||||||
}: IncidentHeaderProps) {
|
}: IncidentHeaderProps) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-card border-b border-default px-4 py-2 flex items-center gap-4 flex-wrap">
|
<div className="bg-card border-b border-default px-4 py-2 flex items-center gap-4 flex-wrap">
|
||||||
@@ -212,21 +209,21 @@ export function IncidentHeader({
|
|||||||
)}
|
)}
|
||||||
<button
|
<button
|
||||||
onClick={onResolve}
|
onClick={onResolve}
|
||||||
className="bg-accent/15 border border-accent rounded px-3 py-1 text-xs font-medium text-accent hover:bg-accent/25 transition-colors"
|
className="flex items-center gap-1.5 bg-emerald-500/10 border border-emerald-500/20 rounded-lg px-3 py-1.5 text-xs font-medium text-emerald-400 hover:bg-emerald-500/20 transition-colors"
|
||||||
>
|
>
|
||||||
|
<CheckCircle2 size={13} />
|
||||||
Resolve
|
Resolve
|
||||||
</button>
|
</button>
|
||||||
{onStatusUpdate && (
|
{onStatusUpdate && (
|
||||||
<button
|
<button
|
||||||
onClick={onStatusUpdate}
|
onClick={onStatusUpdate}
|
||||||
className="flex items-center gap-1.5 bg-blue-500/10 border border-blue-500/20 rounded px-3 py-1 text-xs font-medium text-blue-400 hover:bg-blue-500/20 transition-colors"
|
className="flex items-center gap-1.5 bg-blue-500/10 border border-blue-500/20 rounded-lg px-3 py-1.5 text-xs font-medium text-blue-400 hover:bg-blue-500/20 transition-colors"
|
||||||
>
|
>
|
||||||
<FileText size={13} />
|
<FileText size={13} />
|
||||||
Update
|
Update
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<OverflowMenu onPause={onPause} onClose={onClose} />
|
<OverflowMenu onPause={onPause} onClose={onClose} />
|
||||||
{extraActions}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -18,6 +18,13 @@ interface ViewToggleProps {
|
|||||||
sessionId?: string
|
sessionId?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persistent tab bar for switching between FlowPilot (chat) and Cockpit (triage) views.
|
||||||
|
* Renders as a horizontal tab strip with an active bottom-border indicator.
|
||||||
|
*
|
||||||
|
* NOTE: If the tab bar proves too tall or prominent in certain layouts,
|
||||||
|
* consider pivoting to a compact segmented control (Option A from the critique).
|
||||||
|
*/
|
||||||
export function ViewToggle({ currentView, sessionId }: ViewToggleProps) {
|
export function ViewToggle({ currentView, sessionId }: ViewToggleProps) {
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const hasCockpit = useFeatureFlag('flowpilot_cockpit')
|
const hasCockpit = useFeatureFlag('flowpilot_cockpit')
|
||||||
@@ -37,19 +44,24 @@ export function ViewToggle({ currentView, sessionId }: ViewToggleProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center rounded-lg border border-border bg-card p-0.5 text-xs">
|
<div className="flex items-center border-b border-border px-3 shrink-0" role="tablist">
|
||||||
{VIEW_OPTIONS.map(({ key, label, icon: Icon }) => (
|
{VIEW_OPTIONS.map(({ key, label, icon: Icon }) => (
|
||||||
<button
|
<button
|
||||||
key={key}
|
key={key}
|
||||||
|
role="tab"
|
||||||
|
aria-selected={currentView === key}
|
||||||
|
aria-current={currentView === key ? 'page' : undefined}
|
||||||
onClick={() => handleSwitch(key)}
|
onClick={() => handleSwitch(key)}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex items-center gap-1.5 rounded-md px-2.5 py-1 font-medium transition-colors',
|
'flex items-center gap-1.5 px-3 py-2 text-xs font-medium border-b-2 -mb-px',
|
||||||
|
'transition-[color,border-color] duration-150',
|
||||||
|
'active:opacity-80',
|
||||||
currentView === key
|
currentView === key
|
||||||
? 'bg-elevated text-foreground'
|
? 'text-foreground border-primary'
|
||||||
: 'text-muted-foreground hover:text-foreground'
|
: 'text-muted-foreground hover:text-foreground border-transparent'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Icon size={12} />
|
<Icon size={14} />
|
||||||
{label}
|
{label}
|
||||||
</button>
|
</button>
|
||||||
))}
|
))}
|
||||||
|
|||||||
@@ -1,227 +0,0 @@
|
|||||||
import { useState } from 'react'
|
|
||||||
import { CheckCircle2, ArrowUpRight, Pause, X, FileText } from 'lucide-react'
|
|
||||||
import { EscalateModal } from './EscalateModal'
|
|
||||||
import { StatusUpdateModal } from './StatusUpdateModal'
|
|
||||||
import type {
|
|
||||||
ResolveSessionRequest,
|
|
||||||
EscalateSessionRequest,
|
|
||||||
SessionDocumentation,
|
|
||||||
StatusUpdateAudience,
|
|
||||||
StatusUpdateLength,
|
|
||||||
StatusUpdateContext,
|
|
||||||
StatusUpdateResponse,
|
|
||||||
} from '@/types/ai-session'
|
|
||||||
|
|
||||||
interface FlowPilotActionBarProps {
|
|
||||||
canResolve: boolean
|
|
||||||
canEscalate: boolean
|
|
||||||
isProcessing: boolean
|
|
||||||
hasPsaTicket?: boolean
|
|
||||||
sessionId?: string
|
|
||||||
canShareUpdate?: boolean
|
|
||||||
onResolve: (data: ResolveSessionRequest) => Promise<SessionDocumentation>
|
|
||||||
onEscalate: (data: EscalateSessionRequest) => Promise<SessionDocumentation>
|
|
||||||
onPause?: () => Promise<void>
|
|
||||||
onAbandon?: () => Promise<void>
|
|
||||||
onGenerateStatusUpdate?: (audience: StatusUpdateAudience, length: StatusUpdateLength, context: StatusUpdateContext) => Promise<StatusUpdateResponse>
|
|
||||||
}
|
|
||||||
|
|
||||||
export function FlowPilotActionBar({
|
|
||||||
canResolve,
|
|
||||||
canEscalate,
|
|
||||||
isProcessing,
|
|
||||||
hasPsaTicket = false,
|
|
||||||
sessionId,
|
|
||||||
canShareUpdate = false,
|
|
||||||
onResolve,
|
|
||||||
onEscalate,
|
|
||||||
onPause,
|
|
||||||
onAbandon,
|
|
||||||
onGenerateStatusUpdate,
|
|
||||||
}: FlowPilotActionBarProps) {
|
|
||||||
const [showResolve, setShowResolve] = useState(false)
|
|
||||||
const [showEscalate, setShowEscalate] = useState(false)
|
|
||||||
const [showAbandon, setShowAbandon] = useState(false)
|
|
||||||
const [showStatusUpdate, setShowStatusUpdate] = useState(false)
|
|
||||||
const [resolutionSummary, setResolutionSummary] = useState('')
|
|
||||||
const [submitting, setSubmitting] = useState(false)
|
|
||||||
|
|
||||||
const handleResolve = async () => {
|
|
||||||
if (!resolutionSummary.trim() || resolutionSummary.length < 5) return
|
|
||||||
setSubmitting(true)
|
|
||||||
try {
|
|
||||||
await onResolve({ resolution_summary: resolutionSummary })
|
|
||||||
setShowResolve(false)
|
|
||||||
} finally {
|
|
||||||
setSubmitting(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlePause = async () => {
|
|
||||||
if (onPause) {
|
|
||||||
setSubmitting(true)
|
|
||||||
try {
|
|
||||||
await onPause()
|
|
||||||
} finally {
|
|
||||||
setSubmitting(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleAbandon = async () => {
|
|
||||||
if (onAbandon) {
|
|
||||||
setSubmitting(true)
|
|
||||||
try {
|
|
||||||
await onAbandon()
|
|
||||||
setShowAbandon(false)
|
|
||||||
} finally {
|
|
||||||
setSubmitting(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{/* Bottom bar — fixed to viewport bottom, single row on all screen sizes */}
|
|
||||||
<div
|
|
||||||
className="fixed bottom-0 right-0 z-40 flex items-center gap-1.5 sm:gap-3 border-t border-border bg-card px-2 sm:px-5 py-2 sm:py-3"
|
|
||||||
style={{ left: 'var(--sidebar-w, 0px)' }}
|
|
||||||
>
|
|
||||||
{/* Primary actions */}
|
|
||||||
<button
|
|
||||||
onClick={() => { setShowResolve(true); setShowEscalate(false) }}
|
|
||||||
disabled={!canResolve || isProcessing}
|
|
||||||
className="flex items-center justify-center gap-1.5 rounded-lg bg-emerald-500/10 border border-emerald-500/20 px-2.5 sm:px-4 py-2 min-h-[40px] sm:min-h-[44px] text-xs sm:text-sm font-medium text-emerald-400 hover:bg-emerald-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
|
||||||
>
|
|
||||||
<CheckCircle2 size={15} />
|
|
||||||
Resolve
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={() => setShowEscalate(true)}
|
|
||||||
disabled={!canEscalate || isProcessing}
|
|
||||||
className="flex items-center justify-center gap-1.5 rounded-lg bg-amber-500/10 border border-amber-500/20 px-2.5 sm:px-4 py-2 min-h-[40px] sm:min-h-[44px] text-xs sm:text-sm font-medium text-amber-400 hover:bg-amber-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
|
||||||
>
|
|
||||||
<ArrowUpRight size={15} />
|
|
||||||
Escalate
|
|
||||||
</button>
|
|
||||||
{canShareUpdate && onGenerateStatusUpdate && (
|
|
||||||
<button
|
|
||||||
onClick={() => setShowStatusUpdate(true)}
|
|
||||||
disabled={isProcessing}
|
|
||||||
className="flex items-center justify-center gap-1.5 rounded-lg bg-blue-500/10 border border-blue-500/20 px-2.5 sm:px-4 py-2 min-h-[40px] sm:min-h-[44px] text-xs sm:text-sm font-medium text-blue-400 hover:bg-blue-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
|
||||||
title="Share Update"
|
|
||||||
>
|
|
||||||
<FileText size={15} />
|
|
||||||
<span className="hidden sm:inline">Share Update</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Spacer */}
|
|
||||||
<div className="flex-1" />
|
|
||||||
|
|
||||||
{/* Secondary actions — right side */}
|
|
||||||
{onPause && (
|
|
||||||
<button
|
|
||||||
onClick={handlePause}
|
|
||||||
disabled={isProcessing || submitting}
|
|
||||||
className="flex items-center justify-center gap-1.5 rounded-lg bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-2.5 sm:px-4 py-2 min-h-[40px] sm:min-h-[44px] text-xs sm: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"
|
|
||||||
>
|
|
||||||
<Pause size={15} />
|
|
||||||
<span className="hidden sm:inline">Pause</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
{onAbandon && (
|
|
||||||
<button
|
|
||||||
onClick={() => setShowAbandon(true)}
|
|
||||||
disabled={isProcessing || submitting}
|
|
||||||
className="flex items-center justify-center gap-1.5 rounded-lg bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-2.5 sm:px-4 py-2 min-h-[40px] sm:min-h-[44px] text-xs sm: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"
|
|
||||||
>
|
|
||||||
<X size={15} />
|
|
||||||
<span className="hidden sm:inline">Close</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Resolve modal */}
|
|
||||||
{showResolve && (
|
|
||||||
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/60 backdrop-blur-sm">
|
|
||||||
<div className="card-flat 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
|
|
||||||
value={resolutionSummary}
|
|
||||||
onChange={(e) => setResolutionSummary(e.target.value)}
|
|
||||||
placeholder="What resolved the issue?"
|
|
||||||
className="w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(96,165,250,0.3)] focus:outline-none resize-none"
|
|
||||||
rows={4}
|
|
||||||
autoFocus
|
|
||||||
/>
|
|
||||||
<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 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 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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Close/Abandon confirmation */}
|
|
||||||
{showAbandon && (
|
|
||||||
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/60 backdrop-blur-sm">
|
|
||||||
<div className="card-flat 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">Close Session</h3>
|
|
||||||
<p className="text-sm text-muted-foreground mb-4">
|
|
||||||
Are you sure you want to close this session? The session history will be kept but it won't count as resolved.
|
|
||||||
</p>
|
|
||||||
<div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
|
||||||
<button
|
|
||||||
onClick={() => setShowAbandon(false)}
|
|
||||||
className="rounded-lg px-4 py-2 min-h-[44px] text-sm text-muted-foreground hover:text-foreground transition-colors"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
onClick={handleAbandon}
|
|
||||||
disabled={submitting}
|
|
||||||
className="rounded-lg bg-rose-500/20 border border-rose-500/30 px-4 py-2 min-h-[44px] text-sm font-medium text-rose-400 hover:bg-rose-500/30 disabled:opacity-50 transition-colors"
|
|
||||||
>
|
|
||||||
{submitting ? 'Closing...' : 'Close Session'}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Escalate modal */}
|
|
||||||
<EscalateModal
|
|
||||||
open={showEscalate}
|
|
||||||
onClose={() => setShowEscalate(false)}
|
|
||||||
onEscalate={onEscalate}
|
|
||||||
isProcessing={isProcessing || submitting}
|
|
||||||
hasPsaTicket={hasPsaTicket}
|
|
||||||
sessionId={sessionId}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Status Update modal */}
|
|
||||||
{onGenerateStatusUpdate && (
|
|
||||||
<StatusUpdateModal
|
|
||||||
open={showStatusUpdate}
|
|
||||||
onClose={() => setShowStatusUpdate(false)}
|
|
||||||
onGenerate={onGenerateStatusUpdate}
|
|
||||||
context="status"
|
|
||||||
hasPsaTicket={hasPsaTicket}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ export { FlowPilotIntake } from './FlowPilotIntake'
|
|||||||
export { FlowPilotSession } from './FlowPilotSession'
|
export { FlowPilotSession } from './FlowPilotSession'
|
||||||
export { FlowPilotStepCard } from './FlowPilotStepCard'
|
export { FlowPilotStepCard } from './FlowPilotStepCard'
|
||||||
export { FlowPilotOptions } from './FlowPilotOptions'
|
export { FlowPilotOptions } from './FlowPilotOptions'
|
||||||
export { FlowPilotActionBar } from './FlowPilotActionBar'
|
|
||||||
export { ConfidenceIndicator } from './ConfidenceIndicator'
|
export { ConfidenceIndicator } from './ConfidenceIndicator'
|
||||||
export { SessionDocView } from './SessionDocView'
|
export { SessionDocView } from './SessionDocView'
|
||||||
export { AISessionListItem } from './AISessionListItem'
|
export { AISessionListItem } from './AISessionListItem'
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { useState, useRef, useCallback, useEffect } from 'react'
|
import { useState, useRef, useCallback, useEffect } from 'react'
|
||||||
import { useNavigate } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import { Sparkles, Send, Loader2, Flag, MessageSquare, Paperclip, Terminal, X, RotateCcw, ImagePlus, ListChecks, FileText, GripHorizontal } from 'lucide-react'
|
import { Sparkles, Send, Loader2, MessageSquare, Paperclip, Terminal, X, RotateCcw, ImagePlus, ListChecks, GripHorizontal } from 'lucide-react'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { PageMeta } from '@/components/common/PageMeta'
|
import { PageMeta } from '@/components/common/PageMeta'
|
||||||
import { aiSessionsApi } from '@/api/aiSessions'
|
import { aiSessionsApi } from '@/api/aiSessions'
|
||||||
@@ -254,9 +254,6 @@ export default function CockpitPage() {
|
|||||||
Cases
|
Cases
|
||||||
</button>
|
</button>
|
||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
{session.activeChatId && (
|
|
||||||
<ViewToggle currentView="cockpit" sessionId={session.activeChatId} />
|
|
||||||
)}
|
|
||||||
<button
|
<button
|
||||||
onClick={session.handleNewChat}
|
onClick={session.handleNewChat}
|
||||||
className="rounded-lg px-3 py-2 text-sm font-medium text-primary hover:bg-primary/10 transition-colors"
|
className="rounded-lg px-3 py-2 text-sm font-medium text-primary hover:bg-primary/10 transition-colors"
|
||||||
@@ -265,6 +262,11 @@ export default function CockpitPage() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* View tab bar — persistent when a session is active */}
|
||||||
|
{session.activeChatId && (
|
||||||
|
<ViewToggle currentView="cockpit" sessionId={session.activeChatId} />
|
||||||
|
)}
|
||||||
|
|
||||||
{session.activeChatId ? (
|
{session.activeChatId ? (
|
||||||
<>
|
<>
|
||||||
{/* Incident Header */}
|
{/* Incident Header */}
|
||||||
@@ -275,11 +277,6 @@ export default function CockpitPage() {
|
|||||||
onResolve={() => session.setShowConclude(true)}
|
onResolve={() => session.setShowConclude(true)}
|
||||||
onStatusUpdate={session.messages.length >= 2 ? () => session.setShowStatusUpdate(true) : undefined}
|
onStatusUpdate={session.messages.length >= 2 ? () => session.setShowStatusUpdate(true) : undefined}
|
||||||
onClose={() => session.setShowConclude(true)}
|
onClose={() => session.setShowConclude(true)}
|
||||||
extraActions={
|
|
||||||
<div className="hidden sm:block">
|
|
||||||
<ViewToggle currentView="cockpit" sessionId={session.activeChatId} />
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Resizable work zone + conversation log split */}
|
{/* Resizable work zone + conversation log split */}
|
||||||
@@ -520,18 +517,6 @@ export default function CockpitPage() {
|
|||||||
<span className="hidden sm:inline">Paste Logs</span>
|
<span className="hidden sm:inline">Paste Logs</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{session.messages.length >= 2 && (
|
|
||||||
<>
|
|
||||||
<button type="button" onClick={() => session.setShowStatusUpdate(true)} disabled={session.loading} className="flex items-center gap-1.5 rounded-lg px-2 py-1.5 text-xs text-muted-foreground hover:text-blue-400 hover:bg-blue-500/10 transition-colors disabled:opacity-40" title="Share status update">
|
|
||||||
<FileText size={14} />
|
|
||||||
<span className="hidden sm:inline">Update</span>
|
|
||||||
</button>
|
|
||||||
<button type="button" onClick={() => session.setShowConclude(true)} disabled={session.loading} className="flex items-center gap-1.5 rounded-lg px-2 py-1.5 text-xs text-muted-foreground hover:text-amber-400 hover:bg-amber-400/10 transition-colors disabled:opacity-40" title="Close case">
|
|
||||||
<Flag size={14} />
|
|
||||||
<span className="hidden sm:inline">Close Case</span>
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!session.showTaskLane && (session.activeQuestions.length > 0 || session.activeActions.length > 0) && (
|
{!session.showTaskLane && (session.activeQuestions.length > 0 || session.activeActions.length > 0) && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { useEffect } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { Sparkles, Send, Loader2, Flag, MessageSquare, Paperclip, Terminal, X, RotateCcw, ImagePlus, ListChecks, FileText } from 'lucide-react'
|
import { Sparkles, Send, Loader2, MessageSquare, Paperclip, Terminal, X, RotateCcw, ImagePlus, ListChecks, CheckCircle2, FileText, MoreHorizontal, Pause } from 'lucide-react'
|
||||||
import { cn } from '@/lib/utils'
|
import { cn } from '@/lib/utils'
|
||||||
import { PageMeta } from '@/components/common/PageMeta'
|
import { PageMeta } from '@/components/common/PageMeta'
|
||||||
import { aiSessionsApi } from '@/api/aiSessions'
|
import { aiSessionsApi } from '@/api/aiSessions'
|
||||||
@@ -13,6 +13,7 @@ import { useAssistantSession } from '@/hooks/useAssistantSession'
|
|||||||
|
|
||||||
export default function FlowPilotPage() {
|
export default function FlowPilotPage() {
|
||||||
const session = useAssistantSession()
|
const session = useAssistantSession()
|
||||||
|
const [showOverflow, setShowOverflow] = useState(false)
|
||||||
|
|
||||||
// Handle prefill from dashboard / command palette
|
// Handle prefill from dashboard / command palette
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -112,12 +113,9 @@ export default function FlowPilotPage() {
|
|||||||
className="flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-muted-foreground hover:text-foreground hover:bg-[var(--color-bg-elevated)] transition-colors"
|
className="flex items-center gap-2 rounded-lg px-3 py-2 text-sm text-muted-foreground hover:text-foreground hover:bg-[var(--color-bg-elevated)] transition-colors"
|
||||||
>
|
>
|
||||||
<MessageSquare size={16} />
|
<MessageSquare size={16} />
|
||||||
Chats
|
Cases
|
||||||
</button>
|
</button>
|
||||||
<div className="flex-1" />
|
<div className="flex-1" />
|
||||||
{session.activeChatId && (
|
|
||||||
<ViewToggle currentView="flowpilot" sessionId={session.activeChatId} />
|
|
||||||
)}
|
|
||||||
<button
|
<button
|
||||||
onClick={session.handleNewChat}
|
onClick={session.handleNewChat}
|
||||||
className="rounded-lg px-3 py-2 text-sm font-medium text-primary hover:bg-primary/10 transition-colors"
|
className="rounded-lg px-3 py-2 text-sm font-medium text-primary hover:bg-primary/10 transition-colors"
|
||||||
@@ -126,36 +124,65 @@ export default function FlowPilotPage() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* View tab bar — persistent when a session is active */}
|
||||||
|
{session.activeChatId && (
|
||||||
|
<ViewToggle currentView="flowpilot" sessionId={session.activeChatId} />
|
||||||
|
)}
|
||||||
|
|
||||||
{session.activeChatId ? (
|
{session.activeChatId ? (
|
||||||
<>
|
<>
|
||||||
{/* Desktop view toggle + action bar */}
|
{/* Action bar — Resolve, Update, overflow (Pause/Close) */}
|
||||||
<div className="hidden sm:flex items-center justify-between px-4 py-1.5 border-b border-border/50">
|
{session.messages.length >= 2 && (
|
||||||
<div className="flex items-center gap-1.5">
|
<div className="hidden sm:flex items-center gap-1.5 px-4 py-1.5 border-b border-border/50">
|
||||||
{session.messages.length >= 2 && (
|
<button
|
||||||
<>
|
type="button"
|
||||||
<button
|
onClick={() => session.setShowConclude(true)}
|
||||||
type="button"
|
disabled={session.loading}
|
||||||
onClick={() => session.setShowStatusUpdate(true)}
|
className="flex items-center gap-1.5 rounded-lg bg-emerald-500/10 border border-emerald-500/20 px-3 py-1.5 text-xs font-medium text-emerald-400 hover:bg-emerald-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
||||||
disabled={session.loading}
|
>
|
||||||
className="flex items-center gap-1.5 bg-blue-500/10 border border-blue-500/20 rounded-lg px-3 py-1 text-xs font-medium text-blue-400 hover:bg-blue-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
<CheckCircle2 size={13} />
|
||||||
>
|
Resolve
|
||||||
<FileText size={13} />
|
</button>
|
||||||
Update
|
<button
|
||||||
</button>
|
type="button"
|
||||||
<button
|
onClick={() => session.setShowStatusUpdate(true)}
|
||||||
type="button"
|
disabled={session.loading}
|
||||||
onClick={() => session.setShowConclude(true)}
|
className="flex items-center gap-1.5 bg-blue-500/10 border border-blue-500/20 rounded-lg px-3 py-1.5 text-xs font-medium text-blue-400 hover:bg-blue-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
||||||
disabled={session.loading}
|
>
|
||||||
className="flex items-center gap-1.5 bg-amber-500/10 border border-amber-500/20 rounded-lg px-3 py-1 text-xs font-medium text-amber-400 hover:bg-amber-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
<FileText size={13} />
|
||||||
>
|
Update
|
||||||
<Flag size={13} />
|
</button>
|
||||||
Conclude
|
<div className="relative">
|
||||||
</button>
|
<button
|
||||||
</>
|
onClick={() => setShowOverflow(!showOverflow)}
|
||||||
)}
|
className="flex items-center justify-center rounded-lg px-2 py-1.5 text-muted-foreground hover:text-foreground hover:bg-[rgba(255,255,255,0.06)] transition-colors"
|
||||||
|
>
|
||||||
|
<MoreHorizontal size={16} />
|
||||||
|
</button>
|
||||||
|
{showOverflow && (
|
||||||
|
<>
|
||||||
|
<div className="fixed inset-0 z-40" onClick={() => setShowOverflow(false)} />
|
||||||
|
<div className="absolute right-0 top-full mt-1 z-50 w-40 rounded-lg border border-border bg-card py-1 shadow-lg">
|
||||||
|
<button
|
||||||
|
onClick={() => { setShowOverflow(false); /* pause not wired on chat sessions yet */ }}
|
||||||
|
className="flex w-full items-center gap-2 px-3 py-2 text-xs text-muted-foreground hover:text-foreground hover:bg-[rgba(255,255,255,0.06)] transition-colors"
|
||||||
|
>
|
||||||
|
<Pause size={13} />
|
||||||
|
Pause
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => { setShowOverflow(false); session.setShowConclude(true) }}
|
||||||
|
className="flex w-full items-center gap-2 px-3 py-2 text-xs text-muted-foreground hover:text-rose-400 hover:bg-rose-500/10 transition-colors"
|
||||||
|
>
|
||||||
|
<X size={13} />
|
||||||
|
Close
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<ViewToggle currentView="flowpilot" sessionId={session.activeChatId} />
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Messages */}
|
{/* Messages */}
|
||||||
<div className="flex-1 overflow-y-auto px-4 sm:px-6 py-4 space-y-4">
|
<div className="flex-1 overflow-y-auto px-4 sm:px-6 py-4 space-y-4">
|
||||||
@@ -297,18 +324,6 @@ export default function FlowPilotPage() {
|
|||||||
<span className="hidden sm:inline">Paste Logs</span>
|
<span className="hidden sm:inline">Paste Logs</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
{session.messages.length >= 2 && (
|
|
||||||
<>
|
|
||||||
<button type="button" onClick={() => session.setShowStatusUpdate(true)} disabled={session.loading} className="flex items-center gap-1.5 rounded-lg px-2 py-1.5 text-xs text-muted-foreground hover:text-blue-400 hover:bg-blue-500/10 transition-colors disabled:opacity-40" title="Share status update">
|
|
||||||
<FileText size={14} />
|
|
||||||
<span className="hidden sm:inline">Update</span>
|
|
||||||
</button>
|
|
||||||
<button type="button" onClick={() => session.setShowConclude(true)} disabled={session.loading} className="flex items-center gap-1.5 rounded-lg px-2 py-1.5 text-xs text-muted-foreground hover:text-amber-400 hover:bg-amber-400/10 transition-colors disabled:opacity-40" title="Conclude session">
|
|
||||||
<Flag size={14} />
|
|
||||||
<span className="hidden sm:inline">Conclude</span>
|
|
||||||
</button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{!session.showTaskLane && (session.activeQuestions.length > 0 || session.activeActions.length > 0) && (
|
{!session.showTaskLane && (session.activeQuestions.length > 0 || session.activeActions.length > 0) && (
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -368,7 +383,7 @@ export default function FlowPilotPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Conclude Session Modal */}
|
{/* Close Case Modal */}
|
||||||
<ConcludeSessionModal
|
<ConcludeSessionModal
|
||||||
isOpen={session.showConclude}
|
isOpen={session.showConclude}
|
||||||
onClose={() => session.setShowConclude(false)}
|
onClose={() => session.setShowConclude(false)}
|
||||||
|
|||||||
@@ -264,7 +264,7 @@ export default function FlowPilotSessionPage() {
|
|||||||
onClick={() => setShowStatusUpdate(true)}
|
onClick={() => setShowStatusUpdate(true)}
|
||||||
disabled={fp.isProcessing}
|
disabled={fp.isProcessing}
|
||||||
className="flex items-center gap-1.5 rounded-lg bg-blue-500/10 border border-blue-500/20 px-3 py-1.5 text-xs font-medium text-blue-400 hover:bg-blue-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
className="flex items-center gap-1.5 rounded-lg bg-blue-500/10 border border-blue-500/20 px-3 py-1.5 text-xs font-medium text-blue-400 hover:bg-blue-500/20 disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
||||||
title="Share Update"
|
title="Update"
|
||||||
>
|
>
|
||||||
<FileText size={13} />
|
<FileText size={13} />
|
||||||
Update
|
Update
|
||||||
@@ -281,7 +281,7 @@ export default function FlowPilotSessionPage() {
|
|||||||
{showOverflow && (
|
{showOverflow && (
|
||||||
<>
|
<>
|
||||||
<div className="fixed inset-0 z-40" onClick={() => setShowOverflow(false)} />
|
<div className="fixed inset-0 z-40" onClick={() => setShowOverflow(false)} />
|
||||||
<div className="absolute right-0 top-full mt-1 z-50 w-36 rounded-lg border border-border bg-card py-1 shadow-lg">
|
<div className="absolute right-0 top-full mt-1 z-50 w-40 rounded-lg border border-border bg-card py-1 shadow-lg">
|
||||||
<button
|
<button
|
||||||
onClick={() => { setShowOverflow(false); fp.pauseSession() }}
|
onClick={() => { setShowOverflow(false); fp.pauseSession() }}
|
||||||
className="flex w-full items-center gap-2 px-3 py-2 text-xs text-muted-foreground hover:text-foreground hover:bg-[rgba(255,255,255,0.06)] transition-colors"
|
className="flex w-full items-center gap-2 px-3 py-2 text-xs text-muted-foreground hover:text-foreground hover:bg-[rgba(255,255,255,0.06)] transition-colors"
|
||||||
@@ -294,7 +294,7 @@ export default function FlowPilotSessionPage() {
|
|||||||
className="flex w-full items-center gap-2 px-3 py-2 text-xs text-muted-foreground hover:text-rose-400 hover:bg-rose-500/10 transition-colors"
|
className="flex w-full items-center gap-2 px-3 py-2 text-xs text-muted-foreground hover:text-rose-400 hover:bg-rose-500/10 transition-colors"
|
||||||
>
|
>
|
||||||
<X size={13} />
|
<X size={13} />
|
||||||
Close Session
|
Close
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -313,7 +313,7 @@ export default function FlowPilotSessionPage() {
|
|||||||
{showOverflow && (
|
{showOverflow && (
|
||||||
<>
|
<>
|
||||||
<div className="fixed inset-0 z-40" onClick={() => setShowOverflow(false)} />
|
<div className="fixed inset-0 z-40" onClick={() => setShowOverflow(false)} />
|
||||||
<div className="absolute right-0 top-full mt-1 z-50 w-44 rounded-lg border border-border bg-card py-1 shadow-lg">
|
<div className="absolute right-0 top-full mt-1 z-50 w-40 rounded-lg border border-border bg-card py-1 shadow-lg">
|
||||||
<button
|
<button
|
||||||
onClick={() => { setShowOverflow(false); setShowResolve(true) }}
|
onClick={() => { setShowOverflow(false); setShowResolve(true) }}
|
||||||
disabled={!fp.canResolve}
|
disabled={!fp.canResolve}
|
||||||
@@ -336,7 +336,7 @@ export default function FlowPilotSessionPage() {
|
|||||||
className="flex w-full items-center gap-2 px-3 py-2.5 text-xs text-blue-400 hover:bg-blue-500/10 transition-colors"
|
className="flex w-full items-center gap-2 px-3 py-2.5 text-xs text-blue-400 hover:bg-blue-500/10 transition-colors"
|
||||||
>
|
>
|
||||||
<FileText size={14} />
|
<FileText size={14} />
|
||||||
Share Update
|
Update
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<div className="my-1 border-t border-border" />
|
<div className="my-1 border-t border-border" />
|
||||||
@@ -352,7 +352,7 @@ export default function FlowPilotSessionPage() {
|
|||||||
className="flex w-full items-center gap-2 px-3 py-2.5 text-xs text-muted-foreground hover:text-rose-400 hover:bg-rose-500/10 transition-colors"
|
className="flex w-full items-center gap-2 px-3 py-2.5 text-xs text-muted-foreground hover:text-rose-400 hover:bg-rose-500/10 transition-colors"
|
||||||
>
|
>
|
||||||
<X size={14} />
|
<X size={14} />
|
||||||
Close Session
|
Close
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
@@ -450,7 +450,7 @@ export default function FlowPilotSessionPage() {
|
|||||||
{showAbandon && (
|
{showAbandon && (
|
||||||
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/60 backdrop-blur-sm">
|
<div className="fixed inset-0 z-50 flex items-end sm:items-center justify-center bg-black/60 backdrop-blur-sm">
|
||||||
<div className="card-flat 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">
|
<div className="card-flat 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">Close Session</h3>
|
<h3 className="font-heading text-lg font-semibold text-foreground mb-1">Close</h3>
|
||||||
<p className="text-sm text-muted-foreground mb-4">
|
<p className="text-sm text-muted-foreground mb-4">
|
||||||
Are you sure you want to close this session? The session history will be kept but it won't count as resolved.
|
Are you sure you want to close this session? The session history will be kept but it won't count as resolved.
|
||||||
</p>
|
</p>
|
||||||
@@ -475,7 +475,7 @@ export default function FlowPilotSessionPage() {
|
|||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
className="rounded-lg bg-rose-500/20 border border-rose-500/30 px-4 py-2 min-h-[44px] text-sm font-medium text-rose-400 hover:bg-rose-500/30 disabled:opacity-50 transition-colors"
|
className="rounded-lg bg-rose-500/20 border border-rose-500/30 px-4 py-2 min-h-[44px] text-sm font-medium text-rose-400 hover:bg-rose-500/30 disabled:opacity-50 transition-colors"
|
||||||
>
|
>
|
||||||
{submitting ? 'Closing...' : 'Close Session'}
|
{submitting ? 'Closing...' : 'Close'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -624,7 +624,7 @@ export default function SessionHistoryPage() {
|
|||||||
ref={closePopoverRef}
|
ref={closePopoverRef}
|
||||||
className="absolute right-0 top-full z-20 mt-2 w-72 rounded-xl border border-border bg-card p-4 shadow-xl"
|
className="absolute right-0 top-full z-20 mt-2 w-72 rounded-xl border border-border bg-card p-4 shadow-xl"
|
||||||
>
|
>
|
||||||
<p className="text-sm font-heading font-medium text-foreground mb-3">Close Session</p>
|
<p className="text-sm font-heading font-medium text-foreground mb-3">Close</p>
|
||||||
|
|
||||||
<label className="block text-[0.625rem] font-sans uppercase tracking-[0.1em] text-muted-foreground mb-1">Outcome</label>
|
<label className="block text-[0.625rem] font-sans uppercase tracking-[0.1em] text-muted-foreground mb-1">Outcome</label>
|
||||||
<select
|
<select
|
||||||
|
|||||||
Reference in New Issue
Block a user