diff --git a/client/src/components/EcoconnectSearch.tsx b/client/src/components/EcoconnectSearch.tsx index 8c52ca0..15e1280 100644 --- a/client/src/components/EcoconnectSearch.tsx +++ b/client/src/components/EcoconnectSearch.tsx @@ -1,5 +1,6 @@ import { Button, + Card, Input, Kbd, Modal, @@ -9,15 +10,19 @@ import { useDisclosure, } from "@nextui-org/react"; import { useEffect, useState } from "react"; -import { MagnifyingGlassIcon } from "../icons"; +import { ArrowTopRightOnSquare, MagnifyingGlassIcon } from "../icons"; import config from "../config"; import instance from "../security/http"; import EcoconnectFullLogo from "./EcoconnectFullLogo"; +import { useNavigate } from "react-router-dom"; export default function EcoconnectSearch() { const [searchQuery, setSearchQuery] = useState(""); - const [aiResponse, setAiResponse] = useState(""); + const [aiResponseRoute, setAiResponseRoute] = useState(""); + const [aiResponseRouteDescription, setAiResponseRouteDescription] = + useState(""); const [isQueryLoading, setIsQueryLoading] = useState(false); + const navigate = useNavigate(); const { isOpen: isAiDialogOpen, @@ -28,7 +33,7 @@ export default function EcoconnectSearch() { const dialogOpenChange = () => { onAiDialogOpenChange(); setSearchQuery(""); - setAiResponse(""); + setAiResponseRoute(""); }; const handleKeyPress = (event: KeyboardEvent) => { @@ -42,18 +47,35 @@ export default function EcoconnectSearch() { if (searchQuery.length <= 0) return; setIsQueryLoading(true); instance - .get( - `${config.serverAddress}/connections/openai-chat-completion/${searchQuery}` - ) + .get(`${config.serverAddress}/connections/nls/${searchQuery}`) .then((response) => { - console.log(response.data.response); - setAiResponse(response.data.response); + const rawResponse = response.data.response; + const parsedResponse = JSON.parse(rawResponse); + const resolvedRoute = parsedResponse.route; + setAiResponseRoute(resolvedRoute); + setAiResponseRouteDescription(getRouteDescription(resolvedRoute)); }) .finally(() => { setIsQueryLoading(false); }); }; + const routeDescriptions: { [key: string]: string } = { + "/": "Go home", + "/springboard": "Go to the Dashboard", + "/manage-account": "Management your account", + "/events": "Browse events", + "/karang-guni-schedules": "Browse available Karang Guni", + "/home-bill-contest": "Take part in a contest", + "/home-bill-contest/new-submission": "Submit your bill", + "/community-posts": "Browse community posts", + "/community-posts/create": "Create a post", + }; + + const getRouteDescription = (route: string) => { + return routeDescriptions[route] || "Unknown"; + }; + useEffect(() => { window.addEventListener("keydown", handleKeyPress); @@ -93,7 +115,7 @@ export default function EcoconnectSearch() { placement="top" > - {() => { + {(onClose) => { return ( <> @@ -121,10 +143,30 @@ export default function EcoconnectSearch() { } /> - {aiResponse.length > 0 && ( -

- {aiResponse} -

+ {aiResponseRoute.length > 0 && ( + +
+
+

+ {aiResponseRouteDescription} +

+

+ https://ecoconnect.gov{aiResponseRoute} +

+
+ +
+
)}
diff --git a/client/src/icons.tsx b/client/src/icons.tsx index f40819b..0b6d7cf 100644 --- a/client/src/icons.tsx +++ b/client/src/icons.tsx @@ -520,3 +520,22 @@ export const LifebuoyIcon = () => { ); }; + +export const ArrowTopRightOnSquare = () => { + return ( + + + + ); +}; diff --git a/server/connections/openai.js b/server/connections/openai.js index 5a5b05b..3f16e2e 100644 --- a/server/connections/openai.js +++ b/server/connections/openai.js @@ -1,11 +1,11 @@ const OpenAI = require("openai"); const { getApiKey } = require("./apiKey"); -async function openAiChatCompletion(query) { +async function openAiChatCompletion(query, systemPrompt) { const openai = new OpenAI({ apiKey: await getApiKey("openai_api_key") }); const completion = await openai.chat.completions.create({ messages: [ - { role: "system", content: "You are a helpful assistant." }, + { role: "system", content: systemPrompt }, { role: "user", content: query }, ], model: "gpt-4o-mini", diff --git a/server/routes/connections.js b/server/routes/connections.js index 5f02d63..ae92f6d 100644 --- a/server/routes/connections.js +++ b/server/routes/connections.js @@ -3,20 +3,42 @@ const { openAiChatCompletion } = require("../connections/openai"); const { validateToken } = require("../middlewares/auth"); const router = express.Router(); -router.get( - "/openai-chat-completion/:query", - validateToken, - async (req, res) => { - let data = req.params.query; - console.log(data); - try { - let chatResponse = await openAiChatCompletion(data); - res.json({ response: chatResponse }); - } catch (error) { - console.error("Error with AI:", error); - res.status(500).json({ message: "Internal Server Error" }); - } +const nlsPrompt = ` +You are an AI designed to help navigate a website by interpreting natural language inputs and providing the correct site route. Below are routes and a brief description of each: + +"/" : home +"/springboard" : user dashboard +"/manage-account" : user account management +"/events" : events +"/karang-guni-schedules" : browse slots +"/home-bill-contest" : participate in contest & earn vouchers +"/home-bill-contest/new-submission" : submit bill +"/community-posts" : show posts +"/community-posts/create" : create post + +based on input, provide appropriate route in following format: + +{ + "route": "", +} + +If none matches user query, return empty route. +`; + +async function naturalLanguageSearch(userQuery) { + return await openAiChatCompletion(userQuery, nlsPrompt); +} + +router.get("/nls/:query", validateToken, async (req, res) => { + let data = req.params.query; + console.log(data); + try { + let chatResponse = await naturalLanguageSearch(data); + res.json({ response: chatResponse }); + } catch (error) { + console.error("Error with AI:", error); + res.status(500).json({ message: "Internal Server Error" }); } -); +}); module.exports = router;