refactor: migrate FlowPilot components to Design System v4
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ interface AISessionListItemProps {
|
||||
}
|
||||
|
||||
const STATUS_CONFIG = {
|
||||
active: { icon: Clock, color: 'text-primary', label: 'Active' },
|
||||
active: { icon: Clock, color: 'text-[#22d3ee]', label: 'Active' },
|
||||
paused: { icon: Pause, color: 'text-amber-400', label: 'Paused' },
|
||||
resolved: { icon: CheckCircle2, color: 'text-emerald-400', label: 'Resolved' },
|
||||
escalated: { icon: ArrowUpRight, color: 'text-amber-400', label: 'Escalated' },
|
||||
@@ -22,16 +22,16 @@ export function AISessionListItem({ session }: AISessionListItemProps) {
|
||||
return (
|
||||
<Link
|
||||
to={`/pilot/${session.id}`}
|
||||
className="glass-card block p-4 transition-all"
|
||||
className="card-interactive block p-4 transition-all"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-sm font-medium text-foreground truncate">
|
||||
<p className="text-sm font-medium text-[#e2e5eb] truncate">
|
||||
{session.problem_summary || 'Untitled session'}
|
||||
</p>
|
||||
<div className="mt-1.5 flex items-center gap-3 flex-wrap">
|
||||
{session.problem_domain && (
|
||||
<span className="font-label rounded-md bg-primary/10 px-2 py-0.5 text-[0.625rem] uppercase tracking-wider text-primary">
|
||||
<span className="font-sans text-xs rounded-md bg-[rgba(34,211,238,0.10)] px-2 py-0.5 text-[0.625rem] uppercase tracking-wider text-[#22d3ee]">
|
||||
{session.problem_domain}
|
||||
</span>
|
||||
)}
|
||||
@@ -39,7 +39,7 @@ export function AISessionListItem({ session }: AISessionListItemProps) {
|
||||
<StatusIcon size={12} />
|
||||
{config.label}
|
||||
</span>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
<span className="text-xs text-[#848b9b]">
|
||||
{session.step_count} steps
|
||||
</span>
|
||||
<span className="text-xs text-[#5a6170]">
|
||||
@@ -50,7 +50,7 @@ export function AISessionListItem({ session }: AISessionListItemProps) {
|
||||
</div>
|
||||
</div>
|
||||
{session.session_rating && (
|
||||
<span className="font-label text-xs text-amber-400">
|
||||
<span className="font-sans text-xs text-xs text-amber-400">
|
||||
{'★'.repeat(session.session_rating)}
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -30,12 +30,12 @@ export function ConfidenceIndicator({ tier, score, className }: ConfidenceIndica
|
||||
return (
|
||||
<div className={cn('group relative inline-flex items-center gap-2', className)}>
|
||||
<span className={cn('h-2 w-2 rounded-full', config.color)} />
|
||||
<span className="font-label text-xs text-muted-foreground">{config.label}</span>
|
||||
<span className="font-sans text-xs text-xs text-[#848b9b]">{config.label}</span>
|
||||
|
||||
{/* Tooltip */}
|
||||
<div className="pointer-events-none absolute left-0 top-full z-50 mt-2 w-56 rounded-lg border border-border bg-card p-3 opacity-0 shadow-lg transition-opacity group-hover:pointer-events-auto group-hover:opacity-100">
|
||||
<p className="text-xs text-muted-foreground">{config.description}</p>
|
||||
<p className="mt-1 font-label text-[0.625rem] text-[#5a6170]">
|
||||
<div className="pointer-events-none absolute left-0 top-full z-50 mt-2 w-56 rounded-lg border border-[#1e2130] bg-[#14161d] p-3 opacity-0 shadow-lg transition-opacity group-hover:pointer-events-auto group-hover:opacity-100">
|
||||
<p className="text-xs text-[#848b9b]">{config.description}</p>
|
||||
<p className="mt-1 font-sans text-xs text-[0.625rem] text-[#5a6170]">
|
||||
Confidence: {Math.round(score * 100)}%
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -43,7 +43,7 @@ export function EscalateModal({ open, onClose, onEscalate, isProcessing, hasPsaT
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label className="mb-1.5 block text-sm font-medium text-foreground">
|
||||
<label className="mb-1.5 block text-sm font-medium text-[#e2e5eb]">
|
||||
Why are you escalating?
|
||||
</label>
|
||||
<RichTextInput
|
||||
@@ -63,14 +63,14 @@ export function EscalateModal({ open, onClose, onEscalate, isProcessing, hasPsaT
|
||||
<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 min-h-[44px] 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-[#e2e5eb] 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 min-h-[44px] 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-white hover:bg-amber-500 active:scale-[0.98] disabled:opacity-40 transition-all"
|
||||
>
|
||||
{isProcessing ? (
|
||||
<Loader2 size={14} className="animate-spin" />
|
||||
|
||||
@@ -42,7 +42,7 @@ export function EscalationQueue({ onPickup }: EscalationQueueProps) {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center py-12">
|
||||
<Loader2 size={20} className="animate-spin text-muted-foreground" />
|
||||
<Loader2 size={20} className="animate-spin text-[#848b9b]" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export function EscalationQueue({ onPickup }: EscalationQueueProps) {
|
||||
<p className="text-sm text-rose-400">{error}</p>
|
||||
<button
|
||||
onClick={loadQueue}
|
||||
className="mt-2 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="mt-2 text-xs text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
||||
>
|
||||
Try again
|
||||
</button>
|
||||
@@ -64,11 +64,11 @@ export function EscalationQueue({ onPickup }: EscalationQueueProps) {
|
||||
if (sessions.length === 0) {
|
||||
return (
|
||||
<div className="py-12 text-center">
|
||||
<AlertTriangle size={24} className="mx-auto mb-2 text-muted-foreground/40" />
|
||||
<p className="text-sm text-muted-foreground">No sessions awaiting escalation</p>
|
||||
<AlertTriangle size={24} className="mx-auto mb-2 text-[#848b9b]/40" />
|
||||
<p className="text-sm text-[#848b9b]">No sessions awaiting escalation</p>
|
||||
<button
|
||||
onClick={loadQueue}
|
||||
className="mt-3 flex items-center gap-1.5 mx-auto text-xs text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="mt-3 flex items-center gap-1.5 mx-auto text-xs text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
||||
>
|
||||
<RefreshCw size={12} />
|
||||
Refresh
|
||||
@@ -80,12 +80,12 @@ export function EscalationQueue({ onPickup }: EscalationQueueProps) {
|
||||
return (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between px-1">
|
||||
<h3 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170]">
|
||||
<h3 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170]">
|
||||
Awaiting pickup ({sessions.length})
|
||||
</h3>
|
||||
<button
|
||||
onClick={loadQueue}
|
||||
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="flex items-center gap-1 text-xs text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
||||
>
|
||||
<RefreshCw size={10} />
|
||||
Refresh
|
||||
@@ -93,9 +93,9 @@ export function EscalationQueue({ onPickup }: EscalationQueueProps) {
|
||||
</div>
|
||||
|
||||
{sessions.map((session) => (
|
||||
<div key={session.id} className="glass-card p-3 sm:p-4 space-y-3">
|
||||
<div key={session.id} className="card-interactive p-3 sm:p-4 space-y-3">
|
||||
<div>
|
||||
<p className="text-sm font-semibold text-foreground">
|
||||
<p className="text-sm font-semibold text-[#e2e5eb]">
|
||||
{session.problem_summary || 'Untitled session'}
|
||||
</p>
|
||||
{session.escalation_reason && (
|
||||
@@ -105,9 +105,9 @@ export function EscalationQueue({ onPickup }: EscalationQueueProps) {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground">
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-[#848b9b]">
|
||||
{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">
|
||||
<span className="font-sans text-xs rounded-md bg-[rgba(34,211,238,0.10)] px-1.5 py-0.5 text-[0.5625rem] uppercase tracking-wider text-[#22d3ee]">
|
||||
{session.problem_domain}
|
||||
</span>
|
||||
)}
|
||||
@@ -120,7 +120,7 @@ export function EscalationQueue({ onPickup }: EscalationQueueProps) {
|
||||
{new Date(session.created_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
</span>
|
||||
{session.psa_ticket_id && (
|
||||
<span className="flex items-center gap-1 text-primary">
|
||||
<span className="flex items-center gap-1 text-[#22d3ee]">
|
||||
<Ticket size={10} />
|
||||
#{session.psa_ticket_id}
|
||||
</span>
|
||||
@@ -129,7 +129,7 @@ export function EscalationQueue({ onPickup }: EscalationQueueProps) {
|
||||
|
||||
<button
|
||||
onClick={() => handlePickup(session.id)}
|
||||
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"
|
||||
className="w-full min-h-[44px] rounded-lg bg-[#22d3ee] text-white px-4 py-2 text-sm font-semibold hover:brightness-110 active:scale-[0.98] transition-all"
|
||||
>
|
||||
Pick Up Session
|
||||
</button>
|
||||
|
||||
@@ -70,8 +70,8 @@ export function FlowPilotActionBar({
|
||||
<>
|
||||
{/* Bottom bar — fixed to viewport bottom, works regardless of height chain */}
|
||||
<div
|
||||
className="fixed bottom-0 right-0 z-40 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.95)', backdropFilter: 'blur(16px)', left: 'var(--sidebar-w, 0px)' }}
|
||||
className="fixed bottom-0 right-0 z-40 flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3 border-t border-[#1e2130] bg-[#14161d] px-3 py-3 sm:px-5"
|
||||
style={{ left: 'var(--sidebar-w, 0px)' }}
|
||||
>
|
||||
<div className="flex gap-2 sm:gap-3">
|
||||
<button
|
||||
@@ -96,7 +96,7 @@ export function FlowPilotActionBar({
|
||||
<button
|
||||
onClick={handlePause}
|
||||
disabled={isProcessing || submitting}
|
||||
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"
|
||||
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-[#848b9b] hover:text-[#e2e5eb] hover:border-[rgba(255,255,255,0.12)] disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
||||
>
|
||||
<Pause size={16} />
|
||||
Pause
|
||||
@@ -106,7 +106,7 @@ export function FlowPilotActionBar({
|
||||
<button
|
||||
onClick={() => setShowAbandon(true)}
|
||||
disabled={isProcessing || submitting}
|
||||
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"
|
||||
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-[#848b9b] hover:text-[#e2e5eb] hover:border-[rgba(255,255,255,0.12)] disabled:opacity-40 disabled:pointer-events-none transition-colors"
|
||||
>
|
||||
<X size={16} />
|
||||
Close
|
||||
@@ -118,21 +118,21 @@ export function FlowPilotActionBar({
|
||||
{/* 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="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>
|
||||
<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-[#e2e5eb] mb-1">Resolve Session</h3>
|
||||
<p className="text-sm text-[#848b9b] 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(6,182,212,0.3)] focus:outline-none resize-none"
|
||||
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-[rgba(6,182,212,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"
|
||||
className="rounded-lg px-4 py-2 min-h-[44px] text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
@@ -151,15 +151,15 @@ export function FlowPilotActionBar({
|
||||
{/* 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="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">Close Session</h3>
|
||||
<p className="text-sm text-muted-foreground mb-4">
|
||||
<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-[#e2e5eb] mb-1">Close Session</h3>
|
||||
<p className="text-sm text-[#848b9b] 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"
|
||||
className="rounded-lg px-4 py-2 min-h-[44px] text-sm text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
|
||||
@@ -100,11 +100,11 @@ export function FlowPilotIntake({ onSubmit, isLoading, defaultProblem }: FlowPil
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-[50vh]">
|
||||
<div className="text-center">
|
||||
<div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-2xl bg-primary/10">
|
||||
<Sparkles size={24} className="text-primary animate-pulse" />
|
||||
<div className="mx-auto mb-4 flex h-12 w-12 items-center justify-center rounded-2xl bg-[rgba(34,211,238,0.10)]">
|
||||
<Sparkles size={24} className="text-[#22d3ee] animate-pulse" />
|
||||
</div>
|
||||
<p className="text-sm font-medium text-foreground">Analyzing your issue...</p>
|
||||
<p className="mt-1 text-xs text-muted-foreground">FlowPilot is classifying the problem and searching for relevant flows</p>
|
||||
<p className="text-sm font-medium text-[#e2e5eb]">Analyzing your issue...</p>
|
||||
<p className="mt-1 text-xs text-[#848b9b]">FlowPilot is classifying the problem and searching for relevant flows</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -118,26 +118,26 @@ export function FlowPilotIntake({ onSubmit, isLoading, defaultProblem }: FlowPil
|
||||
<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-4 sm:mb-6">
|
||||
<h1 className="font-heading text-xl sm:text-2xl font-bold tracking-tight text-foreground">
|
||||
<h1 className="font-heading text-xl sm:text-2xl font-bold tracking-tight text-[#e2e5eb]">
|
||||
What are you troubleshooting?
|
||||
</h1>
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
<p className="mt-2 text-sm text-[#848b9b]">
|
||||
Describe the issue, paste an error message, or pull context from a ticket
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="glass-card-static p-3 sm:p-5 space-y-4">
|
||||
<div className="card-flat 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">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm font-semibold text-foreground">
|
||||
<span className="text-primary">#{selectedTicketId}</span>
|
||||
<p className="text-sm font-semibold text-[#e2e5eb]">
|
||||
<span className="text-[#22d3ee]">#{selectedTicketId}</span>
|
||||
{' — '}
|
||||
{selectedTicket.summary}
|
||||
</p>
|
||||
<div className="mt-1 flex flex-wrap items-center gap-x-2 text-xs text-muted-foreground">
|
||||
<div className="mt-1 flex flex-wrap items-center gap-x-2 text-xs text-[#848b9b]">
|
||||
{selectedTicket.company_name && <span>{selectedTicket.company_name}</span>}
|
||||
{selectedTicket.priority_name && (
|
||||
<>
|
||||
@@ -155,7 +155,7 @@ export function FlowPilotIntake({ onSubmit, isLoading, defaultProblem }: FlowPil
|
||||
</div>
|
||||
<button
|
||||
onClick={handleClearTicket}
|
||||
className="ml-2 rounded-md p-1 text-muted-foreground hover:bg-white/[0.06] hover:text-foreground transition-colors"
|
||||
className="ml-2 rounded-md p-1 text-[#848b9b] hover:bg-white/[0.06] hover:text-[#e2e5eb] transition-colors"
|
||||
>
|
||||
<X size={14} />
|
||||
</button>
|
||||
@@ -166,7 +166,7 @@ export function FlowPilotIntake({ onSubmit, isLoading, defaultProblem }: FlowPil
|
||||
value={additionalContext}
|
||||
onChange={(e) => setAdditionalContext(e.target.value)}
|
||||
placeholder="Add extra context (optional) — e.g. 'User called back and said it's also affecting their second monitor'"
|
||||
className="mt-3 w-full rounded-lg border border-border bg-card px-3 py-2 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none resize-none"
|
||||
className="mt-3 w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-[rgba(6,182,212,0.3)] focus:outline-none resize-none"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
@@ -190,8 +190,8 @@ export function FlowPilotIntake({ onSubmit, isLoading, defaultProblem }: FlowPil
|
||||
onClick={() => setShowLogs(!showLogs)}
|
||||
className={`flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-medium transition-colors ${
|
||||
showLogs
|
||||
? 'bg-primary/10 text-primary border border-primary/20'
|
||||
: 'bg-card/50 text-muted-foreground border border-border hover:text-foreground'
|
||||
? 'bg-[rgba(34,211,238,0.10)] text-[#22d3ee] border border-primary/20'
|
||||
: 'bg-[#14161d]/50 text-[#848b9b] border border-[#1e2130] hover:text-[#e2e5eb]'
|
||||
}`}
|
||||
>
|
||||
<Terminal size={12} />
|
||||
@@ -206,8 +206,8 @@ export function FlowPilotIntake({ onSubmit, isLoading, defaultProblem }: FlowPil
|
||||
disabled={!psaChecked || !psaConnection}
|
||||
className={`flex items-center gap-1.5 rounded-lg px-3 py-1.5 text-xs font-medium transition-colors ${
|
||||
psaConnection
|
||||
? 'bg-card/50 text-muted-foreground border border-border hover:text-foreground hover:border-primary/20'
|
||||
: 'bg-card/50 text-[#5a6170] border border-border opacity-50 cursor-not-allowed'
|
||||
? 'bg-[#14161d]/50 text-[#848b9b] border border-[#1e2130] hover:text-[#e2e5eb] hover:border-primary/20'
|
||||
: 'bg-[#14161d]/50 text-[#5a6170] border border-[#1e2130] opacity-50 cursor-not-allowed'
|
||||
}`}
|
||||
title={!psaConnection ? 'Connect your PSA in Settings → Integrations' : 'Search for a ConnectWise ticket'}
|
||||
>
|
||||
@@ -229,7 +229,7 @@ export function FlowPilotIntake({ onSubmit, isLoading, defaultProblem }: FlowPil
|
||||
value={logContent}
|
||||
onChange={(e) => setLogContent(e.target.value)}
|
||||
placeholder="Paste log output, error messages, or Event Viewer entries here..."
|
||||
className="w-full rounded-lg border border-border bg-card px-4 py-3 font-mono text-xs text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none resize-none"
|
||||
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-4 py-3 font-mono text-xs text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-[rgba(6,182,212,0.3)] focus:outline-none resize-none"
|
||||
rows={6}
|
||||
/>
|
||||
)}
|
||||
@@ -242,7 +242,7 @@ export function FlowPilotIntake({ onSubmit, isLoading, defaultProblem }: FlowPil
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
disabled={!hasContent}
|
||||
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"
|
||||
className="w-full sm:w-auto min-h-[44px] rounded-lg bg-[#22d3ee] text-white px-5 py-2.5 text-sm font-semibold hover:brightness-110 active:scale-[0.98] disabled:opacity-40 transition-all whitespace-nowrap"
|
||||
>
|
||||
{submitLabel}
|
||||
</button>
|
||||
|
||||
@@ -48,10 +48,10 @@ export function FlowPilotMessageBar({ onRespond, disabled = false, isProcessing
|
||||
className={cn(
|
||||
'flex items-end gap-2 rounded-xl border p-3 transition-colors',
|
||||
isDisabled
|
||||
? 'border-border/50 opacity-50'
|
||||
: 'border-border focus-within:border-[rgba(6,182,212,0.3)]'
|
||||
? 'border-[#1e2130]/50 opacity-50'
|
||||
: 'border-[#1e2130] focus-within:border-[rgba(6,182,212,0.3)]'
|
||||
)}
|
||||
style={{ background: 'rgba(16, 17, 20, 0.95)', backdropFilter: 'blur(16px)' }}
|
||||
style={{ background: '#14161d' }}
|
||||
>
|
||||
<textarea
|
||||
ref={textareaRef}
|
||||
@@ -61,7 +61,7 @@ export function FlowPilotMessageBar({ onRespond, disabled = false, isProcessing
|
||||
placeholder={isProcessing ? 'FlowPilot is thinking...' : 'Type a message...'}
|
||||
disabled={isDisabled}
|
||||
rows={1}
|
||||
className="flex-1 resize-none bg-transparent text-sm text-foreground placeholder:text-muted-foreground focus:outline-none disabled:cursor-not-allowed py-1.5 px-2"
|
||||
className="flex-1 resize-none bg-transparent text-sm text-[#e2e5eb] placeholder:text-[#848b9b] focus:outline-none disabled:cursor-not-allowed py-1.5 px-2"
|
||||
/>
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
@@ -70,8 +70,8 @@ export function FlowPilotMessageBar({ onRespond, disabled = false, isProcessing
|
||||
className={cn(
|
||||
'flex h-9 w-9 shrink-0 items-center justify-center rounded-lg transition-all',
|
||||
message.trim() && !isDisabled
|
||||
? 'bg-gradient-brand text-[#101114] hover:opacity-90 active:scale-[0.97]'
|
||||
: 'bg-[rgba(255,255,255,0.04)] text-muted-foreground cursor-not-allowed'
|
||||
? 'bg-[#22d3ee] text-white hover:brightness-110 active:scale-[0.98]'
|
||||
: 'bg-[rgba(255,255,255,0.04)] text-[#848b9b] cursor-not-allowed'
|
||||
)}
|
||||
>
|
||||
<Send size={16} />
|
||||
|
||||
@@ -32,20 +32,20 @@ export function FlowPilotOptions({ options, onSelect, disabled }: FlowPilotOptio
|
||||
'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
|
||||
? 'border-primary/40 bg-primary/10'
|
||||
: 'border-border bg-card/50',
|
||||
? 'border-primary/40 bg-[rgba(34,211,238,0.10)]'
|
||||
: 'border-[#1e2130] bg-[#14161d]/50',
|
||||
disabled && 'pointer-events-none opacity-60'
|
||||
)}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex-1">
|
||||
<p className="text-sm font-medium text-foreground">{option.label}</p>
|
||||
<p className="text-sm font-medium text-[#e2e5eb]">{option.label}</p>
|
||||
{option.followup_hint && (
|
||||
<p className="mt-1 text-xs text-muted-foreground">{option.followup_hint}</p>
|
||||
<p className="mt-1 text-xs text-[#848b9b]">{option.followup_hint}</p>
|
||||
)}
|
||||
</div>
|
||||
{isSelected && (
|
||||
<span className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary/20 text-primary">
|
||||
<span className="flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-primary/20 text-[#22d3ee]">
|
||||
<Check size={12} />
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -141,9 +141,9 @@ export function FlowPilotSession({
|
||||
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">
|
||||
<div className="flex items-center gap-3 text-xs text-[#848b9b] 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">
|
||||
<span className="font-sans text-xs rounded-md bg-[rgba(34,211,238,0.10)] px-1.5 py-0.5 text-[0.5625rem] uppercase tracking-wider text-[#22d3ee] shrink-0">
|
||||
{session.problem_domain}
|
||||
</span>
|
||||
)}
|
||||
@@ -156,7 +156,7 @@ export function FlowPilotSession({
|
||||
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" />}
|
||||
{showMobileSidebar ? <ChevronUp size={14} className="text-[#848b9b] shrink-0" /> : <ChevronDown size={14} className="text-[#848b9b] shrink-0" />}
|
||||
</button>
|
||||
{showMobileSidebar && (
|
||||
<div className="px-3 pb-3 sm:px-4 space-y-3">
|
||||
@@ -169,7 +169,7 @@ export function FlowPilotSession({
|
||||
<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]"
|
||||
className="w-full flex items-center gap-2 rounded-xl border border-dashed border-[#1e2130] px-3 py-2.5 text-xs text-[#848b9b] hover:text-[#e2e5eb] hover:border-primary/30 transition-colors disabled:opacity-50 min-h-[44px]"
|
||||
>
|
||||
<Ticket size={14} />
|
||||
{linkingTicket ? 'Linking...' : 'Link Ticket'}
|
||||
@@ -177,14 +177,14 @@ export function FlowPilotSession({
|
||||
) : 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>
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">Problem</h4>
|
||||
<p className="text-sm text-[#e2e5eb]">{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">
|
||||
<Network size={14} className="text-[#848b9b]" />
|
||||
<span className="text-xs text-[#e2e5eb]">
|
||||
{session.match_score ? `${Math.round(session.match_score * 100)}% match` : 'Match found'}
|
||||
</span>
|
||||
</div>
|
||||
@@ -228,7 +228,7 @@ export function FlowPilotSession({
|
||||
<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"
|
||||
className="w-full flex items-center gap-2 rounded-xl border border-dashed border-[#1e2130] px-3 py-2.5 text-xs text-[#848b9b] hover:text-[#e2e5eb] hover:border-primary/30 transition-colors disabled:opacity-50"
|
||||
>
|
||||
<Ticket size={14} />
|
||||
{linkingTicket ? 'Linking...' : 'Link Ticket'}
|
||||
@@ -238,20 +238,20 @@ export function FlowPilotSession({
|
||||
{/* Problem summary */}
|
||||
{session.problem_summary && (
|
||||
<div>
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">
|
||||
Problem
|
||||
</h4>
|
||||
<p className="text-sm text-foreground">{session.problem_summary}</p>
|
||||
<p className="text-sm text-[#e2e5eb]">{session.problem_summary}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Domain */}
|
||||
{session.problem_domain && (
|
||||
<div>
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">
|
||||
Domain
|
||||
</h4>
|
||||
<span className="font-label rounded-md bg-primary/10 px-2 py-0.5 text-[0.625rem] uppercase tracking-wider text-primary">
|
||||
<span className="font-sans text-xs rounded-md bg-[rgba(34,211,238,0.10)] px-2 py-0.5 text-[0.625rem] uppercase tracking-wider text-[#22d3ee]">
|
||||
{session.problem_domain}
|
||||
</span>
|
||||
</div>
|
||||
@@ -259,7 +259,7 @@ export function FlowPilotSession({
|
||||
|
||||
{/* Confidence */}
|
||||
<div>
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">
|
||||
Confidence
|
||||
</h4>
|
||||
<ConfidenceIndicator
|
||||
@@ -271,12 +271,12 @@ export function FlowPilotSession({
|
||||
{/* Matched flow */}
|
||||
{session.matched_flow_id && (
|
||||
<div>
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">
|
||||
Matched flow
|
||||
</h4>
|
||||
<div className="flex items-center gap-2">
|
||||
<Network size={14} className="text-muted-foreground" />
|
||||
<span className="text-xs text-foreground">
|
||||
<Network size={14} className="text-[#848b9b]" />
|
||||
<span className="text-xs text-[#e2e5eb]">
|
||||
{session.match_score ? `${Math.round(session.match_score * 100)}% match` : 'Match found'}
|
||||
</span>
|
||||
</div>
|
||||
@@ -286,12 +286,12 @@ export function FlowPilotSession({
|
||||
{/* Steps */}
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Hash size={12} className="text-muted-foreground" />
|
||||
<span className="text-xs text-muted-foreground">{session.step_count} steps</span>
|
||||
<Hash size={12} className="text-[#848b9b]" />
|
||||
<span className="text-xs text-[#848b9b]">{session.step_count} steps</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Clock size={12} className="text-muted-foreground" />
|
||||
<span className="text-xs text-muted-foreground">
|
||||
<Clock size={12} className="text-[#848b9b]" />
|
||||
<span className="text-xs text-[#848b9b]">
|
||||
{new Date(session.created_at).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
|
||||
</span>
|
||||
</div>
|
||||
@@ -330,13 +330,12 @@ export function FlowPilotSession({
|
||||
{/* Paused banner */}
|
||||
{session.status === 'paused' && onResume && (
|
||||
<div
|
||||
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)' }}
|
||||
className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between border-t border-[#1e2130] bg-[#14161d] px-3 py-3 sm:px-5"
|
||||
>
|
||||
<span className="text-sm text-muted-foreground">Session paused</span>
|
||||
<span className="text-sm text-[#848b9b]">Session paused</span>
|
||||
<button
|
||||
onClick={onResume}
|
||||
className="flex items-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] transition-all"
|
||||
className="flex items-center gap-2 rounded-lg bg-[#22d3ee] text-white px-4 py-2 text-sm font-semibold hover:brightness-110 active:scale-[0.98] transition-all"
|
||||
>
|
||||
<Play size={14} />
|
||||
Resume Session
|
||||
|
||||
@@ -62,12 +62,12 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
return (
|
||||
<button
|
||||
onClick={() => setIsCollapsed(false)}
|
||||
className="w-full text-left glass-card-static p-3 sm:p-4 opacity-70 hover:opacity-90 transition-opacity"
|
||||
className="w-full text-left card-flat 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" />
|
||||
<p className="text-sm text-foreground truncate flex-1">{stepText}</p>
|
||||
<ChevronDown size={14} className="shrink-0 text-muted-foreground" />
|
||||
<Icon size={16} className="shrink-0 text-[#848b9b]" />
|
||||
<p className="text-sm text-[#e2e5eb] truncate flex-1">{stepText}</p>
|
||||
<ChevronDown size={14} className="shrink-0 text-[#848b9b]" />
|
||||
</div>
|
||||
</button>
|
||||
)
|
||||
@@ -76,20 +76,20 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
// Expanded completed step
|
||||
if (!isCurrentStep && !isCollapsed) {
|
||||
return (
|
||||
<div className="glass-card-static p-3 sm:p-4 opacity-80">
|
||||
<div className="card-flat p-3 sm:p-4 opacity-80">
|
||||
<button
|
||||
onClick={() => setIsCollapsed(true)}
|
||||
className="mb-2 flex w-full items-center justify-between text-left"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<Icon size={16} className="text-muted-foreground" />
|
||||
<span className="font-label text-[0.625rem] uppercase tracking-wider text-muted-foreground">
|
||||
<Icon size={16} className="text-[#848b9b]" />
|
||||
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#848b9b]">
|
||||
Step {step.step_order + 1}
|
||||
</span>
|
||||
</div>
|
||||
<ChevronUp size={14} className="text-muted-foreground" />
|
||||
<ChevronUp size={14} className="text-[#848b9b]" />
|
||||
</button>
|
||||
<MarkdownContent content={stepText} className="text-sm text-foreground" />
|
||||
<MarkdownContent content={stepText} className="text-sm text-[#e2e5eb]" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -98,14 +98,14 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'glass-card-static p-3 sm:p-4 lg:p-5',
|
||||
'card-flat p-3 sm:p-4 lg:p-5',
|
||||
isResolutionSuggestion && 'border-emerald-500/30'
|
||||
)}
|
||||
>
|
||||
{/* Context message */}
|
||||
{step.context_message && (
|
||||
<div className="mb-3 rounded-lg bg-primary/5 px-3 py-2 border border-primary/10">
|
||||
<MarkdownContent content={step.context_message} className="text-xs text-muted-foreground" />
|
||||
<MarkdownContent content={step.context_message} className="text-xs text-[#848b9b]" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -113,14 +113,14 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
<div className="flex items-start gap-3 mb-4">
|
||||
<span className={cn(
|
||||
'mt-0.5 flex h-7 w-7 shrink-0 items-center justify-center rounded-lg',
|
||||
isResolutionSuggestion ? 'bg-emerald-500/10 text-emerald-400' : 'bg-primary/10 text-primary'
|
||||
isResolutionSuggestion ? 'bg-emerald-500/10 text-emerald-400' : 'bg-[rgba(34,211,238,0.10)] text-[#22d3ee]'
|
||||
)}>
|
||||
<Icon size={14} />
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
<MarkdownContent content={stepText} className="text-sm" />
|
||||
{isResolutionSuggestion && typeof content.resolution_summary === 'string' && (
|
||||
<p className="mt-2 text-sm text-muted-foreground">
|
||||
<p className="mt-2 text-sm text-[#848b9b]">
|
||||
{content.resolution_summary}
|
||||
</p>
|
||||
)}
|
||||
@@ -141,7 +141,7 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleResolutionResponse(false)}
|
||||
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"
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-[#14161d]/50 border border-[#1e2130] px-4 py-2.5 text-sm font-medium text-[#e2e5eb] hover:bg-[#14161d] transition-colors"
|
||||
>
|
||||
No, keep investigating
|
||||
</button>
|
||||
@@ -180,7 +180,7 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
window.open('/script-builder?from=flowpilot', '_blank')
|
||||
onRespond({ action_result: { success: true, details: 'Opened Script Builder' } })
|
||||
}}
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-gradient-brand px-4 py-2.5 text-sm font-semibold text-[#101114] hover:opacity-90 active:scale-[0.97] transition-all"
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-[#22d3ee] text-white px-4 py-2.5 text-sm font-semibold hover:brightness-110 active:scale-[0.98] transition-all"
|
||||
>
|
||||
Open Script Builder
|
||||
</button>
|
||||
@@ -191,13 +191,13 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
<div className="flex flex-col gap-2 sm:flex-row">
|
||||
<button
|
||||
onClick={() => handleActionComplete(true)}
|
||||
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"
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-[rgba(34,211,238,0.10)] border border-primary/20 px-4 py-2.5 text-sm font-medium text-[#22d3ee] hover:bg-primary/20 transition-colors"
|
||||
>
|
||||
I've completed this action
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleActionComplete(false)}
|
||||
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"
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-[#14161d]/50 border border-[#1e2130] px-4 py-2.5 text-sm font-medium text-[#e2e5eb] hover:bg-[#14161d] transition-colors"
|
||||
>
|
||||
This didn't work
|
||||
</button>
|
||||
@@ -208,7 +208,7 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
{!isResolutionSuggestion && step.allow_skip && (
|
||||
<button
|
||||
onClick={handleSkip}
|
||||
className="flex items-center gap-1.5 text-xs text-[#5a6170] hover:text-muted-foreground transition-colors"
|
||||
className="flex items-center gap-1.5 text-xs text-[#5a6170] hover:text-[#848b9b] transition-colors"
|
||||
>
|
||||
<SkipForward size={12} />
|
||||
I can't check this right now
|
||||
@@ -221,7 +221,7 @@ export function FlowPilotStepCard({ step, isCurrentStep, isProcessing, sessionId
|
||||
{isProcessing && (
|
||||
<div className="flex items-center gap-2 pt-2">
|
||||
<div className="h-1.5 w-1.5 rounded-full bg-primary animate-pulse" />
|
||||
<span className="text-xs text-muted-foreground">FlowPilot is thinking...</span>
|
||||
<span className="text-xs text-[#848b9b]">FlowPilot is thinking...</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -65,14 +65,14 @@ export function InSessionScriptGenerator({
|
||||
return (
|
||||
<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">
|
||||
<Terminal size={14} className="text-[#22d3ee]" />
|
||||
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#22d3ee]">
|
||||
Script Generator
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{instructions && (
|
||||
<p className="text-xs text-muted-foreground">{instructions}</p>
|
||||
<p className="text-xs text-[#848b9b]">{instructions}</p>
|
||||
)}
|
||||
|
||||
{/* Parameter editing */}
|
||||
@@ -80,7 +80,7 @@ export function InSessionScriptGenerator({
|
||||
<>
|
||||
<button
|
||||
onClick={() => setShowParams(!showParams)}
|
||||
className="flex items-center gap-1.5 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="flex items-center gap-1.5 text-xs text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
||||
>
|
||||
{showParams ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
|
||||
Parameters ({Object.keys(params).length})
|
||||
@@ -90,13 +90,13 @@ export function InSessionScriptGenerator({
|
||||
<div className="space-y-2">
|
||||
{Object.entries(params).map(([key, value]) => (
|
||||
<div key={key}>
|
||||
<label className="block text-xs font-medium text-muted-foreground mb-1">
|
||||
<label className="block text-xs font-medium text-[#848b9b] mb-1">
|
||||
{key.replace(/_/g, ' ')}
|
||||
</label>
|
||||
<input
|
||||
value={value}
|
||||
onChange={(e) => setParams(prev => ({ ...prev, [key]: e.target.value }))}
|
||||
className="w-full rounded-lg border border-border bg-card px-3 py-1.5 text-sm text-foreground placeholder:text-muted-foreground focus:border-[rgba(6,182,212,0.3)] focus:outline-none"
|
||||
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-1.5 text-sm text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-[rgba(6,182,212,0.3)] focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
@@ -106,7 +106,7 @@ export function InSessionScriptGenerator({
|
||||
<button
|
||||
onClick={handleGenerate}
|
||||
disabled={isGenerating}
|
||||
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"
|
||||
className="w-full min-h-[44px] flex items-center justify-center gap-2 rounded-lg bg-[#22d3ee] text-white px-4 py-2 text-sm font-semibold hover:brightness-110 active:scale-[0.98] disabled:opacity-40 transition-all"
|
||||
>
|
||||
{isGenerating ? (
|
||||
<Loader2 size={14} className="animate-spin" />
|
||||
@@ -121,12 +121,12 @@ export function InSessionScriptGenerator({
|
||||
{/* Generated script display */}
|
||||
{generatedScript && (
|
||||
<>
|
||||
<div className="relative rounded-lg bg-card/80 border border-border overflow-hidden">
|
||||
<div className="flex items-center justify-between px-3 py-1.5 border-b border-border/50">
|
||||
<span className="text-xs text-muted-foreground font-mono">PowerShell</span>
|
||||
<div className="relative rounded-lg bg-[#14161d]/80 border border-[#1e2130] overflow-hidden">
|
||||
<div className="flex items-center justify-between px-3 py-1.5 border-b border-[#1e2130]/50">
|
||||
<span className="text-xs text-[#848b9b] font-mono">PowerShell</span>
|
||||
<button
|
||||
onClick={handleCopy}
|
||||
className="flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors"
|
||||
className="flex items-center gap-1 text-xs text-[#848b9b] hover:text-[#e2e5eb] transition-colors"
|
||||
>
|
||||
{copied ? <Check size={12} className="text-emerald-400" /> : <Copy size={12} />}
|
||||
{copied ? 'Copied' : 'Copy'}
|
||||
@@ -141,13 +141,13 @@ export function InSessionScriptGenerator({
|
||||
<div className="flex flex-col gap-2 pt-1 sm:flex-row">
|
||||
<button
|
||||
onClick={() => handleContinue(true)}
|
||||
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"
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-[rgba(34,211,238,0.10)] border border-primary/20 px-4 py-2 text-sm font-medium text-[#22d3ee] hover:bg-primary/20 transition-colors"
|
||||
>
|
||||
Script worked — continue
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleContinue(false)}
|
||||
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"
|
||||
className="flex-1 min-h-[44px] rounded-lg bg-[#14161d]/50 border border-[#1e2130] px-4 py-2 text-sm font-medium text-[#e2e5eb] hover:bg-[#14161d] transition-colors"
|
||||
>
|
||||
Didn't resolve it
|
||||
</button>
|
||||
|
||||
@@ -5,7 +5,7 @@ const TYPE_CONFIG = {
|
||||
new_flow: { label: 'New Flow', color: 'text-emerald-400 bg-emerald-400/10 border-emerald-400/20', icon: Sparkles },
|
||||
enhancement: { label: 'Enhancement', color: 'text-amber-400 bg-amber-400/10 border-amber-400/20', icon: ArrowUpRight },
|
||||
branch_addition: { label: 'Branch', color: 'text-blue-400 bg-blue-400/10 border-blue-400/20', icon: GitBranch },
|
||||
auto_reinforced: { label: 'Reinforced', color: 'text-muted-foreground bg-card border-border', icon: Sparkles },
|
||||
auto_reinforced: { label: 'Reinforced', color: 'text-[#848b9b] bg-[#14161d] border-[#1e2130]', icon: Sparkles },
|
||||
} as const
|
||||
|
||||
interface ProposalCardProps {
|
||||
@@ -24,24 +24,24 @@ export function ProposalCard({ proposal, isSelected, onClick }: ProposalCardProp
|
||||
className={`w-full text-left rounded-xl border p-3 space-y-2 transition-all ${
|
||||
isSelected
|
||||
? 'border-primary/30 bg-primary/5'
|
||||
: 'border-[rgba(255,255,255,0.06)] bg-[rgba(24,26,31,0.55)] hover:border-[rgba(255,255,255,0.12)]'
|
||||
: 'border-[rgba(255,255,255,0.06)] bg-[#14161d] hover:border-[rgba(255,255,255,0.12)]'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<p className="text-sm font-semibold text-foreground line-clamp-2">{proposal.title}</p>
|
||||
<span className={`shrink-0 flex items-center gap-1 rounded-md border px-1.5 py-0.5 font-label text-[0.5625rem] uppercase tracking-wider ${typeConfig.color}`}>
|
||||
<p className="text-sm font-semibold text-[#e2e5eb] line-clamp-2">{proposal.title}</p>
|
||||
<span className={`shrink-0 flex items-center gap-1 rounded-md border px-1.5 py-0.5 font-sans text-xs text-[0.5625rem] uppercase tracking-wider ${typeConfig.color}`}>
|
||||
<TypeIcon size={10} />
|
||||
{typeConfig.label}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{proposal.description && (
|
||||
<p className="text-xs text-muted-foreground line-clamp-2">{proposal.description}</p>
|
||||
<p className="text-xs text-[#848b9b] line-clamp-2">{proposal.description}</p>
|
||||
)}
|
||||
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground">
|
||||
<div className="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-[#848b9b]">
|
||||
{proposal.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">
|
||||
<span className="font-sans text-xs rounded-md bg-[rgba(34,211,238,0.10)] px-1.5 py-0.5 text-[0.5625rem] uppercase tracking-wider text-[#22d3ee]">
|
||||
{proposal.problem_domain}
|
||||
</span>
|
||||
)}
|
||||
@@ -53,7 +53,7 @@ export function ProposalCard({ proposal, isSelected, onClick }: ProposalCardProp
|
||||
<Clock size={10} />
|
||||
{new Date(proposal.created_at).toLocaleDateString()}
|
||||
</span>
|
||||
<span className="text-primary">
|
||||
<span className="text-[#22d3ee]">
|
||||
{Math.round(proposal.confidence_score * 100)}%
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -61,13 +61,13 @@ export function ProposalDetail({ proposal, onReview }: ProposalDetailProps) {
|
||||
<div className="flex h-full flex-col">
|
||||
{/* Header */}
|
||||
<div className="border-b px-6 py-4" style={{ borderColor: 'var(--glass-border)' }}>
|
||||
<h2 className="font-heading text-lg font-semibold text-foreground">{proposal.title}</h2>
|
||||
<h2 className="font-heading text-lg font-semibold text-[#e2e5eb]">{proposal.title}</h2>
|
||||
{proposal.description && (
|
||||
<p className="mt-1 text-sm text-muted-foreground">{proposal.description}</p>
|
||||
<p className="mt-1 text-sm text-[#848b9b]">{proposal.description}</p>
|
||||
)}
|
||||
<div className="mt-3 flex flex-wrap items-center gap-3 text-xs text-muted-foreground">
|
||||
<div className="mt-3 flex flex-wrap items-center gap-3 text-xs text-[#848b9b]">
|
||||
{proposal.problem_domain && (
|
||||
<span className="font-label rounded-md bg-primary/10 px-2 py-0.5 text-[0.625rem] uppercase tracking-wider text-primary">
|
||||
<span className="font-sans text-xs rounded-md bg-[rgba(34,211,238,0.10)] px-2 py-0.5 text-[0.625rem] uppercase tracking-wider text-[#22d3ee]">
|
||||
{proposal.problem_domain}
|
||||
</span>
|
||||
)}
|
||||
@@ -89,12 +89,12 @@ export function ProposalDetail({ proposal, onReview }: ProposalDetailProps) {
|
||||
{/* Content */}
|
||||
<div className="flex-1 overflow-y-auto p-6 space-y-5">
|
||||
{/* Source session link */}
|
||||
<div className="glass-card-static p-4">
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-2">Source Session</h4>
|
||||
<div className="card-flat p-4">
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-2">Source Session</h4>
|
||||
<Link
|
||||
to={`/pilot/${proposal.source_session_id}`}
|
||||
target="_blank"
|
||||
className="flex items-center gap-2 text-sm text-primary hover:underline"
|
||||
className="flex items-center gap-2 text-sm text-[#22d3ee] hover:underline"
|
||||
>
|
||||
<ExternalLink size={12} />
|
||||
View session that generated this proposal
|
||||
@@ -105,16 +105,16 @@ export function ProposalDetail({ proposal, onReview }: ProposalDetailProps) {
|
||||
{proposal.proposed_diff && (() => {
|
||||
const diff = proposal.proposed_diff as { diff_description?: string; new_nodes?: Array<{ title?: string; question?: string; description?: string }> }
|
||||
return (
|
||||
<div className="glass-card-static border-l-2 border-l-amber-500 p-4">
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-2">Proposed Changes</h4>
|
||||
<div className="card-flat border-l-2 border-l-amber-500 p-4">
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-2">Proposed Changes</h4>
|
||||
{diff.diff_description && (
|
||||
<p className="text-sm text-foreground">{diff.diff_description}</p>
|
||||
<p className="text-sm text-[#e2e5eb]">{diff.diff_description}</p>
|
||||
)}
|
||||
{diff.new_nodes && diff.new_nodes.length > 0 && (
|
||||
<div className="mt-3 space-y-1.5">
|
||||
<p className="text-xs font-medium text-muted-foreground">New nodes:</p>
|
||||
<p className="text-xs font-medium text-[#848b9b]">New nodes:</p>
|
||||
{diff.new_nodes.map((node, i) => (
|
||||
<div key={i} className="rounded-lg bg-emerald-500/5 border border-emerald-500/10 px-3 py-2 text-xs text-foreground">
|
||||
<div key={i} className="rounded-lg bg-emerald-500/5 border border-emerald-500/10 px-3 py-2 text-xs text-[#e2e5eb]">
|
||||
<span className="text-emerald-400">+ </span>
|
||||
{node.title || node.question || node.description || 'New node'}
|
||||
</div>
|
||||
@@ -126,16 +126,16 @@ export function ProposalDetail({ proposal, onReview }: ProposalDetailProps) {
|
||||
})()}
|
||||
|
||||
{/* Flow data preview */}
|
||||
<div className="glass-card-static p-4">
|
||||
<div className="card-flat p-4">
|
||||
<button
|
||||
onClick={() => setShowFlowData(!showFlowData)}
|
||||
className="flex items-center gap-1.5 font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] hover:text-foreground transition-colors"
|
||||
className="flex items-center gap-1.5 font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] hover:text-[#e2e5eb] transition-colors"
|
||||
>
|
||||
{showFlowData ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
|
||||
Flow Data (JSON)
|
||||
</button>
|
||||
{showFlowData && (
|
||||
<pre className="mt-3 max-h-[400px] overflow-auto rounded-lg bg-card/80 p-3 text-xs text-muted-foreground font-mono">
|
||||
<pre className="mt-3 max-h-[400px] overflow-auto rounded-lg bg-[#14161d]/80 p-3 text-xs text-[#848b9b] font-mono">
|
||||
{JSON.stringify(proposal.proposed_flow_data, null, 2)}
|
||||
</pre>
|
||||
)}
|
||||
@@ -143,8 +143,8 @@ export function ProposalDetail({ proposal, onReview }: ProposalDetailProps) {
|
||||
|
||||
{/* Supporting sessions */}
|
||||
{proposal.supporting_session_ids.length > 1 && (
|
||||
<div className="glass-card-static p-4">
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-2">
|
||||
<div className="card-flat p-4">
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-2">
|
||||
Supporting Sessions ({proposal.supporting_session_ids.length})
|
||||
</h4>
|
||||
<div className="space-y-1">
|
||||
@@ -153,7 +153,7 @@ export function ProposalDetail({ proposal, onReview }: ProposalDetailProps) {
|
||||
key={sid}
|
||||
to={`/pilot/${sid}`}
|
||||
target="_blank"
|
||||
className="block text-xs text-primary hover:underline truncate"
|
||||
className="block text-xs text-[#22d3ee] hover:underline truncate"
|
||||
>
|
||||
{sid}
|
||||
</Link>
|
||||
@@ -164,14 +164,14 @@ export function ProposalDetail({ proposal, onReview }: ProposalDetailProps) {
|
||||
|
||||
{/* Review info (for already-reviewed proposals) */}
|
||||
{proposal.reviewed_at && (
|
||||
<div className="glass-card-static p-4">
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-2">Review</h4>
|
||||
<p className="text-sm text-foreground">
|
||||
<div className="card-flat p-4">
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-2">Review</h4>
|
||||
<p className="text-sm text-[#e2e5eb]">
|
||||
<span className="capitalize">{proposal.status}</span> on{' '}
|
||||
{new Date(proposal.reviewed_at).toLocaleString()}
|
||||
</p>
|
||||
{proposal.reviewer_notes && (
|
||||
<p className="mt-1 text-xs text-muted-foreground">{proposal.reviewer_notes}</p>
|
||||
<p className="mt-1 text-xs text-[#848b9b]">{proposal.reviewer_notes}</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
@@ -180,15 +180,14 @@ export function ProposalDetail({ proposal, onReview }: ProposalDetailProps) {
|
||||
{/* Review actions bar */}
|
||||
{canReview && (
|
||||
<div
|
||||
className="border-t px-5 py-3 space-y-3"
|
||||
style={{ borderColor: 'var(--glass-border)', background: 'rgba(16, 17, 20, 0.8)', backdropFilter: 'blur(12px)' }}
|
||||
className="border-t border-[#1e2130] bg-[#14161d] px-5 py-3 space-y-3"
|
||||
>
|
||||
{/* Notes input */}
|
||||
<input
|
||||
value={reviewNotes}
|
||||
onChange={(e) => setReviewNotes(e.target.value)}
|
||||
placeholder="Reviewer notes (optional)"
|
||||
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(6,182,212,0.3)] focus:outline-none"
|
||||
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-3 py-2 text-sm text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-[rgba(6,182,212,0.3)] focus:outline-none"
|
||||
/>
|
||||
|
||||
{/* Action buttons */}
|
||||
@@ -203,14 +202,14 @@ export function ProposalDetail({ proposal, onReview }: ProposalDetailProps) {
|
||||
Approve & Publish
|
||||
</button>
|
||||
) : (
|
||||
<span className="text-xs text-muted-foreground italic px-2">
|
||||
<span className="text-xs text-[#848b9b] italic px-2">
|
||||
Enhancement proposals require Edit & Publish
|
||||
</span>
|
||||
)}
|
||||
<button
|
||||
onClick={handleEditAndPublish}
|
||||
disabled={isSubmitting}
|
||||
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-foreground hover:border-[rgba(255,255,255,0.12)] disabled:opacity-40 transition-colors"
|
||||
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-[#e2e5eb] hover:border-[rgba(255,255,255,0.12)] disabled:opacity-40 transition-colors"
|
||||
>
|
||||
<Pencil size={14} />
|
||||
Edit & Publish
|
||||
@@ -218,7 +217,7 @@ export function ProposalDetail({ proposal, onReview }: ProposalDetailProps) {
|
||||
<button
|
||||
onClick={() => handleAction('dismiss')}
|
||||
disabled={isSubmitting}
|
||||
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 transition-colors ml-auto"
|
||||
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-[#848b9b] hover:text-[#e2e5eb] hover:border-[rgba(255,255,255,0.12)] disabled:opacity-40 transition-colors ml-auto"
|
||||
>
|
||||
<EyeOff size={14} />
|
||||
Dismiss
|
||||
|
||||
@@ -38,12 +38,12 @@ export function SessionBriefing({
|
||||
const pkg = escalationPackage
|
||||
|
||||
return (
|
||||
<div className="glass-card-static border-l-2 border-l-amber-500 p-5 space-y-4">
|
||||
<div className="card-flat border-l-2 border-l-amber-500 p-5 space-y-4">
|
||||
<div>
|
||||
<h3 className="font-heading text-base font-semibold text-foreground">
|
||||
<h3 className="font-heading text-base font-semibold text-[#e2e5eb]">
|
||||
Escalation from {originalEngineerName || 'another engineer'}
|
||||
</h3>
|
||||
<p className="mt-1 text-sm text-muted-foreground">
|
||||
<p className="mt-1 text-sm text-[#848b9b]">
|
||||
Review the briefing below, then choose how to proceed.
|
||||
</p>
|
||||
</div>
|
||||
@@ -51,15 +51,15 @@ export function SessionBriefing({
|
||||
{/* Problem */}
|
||||
{pkg.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">{pkg.problem_summary}</p>
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">Problem</h4>
|
||||
<p className="text-sm text-[#e2e5eb]">{pkg.problem_summary}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Escalation reason */}
|
||||
{pkg.escalation_reason && (
|
||||
<div>
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">Why escalated</h4>
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">Why escalated</h4>
|
||||
<p className="text-sm text-amber-400">{pkg.escalation_reason}</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -69,7 +69,7 @@ export function SessionBriefing({
|
||||
<div>
|
||||
<button
|
||||
onClick={() => setShowSteps(!showSteps)}
|
||||
className="flex items-center gap-1.5 font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] hover:text-foreground transition-colors"
|
||||
className="flex items-center gap-1.5 font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] hover:text-[#e2e5eb] transition-colors"
|
||||
>
|
||||
{showSteps ? <ChevronDown size={12} /> : <ChevronRight size={12} />}
|
||||
Steps taken ({pkg.steps_tried.length})
|
||||
@@ -77,10 +77,10 @@ export function SessionBriefing({
|
||||
{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>
|
||||
<div key={i} className="rounded-lg bg-[#14161d]/50 px-3 py-2 text-xs">
|
||||
<p className="text-[#e2e5eb]">{i + 1}. {step.description}</p>
|
||||
{step.response && (
|
||||
<p className="mt-0.5 text-primary">→ {step.response}</p>
|
||||
<p className="mt-0.5 text-[#22d3ee]">→ {step.response}</p>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
@@ -92,11 +92,11 @@ export function SessionBriefing({
|
||||
{/* Remaining hypotheses */}
|
||||
{pkg.remaining_hypotheses && pkg.remaining_hypotheses.length > 0 && (
|
||||
<div>
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">Remaining hypotheses</h4>
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] 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>
|
||||
<li key={i} className="text-sm text-[#e2e5eb] flex items-start gap-2">
|
||||
<span className="text-[#22d3ee] mt-0.5">•</span>
|
||||
{h}
|
||||
</li>
|
||||
))}
|
||||
@@ -107,10 +107,10 @@ export function SessionBriefing({
|
||||
{/* Suggested next steps */}
|
||||
{pkg.suggested_next_steps && pkg.suggested_next_steps.length > 0 && (
|
||||
<div>
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170] mb-1">Suggested next steps</h4>
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170] 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">
|
||||
<li key={i} className="text-sm text-[#e2e5eb] flex items-start gap-2">
|
||||
<span className="text-emerald-400 mt-0.5">→</span>
|
||||
{s}
|
||||
</li>
|
||||
@@ -125,7 +125,7 @@ export function SessionBriefing({
|
||||
<button
|
||||
onClick={onContinue}
|
||||
disabled={isProcessing}
|
||||
className="flex-1 flex items-center justify-center gap-2 rounded-lg bg-gradient-brand px-4 py-2.5 text-sm font-semibold text-[#101114] shadow-lg shadow-primary/20 hover:opacity-90 active:scale-[0.97] disabled:opacity-40 transition-all"
|
||||
className="flex-1 flex items-center justify-center gap-2 rounded-lg bg-[#22d3ee] 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
|
||||
@@ -133,7 +133,7 @@ export function SessionBriefing({
|
||||
<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"
|
||||
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-[#e2e5eb] hover:border-[rgba(255,255,255,0.12)] disabled:opacity-40 transition-colors"
|
||||
>
|
||||
<MessageSquare size={14} />
|
||||
Start Fresh With Context
|
||||
@@ -145,7 +145,7 @@ export function SessionBriefing({
|
||||
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(6,182,212,0.3)] focus:outline-none resize-none"
|
||||
className="w-full rounded-lg border border-[#1e2130] bg-[#14161d] px-4 py-3 text-sm text-[#e2e5eb] placeholder:text-[#848b9b] focus:border-[rgba(6,182,212,0.3)] focus:outline-none resize-none"
|
||||
rows={3}
|
||||
autoFocus
|
||||
/>
|
||||
@@ -153,14 +153,14 @@ export function SessionBriefing({
|
||||
<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"
|
||||
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-[#e2e5eb] 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-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="flex-1 flex items-center justify-center gap-2 rounded-lg bg-[#22d3ee] 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
|
||||
|
||||
@@ -107,27 +107,27 @@ export function SessionDocView({
|
||||
)}
|
||||
|
||||
{/* Header */}
|
||||
<div className="glass-card-static p-3 sm:p-4 lg:p-5">
|
||||
<div className="card-flat 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">
|
||||
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-[rgba(34,211,238,0.10)] text-[#22d3ee]">
|
||||
<FileText size={16} />
|
||||
</span>
|
||||
<div className="flex-1">
|
||||
<h3 className="font-heading text-lg font-semibold text-foreground">Session Documentation</h3>
|
||||
<p className="mt-1 text-sm text-muted-foreground">{documentation.problem_summary}</p>
|
||||
<h3 className="font-heading text-lg font-semibold text-[#e2e5eb]">Session Documentation</h3>
|
||||
<p className="mt-1 text-sm text-[#848b9b]">{documentation.problem_summary}</p>
|
||||
<div className="mt-2 flex items-center gap-3 flex-wrap">
|
||||
{documentation.problem_domain && (
|
||||
<span className="font-label rounded-md bg-primary/10 px-2 py-0.5 text-[0.625rem] uppercase tracking-wider text-primary">
|
||||
<span className="font-sans text-xs rounded-md bg-[rgba(34,211,238,0.10)] px-2 py-0.5 text-[0.625rem] uppercase tracking-wider text-[#22d3ee]">
|
||||
{documentation.problem_domain}
|
||||
</span>
|
||||
)}
|
||||
{documentation.duration_display && (
|
||||
<span className="flex items-center gap-1 text-xs text-muted-foreground">
|
||||
<span className="flex items-center gap-1 text-xs text-[#848b9b]">
|
||||
<Clock size={12} />
|
||||
{documentation.duration_display}
|
||||
</span>
|
||||
)}
|
||||
<span className="text-xs text-muted-foreground">
|
||||
<span className="text-xs text-[#848b9b]">
|
||||
{documentation.total_steps} steps
|
||||
</span>
|
||||
</div>
|
||||
@@ -137,49 +137,49 @@ export function SessionDocView({
|
||||
|
||||
{/* Outcome */}
|
||||
{(documentation.resolution_summary || documentation.escalation_reason) && (
|
||||
<div className={`glass-card-static p-4 border-l-2 ${documentation.resolution_summary ? 'border-l-emerald-500' : 'border-l-amber-500'}`}>
|
||||
<div className={`card-flat p-4 border-l-2 ${documentation.resolution_summary ? 'border-l-emerald-500' : 'border-l-amber-500'}`}>
|
||||
<div className="flex items-center gap-2 mb-1">
|
||||
{documentation.resolution_summary ? (
|
||||
<CheckCircle2 size={14} className="text-emerald-400" />
|
||||
) : (
|
||||
<ArrowUpRight size={14} className="text-amber-400" />
|
||||
)}
|
||||
<span className="font-label text-[0.625rem] uppercase tracking-wider text-muted-foreground">
|
||||
<span className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#848b9b]">
|
||||
{documentation.resolution_summary ? 'Resolved' : 'Escalated'}
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-sm text-foreground">
|
||||
<p className="text-sm text-[#e2e5eb]">
|
||||
{documentation.resolution_summary || documentation.escalation_reason}
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Intake summary */}
|
||||
<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">
|
||||
<div className="card-flat p-3 sm:p-4">
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#848b9b] mb-2">
|
||||
Original intake
|
||||
</h4>
|
||||
<p className="text-sm text-foreground whitespace-pre-wrap">{documentation.intake_summary}</p>
|
||||
<p className="text-sm text-[#e2e5eb] whitespace-pre-wrap">{documentation.intake_summary}</p>
|
||||
</div>
|
||||
|
||||
{/* Diagnostic steps */}
|
||||
<div className="space-y-2">
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-muted-foreground px-1">
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#848b9b] px-1">
|
||||
Diagnostic trail
|
||||
</h4>
|
||||
{documentation.diagnostic_steps.map((step) => (
|
||||
<div key={step.step_number} className="glass-card-static p-3 sm:p-4">
|
||||
<div key={step.step_number} className="card-flat 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">
|
||||
<span className="font-sans text-xs flex h-5 w-5 shrink-0 items-center justify-center rounded-full bg-[#14161d] text-[0.625rem] text-[#848b9b] border border-[#1e2130]">
|
||||
{step.step_number}
|
||||
</span>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm text-foreground">{step.description}</p>
|
||||
<p className="text-sm text-[#e2e5eb]">{step.description}</p>
|
||||
{step.engineer_response && (
|
||||
<p className="mt-1 text-xs text-primary">→ {step.engineer_response}</p>
|
||||
<p className="mt-1 text-xs text-[#22d3ee]">→ {step.engineer_response}</p>
|
||||
)}
|
||||
{step.outcome && (
|
||||
<p className="mt-1 text-xs text-muted-foreground">Outcome: {step.outcome}</p>
|
||||
<p className="mt-1 text-xs text-[#848b9b]">Outcome: {step.outcome}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -189,8 +189,8 @@ export function SessionDocView({
|
||||
|
||||
{/* Rating */}
|
||||
{onRate && (
|
||||
<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="card-flat p-3 sm:p-4 text-center">
|
||||
<p className="text-sm text-[#848b9b] 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
|
||||
@@ -203,7 +203,7 @@ export function SessionDocView({
|
||||
className={
|
||||
(currentRating ?? 0) >= star
|
||||
? 'fill-amber-400 text-amber-400'
|
||||
: 'text-muted-foreground hover:text-amber-400'
|
||||
: 'text-[#848b9b] hover:text-amber-400'
|
||||
}
|
||||
/>
|
||||
</button>
|
||||
|
||||
@@ -36,7 +36,7 @@ export function SessionTicketCard({ ticketId, ticketData, siteUrl }: SessionTick
|
||||
return (
|
||||
<div className="rounded-xl border border-primary/20 bg-primary/5 p-3 space-y-2">
|
||||
<div className="flex items-start justify-between">
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-wider text-[#5a6170]">
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-wider text-[#5a6170]">
|
||||
Linked Ticket
|
||||
</h4>
|
||||
{ticketUrl && (
|
||||
@@ -44,7 +44,7 @@ export function SessionTicketCard({ ticketId, ticketData, siteUrl }: SessionTick
|
||||
href={ticketUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-muted-foreground hover:text-primary transition-colors"
|
||||
className="text-[#848b9b] hover:text-[#22d3ee] transition-colors"
|
||||
title="Open in ConnectWise"
|
||||
>
|
||||
<ExternalLink size={12} />
|
||||
@@ -52,14 +52,14 @@ export function SessionTicketCard({ ticketId, ticketData, siteUrl }: SessionTick
|
||||
)}
|
||||
</div>
|
||||
|
||||
<p className="text-sm font-semibold text-foreground">
|
||||
<span className="text-primary">#{ticketId}</span>
|
||||
<p className="text-sm font-semibold text-[#e2e5eb]">
|
||||
<span className="text-[#22d3ee]">#{ticketId}</span>
|
||||
{ticket?.summary && (
|
||||
<span> — {ticket.summary}</span>
|
||||
)}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-muted-foreground">
|
||||
<div className="flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-[#848b9b]">
|
||||
{company?.name && (
|
||||
<span className="flex items-center gap-1">
|
||||
<Building2 size={10} />
|
||||
@@ -81,13 +81,13 @@ export function SessionTicketCard({ ticketId, ticketData, siteUrl }: SessionTick
|
||||
</div>
|
||||
|
||||
{configs && configs.length > 0 && (
|
||||
<div className="border-t border-border/50 pt-2 mt-2">
|
||||
<p className="font-label text-[0.5625rem] uppercase tracking-wider text-[#5a6170] mb-1">
|
||||
<div className="border-t border-[#1e2130]/50 pt-2 mt-2">
|
||||
<p className="font-sans text-xs text-[0.5625rem] uppercase tracking-wider text-[#5a6170] mb-1">
|
||||
Devices
|
||||
</p>
|
||||
<div className="space-y-0.5">
|
||||
{configs.slice(0, 3).map((cfg, i) => (
|
||||
<div key={i} className="flex items-center gap-1.5 text-xs text-muted-foreground">
|
||||
<div key={i} className="flex items-center gap-1.5 text-xs text-[#848b9b]">
|
||||
<Cpu size={10} />
|
||||
<span>{cfg.device_identifier}</span>
|
||||
{cfg.type && <span className="text-[#5a6170]">({cfg.type})</span>}
|
||||
|
||||
@@ -35,8 +35,8 @@ export function SimilarSessions({ sessionId }: SimilarSessionsProps) {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center gap-1.5 py-1">
|
||||
<Loader2 size={10} className="animate-spin text-muted-foreground" />
|
||||
<span className="text-[0.625rem] text-muted-foreground font-label">Loading similar sessions…</span>
|
||||
<Loader2 size={10} className="animate-spin text-[#848b9b]" />
|
||||
<span className="text-[0.625rem] text-[#848b9b] font-sans text-xs">Loading similar sessions…</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -47,42 +47,42 @@ export function SimilarSessions({ sessionId }: SimilarSessionsProps) {
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<h4 className="font-label text-[0.625rem] uppercase tracking-[0.1em] text-muted-foreground">
|
||||
<h4 className="font-sans text-xs text-[0.625rem] uppercase tracking-[0.1em] text-[#848b9b]">
|
||||
Similar Past Sessions
|
||||
</h4>
|
||||
{sessions.map((session) => (
|
||||
<Link
|
||||
key={session.id}
|
||||
to={`/pilot/${session.id}`}
|
||||
className="glass-card p-3 block hover:border-[rgba(255,255,255,0.12)] transition-all"
|
||||
className="card-interactive p-3 block hover:border-[rgba(255,255,255,0.12)] transition-all"
|
||||
>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<p className="text-xs text-foreground line-clamp-2">
|
||||
<p className="text-xs text-[#e2e5eb] line-clamp-2">
|
||||
{session.problem_summary || 'Untitled session'}
|
||||
</p>
|
||||
<span className="text-[0.625rem] font-label text-primary shrink-0">
|
||||
<span className="text-[0.625rem] font-sans text-xs text-[#22d3ee] shrink-0">
|
||||
{Math.round(session.similarity * 100)}%
|
||||
</span>
|
||||
</div>
|
||||
{session.resolution_summary && (
|
||||
<p className="text-[0.625rem] text-muted-foreground mt-1 line-clamp-1">
|
||||
<p className="text-[0.625rem] text-[#848b9b] mt-1 line-clamp-1">
|
||||
✓ {session.resolution_summary}
|
||||
</p>
|
||||
)}
|
||||
<div className="flex items-center gap-2 mt-1.5">
|
||||
{session.problem_domain && (
|
||||
<span className="text-[0.5rem] font-label uppercase tracking-wider text-muted-foreground/70">
|
||||
<span className="text-[0.5rem] font-sans text-xs uppercase tracking-wider text-[#848b9b]/70">
|
||||
{session.problem_domain}
|
||||
</span>
|
||||
)}
|
||||
<span
|
||||
className={cn(
|
||||
'text-[0.5rem] font-label uppercase',
|
||||
'text-[0.5rem] font-sans text-xs uppercase',
|
||||
session.status === 'resolved'
|
||||
? 'text-emerald-400'
|
||||
: session.status === 'escalated'
|
||||
? 'text-amber-400'
|
||||
: 'text-muted-foreground'
|
||||
: 'text-[#848b9b]'
|
||||
)}
|
||||
>
|
||||
{session.status}
|
||||
|
||||
Reference in New Issue
Block a user