Scaffolding of Natural Language Search
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
"axios": "^1.7.2",
|
||||
"formik": "^2.4.6",
|
||||
"framer-motion": "^11.2.10",
|
||||
"openai": "^4.53.2",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-hot-toast": "^2.4.1",
|
||||
|
||||
5810
client/pnpm-lock.yaml
generated
5810
client/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
146
client/src/components/EcoconnectSearch.tsx
Normal file
146
client/src/components/EcoconnectSearch.tsx
Normal file
@@ -0,0 +1,146 @@
|
||||
import {
|
||||
Button,
|
||||
Input,
|
||||
Kbd,
|
||||
Modal,
|
||||
ModalBody,
|
||||
ModalContent,
|
||||
ModalFooter,
|
||||
useDisclosure,
|
||||
} from "@nextui-org/react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { MagnifyingGlassIcon } from "../icons";
|
||||
import config from "../config";
|
||||
import instance from "../security/http";
|
||||
import EcoconnectFullLogo from "./EcoconnectFullLogo";
|
||||
|
||||
export default function EcoconnectSearch() {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [aiResponse, setAiResponse] = useState("");
|
||||
const [isQueryLoading, setIsQueryLoading] = useState(false);
|
||||
|
||||
const {
|
||||
isOpen: isAiDialogOpen,
|
||||
onOpen: onAiDialogOpen,
|
||||
onOpenChange: onAiDialogOpenChange,
|
||||
} = useDisclosure();
|
||||
|
||||
const dialogOpenChange = () => {
|
||||
onAiDialogOpenChange();
|
||||
setSearchQuery("");
|
||||
setAiResponse("");
|
||||
};
|
||||
|
||||
const handleKeyPress = (event: KeyboardEvent) => {
|
||||
if (event.ctrlKey && event.key === "s") {
|
||||
event.preventDefault();
|
||||
onAiDialogOpen();
|
||||
}
|
||||
};
|
||||
|
||||
const executeSearch = async () => {
|
||||
if (searchQuery.length <= 0) return;
|
||||
setIsQueryLoading(true);
|
||||
instance
|
||||
.get(
|
||||
`${config.serverAddress}/connections/openai-chat-completion/${searchQuery}`
|
||||
)
|
||||
.then((response) => {
|
||||
console.log(response.data.response);
|
||||
setAiResponse(response.data.response);
|
||||
})
|
||||
.finally(() => {
|
||||
setIsQueryLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", handleKeyPress);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyPress);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="-mb-1">
|
||||
<Button
|
||||
size="sm"
|
||||
className="p-0"
|
||||
variant="light"
|
||||
isDisabled={isAiDialogOpen}
|
||||
onPress={() => {
|
||||
onAiDialogOpen();
|
||||
}}
|
||||
>
|
||||
<Input
|
||||
size="sm"
|
||||
disabled
|
||||
className="w-44 h-min"
|
||||
startContent={<MagnifyingGlassIcon />}
|
||||
endContent={
|
||||
<div className="-mr-1">
|
||||
<Kbd keys={["ctrl"]}>S</Kbd>
|
||||
</div>
|
||||
}
|
||||
placeholder="Search..."
|
||||
/>
|
||||
</Button>
|
||||
<Modal
|
||||
isOpen={isAiDialogOpen}
|
||||
onOpenChange={dialogOpenChange}
|
||||
closeButton={<></>}
|
||||
placement="top"
|
||||
>
|
||||
<ModalContent>
|
||||
{() => {
|
||||
return (
|
||||
<>
|
||||
<ModalBody>
|
||||
<div className="py-4 flex flex-col gap-4">
|
||||
<Input
|
||||
placeholder="Search..."
|
||||
size="lg"
|
||||
value={searchQuery}
|
||||
onValueChange={setSearchQuery}
|
||||
isDisabled={isQueryLoading}
|
||||
onKeyDown={(keyEvent) => {
|
||||
if (keyEvent.key == "Enter") {
|
||||
executeSearch();
|
||||
}
|
||||
}}
|
||||
endContent={
|
||||
<Button
|
||||
isIconOnly
|
||||
variant="flat"
|
||||
onPress={executeSearch}
|
||||
isLoading={isQueryLoading}
|
||||
className="-mr-2"
|
||||
>
|
||||
<MagnifyingGlassIcon />
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
{aiResponse.length > 0 && (
|
||||
<p className="bg-neutral-50 p-4 border-2 rounded-xl">
|
||||
{aiResponse}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</ModalBody>
|
||||
<ModalFooter className="bg-red-50 dark:bg-red-950 w-full h-full">
|
||||
<div className="w-full h-full flex flex-row justify-between *:my-auto">
|
||||
<EcoconnectFullLogo />
|
||||
<p className="text-lg text-red-900 dark:text-red-100">
|
||||
Natural Language Search
|
||||
</p>
|
||||
</div>
|
||||
</ModalFooter>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import { retrieveUserInformation } from "../security/users";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import EcoconnectFullLogo from "./EcoconnectFullLogo";
|
||||
import EcoconnectSearch from "./EcoconnectSearch";
|
||||
|
||||
export default function NavigationBar() {
|
||||
const [userProfileImageURL, setUserProfileImageURL] = useState("");
|
||||
@@ -58,7 +59,7 @@ export default function NavigationBar() {
|
||||
}
|
||||
>
|
||||
<div className="relative bg-primary-50 dark:bg-primary-800 border-2 border-primary-100 dark:border-primary-950 shadow-lg w-full h-full rounded-xl flex flex-col justify-center p-1">
|
||||
<div className=" w-full flex flex-row justify-between gap-4">
|
||||
<div className="w-full flex flex-row justify-between gap-4">
|
||||
<div className="flex flex-row gap-0 my-auto *:my-auto">
|
||||
<Button
|
||||
variant="light"
|
||||
@@ -109,60 +110,65 @@ export default function NavigationBar() {
|
||||
</div>
|
||||
</div>
|
||||
{userInformation && (
|
||||
<div className="my-auto pr-1">
|
||||
<Dropdown placement="bottom" backdrop="blur">
|
||||
<DropdownTrigger>
|
||||
<Avatar src={userProfileImageURL} as="button" size="sm" />
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu aria-label="Profile Actions">
|
||||
<DropdownSection showDivider>
|
||||
<DropdownItem key="account-overview" isReadOnly>
|
||||
<div className="flex flex-col gap-2 text-center *:mx-auto p-2">
|
||||
<Avatar
|
||||
src={userProfileImageURL}
|
||||
as="button"
|
||||
size="lg"
|
||||
isBordered
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<p>Signed in as</p>
|
||||
<p className="text-lg font-bold">
|
||||
<span>{userInformation.firstName}</span>{" "}
|
||||
<span>{userInformation.lastName}</span>
|
||||
</p>
|
||||
<p className="opacity-50">{userInformation.email}</p>
|
||||
<div className="my-auto pr-1 flex flex-row justify-end">
|
||||
<div className="flex flex-row gap-2 w-min">
|
||||
<EcoconnectSearch />
|
||||
<Dropdown placement="bottom" backdrop="blur">
|
||||
<DropdownTrigger>
|
||||
<Avatar src={userProfileImageURL} as="button" size="sm" />
|
||||
</DropdownTrigger>
|
||||
<DropdownMenu aria-label="Profile Actions">
|
||||
<DropdownSection showDivider>
|
||||
<DropdownItem key="account-overview" isReadOnly>
|
||||
<div className="flex flex-col gap-2 text-center *:mx-auto p-2 w-full">
|
||||
<Avatar
|
||||
src={userProfileImageURL}
|
||||
as="button"
|
||||
size="lg"
|
||||
isBordered
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<p>Signed in as</p>
|
||||
<p className="text-lg font-bold">
|
||||
<span>{userInformation.firstName}</span>{" "}
|
||||
<span>{userInformation.lastName}</span>
|
||||
</p>
|
||||
<p className="opacity-50">
|
||||
{userInformation.email}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DropdownItem>
|
||||
</DropdownItem>
|
||||
<DropdownItem
|
||||
key="dashboard"
|
||||
title="Dashboard"
|
||||
startContent={<RocketLaunchIcon />}
|
||||
onPress={() => {
|
||||
navigate("/springboard");
|
||||
}}
|
||||
/>
|
||||
<DropdownItem
|
||||
key="manage-account"
|
||||
title="Manage your account"
|
||||
startContent={<PencilSquareIcon />}
|
||||
onPress={() => {
|
||||
navigate("/manage-account");
|
||||
}}
|
||||
/>
|
||||
</DropdownSection>
|
||||
<DropdownItem
|
||||
key="dashboard"
|
||||
title="Dashboard"
|
||||
startContent={<RocketLaunchIcon />}
|
||||
key="signout"
|
||||
startContent={<ArrowRightStartOnRectangleIcon />}
|
||||
color="danger"
|
||||
title="Sign out"
|
||||
onPress={() => {
|
||||
navigate("/springboard");
|
||||
localStorage.clear();
|
||||
window.location.reload();
|
||||
}}
|
||||
/>
|
||||
<DropdownItem
|
||||
key="manage-account"
|
||||
title="Manage your account"
|
||||
startContent={<PencilSquareIcon />}
|
||||
onPress={() => {
|
||||
navigate("/manage-account");
|
||||
}}
|
||||
/>
|
||||
</DropdownSection>
|
||||
<DropdownItem
|
||||
key="signout"
|
||||
startContent={<ArrowRightStartOnRectangleIcon />}
|
||||
color="danger"
|
||||
title="Sign out"
|
||||
onPress={() => {
|
||||
localStorage.clear();
|
||||
window.location.reload();
|
||||
}}
|
||||
/>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!userInformation && doneLoading && (
|
||||
|
||||
Reference in New Issue
Block a user