guardia-messenger/components/IncidentTimeline.tsx

83 lines
2.7 KiB
TypeScript

import { View, Text, FlatList, StyleSheet } from 'react-native'
import { COLORS } from '../constants/Config'
export interface TimelineEvent {
timestamp: string
actor: string
action: string
detail?: string
}
interface Props {
events: TimelineEvent[]
}
const ACTION_COLOR: Record<string, string> = {
created: COLORS.accent,
assigned: COLORS.blue,
status: '#4f6ef7',
escalated: COLORS.danger,
comment: COLORS.muted,
resolved: COLORS.success,
closed: COLORS.success,
}
/**
* 기능 #6 — 인시던트 타임라인
* 수직 타임라인 UI (점 + 연결선 + 이벤트 카드)
*/
export default function IncidentTimeline({ events }: Props) {
if (!events || events.length === 0) {
return <Text style={s.empty}> .</Text>
}
return (
<FlatList
data={events}
scrollEnabled={false}
keyExtractor={(_, i) => String(i)}
renderItem={({ item, index }) => {
const color = ACTION_COLOR[item.action?.toLowerCase()] ?? COLORS.accent
const isLast = index === events.length - 1
return (
<View style={s.row}>
<View style={s.gutter}>
<View style={[s.dot, { backgroundColor: color }]} />
{!isLast && <View style={s.line} />}
</View>
<View style={s.content}>
<View style={s.head}>
<Text style={s.action}>{item.action}</Text>
<Text style={s.time}>{formatTime(item.timestamp)}</Text>
</View>
<Text style={s.actor}>{item.actor}</Text>
{!!item.detail && <Text style={s.detail}>{item.detail}</Text>}
</View>
</View>
)
}}
/>
)
}
function formatTime(ts: string): string {
try {
const d = new Date(ts)
return `${d.getMonth() + 1}/${d.getDate()} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`
} catch { return ts }
}
const s = StyleSheet.create({
empty: { textAlign: 'center', color: COLORS.muted, paddingVertical: 24, fontSize: 13 },
row: { flexDirection: 'row' },
gutter: { width: 24, alignItems: 'center' },
dot: { width: 12, height: 12, borderRadius: 6, marginTop: 4 },
line: { flex: 1, width: 2, backgroundColor: COLORS.border, marginTop: 2 },
content: { flex: 1, paddingBottom: 18, paddingLeft: 8 },
head: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' },
action: { fontSize: 13, fontWeight: '700', color: COLORS.text, textTransform: 'capitalize' },
time: { fontSize: 11, color: COLORS.muted },
actor: { fontSize: 12, color: COLORS.blue, marginTop: 2 },
detail: { fontSize: 12, color: COLORS.muted, marginTop: 4, lineHeight: 17 },
})