From 7badaefbdee55c19fbf1caaa9dfdcd0d299df916 Mon Sep 17 00:00:00 2001 From: TharinduMahesh <155628660+TharinduMahesh@users.noreply.github.com> Date: Mon, 10 Nov 2025 23:39:48 +0530 Subject: [PATCH 1/2] style: Update AIChatWidget UI --- src/app/components/chatbot/AIChatWidget.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/app/components/chatbot/AIChatWidget.tsx b/src/app/components/chatbot/AIChatWidget.tsx index b85f0b5..b65bebd 100644 --- a/src/app/components/chatbot/AIChatWidget.tsx +++ b/src/app/components/chatbot/AIChatWidget.tsx @@ -1,8 +1,9 @@ // components/AIChatWidget.tsx import React, { useState, useRef, useEffect, useCallback } from 'react'; -// Assuming you have a way to get the current user's JWT +// Assuming you have a way to get the current user's JWT ll import { useAuth } from '../hooks/useAuth'; // Placeholder for your auth hook +//AIBOt Chat Widget Component // --- TypeScript Interface Definitions --- interface Message { text: string; From f801830e31d98e67eff61b14c7879a53ca39e7a3 Mon Sep 17 00:00:00 2001 From: TharinduMahesh <155628660+TharinduMahesh@users.noreply.github.com> Date: Tue, 11 Nov 2025 15:39:47 +0530 Subject: [PATCH 2/2] updatetd bot ui --- package-lock.json | 10 ++ package.json | 1 + src/app/components/chatbot/AIChatWidget.tsx | 127 +++++++++++++------- 3 files changed, 96 insertions(+), 42 deletions(-) diff --git a/package-lock.json b/package-lock.json index d1d8ea9..9a3b413 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "axios": "^1.12.2", "js-cookie": "^3.0.5", + "lucide-react": "^0.553.0", "next": "15.5.3", "react": "19.1.0", "react-dom": "19.1.0" @@ -4533,6 +4534,15 @@ "loose-envify": "cli.js" } }, + "node_modules/lucide-react": { + "version": "0.553.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.553.0.tgz", + "integrity": "sha512-BRgX5zrWmNy/lkVAe0dXBgd7XQdZ3HTf+Hwe3c9WK6dqgnj9h+hxV+MDncM88xDWlCq27+TKvHGE70ViODNILw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.19", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz", diff --git a/package.json b/package.json index 3b38aba..2676680 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "axios": "^1.12.2", "js-cookie": "^3.0.5", + "lucide-react": "^0.553.0", "next": "15.5.3", "react": "19.1.0", "react-dom": "19.1.0" diff --git a/src/app/components/chatbot/AIChatWidget.tsx b/src/app/components/chatbot/AIChatWidget.tsx index b65bebd..5976ec1 100644 --- a/src/app/components/chatbot/AIChatWidget.tsx +++ b/src/app/components/chatbot/AIChatWidget.tsx @@ -1,10 +1,20 @@ -// components/AIChatWidget.tsx import React, { useState, useRef, useEffect, useCallback } from 'react'; -// Assuming you have a way to get the current user's JWT ll -import { useAuth } from '../hooks/useAuth'; // Placeholder for your auth hook +import { Sparkles, Bolt } from 'lucide-react'; + +// --- Theme Simulation & Constants --- +// NOTE: These theme classes are defined here for self-containment. +const theme = { + 'theme-text-primary': 'text-gray-900 dark:text-gray-100', + 'theme-text-muted': 'text-gray-500 dark:text-gray-400', + 'theme-bg-primary': 'bg-gray-50 dark:bg-gray-800', + 'theme-button-primary': 'bg-indigo-600 hover:bg-indigo-700 text-white', + 'theme-button-secondary': 'bg-gray-200 dark:bg-gray-700', + 'theme-border': 'border-gray-300 dark:border-gray-700', + 'theme-input': 'bg-white dark:bg-gray-600 border theme-border p-2 rounded-lg focus:ring-indigo-500 focus:border-indigo-500', + 'automotive-card': 'bg-white dark:bg-gray-800' +}; -//AIBOt Chat Widget Component -// --- TypeScript Interface Definitions --- +// --- TypeScript Interface Definitions (CRITICAL for TSX) --- interface Message { text: string; sender: 'user' | 'ai' | 'system'; @@ -13,22 +23,36 @@ interface Message { interface ChatResponse { reply: string; session_id: string; + tool_executed?: string | null; // Added tool_executed matching Python model +} + +// Mocking the user authentication hook +interface AuthHook { + token: string | null; + isLoggedIn: boolean; } -const API_ENDPOINT = '/api/v1/ai/chat'; // This will be routed by your API Gateway +// NOTE: Using localhost:8091 to test against the locally running FastAPI service +const API_ENDPOINT = 'http://localhost:8091/api/v1/ai/chat'; + +// --- MOCK AUTH HOOK --- +// Provides a mock token; replace this with your actual useAuth hook +const useAuth = (): AuthHook => ({ + token: "mock-jwt-token-for-user-12345", + isLoggedIn: true +}); const AIChatWidget: React.FC = () => { // 1. State Management + const { token: userToken } = useAuth(); + const [conversationHistory, setConversationHistory] = useState([ { text: "Hello! I'm TechTorque Assistant. How can I help you with your services or appointments?", sender: 'ai' } ]); const [inputMessage, setInputMessage] = useState(''); const [sessionId, setSessionId] = useState(null); const [isLoading, setIsLoading] = useState(false); - - // 2. Auth/Token Retrieval (Crucial for secure API call) - const { token: userToken } = useAuth(); // Assuming this hook provides the JWT - + const messagesEndRef = useRef(null); // Auto-scroll to the latest message @@ -36,9 +60,10 @@ const AIChatWidget: React.FC = () => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [conversationHistory]); - // 3. The Core Logic (Memoized for performance) + // 2. The Core Logic const sendMessage = useCallback(async (message: string) => { - if (!message.trim() || isLoading || !userToken) return; + // Ensure token exists before proceeding with any action + if (!message.trim() || isLoading || !userToken) return; // Add user message to history const userMessage: Message = { text: message, sender: 'user' }; @@ -47,50 +72,54 @@ const AIChatWidget: React.FC = () => { setIsLoading(true); try { - // 4. API Payload (Must match the Python ChatRequest model!) + // 3. API Payload (Must match the Python ChatRequest model!) const payload = { query: message, session_id: sessionId, - token: userToken, // Passed in body for Agent_Bot context retrieval + token: userToken, // Sent in body for Agent_Bot context }; const response = await fetch(API_ENDPOINT, { method: 'POST', headers: { 'Content-Type': 'application/json', - 'Authorization': `Bearer ${userToken}`, // Passed in header for Gateway validation + 'Authorization': `Bearer ${userToken}`, // Sent in header for Gateway validation }, body: JSON.stringify(payload), }); if (!response.ok) { - // If 401/403, log out or show an error + // Handle non-200 responses (e.g., 401, 500) const errorData = await response.json(); - throw new Error(errorData.detail || 'Failed to get a reply.'); + throw new Error(errorData.detail || `HTTP Error ${response.status}`); } const data: ChatResponse = await response.json(); - // 5. Update state with AI response and new session ID - const aiResponse: Message = { text: data.reply, sender: 'ai' }; + // 4. Update state with AI response and new session ID + let replyText = data.reply; + if (data.tool_executed) { + // Indicate tool execution for debugging/clarity + replyText = `⚙️ (Tool used: ${data.tool_executed}) ${replyText}`; + } + + const aiResponse: Message = { text: replyText, sender: 'ai' }; setConversationHistory(prev => [...prev, aiResponse]); - setSessionId(data.session_id); // CRITICAL: Save the session ID for the next turn + setSessionId(data.session_id); // CRITICAL: Save the session ID } catch (error: any) { console.error("Chat Error:", error); const errorMessage: Message = { - text: error.message.includes('401') - ? "Your session has expired. Please log in again." - : "Sorry, I'm having trouble with the services. Try again later.", + text: "Sorry, I'm having trouble connecting to the services. Please try again later.", sender: 'system' }; setConversationHistory(prev => [...prev, errorMessage]); } finally { setIsLoading(false); } - }, [isLoading, sessionId, userToken]); // Dependencies for useCallback + }, [isLoading, sessionId, userToken]); - // 6. Handler for form submission + // 5. Handler for form submission const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); sendMessage(inputMessage); @@ -98,62 +127,76 @@ const AIChatWidget: React.FC = () => { // --- RENDER FUNCTION (JSX) --- return ( -
+
+ {/* Header */} -
-

TechTorque AI Assistant

+
+
+ +
+

+ TechTorque AI Assistant +

{/* Message Display Area */} -
+
{conversationHistory.map((msg, index) => (
-
+ {msg.sender === 'ai' && } {msg.text}
))} + {/* Typing Indicator */} {isLoading && (
-
- Thinking... +
+ Thinking...
)} -
{/* Auto-scroll reference */} +
{/* Input Form */} -
+ setInputMessage(e.target.value)} - placeholder="Ask about appointments, status, or services..." - className="flex-1 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500" + placeholder={!userToken ? "Please sign in to chat..." : "Ask about status, scheduling, or services..."} + className={`${theme['theme-input']} flex-1 w-full`} disabled={isLoading || !userToken} />
- {!userToken &&
Please log in to use the assistant.
} + + {/* Login Warning Message */} + {!userToken && ( +
+

Please log in to start using the AI assistant.

+
+ )}
); };