diff --git a/frontend/src/api/aiChat.ts b/frontend/src/api/aiChat.ts new file mode 100644 index 00000000..1722b272 --- /dev/null +++ b/frontend/src/api/aiChat.ts @@ -0,0 +1,44 @@ +import { apiClient } from './client' +import type { + AIChatStartResponse, + AIChatMessageResponse, + AIChatSessionResponse, + AIChatGenerateResponse, + AIChatImportResponse, +} from '@/types' + +export const aiChatApi = { + startSession: async (flowType: 'troubleshooting' | 'procedural'): Promise => { + const { data } = await apiClient.post('/ai/chat/sessions', { flow_type: flowType }) + return data + }, + + sendMessage: async (sessionId: string, content: string): Promise => { + const { data } = await apiClient.post(`/ai/chat/sessions/${sessionId}/messages`, { content }) + return data + }, + + getSession: async (sessionId: string): Promise => { + const { data } = await apiClient.get(`/ai/chat/sessions/${sessionId}`) + return data + }, + + generateTree: async (sessionId: string): Promise => { + const { data } = await apiClient.post(`/ai/chat/sessions/${sessionId}/generate`) + return data + }, + + importTree: async ( + sessionId: string, + params?: { name?: string; description?: string; category_id?: string; tags?: string[] } + ): Promise => { + const { data } = await apiClient.post(`/ai/chat/sessions/${sessionId}/import`, params || {}) + return data + }, + + abandonSession: async (sessionId: string): Promise => { + await apiClient.delete(`/ai/chat/sessions/${sessionId}`) + }, +} + +export default aiChatApi diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts index fba65d24..016efcbe 100644 --- a/frontend/src/api/index.ts +++ b/frontend/src/api/index.ts @@ -17,3 +17,4 @@ export { targetListsApi } from './targetLists' export { maintenanceSchedulesApi, batchLaunchApi } from './maintenanceSchedules' export { default as feedbackApi } from './feedback' export { default as aiBuilderApi } from './aiBuilder' +export { default as aiChatApi } from './aiChat' diff --git a/frontend/src/components/ai-chat/ChatInput.tsx b/frontend/src/components/ai-chat/ChatInput.tsx new file mode 100644 index 00000000..c10f4970 --- /dev/null +++ b/frontend/src/components/ai-chat/ChatInput.tsx @@ -0,0 +1,72 @@ +import { useState, useRef, useCallback } from 'react' +import { Send } from 'lucide-react' +import { cn } from '@/lib/utils' + +interface ChatInputProps { + onSend: (content: string) => void + disabled?: boolean + placeholder?: string +} + +export function ChatInput({ onSend, disabled, placeholder = 'Type a message...' }: ChatInputProps) { + const [value, setValue] = useState('') + const textareaRef = useRef(null) + + const handleSend = useCallback(() => { + const trimmed = value.trim() + if (!trimmed || disabled) return + onSend(trimmed) + setValue('') + if (textareaRef.current) { + textareaRef.current.style.height = 'auto' + } + }, [value, disabled, onSend]) + + const handleKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault() + handleSend() + } + } + + const handleInput = () => { + if (textareaRef.current) { + textareaRef.current.style.height = 'auto' + textareaRef.current.style.height = Math.min(textareaRef.current.scrollHeight, 160) + 'px' + } + } + + return ( +
+