import React, { useState, useEffect } from 'react'; import { View, Text, ScrollView, TouchableOpacity, StyleSheet, Switch } from 'react-native'; import { ITSM_BASE } from '../../services/api'; interface PredictiveAlert { id: string; type: string; title: string; detail: string; probability: number; eta_min: number; severity: string; auto_action?: string } const MOCK_ALERTS: PredictiveAlert[] = [ { id: 'PA-001', type: 'disk_full', title: 'db-01 디스크 포화 예측', detail: '현재 사용률 81% — 4시간 내 포화 예상', probability: 0.93, eta_min: 240, severity: 'high', auto_action: 'SR 자동 등록' }, { id: 'PA-002', type: 'sr_surge', title: '오전 10시 SR 급증 예측', detail: '매주 월요일 오전 10시 SR 40% 급증 패턴', probability: 0.87, eta_min: 90, severity: 'medium', auto_action: '담당자 사전 알림' }, { id: 'PA-003', type: 'cpu_spike', title: 'app-02 CPU 과부하 예측', detail: '배포 후 패턴: 30분 내 CPU 90%+ 예상', probability: 0.72, eta_min: 30, severity: 'medium' }, ]; export default function PredictiveAlertScreen() { const [alerts, setAlerts] = useState(MOCK_ALERTS); const [autoAction, setAutoAction] = useState(true); const [smartFilter, setSmartFilter] = useState(true); const [loading, setLoading] = useState(false); const fetchPredictions = async () => { setLoading(true); try { const r = await fetch(`${ITSM_BASE}/api/failure-prevention/predictions`); if (r.ok) { const d = await r.json(); if (d.predictions?.length) setAlerts(d.predictions); } } catch {} setLoading(false); }; useEffect(() => { fetchPredictions(); }, []); const severityColor = (s: string) => ({ high: '#ff8800', medium: '#ffbb00', critical: '#ff4444', low: '#44bb44' })[s] || '#888'; const applyAction = async (alert: PredictiveAlert) => { try { await fetch(`${ITSM_BASE}/api/tasks`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ title: `[예측알림] ${alert.title}`, description: alert.detail, priority: alert.severity }), }); setAlerts(prev => prev.filter(a => a.id !== alert.id)); } catch {} }; return ( 예측 알림 AI가 장애를 미리 감지하고 알려드립니다 자동 조치 스마트 필터 (낮은 확률 제외) {alerts.length}예측 알림 {alerts.filter(a => a.severity === 'high' || a.severity === 'critical').length}높은 위험 {alerts.filter(a => a.auto_action).length}자동 조치 {(smartFilter ? alerts.filter(a => a.probability >= 0.7) : alerts).map(alert => ( {Math.round(alert.probability * 100)}% ⏱ {alert.eta_min}분 내 {alert.title} {alert.detail} {alert.auto_action && ( applyAction(alert)}> ⚡ {alert.auto_action} )} ))} ); } 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 }, settingsCard: { backgroundColor: '#1A1F2E', borderRadius: 12, padding: 14, marginBottom: 16, borderWidth: 1, borderColor: '#333' }, settingRow: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', paddingVertical: 8 }, settingLabel: { color: '#fff', fontSize: 14 }, summaryRow: { flexDirection: 'row', justifyContent: 'space-around', backgroundColor: '#1A1F2E', borderRadius: 12, padding: 14, marginBottom: 16, borderWidth: 1, borderColor: '#333' }, summaryItem: { alignItems: 'center' }, summaryVal: { color: '#00A0C8', fontSize: 22, fontWeight: '700' }, summaryLbl: { color: '#888', fontSize: 11, marginTop: 2 }, alertCard: { backgroundColor: '#1A1F2E', borderRadius: 12, padding: 14, marginBottom: 12, borderLeftWidth: 4, borderWidth: 1, borderColor: '#333' }, alertHeader: { flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', marginBottom: 8 }, badge: { paddingHorizontal: 10, paddingVertical: 3, borderRadius: 6 }, badgeText: { color: '#fff', fontWeight: '700', fontSize: 12 }, etaText: { color: '#888', fontSize: 12 }, alertTitle: { color: '#fff', fontWeight: '700', fontSize: 15, marginBottom: 6 }, alertDetail: { color: '#aaa', fontSize: 13, marginBottom: 10 }, actionBtn: { backgroundColor: '#003366', padding: 10, borderRadius: 8, borderWidth: 1, borderColor: '#00A0C8' }, actionBtnText: { color: '#fff', fontWeight: '600', fontSize: 13 }, });