94 lines
2.8 KiB
TypeScript
94 lines
2.8 KiB
TypeScript
/**
|
|
* 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<KBItem[]>([])
|
|
const [loading, setLoading] = useState(false)
|
|
const timer = useRef<ReturnType<typeof setTimeout> | 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 (
|
|
<View style={S.wrap}>
|
|
<Text style={S.title}>📚 관련 지식베이스</Text>
|
|
{loading ? (
|
|
<ActivityIndicator color={COLORS.accent} style={{ marginVertical: 6 }} />
|
|
) : (
|
|
items.map(kb => (
|
|
<Pressable key={kb.id} style={S.card} onPress={() => onOpen?.(kb.id)}>
|
|
<Text style={S.cardTitle} numberOfLines={1}>
|
|
{kb.title}
|
|
</Text>
|
|
{kb.summary ? (
|
|
<Text style={S.cardSummary} numberOfLines={2}>
|
|
{kb.summary}
|
|
</Text>
|
|
) : null}
|
|
{kb.category ? <Text style={S.cardCat}>{kb.category}</Text> : null}
|
|
</Pressable>
|
|
))
|
|
)}
|
|
</View>
|
|
)
|
|
}
|
|
|
|
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 },
|
|
})
|