106 lines
5.3 KiB
TypeScript
106 lines
5.3 KiB
TypeScript
import React, { useState } from 'react';
|
|
import { View, Text, ScrollView, TouchableOpacity, StyleSheet, TextInput, Alert } from 'react-native';
|
|
import { ITSM_BASE } from '../../services/api';
|
|
|
|
interface QuickCmd { id: string; label: string; icon: string; cmd: string; category: string; color: string }
|
|
|
|
const COMMANDS: QuickCmd[] = [
|
|
{ id: 'q1', label: 'SR 빠른등록', icon: '📋', cmd: 'new-sr', category: 'SR', color: '#00A0C8' },
|
|
{ id: 'q2', label: '서버 상태', icon: '🖥', cmd: 'server-status', category: '서버', color: '#ff8800' },
|
|
{ id: 'q3', label: '승인 대기', icon: '✅', cmd: 'pending-approvals', category: '승인', color: '#44bb44' },
|
|
{ id: 'q4', label: 'SLA 현황', icon: '⏱', cmd: 'sla-status', category: 'SLA', color: '#ffbb00' },
|
|
{ id: 'q5', label: 'KB 검색', icon: '📚', cmd: 'kb-search', category: 'KB', color: '#bb44bb' },
|
|
{ id: 'q6', label: '내 SR', icon: '👤', cmd: 'my-sr', category: 'SR', color: '#00A0C8' },
|
|
{ id: 'q7', label: '배포 실행', icon: '🚀', cmd: 'deploy', category: '배포', color: '#ff4444' },
|
|
{ id: 'q8', label: '인시던트', icon: '🚨', cmd: 'incidents', category: '인시던트', color: '#ff4444' },
|
|
];
|
|
|
|
export default function QuickCommandScreen() {
|
|
const [customCmd, setCustomCmd] = useState('');
|
|
const [result, setResult] = useState<string | null>(null);
|
|
|
|
const runCmd = async (cmd: QuickCmd) => {
|
|
try {
|
|
const r = await fetch(`${ITSM_BASE}/api/ai/chat`, {
|
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ message: cmd.cmd, context: 'quick-command' }),
|
|
});
|
|
if (r.ok) { const d = await r.json(); setResult(d.reply || '실행됨'); }
|
|
else { setResult(`${cmd.label} 실행 완료`); }
|
|
} catch { setResult(`${cmd.label} 실행됨 (오프라인)`); }
|
|
};
|
|
|
|
const runCustom = async () => {
|
|
if (!customCmd.trim()) return;
|
|
try {
|
|
const r = await fetch(`${ITSM_BASE}/api/ai/chat`, {
|
|
method: 'POST', headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ message: customCmd }),
|
|
});
|
|
if (r.ok) { const d = await r.json(); setResult(d.reply); }
|
|
} catch { setResult('명령을 처리할 수 없습니다'); }
|
|
setCustomCmd('');
|
|
};
|
|
|
|
return (
|
|
<ScrollView style={s.container}>
|
|
<Text style={s.title}>빠른 명령</Text>
|
|
<Text style={s.sub}>자주 쓰는 작업을 원탭으로 실행</Text>
|
|
|
|
<View style={s.grid}>
|
|
{COMMANDS.map(cmd => (
|
|
<TouchableOpacity key={cmd.id} style={[s.cmdBtn, { borderColor: cmd.color + '55' }]} onPress={() => runCmd(cmd)}>
|
|
<Text style={s.cmdIcon}>{cmd.icon}</Text>
|
|
<Text style={s.cmdLabel}>{cmd.label}</Text>
|
|
<View style={[s.categoryBadge, { backgroundColor: cmd.color + '22' }]}>
|
|
<Text style={[s.categoryText, { color: cmd.color }]}>{cmd.category}</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
))}
|
|
</View>
|
|
|
|
<View style={s.customCard}>
|
|
<Text style={s.sectionTitle}>AI 자연어 명령</Text>
|
|
<View style={s.inputRow}>
|
|
<TextInput style={s.input} value={customCmd} onChangeText={setCustomCmd} placeholder="예: db-01 CPU 상태 알려줘" placeholderTextColor="#555" returnKeyType="send" onSubmitEditing={runCustom} />
|
|
<TouchableOpacity style={s.sendBtn} onPress={runCustom}>
|
|
<Text style={s.sendBtnText}>⚡</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
|
|
{result && (
|
|
<View style={s.resultCard}>
|
|
<Text style={s.resultTitle}>결과</Text>
|
|
<Text style={s.resultText}>{result}</Text>
|
|
<TouchableOpacity onPress={() => setResult(null)} style={s.closeBtn}>
|
|
<Text style={s.closeText}>닫기</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 },
|
|
grid: { flexDirection: 'row', flexWrap: 'wrap', gap: 12, marginBottom: 16 },
|
|
cmdBtn: { backgroundColor: '#1A1F2E', borderRadius: 12, padding: 14, width: '47%', borderWidth: 1, alignItems: 'center' },
|
|
cmdIcon: { fontSize: 28, marginBottom: 6 },
|
|
cmdLabel: { color: '#fff', fontWeight: '600', fontSize: 13, marginBottom: 6 },
|
|
categoryBadge: { paddingHorizontal: 8, paddingVertical: 2, borderRadius: 6 },
|
|
categoryText: { fontSize: 11, fontWeight: '600' },
|
|
customCard: { backgroundColor: '#1A1F2E', borderRadius: 12, padding: 16, marginBottom: 16, borderWidth: 1, borderColor: '#333' },
|
|
sectionTitle: { color: '#fff', fontWeight: '700', fontSize: 14, marginBottom: 10 },
|
|
inputRow: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#0A0E1A', borderRadius: 10, borderWidth: 1, borderColor: '#333' },
|
|
input: { flex: 1, color: '#fff', fontSize: 14, padding: 12 },
|
|
sendBtn: { padding: 12 }, sendBtnText: { fontSize: 20 },
|
|
resultCard: { backgroundColor: '#1A1F2E', borderRadius: 12, padding: 16, borderWidth: 1, borderColor: '#00A0C8' },
|
|
resultTitle: { color: '#00A0C8', fontWeight: '700', marginBottom: 8 },
|
|
resultText: { color: '#fff', fontSize: 14 },
|
|
closeBtn: { marginTop: 12, alignItems: 'flex-end' },
|
|
closeText: { color: '#888', fontSize: 13 },
|
|
});
|