import React, { useState, useEffect } from 'react'; import { View, Text, ScrollView, TouchableOpacity, StyleSheet, Switch, ActivityIndicator } from 'react-native'; import { ITSM_BASE } from '../../services/api'; interface HealingEvent { id: string; trigger: string; action: string; target: string; status: string; duration_ms: number; ts: string } const MOCK_EVENTS: HealingEvent[] = [ { id: 'HE-001', trigger: 'CPU > 90%', action: 'nginx 재시작', target: 'app-01', status: 'success', duration_ms: 1240, ts: new Date(Date.now() - 300000).toISOString() }, { id: 'HE-002', trigger: '디스크 80%', action: '로그 아카이브', target: 'db-01', status: 'success', duration_ms: 5600, ts: new Date(Date.now() - 900000).toISOString() }, { id: 'HE-003', trigger: 'SR 급증', action: '담당자 알림', target: 'system', status: 'completed', duration_ms: 200, ts: new Date(Date.now() - 1800000).toISOString() }, ]; export default function SelfHealingScreen() { const [autoHeal, setAutoHeal] = useState(true); const [requireApproval, setRequireApproval] = useState(false); const [events, setEvents] = useState(MOCK_EVENTS); const [stats, setStats] = useState({ healed: 14, prevented: 6, success_rate: 93.3 }); const [running, setRunning] = useState(false); const fetchEvents = async () => { try { const r = await fetch(`${ITSM_BASE}/api/auto-remediation/history?limit=20`); if (r.ok) { const d = await r.json(); if (d.items?.length) setEvents(d.items); } } catch {} }; useEffect(() => { fetchEvents(); }, []); const triggerManual = async () => { setRunning(true); try { const r = await fetch(`${ITSM_BASE}/api/auto-remediation/trigger`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ mode: 'manual', scope: 'all' }), }); if (r.ok) { await fetchEvents(); } } catch {} setRunning(false); }; const statusColor = (s: string) => ({ success: '#44bb44', completed: '#44bb44', failed: '#ff4444', running: '#00A0C8', pending: '#ffbb00' })[s] || '#888'; return ( 자가 치유 (Self-Healing) GUARDiA가 스스로 장애를 감지하고 복구합니다 {stats.healed}자동 복구 {stats.prevented}사전 예방 {stats.success_rate}%성공률 자동 치유 활성화 조치 전 승인 필요 {running ? : ⚡ 수동 진단 실행} 자가 치유 이력 {events.map(ev => ( {ev.status} {ev.duration_ms}ms 트리거: {ev.trigger} 조치: {ev.action} → {ev.target} {new Date(ev.ts).toLocaleString()} ))} ); } const s = StyleSheet.create({ container: { flex: 1, backgroundColor: '#0A0E1A', padding: 16 }, title: { color: '#fff', fontSize: 20, fontWeight: '700', marginBottom: 4 }, sub: { color: '#888', fontSize: 13, marginBottom: 16 }, statsRow: { flexDirection: 'row', justifyContent: 'space-around', backgroundColor: '#1A1F2E', borderRadius: 12, padding: 16, marginBottom: 16, borderWidth: 1, borderColor: '#333' }, statBox: { alignItems: 'center' }, statVal: { color: '#00A0C8', fontSize: 24, fontWeight: '700' }, statLbl: { color: '#888', fontSize: 11, marginTop: 2 }, card: { backgroundColor: '#1A1F2E', borderRadius: 12, padding: 16, marginBottom: 16, borderWidth: 1, borderColor: '#333' }, row: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 10, borderBottomWidth: 1, borderBottomColor: '#222' }, label: { color: '#fff', fontSize: 15 }, manualBtn: { backgroundColor: '#003366', padding: 12, borderRadius: 10, alignItems: 'center', marginTop: 12, borderWidth: 1, borderColor: '#00A0C8' }, manualBtnText: { color: '#fff', fontWeight: '700' }, sectionTitle: { color: '#fff', fontSize: 16, fontWeight: '700', marginBottom: 12 }, eventCard: { backgroundColor: '#1A1F2E', borderRadius: 12, padding: 14, marginBottom: 10, borderWidth: 1, borderColor: '#333' }, eventHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 8 }, statusDot: { width: 8, height: 8, borderRadius: 4, marginRight: 6 }, statusText: { fontWeight: '700', fontSize: 12, flex: 1 }, durationText: { color: '#888', fontSize: 11 }, trigger: { color: '#aaa', fontSize: 13, marginBottom: 4 }, action: { color: '#fff', fontSize: 13, fontWeight: '600', marginBottom: 4 }, ts: { color: '#555', fontSize: 11 }, });