89 lines
3.0 KiB
TypeScript
89 lines
3.0 KiB
TypeScript
import { useEffect, useState, useCallback } from 'react'
|
|
import { ScrollView, Text, Pressable, StyleSheet, View } from 'react-native'
|
|
import { COLORS, API_BASE } from '../constants/Config'
|
|
import { getToken } from '../utils/auth'
|
|
|
|
interface Props {
|
|
recentMessages: string[]
|
|
room?: string
|
|
onSelect: (cmd: string) => void
|
|
visible?: boolean
|
|
}
|
|
|
|
const DEFAULT_CMDS = ['/sr create', '/server status', '/dashboard', '/deploy', '/alert list']
|
|
|
|
export function SuggestedCommands({ recentMessages, room = 'general', onSelect, visible = true }: Props) {
|
|
const [suggestions, setSuggestions] = useState<string[]>(DEFAULT_CMDS)
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const fetchSuggestions = useCallback(async () => {
|
|
if (recentMessages.length === 0) return
|
|
setLoading(true)
|
|
try {
|
|
const token = await getToken()
|
|
const res = await fetch(`${API_BASE}/api/ux/next-commands`, {
|
|
method: 'POST',
|
|
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
recent_messages: recentMessages.slice(-5),
|
|
context: room,
|
|
}),
|
|
})
|
|
if (res.ok) {
|
|
const data = await res.json()
|
|
if (data.commands?.length) setSuggestions(data.commands)
|
|
}
|
|
} catch {
|
|
// 네트워크 오류 시 기본 명령 유지
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}, [recentMessages.length, room])
|
|
|
|
useEffect(() => {
|
|
const timer = setTimeout(fetchSuggestions, 600) // 디바운스
|
|
return () => clearTimeout(timer)
|
|
}, [fetchSuggestions])
|
|
|
|
async function handleSelect(cmd: string) {
|
|
onSelect(cmd)
|
|
// 명령 학습 API 호출
|
|
try {
|
|
const token = await getToken()
|
|
await fetch(`${API_BASE}/api/ux/learn`, {
|
|
method: 'POST',
|
|
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ command: cmd, room }),
|
|
})
|
|
} catch {}
|
|
}
|
|
|
|
if (!visible) return null
|
|
|
|
return (
|
|
<View style={S.container}>
|
|
<Text style={S.label}>추천 명령</Text>
|
|
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={S.scroll}>
|
|
{suggestions.map((cmd, i) => (
|
|
<Pressable key={i} onPress={() => handleSelect(cmd)} style={S.chip}>
|
|
<Text style={S.chipText}>{cmd}</Text>
|
|
</Pressable>
|
|
))}
|
|
</ScrollView>
|
|
</View>
|
|
)
|
|
}
|
|
|
|
const S = StyleSheet.create({
|
|
container: { paddingVertical: 6, paddingHorizontal: 12,
|
|
borderTopWidth: 1, borderTopColor: COLORS.border,
|
|
backgroundColor: '#f8fafc' },
|
|
label: { fontSize: 10, color: COLORS.muted, marginBottom: 4 },
|
|
scroll: { flexDirection: 'row' },
|
|
chip: { paddingHorizontal: 12, paddingVertical: 6, borderRadius: 16,
|
|
backgroundColor: '#e0e7ff', marginRight: 6,
|
|
borderWidth: 1, borderColor: '#c7d2fe' },
|
|
chipText: { fontSize: 12, color: COLORS.gnbBg, fontWeight: '600',
|
|
fontFamily: 'monospace' },
|
|
})
|