import { useEffect, useState } from 'react' import { View, Text, ScrollView, StyleSheet, TouchableOpacity, ActivityIndicator, RefreshControl } from 'react-native' import { COLORS } from '../../constants/Config' import { apiClient } from '../../services/api' import { useAuth } from '../../hooks/useAuth' interface Weekly { stats: any; ai_insight: string; top_categories: any[] } interface Anomaly { anomalies: any[]; today_sr: number; avg_7d: number; open_sr: number } interface Predict { breach_probability_7d: number; current_rate: number; status: string; insight: string } const STATUS_COLOR: Record = { CRITICAL: '#ef4444', WARNING: '#f59e0b', NORMAL: '#22c55e', NO_DATA: '#94a3b8', } export default function InsightsScreen() { const { token } = useAuth() const [weekly, setWeekly] = useState(null) const [anomaly, setAnomaly] = useState(null) const [predict, setPredict] = useState(null) const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false) useEffect(() => { load() }, []) async function load() { setLoading(true) try { const [w, a, p] = await Promise.all([ apiClient.get('/api/insights/weekly', token), apiClient.get('/api/insights/anomalies', token), apiClient.get('/api/predict/sla-breach', token), ]) setWeekly(w); setAnomaly(a); setPredict(p) } catch { /* 오류 무시 */ } setLoading(false); setRefreshing(false) } function onRefresh() { setRefreshing(true); load() } if (loading) return ( ) return ( }> {/* 이상 감지 알림 */} {(anomaly?.anomalies?.length || 0) > 0 && ( ⚠️ 이상 감지 {anomaly!.anomalies.length}건 {anomaly!.anomalies.map((a: any, i: number) => ( {a.message} ))} )} {/* 통계 카드 */} {[ { label: '신규 SR', val: weekly?.stats?.total || 0, color: COLORS.primary }, { label: '완료율', val: `${weekly?.stats?.completion_rate || 0}%`, color: '#22c55e' }, { label: '미처리', val: weekly?.stats?.open || 0, color: '#ef4444' }, ].map(item => ( {item.val} {item.label} ))} {/* SLA 예측 */} {predict && ( 📉 SLA 위반 예측 (7일) {Math.round((predict.breach_probability_7d || 0) * 100)}% 현재 SLA: {predict.current_rate || 0}% {predict.insight ? {predict.insight} : null} )} {/* AI 주간 인사이트 */} {weekly?.ai_insight && ( 🤖 AI 주간 인사이트 {weekly.ai_insight} )} {/* 상위 카테고리 */} {(weekly?.top_categories?.length || 0) > 0 && ( 📊 SR 상위 카테고리 {weekly!.top_categories.map((c: any, i: number) => ( {c.category} {c.count}건 ))} )} ) } const s = StyleSheet.create({ scroll: { flex: 1, backgroundColor: '#f8fafc' }, center: { flex: 1, justifyContent: 'center', alignItems: 'center' }, alertCard: { margin: 12, padding: 14, backgroundColor: '#fef2f2', borderRadius: 12, borderLeftWidth: 4, borderLeftColor: '#ef4444' }, alertTitle: { fontWeight: '700', fontSize: 14, color: '#7f1d1d', marginBottom: 4 }, alertMsg: { fontSize: 12, color: '#991b1b', marginTop: 3 }, statsRow: { flexDirection: 'row', padding: 12, gap: 8 }, statCard: { flex: 1, backgroundColor: '#fff', borderRadius: 12, padding: 14, alignItems: 'center', borderTopWidth: 3, shadowColor: '#000', shadowOpacity: 0.05, shadowRadius: 4, elevation: 2 }, statVal: { fontSize: 24, fontWeight: '800' }, statLabel: { fontSize: 11, color: '#64748b', marginTop: 2 }, card: { margin: 12, marginTop: 0, backgroundColor: '#fff', borderRadius: 12, padding: 16, shadowColor: '#000', shadowOpacity: 0.05, shadowRadius: 4, elevation: 2 }, cardTitle: { fontSize: 14, fontWeight: '700', color: '#1e293b', marginBottom: 10 }, bigNum: { fontSize: 36, fontWeight: '800' }, subText: { fontSize: 12, color: '#64748b', marginTop: 2 }, insightText: { fontSize: 13, lineHeight: 20, color: '#374151', marginTop: 8 }, categoryRow: { flexDirection: 'row', alignItems: 'center', gap: 8, marginBottom: 8 }, categoryName: { fontSize: 12, width: 80, color: '#374151' }, barBg: { flex: 1, backgroundColor: '#f1f5f9', borderRadius: 3, height: 6 }, barFill: { height: 6, backgroundColor: COLORS.primary, borderRadius: 3 }, categoryCount:{ fontSize: 11, color: '#64748b', width: 30, textAlign: 'right' }, })