/** * KBSuggest (#23) — SR 입력 중 실시간 KB 추천 * * query(SR 제목 입력값) → debounce 600ms → GET /api/kb/?q={query}&limit=3 * 최대 3개 KB 카드 표시, 탭 → onOpen(KB id) 콜백 (상세 화면 이동). */ import { useState, useEffect, useRef } from 'react' import { View, Text, Pressable, StyleSheet, ActivityIndicator } from 'react-native' import { COLORS, API_BASE } from '../constants/Config' import { authFetch } from '../utils/auth' interface KBItem { id: number title: string summary?: string category?: string } interface Props { query: string onOpen?: (id: number) => void } export function KBSuggest({ query, onOpen }: Props) { const [items, setItems] = useState([]) const [loading, setLoading] = useState(false) const timer = useRef | null>(null) useEffect(() => { if (timer.current) clearTimeout(timer.current) const q = query?.trim() ?? '' if (q.length < 2) { setItems([]) return } timer.current = setTimeout(async () => { setLoading(true) try { const res = await authFetch(`${API_BASE}/api/kb/?q=${encodeURIComponent(q)}&limit=3`) if (res.ok) { const d = await res.json() const list: KBItem[] = Array.isArray(d) ? d : d.items ?? d.results ?? [] setItems(list.slice(0, 3)) } else { setItems([]) } } catch { setItems([]) } finally { setLoading(false) } }, 600) return () => { if (timer.current) clearTimeout(timer.current) } }, [query]) if (!loading && items.length === 0) return null return ( 📚 관련 지식베이스 {loading ? ( ) : ( items.map(kb => ( onOpen?.(kb.id)}> {kb.title} {kb.summary ? ( {kb.summary} ) : null} {kb.category ? {kb.category} : null} )) )} ) } export default KBSuggest const S = StyleSheet.create({ wrap: { marginTop: 8 }, title: { fontSize: 12, fontWeight: '700', color: COLORS.muted, marginBottom: 6 }, card: { backgroundColor: COLORS.light, borderRadius: 10, padding: 11, marginBottom: 6 }, cardTitle: { fontSize: 13, fontWeight: '600', color: COLORS.blue }, cardSummary: { fontSize: 12, color: COLORS.text, marginTop: 3, lineHeight: 17 }, cardCat: { fontSize: 10, color: COLORS.muted, marginTop: 4 }, })