feat(frontend): manage current brain (#2165)

# Description

Please include a summary of the changes and the related issue. Please
also include relevant motivation and context.

## Checklist before requesting a review

Please delete options that are not relevant.

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my code
- [ ] I have commented hard-to-understand areas
- [ ] I have ideally added tests that prove my fix is effective or that
my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged

## Screenshots (if appropriate):
This commit is contained in:
Antoine Dewez 2024-02-06 23:34:50 -08:00 committed by GitHub
parent 9d948b33d5
commit 9517b01d9a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 95 additions and 22 deletions

View File

@ -33,7 +33,7 @@ export const ChatInput = (): JSX.Element => {
}}
>
<div className={styles.chat_container}>
<CurrentBrain />
<CurrentBrain allowingRemoveBrain={false} />
<div
className={`
${styles.chat_wrapper}

View File

@ -1,9 +1,14 @@
"use client";
import { UUID } from "crypto";
import { useEffect } from "react";
import { AddBrainModal } from "@/lib/components/AddBrainModal";
import { useBrainCreationContext } from "@/lib/components/AddBrainModal/components/AddBrainSteps/brainCreation-provider";
import PageHeader from "@/lib/components/PageHeader/PageHeader";
import { UploadDocumentModal } from "@/lib/components/UploadDocumentModal/UploadDocumentModal";
import { useChatContext } from "@/lib/context";
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
import { useKnowledgeToFeedContext } from "@/lib/context/KnowledgeToFeedProvider/hooks/useKnowledgeToFeedContext";
import { useDevice } from "@/lib/hooks/useDevice";
import { useCustomDropzone } from "@/lib/hooks/useDropzone";
@ -23,6 +28,9 @@ const SelectedChatPage = (): JSX.Element => {
const { setShouldDisplayFeedCard } = useKnowledgeToFeedContext();
const { setIsBrainCreationModalOpened } = useBrainCreationContext();
const { currentBrain, setCurrentBrainId } = useBrainContext();
const { messages } = useChatContext();
useChatNotificationsSync();
const buttons: ButtonType[] = [
@ -32,6 +40,7 @@ const SelectedChatPage = (): JSX.Element => {
onClick: () => {
setIsBrainCreationModalOpened(true);
},
iconName: "brain",
},
{
label: "Add knowledge",
@ -39,9 +48,24 @@ const SelectedChatPage = (): JSX.Element => {
onClick: () => {
setShouldDisplayFeedCard(true);
},
iconName: "upload",
},
{
label: "Manage current brain",
color: "primary",
onClick: () => {
window.location.href = `/studio/${currentBrain?.id}`;
},
iconName: "edit",
},
];
useEffect(() => {
if (!currentBrain && messages.length > 0) {
setCurrentBrainId(messages[messages.length - 1].brain_id as UUID);
}
}, [messages]);
return (
<div className={styles.main_container}>
<div className={styles.page_header}>

View File

@ -34,6 +34,7 @@ const Search = (): JSX.Element => {
onClick: () => {
setIsBrainCreationModalOpened(true);
},
iconName: "brain",
},
{
label: "Add knowledge",
@ -41,6 +42,7 @@ const Search = (): JSX.Element => {
onClick: () => {
setShouldDisplayFeedCard(true);
},
iconName: "upload",
},
];

View File

@ -42,6 +42,7 @@ const Studio = (): JSX.Element => {
onClick: () => {
setIsBrainCreationModalOpened(true);
},
iconName: "brain",
},
{
label: "Add knowledge",
@ -49,6 +50,7 @@ const Studio = (): JSX.Element => {
onClick: () => {
setShouldDisplayFeedCard(true);
},
iconName: "upload",
},
];

View File

@ -38,6 +38,7 @@ const UserPage = (): JSX.Element => {
onClick: () => {
setIsLogoutModalOpened(true);
},
iconName: "logout",
};
const userTabs: Tab[] = [
{
@ -91,12 +92,14 @@ const UserPage = (): JSX.Element => {
onClick={() => setIsLogoutModalOpened(false)}
color="primary"
label={t("cancel", { ns: "logout" })}
iconName="close"
></QuivrButton>
<QuivrButton
isLoading={isLoggingOut}
color="dangerous"
onClick={() => void handleLogout()}
label={t("logoutButton")}
iconName="logout"
></QuivrButton>
</div>
</div>

View File

@ -4,7 +4,13 @@ import styles from "./CurrentBrain.module.scss";
import { Icon } from "../ui/Icon/Icon";
export const CurrentBrain = (): JSX.Element => {
interface CurrentBrainProps {
allowingRemoveBrain: boolean;
}
export const CurrentBrain = ({
allowingRemoveBrain,
}: CurrentBrainProps): JSX.Element => {
const { currentBrain, setCurrentBrainId } = useBrainContext();
const removeCurrentBrain = (): void => {
@ -25,14 +31,16 @@ export const CurrentBrain = (): JSX.Element => {
<span className={styles.brain_name}>{currentBrain.name}</span>
</div>
</div>
<div
onClick={(event) => {
event.nativeEvent.stopImmediatePropagation();
removeCurrentBrain();
}}
>
<Icon size="normal" name="close" color="black" handleHover={true} />
</div>
{allowingRemoveBrain && (
<div
onClick={(event) => {
event.nativeEvent.stopImmediatePropagation();
removeCurrentBrain();
}}
>
<Icon size="normal" name="close" color="black" handleHover={true} />
</div>
)}
</div>
</div>
);

View File

@ -32,6 +32,7 @@ export const PageHeader = ({
label={button.label}
onClick={button.onClick}
color={button.color}
iconName={button.iconName}
/>
))}
</div>

View File

@ -51,10 +51,6 @@
.white {
color: Colors.$white;
&:hover {
color: Colors.$accent;
}
}
.dangerous {

View File

@ -1,5 +1,6 @@
@use "@/styles/Colors.module.scss";
@use "@/styles/Radius.module.scss";
@use "@/styles/ScreenSizes.module.scss";
@use "@/styles/Spacings.module.scss";
@use "@/styles/Typography.module.scss";
@ -11,6 +12,7 @@
border-radius: Radius.$normal;
border: 1.5px solid transparent;
cursor: pointer;
display: flex;
&.primary {
border-color: Colors.$primary;
@ -32,3 +34,16 @@
}
}
}
.icon_label {
display: flex;
flex-direction: row;
gap: Spacings.$spacing02;
align-items: center;
@media (max-width: ScreenSizes.$small) {
.label {
display: none;
}
}
}

View File

@ -1,7 +1,10 @@
import { useState } from "react";
import { ButtonType } from "@/lib/types/QuivrButton";
import styles from "./QuivrButton.module.scss";
import { Icon } from "../Icon/Icon";
import { LoaderIcon } from "../LoaderIcon/LoaderIcon";
export const QuivrButton = ({
@ -9,14 +12,27 @@ export const QuivrButton = ({
label,
color,
isLoading,
iconName,
}: ButtonType): JSX.Element => {
const [hovered, setHovered] = useState<boolean>(false);
return (
<div
className={`${styles.button_wrapper} ${styles[color]}`}
onClick={onClick}
onMouseEnter={() => setHovered(true)}
onMouseLeave={() => setHovered(false)}
>
{!isLoading ? (
<span>{label}</span>
<div className={styles.icon_label}>
<Icon
name={iconName}
size="normal"
color={hovered ? "white" : color}
handleHover={false}
/>
<span className={styles.label}>{label}</span>
</div>
) : (
<LoaderIcon color="black" size="small" />
)}

View File

@ -56,7 +56,7 @@ export const SearchBar = ({
${currentBrain ? styles.with_brain : ""}
`}
>
<CurrentBrain />
<CurrentBrain allowingRemoveBrain={true} />
<div
className={`
${styles.editor_wrapper}

View File

@ -1,6 +1,6 @@
import { AiOutlineLoading3Quarters } from "react-icons/ai";
import { BsArrowRightShort } from "react-icons/bs";
import { CiChat1, CiFlag1 } from "react-icons/ci";
import { BsArrowRightShort, BsChatLeftText } from "react-icons/bs";
import { CiFlag1 } from "react-icons/ci";
import {
FaCheck,
FaCheckCircle,
@ -9,9 +9,12 @@ import {
FaRegUserCircle,
FaUnlock,
} from "react-icons/fa";
import { FaArrowUpFromBracket } from "react-icons/fa6";
import { IoIosAdd, IoMdClose, IoMdLogOut } from "react-icons/io";
import { IoHomeOutline, IoSettingsSharp } from "react-icons/io5";
import {
IoArrowUpCircleOutline,
IoHomeOutline,
IoSettingsSharp,
} from "react-icons/io5";
import { IconType } from "react-icons/lib";
import {
LuBrain,
@ -38,7 +41,7 @@ export const iconList: { [name: string]: IconType } = {
addWithoutCircle: IoIosAdd,
brain: LuBrain,
brainCircuit: LuBrainCircuit,
chat: CiChat1,
chat: BsChatLeftText,
check: FaCheck,
checkCircle: FaCheckCircle,
chevronDown: LuChevronDown,
@ -50,7 +53,7 @@ export const iconList: { [name: string]: IconType } = {
email: MdAlternateEmail,
file: LuFile,
flag: CiFlag1,
followUp: FaArrowUpFromBracket,
followUp: IoArrowUpCircleOutline,
graph: VscGraph,
hastag: RiHashtag,
history: MdHistory,

View File

@ -1,8 +1,11 @@
import { Color } from "./Colors";
import { iconList } from "../helpers/iconList";
export interface ButtonType {
label: string;
color: Color;
isLoading?: boolean;
iconName: keyof typeof iconList;
onClick: () => void;
}