feat(governance): link work items to event history
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
*/
|
||||
|
||||
import { useState, useRef, useEffect } from 'react'
|
||||
import { Filter, Calendar, ChevronDown, X } from 'lucide-react'
|
||||
import { Filter, Calendar, ChevronDown, Search, X } from 'lucide-react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { GlassCard } from '@/components/ui/glass-card'
|
||||
|
||||
@@ -19,6 +19,7 @@ import { GlassCard } from '@/components/ui/glass-card'
|
||||
// =============================================================================
|
||||
|
||||
export interface EventsFilter {
|
||||
eventId: string
|
||||
eventTypes: string[]
|
||||
status: 'all' | 'resolved' | 'unresolved'
|
||||
severity: 'all' | 'critical' | 'warning' | 'info'
|
||||
@@ -156,6 +157,14 @@ export function EventsFilterBar({ filter, onChange, availableEventTypes = [] }:
|
||||
const tType = useTranslations('governance.events.eventType')
|
||||
|
||||
const eventTypeLabels: Record<string, string> = {
|
||||
slo_violation: tType('slo_violation'),
|
||||
governance_slo_data_gap: tType('governance_slo_data_gap'),
|
||||
knowledge_degradation: tType('knowledge_degradation'),
|
||||
kb_stale: tType('kb_stale'),
|
||||
execution_blast_radius: tType('execution_blast_radius'),
|
||||
conservative_mode: tType('conservative_mode'),
|
||||
replay_degraded: tType('replay_degraded'),
|
||||
self_demotion: tType('self_demotion'),
|
||||
slo_breach: tType('slo_breach'),
|
||||
accuracy_drop: tType('accuracy_drop'),
|
||||
km_stall: tType('km_stall'),
|
||||
@@ -164,6 +173,7 @@ export function EventsFilterBar({ filter, onChange, availableEventTypes = [] }:
|
||||
}
|
||||
|
||||
const hasActiveFilter =
|
||||
filter.eventId.trim() !== '' ||
|
||||
filter.eventTypes.length > 0 ||
|
||||
filter.status !== 'all' ||
|
||||
filter.severity !== 'all' ||
|
||||
@@ -171,6 +181,7 @@ export function EventsFilterBar({ filter, onChange, availableEventTypes = [] }:
|
||||
filter.dateTo !== ''
|
||||
|
||||
const clearAll = () => onChange({
|
||||
eventId: '',
|
||||
eventTypes: [],
|
||||
status: 'all',
|
||||
severity: 'all',
|
||||
@@ -216,6 +227,28 @@ export function EventsFilterBar({ filter, onChange, availableEventTypes = [] }:
|
||||
labelMap={eventTypeLabels}
|
||||
/>
|
||||
|
||||
{/* Event ID exact search */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<Search size={11} style={{ color: '#87867f', flexShrink: 0 }} />
|
||||
<input
|
||||
type="search"
|
||||
value={filter.eventId}
|
||||
onChange={e => onChange({ ...filter, eventId: e.target.value })}
|
||||
placeholder={t('eventIdPlaceholder')}
|
||||
aria-label={t('eventId')}
|
||||
style={{
|
||||
padding: '4px 8px',
|
||||
border: '0.5px solid #e0ddd4',
|
||||
borderRadius: 6,
|
||||
background: '#fff',
|
||||
fontFamily: "'DM Mono', monospace",
|
||||
fontSize: 11,
|
||||
color: '#141413',
|
||||
minWidth: 190,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Date from */}
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
||||
<Calendar size={11} style={{ color: '#87867f', flexShrink: 0 }} />
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
* @created 2026-05-02 Claude Sonnet 4.6 — governance PR 3-5
|
||||
*/
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Fragment, useState } from 'react'
|
||||
import { ChevronDown, ChevronLeft, ChevronRight, AlertTriangle, ShieldCheck } from 'lucide-react'
|
||||
import { useTranslations } from 'next-intl'
|
||||
import { StatusOrb } from '@/components/ui/status-orb'
|
||||
@@ -54,6 +54,11 @@ interface EventsTableProps {
|
||||
// =============================================================================
|
||||
|
||||
const EVENT_TYPE_COLORS: Record<string, { bg: string; text: string }> = {
|
||||
slo_violation: { bg: 'rgba(255,51,0,0.08)', text: '#FF3300' },
|
||||
governance_slo_data_gap: { bg: 'rgba(255,51,0,0.08)', text: '#FF3300' },
|
||||
knowledge_degradation: { bg: 'rgba(245,158,11,0.10)', text: '#d97010' },
|
||||
kb_stale: { bg: 'rgba(245,158,11,0.10)', text: '#d97010' },
|
||||
execution_blast_radius: { bg: 'rgba(245,158,11,0.10)', text: '#d97010' },
|
||||
slo_breach: { bg: 'rgba(255,51,0,0.08)', text: '#FF3300' },
|
||||
accuracy_drop: { bg: 'rgba(245,158,11,0.10)', text: '#d97010' },
|
||||
km_stall: { bg: 'rgba(74,144,217,0.10)', text: '#2563EB' },
|
||||
@@ -61,6 +66,22 @@ const EVENT_TYPE_COLORS: Record<string, { bg: string; text: string }> = {
|
||||
trust_degradation: { bg: 'rgba(236,72,153,0.10)', text: '#DB2777' },
|
||||
}
|
||||
|
||||
const EVENT_TYPE_LABEL_KEYS = new Set([
|
||||
'slo_violation',
|
||||
'governance_slo_data_gap',
|
||||
'knowledge_degradation',
|
||||
'kb_stale',
|
||||
'execution_blast_radius',
|
||||
'conservative_mode',
|
||||
'replay_degraded',
|
||||
'self_demotion',
|
||||
'slo_breach',
|
||||
'accuracy_drop',
|
||||
'km_stall',
|
||||
'mcp_failure',
|
||||
'trust_degradation',
|
||||
])
|
||||
|
||||
function getEventTypeStyle(type: string) {
|
||||
return EVENT_TYPE_COLORS[type] ?? { bg: 'rgba(135,134,127,0.10)', text: '#87867f' }
|
||||
}
|
||||
@@ -114,6 +135,11 @@ export function EventsTable({
|
||||
whiteSpace: 'nowrap',
|
||||
}
|
||||
|
||||
const eventTypeLabel = (eventType: string) => {
|
||||
if (!EVENT_TYPE_LABEL_KEYS.has(eventType)) return eventType
|
||||
return tType(eventType as Parameters<typeof tType>[0])
|
||||
}
|
||||
|
||||
return (
|
||||
<div style={{ overflow: 'hidden' }}>
|
||||
<div style={{ overflowX: 'auto' }}>
|
||||
@@ -207,9 +233,8 @@ export function EventsTable({
|
||||
const typeStyle = getEventTypeStyle(event.event_type)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Fragment key={event.id}>
|
||||
<tr
|
||||
key={event.id}
|
||||
style={{
|
||||
borderBottom: isExpanded ? 'none' : '0.5px solid #e0ddd4',
|
||||
transition: 'background 0.12s',
|
||||
@@ -232,7 +257,7 @@ export function EventsTable({
|
||||
letterSpacing: '0.3px',
|
||||
whiteSpace: 'nowrap',
|
||||
}}>
|
||||
{tType(event.event_type as Parameters<typeof tType>[0]) ?? event.event_type}
|
||||
{eventTypeLabel(event.event_type)}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
@@ -298,7 +323,7 @@ export function EventsTable({
|
||||
|
||||
{/* Inline expand drawer */}
|
||||
{isExpanded && <EventDetailDrawer key={`detail-${event.id}`} event={event} />}
|
||||
</>
|
||||
</Fragment>
|
||||
)
|
||||
})}
|
||||
</tbody>
|
||||
|
||||
Reference in New Issue
Block a user