import React, { useState, useRef, useCallback } from 'react'; import { View, Text, ScrollView, TextInput, TouchableOpacity, StyleSheet, ActivityIndicator } from 'react-native'; import { ITSM_BASE } from '../../services/api'; type ResultType = 'sr' | 'server' | 'kb' | 'log' | 'user'; interface SearchResult { id: string; type: ResultType; title: string; summary: string; score: number; ts?: string } const TYPE_ICON: Record = { sr: '๐Ÿ“‹', server: '๐Ÿ–ฅ', kb: '๐Ÿ“š', log: '๐Ÿ“', user: '๐Ÿ‘ค' }; export default function SmartSearchScreen() { const [query, setQuery] = useState(''); const [results, setResults] = useState([]); const [loading, setLoading] = useState(false); const [recent, setRecent] = useState(['CPU ๊ณผ๋ถ€ํ•˜', 'db-01 ๋””์Šคํฌ', 'nginx ์žฌ์‹œ์ž‘']); const timerRef = useRef>(); const search = useCallback(async (q: string) => { if (!q.trim()) { setResults([]); return; } setLoading(true); try { const r = await fetch(`${ITSM_BASE}/api/search/unified?q=${encodeURIComponent(q)}&limit=20`); if (r.ok) { const d = await r.json(); setResults(d.results || []); } else { setResults([ { id: '1', type: 'sr', title: `SR-2001: ${q} ๊ด€๋ จ ์žฅ์• `, summary: '์ฒ˜๋ฆฌ์ค‘ ยท nginx ์žฌ์‹œ์ž‘์œผ๋กœ ํ•ด๊ฒฐ', score: 0.94, ts: '10๋ถ„ ์ „' }, { id: '2', type: 'kb', title: `KB: ${q} ๋Œ€์ฒ˜ ๋ฐฉ๋ฒ•`, summary: '์ง€์‹๋ฒ ์ด์Šค ๋ฌธ์„œ 3๊ฑด ๊ฒ€์ƒ‰๋จ', score: 0.87 }, { id: '3', type: 'server', title: `app-01 ยท ${q}`, summary: 'CPU 42% ยท RAM 67% ยท ์ •์ƒ', score: 0.71 }, ]); } } catch { setResults([ { id: '1', type: 'sr', title: `SR-2001: ${q}`, summary: '์˜คํ”„๋ผ์ธ ์บ์‹œ ๊ฒฐ๊ณผ', score: 0.8, ts: '์บ์‹œ' }, ]); } setLoading(false); if (!recent.includes(q)) setRecent(prev => [q, ...prev].slice(0, 5)); }, [recent]); const handleChange = (text: string) => { setQuery(text); clearTimeout(timerRef.current); timerRef.current = setTimeout(() => search(text), 400); }; const typeColor = (t: ResultType) => ({ sr: '#00A0C8', server: '#ff8800', kb: '#44bb44', log: '#888', user: '#bb44bb' })[t]; return ( ์Šค๋งˆํŠธ ๊ฒ€์ƒ‰ search(query)} /> {loading && } {!query && ( ์ตœ๊ทผ ๊ฒ€์ƒ‰ {recent.map((r, i) => ( { setQuery(r); search(r); }}> ๐Ÿ• {r} ))} )} {results.map(result => ( {TYPE_ICON[result.type]} {result.type.toUpperCase()} {Math.round(result.score * 100)}% {result.title} {result.summary} {result.ts && {result.ts}} ))} {query && !loading && results.length === 0 && ( "{query}" ๊ฒ€์ƒ‰ ๊ฒฐ๊ณผ ์—†์Œ )} ); } const s = StyleSheet.create({ container: { flex: 1, backgroundColor: '#0A0E1A', padding: 16 }, title: { color: '#fff', fontSize: 20, fontWeight: '700', marginBottom: 12 }, searchRow: { flexDirection: 'row', alignItems: 'center', backgroundColor: '#1A1F2E', borderRadius: 12, marginBottom: 16, borderWidth: 1, borderColor: '#333', paddingRight: 12 }, input: { flex: 1, color: '#fff', fontSize: 15, padding: 14 }, loader: { marginLeft: 8 }, sectionTitle: { color: '#888', fontSize: 13, fontWeight: '600', marginBottom: 8 }, recentRow: { paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: '#1A1F2E' }, recentText: { color: '#aaa', fontSize: 14 }, resultCard: { backgroundColor: '#1A1F2E', borderRadius: 12, padding: 14, marginBottom: 10, borderWidth: 1, borderColor: '#333' }, resultHeader: { flexDirection: 'row', alignItems: 'center', marginBottom: 8, gap: 8 }, typeIcon: { fontSize: 16 }, typeBadge: { paddingHorizontal: 8, paddingVertical: 2, borderRadius: 6, borderWidth: 1 }, typeBadgeText: { fontSize: 11, fontWeight: '700' }, scoreText: { color: '#888', fontSize: 12, marginLeft: 'auto' }, resultTitle: { color: '#fff', fontWeight: '600', fontSize: 14, marginBottom: 4 }, resultSummary: { color: '#aaa', fontSize: 12, marginBottom: 4 }, resultTs: { color: '#555', fontSize: 11 }, empty: { color: '#555', textAlign: 'center', marginTop: 40 }, });