Files
resolutionflow/frontend/src/components/flowpilot/SessionBriefing.tsx
Michael Chihlas cef853d7ea refactor: normalize FlowPilot/Assistant/ScriptBuilder to design system tokens
Replace hardcoded Tailwind color utilities with semantic CSS variable tokens
across 31 files in the FlowPilot, Assistant Chat, and Script Builder feature
communities — the areas graphify identified as design-system-free.

- text-blue-400 → text-accent, bg-blue-500/10 → bg-accent-dim, border-blue-500/20 → border-accent/20
- text-amber-400 → text-warning, bg-amber-400/10 → bg-warning-dim, border-l-amber-500 → border-l-warning
- text-rose-400/500 → text-danger, bg-rose-500/10 → bg-danger-dim
- text-emerald-400 → text-success, bg-emerald-500/10 → bg-success-dim, border-l-emerald-500 → border-l-success
- bg-white/[0.08] → bg-elevated (opacity hack → semantic surface token)
- bg-gradient-to-r from-blue-500 to-blue-400 → bg-accent (no gradient surfaces)
- bg-[#60a5fa] → bg-accent (hard-coded hex removed)

Also adds graphify-out/ to .gitignore.

Theme resilience: accent color has changed twice in 5 weeks. Semantic tokens
mean the next change is a 1-line edit in index.css, not 110 grep-and-replace.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 20:20:07 -04:00

174 lines
6.6 KiB
TypeScript

import { useState } from 'react'
import { ArrowRight, ChevronDown, ChevronRight, MessageSquare } from 'lucide-react'
interface EscalationPackage {
original_user_id?: string
problem_summary?: string
escalation_reason?: string
steps_tried?: Array<{
step_type?: string
description?: string
response?: string
}>
steps_ruled_out?: string[]
remaining_hypotheses?: string[]
suggested_next_steps?: string[]
confidence_at_escalation?: number
}
interface SessionBriefingProps {
escalationPackage: EscalationPackage
originalEngineerName?: string
onContinue: () => void
onFresh: (context: string) => void
isProcessing: boolean
}
export function SessionBriefing({
escalationPackage,
originalEngineerName,
onContinue,
onFresh,
isProcessing,
}: SessionBriefingProps) {
const [showSteps, setShowSteps] = useState(false)
const [freshMode, setFreshMode] = useState(false)
const [freshContext, setFreshContext] = useState('')
const pkg = escalationPackage
return (
<div className="card-flat border-l-2 border-l-warning p-5 space-y-4">
<div>
<h3 className="font-heading text-base font-semibold text-foreground">
Escalation from {originalEngineerName || 'another engineer'}
</h3>
<p className="mt-1 text-sm text-muted-foreground">
Review the briefing below, then choose how to proceed.
</p>
</div>
{/* Problem */}
{pkg.problem_summary && (
<div>
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-text-muted mb-1">Problem</h4>
<p className="text-sm text-foreground">{pkg.problem_summary}</p>
</div>
)}
{/* Escalation reason */}
{pkg.escalation_reason && (
<div>
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-text-muted mb-1">Why escalated</h4>
<p className="text-sm text-warning">{pkg.escalation_reason}</p>
</div>
)}
{/* Steps taken (collapsible) */}
{pkg.steps_tried && pkg.steps_tried.length > 0 && (
<div>
<button
onClick={() => setShowSteps(!showSteps)}
className="flex items-center gap-1.5 font-sans text-xs text-[0.625rem] uppercase tracking-wider text-text-muted hover:text-foreground transition-colors"
>
{showSteps ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
Steps taken ({pkg.steps_tried.length})
</button>
{showSteps && (
<div className="mt-2 space-y-1.5">
{pkg.steps_tried.map((step, i) => (
<div key={i} className="rounded-lg bg-card/50 px-3 py-2 text-xs">
<p className="text-foreground">{i + 1}. {step.description}</p>
{step.response && (
<p className="mt-0.5 text-primary"> {step.response}</p>
)}
</div>
))}
</div>
)}
</div>
)}
{/* Remaining hypotheses */}
{pkg.remaining_hypotheses && pkg.remaining_hypotheses.length > 0 && (
<div>
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-text-muted mb-1">Remaining hypotheses</h4>
<ul className="space-y-1">
{pkg.remaining_hypotheses.map((h, i) => (
<li key={i} className="text-sm text-foreground flex items-start gap-2">
<span className="text-primary mt-0.5"></span>
{h}
</li>
))}
</ul>
</div>
)}
{/* Suggested next steps */}
{pkg.suggested_next_steps && pkg.suggested_next_steps.length > 0 && (
<div>
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-text-muted mb-1">Suggested next steps</h4>
<ul className="space-y-1">
{pkg.suggested_next_steps.map((s, i) => (
<li key={i} className="text-sm text-foreground flex items-start gap-2">
<span className="text-success mt-0.5"></span>
{s}
</li>
))}
</ul>
</div>
)}
{/* Action buttons */}
{!freshMode ? (
<div className="flex gap-2 pt-2">
<button
onClick={onContinue}
disabled={isProcessing}
className="flex-1 flex items-center justify-center gap-2 rounded-lg bg-primary text-white px-4 py-2.5 text-sm font-semibold hover:brightness-110 active:scale-[0.98] disabled:opacity-40 transition-all"
>
<ArrowRight size={14} />
Continue Where They Left Off
</button>
<button
onClick={() => setFreshMode(true)}
disabled={isProcessing}
className="flex-1 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.5 text-sm font-medium text-foreground hover:border-[rgba(255,255,255,0.12)] disabled:opacity-40 transition-colors"
>
<MessageSquare size={14} />
Start Fresh With Context
</button>
</div>
) : (
<div className="space-y-3 pt-2">
<textarea
value={freshContext}
onChange={(e) => setFreshContext(e.target.value)}
placeholder="What additional information do you have, or what would you like to investigate first?"
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(96,165,250,0.3)] focus:outline-none resize-none"
rows={3}
autoFocus
/>
<div className="flex gap-2">
<button
onClick={() => setFreshMode(false)}
disabled={isProcessing}
className="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-foreground hover:border-[rgba(255,255,255,0.12)] disabled:opacity-40 transition-colors"
>
Back
</button>
<button
onClick={() => freshContext.trim() && onFresh(freshContext.trim())}
disabled={!freshContext.trim() || isProcessing}
className="flex-1 flex items-center justify-center gap-2 rounded-lg bg-primary text-white px-4 py-2 text-sm font-semibold hover:brightness-110 active:scale-[0.98] disabled:opacity-40 transition-all"
>
<ArrowRight size={14} />
Start Diagnosis
</button>
</div>
</div>
)}
</div>
)
}