guardia-messenger/app/(tabs)/camera_ar.tsx

117 lines
6.0 KiB
TypeScript

import React, { useState } from 'react';
import { View, Text, ScrollView, TouchableOpacity, StyleSheet, Alert } from 'react-native';
import { ITSM_BASE } from '../../services/api';
interface AROverlay { label: string; value: string; color: string; x: number; y: number }
const SAMPLE_OVERLAYS: AROverlay[] = [
{ label: 'CPU', value: '42%', color: '#44bb44', x: 20, y: 80 },
{ label: 'RAM', value: '67%', color: '#ffbb00', x: 60, y: 40 },
{ label: 'DISK', value: '81%', color: '#ff8800', x: 70, y: 70 },
{ label: 'NET', value: '1.2GB/s', color: '#44bb44', x: 30, y: 50 },
{ label: 'TEMP', value: '52°C', color: '#44bb44', x: 50, y: 20 },
];
export default function CameraARScreen() {
const [scanning, setScanning] = useState(false);
const [overlays, setOverlays] = useState<AROverlay[]>([]);
const [detectedServer, setDetectedServer] = useState<string | null>(null);
const [serverInfo, setServerInfo] = useState<any>(null);
const startScan = async () => {
setScanning(true);
setTimeout(() => {
setOverlays(SAMPLE_OVERLAYS);
setDetectedServer('app-svr-01');
setScanning(false);
}, 1500);
};
const fetchServerDetail = async (name: string) => {
try {
const r = await fetch(`${ITSM_BASE}/api/cmdb/servers?search=${name}`);
if (r.ok) { const d = await r.json(); setServerInfo(d.servers?.[0]); }
} catch { setServerInfo({ hostname: name, os: 'CentOS 7', status: 'active', role: '애플리케이션 서버' }); }
};
const createSR = async () => {
const critical = overlays.filter(o => o.color === '#ff4444' || o.color === '#ff8800');
if (critical.length === 0) { Alert.alert('정상', '감지된 이상 없음'); return; }
try {
await fetch(`${ITSM_BASE}/api/tasks`, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ title: `[AR스캔] ${detectedServer} 리소스 이상`, description: critical.map(c => `${c.label}: ${c.value}`).join('\n'), priority: 'high' }),
});
Alert.alert('SR 등록', 'AR 스캔 기반 SR이 등록되었습니다.');
} catch { Alert.alert('오류', 'SR 등록 실패'); }
};
return (
<ScrollView style={s.container}>
<Text style={s.title}> AR </Text>
<Text style={s.sub}>/ </Text>
<View style={s.cameraView}>
<View style={s.cameraFrame}>
{!scanning && overlays.length === 0 && (
<Text style={s.cameraPlaceholder}>📷 {'\n'}( )</Text>
)}
{scanning && <Text style={s.scanningText}>🔍 ...</Text>}
{overlays.map((o, i) => (
<View key={i} style={[s.overlay, { left: `${o.x}%`, top: `${o.y}%`, borderColor: o.color }]}>
<Text style={s.overlayLabel}>{o.label}</Text>
<Text style={[s.overlayValue, { color: o.color }]}>{o.value}</Text>
</View>
))}
{detectedServer && <View style={s.detectedBadge}><Text style={s.detectedText}> {detectedServer}</Text></View>}
</View>
<TouchableOpacity style={s.scanBtn} onPress={startScan} disabled={scanning}>
<Text style={s.scanBtnText}>{scanning ? '스캔 중...' : '📡 AR 스캔 시작'}</Text>
</TouchableOpacity>
</View>
{overlays.length > 0 && (
<View style={s.metricsCard}>
<Text style={s.metricsTitle}> </Text>
<View style={s.metricsGrid}>
{overlays.map((o, i) => (
<View key={i} style={[s.metricItem, { borderColor: o.color }]}>
<Text style={s.metricLabel}>{o.label}</Text>
<Text style={[s.metricValue, { color: o.color }]}>{o.value}</Text>
</View>
))}
</View>
<TouchableOpacity style={s.srBtn} onPress={createSR}>
<Text style={s.srBtnText}>📋 SR </Text>
</TouchableOpacity>
</View>
)}
</ScrollView>
);
}
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 },
cameraView: { backgroundColor: '#1A1F2E', borderRadius: 12, overflow: 'hidden', marginBottom: 16, borderWidth: 1, borderColor: '#333' },
cameraFrame: { height: 260, position: 'relative', alignItems: 'center', justifyContent: 'center' },
cameraPlaceholder: { color: '#555', textAlign: 'center', fontSize: 14 },
scanningText: { color: '#00A0C8', fontSize: 16, fontWeight: '600' },
overlay: { position: 'absolute', backgroundColor: 'rgba(0,0,0,0.75)', borderWidth: 1, borderRadius: 6, padding: 6 },
overlayLabel: { color: '#fff', fontSize: 10, fontWeight: '600' },
overlayValue: { fontSize: 12, fontWeight: '700' },
detectedBadge: { position: 'absolute', bottom: 12, right: 12, backgroundColor: '#003366', paddingHorizontal: 10, paddingVertical: 4, borderRadius: 12, borderWidth: 1, borderColor: '#00A0C8' },
detectedText: { color: '#fff', fontSize: 12, fontWeight: '600' },
scanBtn: { backgroundColor: '#00A0C8', padding: 14, alignItems: 'center' },
scanBtnText: { color: '#fff', fontWeight: '700', fontSize: 15 },
metricsCard: { backgroundColor: '#1A1F2E', borderRadius: 12, padding: 16, borderWidth: 1, borderColor: '#333' },
metricsTitle: { color: '#fff', fontWeight: '700', fontSize: 16, marginBottom: 12 },
metricsGrid: { flexDirection: 'row', flexWrap: 'wrap', gap: 10, marginBottom: 14 },
metricItem: { backgroundColor: '#0A0E1A', borderWidth: 1, borderRadius: 10, padding: 10, minWidth: '28%', alignItems: 'center' },
metricLabel: { color: '#aaa', fontSize: 11, marginBottom: 4 },
metricValue: { fontSize: 16, fontWeight: '700' },
srBtn: { backgroundColor: '#003366', padding: 12, borderRadius: 10, alignItems: 'center', borderWidth: 1, borderColor: '#00A0C8' },
srBtnText: { color: '#fff', fontWeight: '700' },
});