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