Search working!!!
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
|
Card,
|
||||||
Input,
|
Input,
|
||||||
Kbd,
|
Kbd,
|
||||||
Modal,
|
Modal,
|
||||||
@@ -9,15 +10,19 @@ import {
|
|||||||
useDisclosure,
|
useDisclosure,
|
||||||
} from "@nextui-org/react";
|
} from "@nextui-org/react";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { MagnifyingGlassIcon } from "../icons";
|
import { ArrowTopRightOnSquare, MagnifyingGlassIcon } from "../icons";
|
||||||
import config from "../config";
|
import config from "../config";
|
||||||
import instance from "../security/http";
|
import instance from "../security/http";
|
||||||
import EcoconnectFullLogo from "./EcoconnectFullLogo";
|
import EcoconnectFullLogo from "./EcoconnectFullLogo";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
export default function EcoconnectSearch() {
|
export default function EcoconnectSearch() {
|
||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [aiResponse, setAiResponse] = useState("");
|
const [aiResponseRoute, setAiResponseRoute] = useState("");
|
||||||
|
const [aiResponseRouteDescription, setAiResponseRouteDescription] =
|
||||||
|
useState("");
|
||||||
const [isQueryLoading, setIsQueryLoading] = useState(false);
|
const [isQueryLoading, setIsQueryLoading] = useState(false);
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isOpen: isAiDialogOpen,
|
isOpen: isAiDialogOpen,
|
||||||
@@ -28,7 +33,7 @@ export default function EcoconnectSearch() {
|
|||||||
const dialogOpenChange = () => {
|
const dialogOpenChange = () => {
|
||||||
onAiDialogOpenChange();
|
onAiDialogOpenChange();
|
||||||
setSearchQuery("");
|
setSearchQuery("");
|
||||||
setAiResponse("");
|
setAiResponseRoute("");
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleKeyPress = (event: KeyboardEvent) => {
|
const handleKeyPress = (event: KeyboardEvent) => {
|
||||||
@@ -42,18 +47,35 @@ export default function EcoconnectSearch() {
|
|||||||
if (searchQuery.length <= 0) return;
|
if (searchQuery.length <= 0) return;
|
||||||
setIsQueryLoading(true);
|
setIsQueryLoading(true);
|
||||||
instance
|
instance
|
||||||
.get(
|
.get(`${config.serverAddress}/connections/nls/${searchQuery}`)
|
||||||
`${config.serverAddress}/connections/openai-chat-completion/${searchQuery}`
|
|
||||||
)
|
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
console.log(response.data.response);
|
const rawResponse = response.data.response;
|
||||||
setAiResponse(response.data.response);
|
const parsedResponse = JSON.parse(rawResponse);
|
||||||
|
const resolvedRoute = parsedResponse.route;
|
||||||
|
setAiResponseRoute(resolvedRoute);
|
||||||
|
setAiResponseRouteDescription(getRouteDescription(resolvedRoute));
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
setIsQueryLoading(false);
|
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(() => {
|
useEffect(() => {
|
||||||
window.addEventListener("keydown", handleKeyPress);
|
window.addEventListener("keydown", handleKeyPress);
|
||||||
|
|
||||||
@@ -93,7 +115,7 @@ export default function EcoconnectSearch() {
|
|||||||
placement="top"
|
placement="top"
|
||||||
>
|
>
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
{() => {
|
{(onClose) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
@@ -121,10 +143,30 @@ export default function EcoconnectSearch() {
|
|||||||
</Button>
|
</Button>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{aiResponse.length > 0 && (
|
{aiResponseRoute.length > 0 && (
|
||||||
<p className="bg-neutral-50 p-4 border-2 rounded-xl">
|
<Card className="p-4">
|
||||||
{aiResponse}
|
<div className="flex flex-row justify-between *:my-auto">
|
||||||
</p>
|
<div className="flex flex-col">
|
||||||
|
<p className="text-xl font-bold">
|
||||||
|
{aiResponseRouteDescription}
|
||||||
|
</p>
|
||||||
|
<p className="text-sm opacity-50">
|
||||||
|
https://ecoconnect.gov{aiResponseRoute}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
isIconOnly
|
||||||
|
variant="light"
|
||||||
|
className="text-red-500"
|
||||||
|
onPress={() => {
|
||||||
|
onClose();
|
||||||
|
navigate(aiResponseRoute);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ArrowTopRightOnSquare />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
|||||||
@@ -520,3 +520,22 @@ export const LifebuoyIcon = () => {
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const ArrowTopRightOnSquare = () => {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
strokeWidth={1.5}
|
||||||
|
stroke="currentColor"
|
||||||
|
className="size-6"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
d="M13.5 6H5.25A2.25 2.25 0 0 0 3 8.25v10.5A2.25 2.25 0 0 0 5.25 21h10.5A2.25 2.25 0 0 0 18 18.75V10.5m-10.5 6L21 3m0 0h-5.25M21 3v5.25"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
const OpenAI = require("openai");
|
const OpenAI = require("openai");
|
||||||
const { getApiKey } = require("./apiKey");
|
const { getApiKey } = require("./apiKey");
|
||||||
|
|
||||||
async function openAiChatCompletion(query) {
|
async function openAiChatCompletion(query, systemPrompt) {
|
||||||
const openai = new OpenAI({ apiKey: await getApiKey("openai_api_key") });
|
const openai = new OpenAI({ apiKey: await getApiKey("openai_api_key") });
|
||||||
const completion = await openai.chat.completions.create({
|
const completion = await openai.chat.completions.create({
|
||||||
messages: [
|
messages: [
|
||||||
{ role: "system", content: "You are a helpful assistant." },
|
{ role: "system", content: systemPrompt },
|
||||||
{ role: "user", content: query },
|
{ role: "user", content: query },
|
||||||
],
|
],
|
||||||
model: "gpt-4o-mini",
|
model: "gpt-4o-mini",
|
||||||
|
|||||||
@@ -3,20 +3,42 @@ const { openAiChatCompletion } = require("../connections/openai");
|
|||||||
const { validateToken } = require("../middlewares/auth");
|
const { validateToken } = require("../middlewares/auth");
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
router.get(
|
const nlsPrompt = `
|
||||||
"/openai-chat-completion/:query",
|
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:
|
||||||
validateToken,
|
|
||||||
async (req, res) => {
|
"/" : home
|
||||||
let data = req.params.query;
|
"/springboard" : user dashboard
|
||||||
console.log(data);
|
"/manage-account" : user account management
|
||||||
try {
|
"/events" : events
|
||||||
let chatResponse = await openAiChatCompletion(data);
|
"/karang-guni-schedules" : browse slots
|
||||||
res.json({ response: chatResponse });
|
"/home-bill-contest" : participate in contest & earn vouchers
|
||||||
} catch (error) {
|
"/home-bill-contest/new-submission" : submit bill
|
||||||
console.error("Error with AI:", error);
|
"/community-posts" : show posts
|
||||||
res.status(500).json({ message: "Internal Server Error" });
|
"/community-posts/create" : create post
|
||||||
}
|
|
||||||
|
based on input, provide appropriate route in following format:
|
||||||
|
|
||||||
|
{
|
||||||
|
"route": "<appropriate 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;
|
module.exports = router;
|
||||||
|
|||||||
Reference in New Issue
Block a user