import React from 'react'; import { Link, useParams } from 'react-router-dom'; import { useApi } from '../hooks/useApi'; import { getGuardrailStatus, type GuardrailRuleData, type GuardrailTriggerData, } from '../api/client'; // ─── Helpers ──────────────────────────────────────────────────── function formatTimestamp(ts: string): string { try { return new Date(ts).toLocaleString(); } catch { return ts; } } function conditionLabel(type: string): string { const map: Record = { error_rate_threshold: 'Error Rate Threshold', cost_limit: 'Cost Limit', health_score_threshold: 'Health Score Threshold', custom_metric: 'Custom Metric', }; return map[type] ?? type; } function actionLabel(type: string): string { const map: Record = { pause_agent: '⏸ Pause Agent', notify_webhook: '🔔 Notify Webhook', downgrade_model: '⬇ Downgrade Model', agentgate_policy: '🚪 AgentGate Policy', }; return map[type] ?? type; } function renderConfig(config: Record): React.ReactNode { const entries = Object.entries(config); if (entries.length === 0) return ; return (
{entries.map(([k, v]) => ( {k} {typeof v === 'object' ? JSON.stringify(v) : String(v)} ))}
); } // ─── Main Page ────────────────────────────────────────────────── export default function GuardrailDetail() { const { id } = useParams<{ id: string }>(); if (!id) return

No guardrail ID specified

; const query = useApi( () => getGuardrailStatus(id!), [id], ); const rule = query.data?.rule; const state = query.data?.state; const triggers = query.data?.recentTriggers ?? []; if (query.loading) return
Loading...
; if (query.error) return
Error: {query.error}
; if (!rule) return
Rule not found.
; return (
{/* Header */}
← Back to Guardrails

🛡️ {rule.name} {rule.dryRun && [DRY RUN]} {!rule.enabled && [DISABLED]}

✏️ Edit
{/* Rule Configuration */}

Rule Configuration

Name
{rule.name}
Agent
{rule.agentId ?? All Agents}
Enabled
{rule.enabled ? '✅ Yes' : '❌ No'}
Dry Run
{rule.dryRun ? '🔸 Yes' : 'No'}
Cooldown
{rule.cooldownMinutes} minutes
Created
{formatTimestamp(rule.createdAt)}
{/* Condition */}

Condition — {conditionLabel(rule.conditionType)}

{renderConfig(rule.conditionConfig)}
{/* Action */}

Action — {actionLabel(rule.actionType)}

{renderConfig(rule.actionConfig)}
{/* State */}

Runtime State

{state ? (
Trigger Count
{state.triggerCount}
Last Triggered
{state.lastTriggeredAt ? formatTimestamp(state.lastTriggeredAt) : '—'}
Current Value
{state.currentValue !== undefined ? state.currentValue : '—'}
) : (

No state data — rule has not been evaluated yet.

)}
{/* Trigger History */}

Trigger History (Recent)

{triggers.length === 0 ? (

No triggers recorded yet.

) : ( {triggers.map((t) => ( ))}
Timestamp Value Threshold Action Executed Result
{formatTimestamp(t.triggeredAt)} {t.conditionValue} {t.conditionThreshold} {t.actionExecuted ? ✓ Yes : Dry Run} {t.actionResult ?? '—'}
)}
); } // ─── Styles ───────────────────────────────────────────────────── const btnStyle: React.CSSProperties = { padding: '8px 16px', background: '#3b82f6', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer', fontSize: '14px', textDecoration: 'none', display: 'inline-block', }; const sectionStyle: React.CSSProperties = { padding: '16px', border: '1px solid #e2e8f0', borderRadius: '8px', marginBottom: '16px', background: 'white', }; const sectionTitleStyle: React.CSSProperties = { margin: '0 0 12px', fontSize: '16px', color: '#334155' }; const labelStyle: React.CSSProperties = { fontSize: '12px', color: '#64748b', fontWeight: 600, marginBottom: '4px', textTransform: 'uppercase' as const }; const thStyle: React.CSSProperties = { padding: '8px 12px', fontSize: '12px', color: '#64748b', fontWeight: 600 }; const tdStyle: React.CSSProperties = { padding: '8px 12px', fontSize: '13px' }; export { GuardrailDetail };