Replace hardcoded Tailwind color utilities with semantic CSS variable tokens across 31 files in the FlowPilot, Assistant Chat, and Script Builder feature communities — the areas graphify identified as design-system-free. - text-blue-400 → text-accent, bg-blue-500/10 → bg-accent-dim, border-blue-500/20 → border-accent/20 - text-amber-400 → text-warning, bg-amber-400/10 → bg-warning-dim, border-l-amber-500 → border-l-warning - text-rose-400/500 → text-danger, bg-rose-500/10 → bg-danger-dim - text-emerald-400 → text-success, bg-emerald-500/10 → bg-success-dim, border-l-emerald-500 → border-l-success - bg-white/[0.08] → bg-elevated (opacity hack → semantic surface token) - bg-gradient-to-r from-blue-500 to-blue-400 → bg-accent (no gradient surfaces) - bg-[#60a5fa] → bg-accent (hard-coded hex removed) Also adds graphify-out/ to .gitignore. Theme resilience: accent color has changed twice in 5 weeks. Semantic tokens mean the next change is a 1-line edit in index.css, not 110 grep-and-replace. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
87 lines
3.3 KiB
TypeScript
87 lines
3.3 KiB
TypeScript
import { useState } from 'react'
|
|
import { AlertTriangle, Loader2 } from 'lucide-react'
|
|
import { Modal } from '@/components/common/Modal'
|
|
import { RichTextInput } from '@/components/common/RichTextInput'
|
|
import type { EscalateSessionRequest } from '@/types/ai-session'
|
|
import type { FileUploadResponse } from '@/types/upload'
|
|
|
|
interface EscalateModalProps {
|
|
open: boolean
|
|
onClose: () => void
|
|
onEscalate: (data: EscalateSessionRequest) => Promise<unknown>
|
|
isProcessing: boolean
|
|
hasPsaTicket: boolean
|
|
sessionId?: string
|
|
}
|
|
|
|
export function EscalateModal({ open, onClose, onEscalate, isProcessing, hasPsaTicket, sessionId }: EscalateModalProps) {
|
|
const [reason, setReason] = useState('')
|
|
const [, setEscalateUploads] = useState<FileUploadResponse[]>([])
|
|
|
|
const handleSubmit = async () => {
|
|
if (!reason.trim() || reason.trim().length < 5) return
|
|
await onEscalate({ escalation_reason: reason.trim() })
|
|
setReason('')
|
|
onClose()
|
|
}
|
|
|
|
const handleClose = () => {
|
|
if (!isProcessing) {
|
|
setReason('')
|
|
onClose()
|
|
}
|
|
}
|
|
|
|
return (
|
|
<Modal isOpen={open} onClose={handleClose} title="Escalate Session" size="sm">
|
|
<div className="space-y-4">
|
|
<div className="flex items-start gap-3 rounded-xl border border-warning/20 bg-warning/5 p-3">
|
|
<AlertTriangle size={16} className="text-warning shrink-0 mt-0.5" />
|
|
<p className="text-sm text-warning">
|
|
This will mark the session as requesting escalation. Team members will see it in their escalation queue and can pick it up with full context.
|
|
</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="mb-1.5 block text-sm font-medium text-foreground">
|
|
Why are you escalating?
|
|
</label>
|
|
<RichTextInput
|
|
value={reason}
|
|
onChange={setReason}
|
|
onFilesChange={setEscalateUploads}
|
|
sessionId={sessionId}
|
|
placeholder="e.g. I've exhausted all networking diagnostics and suspect this is a firewall policy issue that requires senior admin access..."
|
|
rows={4}
|
|
/>
|
|
<p className="mt-1 text-[0.625rem] text-text-muted">
|
|
Minimum 5 characters. This will be shown to the engineer who picks up.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex flex-col-reverse gap-2 sm:flex-row">
|
|
<button
|
|
onClick={handleClose}
|
|
disabled={isProcessing}
|
|
className="flex-1 rounded-lg bg-[rgba(255,255,255,0.04)] border border-[rgba(255,255,255,0.06)] px-4 py-2.5 min-h-[44px] text-sm font-medium text-foreground hover:border-[rgba(255,255,255,0.12)] transition-colors disabled:opacity-50"
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
onClick={handleSubmit}
|
|
disabled={!reason.trim() || reason.trim().length < 5 || isProcessing}
|
|
className="flex-1 flex items-center justify-center gap-2 rounded-lg bg-warning px-4 py-2.5 min-h-[44px] text-sm font-semibold text-white hover:bg-warning active:scale-[0.98] disabled:opacity-40 transition-all"
|
|
>
|
|
{isProcessing ? (
|
|
<Loader2 size={14} className="animate-spin" />
|
|
) : (
|
|
<AlertTriangle size={14} />
|
|
)}
|
|
{hasPsaTicket ? 'Escalate & Update Ticket' : 'Escalate'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</Modal>
|
|
)
|
|
}
|