mirror of
https://github.com/QuivrHQ/quivr.git
synced 2024-10-05 18:38:06 +03:00
feat(frontend): new brain creation modal (#2192)
# 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:
parent
08e015af6c
commit
6383918f7b
@ -5,7 +5,7 @@ import { posthog } from "posthog-js";
|
|||||||
import { PostHogProvider } from "posthog-js/react";
|
import { PostHogProvider } from "posthog-js/react";
|
||||||
import { PropsWithChildren, useEffect } from "react";
|
import { PropsWithChildren, useEffect } from "react";
|
||||||
|
|
||||||
import { BrainCreationProvider } from "@/lib/components/AddBrainModal/components/AddBrainSteps/brainCreation-provider";
|
import { BrainCreationProvider } from "@/lib/components/AddBrainModal/brainCreation-provider";
|
||||||
import { Menu } from "@/lib/components/Menu/Menu";
|
import { Menu } from "@/lib/components/Menu/Menu";
|
||||||
import { useOutsideClickListener } from "@/lib/components/Menu/hooks/useOutsideClickListener";
|
import { useOutsideClickListener } from "@/lib/components/Menu/hooks/useOutsideClickListener";
|
||||||
import SearchModal from "@/lib/components/SearchModal/SearchModal";
|
import SearchModal from "@/lib/components/SearchModal/SearchModal";
|
||||||
|
@ -2,7 +2,7 @@ import { SuggestionKeyDownProps } from "@tiptap/suggestion";
|
|||||||
import { forwardRef } from "react";
|
import { forwardRef } from "react";
|
||||||
import { FaAngleDoubleDown } from "react-icons/fa";
|
import { FaAngleDoubleDown } from "react-icons/fa";
|
||||||
|
|
||||||
import { useBrainCreationContext } from "@/lib/components/AddBrainModal/components/AddBrainSteps/brainCreation-provider";
|
import { useBrainCreationContext } from "@/lib/components/AddBrainModal/brainCreation-provider";
|
||||||
import TextButton from "@/lib/components/ui/TextButton/TextButton";
|
import TextButton from "@/lib/components/ui/TextButton/TextButton";
|
||||||
|
|
||||||
import { AddNewPromptButton } from "./components/AddNewPromptButton";
|
import { AddNewPromptButton } from "./components/AddNewPromptButton";
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
padding-block: Spacings.$spacing05;
|
padding-block: Spacings.$spacing05;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
gap: Spacings.$spacing05;
|
gap: Spacings.$spacing05;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
.single_selector_wrapper {
|
.single_selector_wrapper {
|
||||||
width: 30%;
|
width: 30%;
|
||||||
@ -37,6 +38,7 @@
|
|||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: Spacings.$spacing02;
|
gap: Spacings.$spacing02;
|
||||||
|
flex-grow: 1;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
|
||||||
.uploaded_knowledge {
|
.uploaded_knowledge {
|
||||||
@ -46,6 +48,7 @@
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
font-size: Typography.$small;
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
@ -13,7 +13,11 @@ import { FromDocuments } from "./components/FromDocuments/FromDocuments";
|
|||||||
import { FromWebsites } from "./components/FromWebsites/FromWebsites";
|
import { FromWebsites } from "./components/FromWebsites/FromWebsites";
|
||||||
import { formatMinimalBrainsToSelectComponentInput } from "./utils/formatMinimalBrainsToSelectComponentInput";
|
import { formatMinimalBrainsToSelectComponentInput } from "./utils/formatMinimalBrainsToSelectComponentInput";
|
||||||
|
|
||||||
export const KnowledgeToFeed = (): JSX.Element => {
|
export const KnowledgeToFeed = ({
|
||||||
|
hideBrainSelector,
|
||||||
|
}: {
|
||||||
|
hideBrainSelector?: boolean;
|
||||||
|
}): JSX.Element => {
|
||||||
const { allBrains, setCurrentBrainId, currentBrain } = useBrainContext();
|
const { allBrains, setCurrentBrainId, currentBrain } = useBrainContext();
|
||||||
const [selectedTab, setSelectedTab] = useState("From documents");
|
const [selectedTab, setSelectedTab] = useState("From documents");
|
||||||
const { knowledgeToFeed, removeKnowledgeToFeed } =
|
const { knowledgeToFeed, removeKnowledgeToFeed } =
|
||||||
@ -46,18 +50,20 @@ export const KnowledgeToFeed = (): JSX.Element => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.knowledge_to_feed_wrapper}>
|
<div className={styles.knowledge_to_feed_wrapper}>
|
||||||
<div className={styles.single_selector_wrapper}>
|
{!hideBrainSelector && (
|
||||||
<SingleSelector
|
<div className={styles.single_selector_wrapper}>
|
||||||
options={brainsWithUploadRights}
|
<SingleSelector
|
||||||
onChange={setCurrentBrainId}
|
options={brainsWithUploadRights}
|
||||||
selectedOption={
|
onChange={setCurrentBrainId}
|
||||||
currentBrain
|
selectedOption={
|
||||||
? { label: currentBrain.name, value: currentBrain.id }
|
currentBrain
|
||||||
: undefined
|
? { label: currentBrain.name, value: currentBrain.id }
|
||||||
}
|
: undefined
|
||||||
placeholder="Select a brain"
|
}
|
||||||
/>
|
placeholder="Select a brain"
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
<Tabs tabList={knowledgesTabs} />
|
<Tabs tabList={knowledgesTabs} />
|
||||||
<div className={styles.tabs_content_wrapper}>
|
<div className={styles.tabs_content_wrapper}>
|
||||||
{selectedTab === "From documents" && <FromDocuments />}
|
{selectedTab === "From documents" && <FromDocuments />}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
@use "@/styles/Colors.module.scss";
|
@use "@/styles/Colors.module.scss";
|
||||||
@use "@/styles/Radius.module.scss";
|
@use "@/styles/Radius.module.scss";
|
||||||
|
@use "@/styles/ScreenSizes.module.scss";
|
||||||
@use "@/styles/Spacings.module.scss";
|
@use "@/styles/Spacings.module.scss";
|
||||||
|
|
||||||
.from_document_wrapper {
|
.from_document_wrapper {
|
||||||
@ -24,6 +25,10 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
gap: Spacings.$spacing02;
|
gap: Spacings.$spacing02;
|
||||||
|
|
||||||
|
@media (max-width: ScreenSizes.$small) {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
.clickable {
|
.clickable {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -4,7 +4,7 @@ import { UUID } from "crypto";
|
|||||||
import { useEffect } from "react";
|
import { useEffect } from "react";
|
||||||
|
|
||||||
import { AddBrainModal } from "@/lib/components/AddBrainModal";
|
import { AddBrainModal } from "@/lib/components/AddBrainModal";
|
||||||
import { useBrainCreationContext } from "@/lib/components/AddBrainModal/components/AddBrainSteps/brainCreation-provider";
|
import { useBrainCreationContext } from "@/lib/components/AddBrainModal/brainCreation-provider";
|
||||||
import PageHeader from "@/lib/components/PageHeader/PageHeader";
|
import PageHeader from "@/lib/components/PageHeader/PageHeader";
|
||||||
import { UploadDocumentModal } from "@/lib/components/UploadDocumentModal/UploadDocumentModal";
|
import { UploadDocumentModal } from "@/lib/components/UploadDocumentModal/UploadDocumentModal";
|
||||||
import { useChatContext } from "@/lib/context";
|
import { useChatContext } from "@/lib/context";
|
||||||
|
@ -4,7 +4,7 @@ import { useEffect } from "react";
|
|||||||
|
|
||||||
import { QuivrLogo } from "@/lib/assets/QuivrLogo";
|
import { QuivrLogo } from "@/lib/assets/QuivrLogo";
|
||||||
import { AddBrainModal } from "@/lib/components/AddBrainModal";
|
import { AddBrainModal } from "@/lib/components/AddBrainModal";
|
||||||
import { useBrainCreationContext } from "@/lib/components/AddBrainModal/components/AddBrainSteps/brainCreation-provider";
|
import { useBrainCreationContext } from "@/lib/components/AddBrainModal/brainCreation-provider";
|
||||||
import PageHeader from "@/lib/components/PageHeader/PageHeader";
|
import PageHeader from "@/lib/components/PageHeader/PageHeader";
|
||||||
import { UploadDocumentModal } from "@/lib/components/UploadDocumentModal/UploadDocumentModal";
|
import { UploadDocumentModal } from "@/lib/components/UploadDocumentModal/UploadDocumentModal";
|
||||||
import { SearchBar } from "@/lib/components/ui/SearchBar/SearchBar";
|
import { SearchBar } from "@/lib/components/ui/SearchBar/SearchBar";
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
import { AddBrainModal } from "@/lib/components/AddBrainModal";
|
import { AddBrainModal } from "@/lib/components/AddBrainModal";
|
||||||
import { useBrainCreationContext } from "@/lib/components/AddBrainModal/components/AddBrainSteps/brainCreation-provider";
|
import { useBrainCreationContext } from "@/lib/components/AddBrainModal/brainCreation-provider";
|
||||||
import PageHeader from "@/lib/components/PageHeader/PageHeader";
|
import PageHeader from "@/lib/components/PageHeader/PageHeader";
|
||||||
import { UploadDocumentModal } from "@/lib/components/UploadDocumentModal/UploadDocumentModal";
|
import { UploadDocumentModal } from "@/lib/components/UploadDocumentModal/UploadDocumentModal";
|
||||||
import { Tabs } from "@/lib/components/ui/Tabs/Tabs";
|
import { Tabs } from "@/lib/components/ui/Tabs/Tabs";
|
||||||
|
@ -0,0 +1,21 @@
|
|||||||
|
@use "@/styles/Spacings.module.scss";
|
||||||
|
|
||||||
|
.add_brain_modal_container {
|
||||||
|
display: flex;
|
||||||
|
padding-block: Spacings.$spacing05;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
gap: Spacings.$spacing08;
|
||||||
|
|
||||||
|
.stepper_container {
|
||||||
|
width: 100%;
|
||||||
|
padding-inline: Spacings.$spacing08;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content_wrapper {
|
||||||
|
flex-grow: 1;
|
||||||
|
overflow: scroll;
|
||||||
|
}
|
||||||
|
}
|
@ -1,11 +1,23 @@
|
|||||||
import { FormProvider, useForm } from "react-hook-form";
|
import { FormProvider, useForm } from "react-hook-form";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { Modal } from "@/lib/components/ui/Modal/Modal";
|
||||||
import { addBrainDefaultValues } from "@/lib/config/defaultBrainConfig";
|
import { addBrainDefaultValues } from "@/lib/config/defaultBrainConfig";
|
||||||
|
|
||||||
import { AddBrainSteps } from "./components/AddBrainSteps/AddBrainSteps";
|
import styles from "./AddBrainModal.module.scss";
|
||||||
import { CreateBrainProps } from "./types";
|
import { useBrainCreationContext } from "./brainCreation-provider";
|
||||||
|
import { BrainKnowledgeStep } from "./components/BrainKnowledgeStep/BrainKnowledgeStep";
|
||||||
|
import { BrainMainInfosStep } from "./components/BrainMainInfosStep/BrainMainInfosStep";
|
||||||
|
import { BrainTypeSelectionStep } from "./components/BrainTypeSelectionStep/BrainTypeSelectionStep";
|
||||||
|
import { Stepper } from "./components/Stepper/Stepper";
|
||||||
|
import { CreateBrainProps } from "./types/types";
|
||||||
|
|
||||||
export const AddBrainModal = (): JSX.Element => {
|
export const AddBrainModal = (): JSX.Element => {
|
||||||
|
const { t } = useTranslation(["translation", "brain", "config"]);
|
||||||
|
|
||||||
|
const { isBrainCreationModalOpened, setIsBrainCreationModalOpened } =
|
||||||
|
useBrainCreationContext();
|
||||||
|
|
||||||
const defaultValues: CreateBrainProps = {
|
const defaultValues: CreateBrainProps = {
|
||||||
...addBrainDefaultValues,
|
...addBrainDefaultValues,
|
||||||
setDefault: true,
|
setDefault: true,
|
||||||
@ -18,7 +30,25 @@ export const AddBrainModal = (): JSX.Element => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<FormProvider {...methods}>
|
<FormProvider {...methods}>
|
||||||
<AddBrainSteps />
|
<Modal
|
||||||
|
title={t("newBrainTitle", { ns: "brain" })}
|
||||||
|
desc={t("newBrainSubtitle", { ns: "brain" })}
|
||||||
|
isOpen={isBrainCreationModalOpened}
|
||||||
|
setOpen={setIsBrainCreationModalOpened}
|
||||||
|
bigModal={true}
|
||||||
|
CloseTrigger={<div />}
|
||||||
|
>
|
||||||
|
<div className={styles.add_brain_modal_container}>
|
||||||
|
<div className={styles.stepper_container}>
|
||||||
|
<Stepper />
|
||||||
|
</div>
|
||||||
|
<div className={styles.content_wrapper}>
|
||||||
|
<BrainTypeSelectionStep />
|
||||||
|
<BrainMainInfosStep />
|
||||||
|
<BrainKnowledgeStep />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
</FormProvider>
|
</FormProvider>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,8 @@ import { createContext, useContext, useState } from "react";
|
|||||||
interface BrainCreationContextProps {
|
interface BrainCreationContextProps {
|
||||||
isBrainCreationModalOpened: boolean;
|
isBrainCreationModalOpened: boolean;
|
||||||
setIsBrainCreationModalOpened: React.Dispatch<React.SetStateAction<boolean>>;
|
setIsBrainCreationModalOpened: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
|
creating: boolean;
|
||||||
|
setCreating: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const BrainCreationContext = createContext<
|
export const BrainCreationContext = createContext<
|
||||||
@ -15,11 +17,17 @@ export const BrainCreationProvider = ({
|
|||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}): JSX.Element => {
|
}): JSX.Element => {
|
||||||
const [isBrainCreationModalOpened, setIsBrainCreationModalOpened] =
|
const [isBrainCreationModalOpened, setIsBrainCreationModalOpened] =
|
||||||
useState(false);
|
useState<boolean>(false);
|
||||||
|
const [creating, setCreating] = useState<boolean>(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BrainCreationContext.Provider
|
<BrainCreationContext.Provider
|
||||||
value={{ isBrainCreationModalOpened, setIsBrainCreationModalOpened }}
|
value={{
|
||||||
|
isBrainCreationModalOpened,
|
||||||
|
setIsBrainCreationModalOpened,
|
||||||
|
creating,
|
||||||
|
setCreating,
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</BrainCreationContext.Provider>
|
</BrainCreationContext.Provider>
|
@ -1,44 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import { Modal } from "@/lib/components/ui/Modal/Modal";
|
|
||||||
|
|
||||||
import { useBrainCreationContext } from "./brainCreation-provider";
|
|
||||||
import { BrainKnowledgeStep } from "./components/BrainKnowledgeStep/BrainKnowledgeStep";
|
|
||||||
import { BrainParamsStep } from "./components/BrainParamsStep/BrainParamsStep";
|
|
||||||
import { BrainTypeSelectionStep } from "./components/BrainTypeSelectionStep/BrainTypeSelectionStep";
|
|
||||||
import { Stepper } from "./components/Stepper/Stepper";
|
|
||||||
|
|
||||||
export const AddBrainSteps = (): JSX.Element => {
|
|
||||||
const { t } = useTranslation(["translation", "brain", "config"]);
|
|
||||||
|
|
||||||
const { isBrainCreationModalOpened, setIsBrainCreationModalOpened } =
|
|
||||||
useBrainCreationContext();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal
|
|
||||||
title={t("newBrainTitle", { ns: "brain" })}
|
|
||||||
desc={t("newBrainSubtitle", { ns: "brain" })}
|
|
||||||
isOpen={isBrainCreationModalOpened}
|
|
||||||
setOpen={setIsBrainCreationModalOpened}
|
|
||||||
CloseTrigger={<div />}
|
|
||||||
>
|
|
||||||
<form
|
|
||||||
onSubmit={(e) => {
|
|
||||||
e.preventDefault();
|
|
||||||
}}
|
|
||||||
className="my-10 flex flex-col items-center gap-2 justify-center"
|
|
||||||
>
|
|
||||||
<Stepper />
|
|
||||||
<BrainTypeSelectionStep
|
|
||||||
onCancelBrainCreation={() => setIsBrainCreationModalOpened(false)}
|
|
||||||
/>
|
|
||||||
<BrainParamsStep
|
|
||||||
onCancelBrainCreation={() => setIsBrainCreationModalOpened(false)}
|
|
||||||
/>
|
|
||||||
<BrainKnowledgeStep
|
|
||||||
onCancelBrainCreation={() => setIsBrainCreationModalOpened(false)}
|
|
||||||
/>
|
|
||||||
</form>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,80 +0,0 @@
|
|||||||
import { Fragment } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { FaArrowLeft } from "react-icons/fa";
|
|
||||||
|
|
||||||
import { ApiRequestDefinition } from "@/lib/components/ApiRequestDefinition";
|
|
||||||
import Button from "@/lib/components/ui/Button";
|
|
||||||
import { BrainType } from "@/lib/types/BrainConfig";
|
|
||||||
|
|
||||||
import { CompositeBrainConnections } from "./components/CompositeBrainConnections/CompositeBrainConnections";
|
|
||||||
import { KnowledgeToFeedInput } from "./components/KnowledgeToFeedInput";
|
|
||||||
import { useBrainCreationHandler } from "./hooks/useBrainCreationHandler";
|
|
||||||
import { useBrainKnowledgeStep } from "./hooks/useBrainKnowledgeStep";
|
|
||||||
|
|
||||||
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
|
||||||
|
|
||||||
type BrainKnowledgeStepProps = {
|
|
||||||
onCancelBrainCreation: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const BrainKnowledgeStep = ({
|
|
||||||
onCancelBrainCreation,
|
|
||||||
}: BrainKnowledgeStepProps): JSX.Element => {
|
|
||||||
const { brainType, isSubmitButtonDisabled } = useBrainKnowledgeStep();
|
|
||||||
const { t } = useTranslation(["translation"]);
|
|
||||||
const { goToPreviousStep, currentStep } = useBrainCreationSteps();
|
|
||||||
const { handleCreateBrain, isBrainCreationPending } = useBrainCreationHandler(
|
|
||||||
{
|
|
||||||
closeBrainCreationModal: onCancelBrainCreation,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const brainTypeToKnowledgeComponent: Record<BrainType, JSX.Element> = {
|
|
||||||
doc: <KnowledgeToFeedInput />,
|
|
||||||
api: <ApiRequestDefinition />,
|
|
||||||
composite: <CompositeBrainConnections />,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (currentStep !== "KNOWLEDGE" || brainType === undefined) {
|
|
||||||
return <Fragment />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{brainTypeToKnowledgeComponent[brainType]}
|
|
||||||
<div className="flex flex-row justify-between items-center flex-1 mt-10 w-full">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="tertiary"
|
|
||||||
onClick={onCancelBrainCreation}
|
|
||||||
className="text-primary"
|
|
||||||
disabled={isBrainCreationPending}
|
|
||||||
>
|
|
||||||
{t("cancel", { ns: "translation" })}
|
|
||||||
</Button>
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="secondary"
|
|
||||||
onClick={goToPreviousStep}
|
|
||||||
className="py-2 border-primary text-primary"
|
|
||||||
disabled={isBrainCreationPending}
|
|
||||||
>
|
|
||||||
<FaArrowLeft className="text-xl" size={16} />
|
|
||||||
{t("previous", { ns: "translation" })}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
className="bg-primary text-white py-2 border-none"
|
|
||||||
type="button"
|
|
||||||
onClick={() => void handleCreateBrain()}
|
|
||||||
disabled={isSubmitButtonDisabled || isBrainCreationPending}
|
|
||||||
isLoading={isBrainCreationPending}
|
|
||||||
>
|
|
||||||
{t("createButton", { ns: "translation" })}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,26 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import {
|
|
||||||
Crawler,
|
|
||||||
FeedItems,
|
|
||||||
FileUploader,
|
|
||||||
} from "@/lib/components/KnowledgeToFeedInput/components";
|
|
||||||
|
|
||||||
export const KnowledgeToFeedInput = (): JSX.Element => {
|
|
||||||
const { t } = useTranslation(["translation", "upload"]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<div className="flex flex-row gap-10 justify-between items-center mt-5">
|
|
||||||
<FileUploader />
|
|
||||||
<span className="whitespace-nowrap ">
|
|
||||||
{`${t("and", { ns: "translation" })} / ${t("or", {
|
|
||||||
ns: "translation",
|
|
||||||
})}`}
|
|
||||||
</span>
|
|
||||||
<Crawler />
|
|
||||||
</div>
|
|
||||||
<FeedItems />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,52 +0,0 @@
|
|||||||
import { useFormContext } from "react-hook-form";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
|
||||||
import { useToast } from "@/lib/hooks";
|
|
||||||
|
|
||||||
import { useBrainCreationApi } from "./useBrainCreationApi";
|
|
||||||
|
|
||||||
type UseBrainCreationHandler = {
|
|
||||||
closeBrainCreationModal: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
||||||
export const useBrainCreationHandler = ({
|
|
||||||
closeBrainCreationModal,
|
|
||||||
}: UseBrainCreationHandler) => {
|
|
||||||
const { getValues } = useFormContext<CreateBrainProps>();
|
|
||||||
const { publish } = useToast();
|
|
||||||
const { t } = useTranslation(["brain", "config"]);
|
|
||||||
|
|
||||||
const { isBrainCreationPending, createBrain } = useBrainCreationApi({
|
|
||||||
closeBrainCreationModal,
|
|
||||||
});
|
|
||||||
|
|
||||||
const handleCreateBrain = () => {
|
|
||||||
const { name, description } = getValues();
|
|
||||||
|
|
||||||
if (name.trim() === "" || isBrainCreationPending) {
|
|
||||||
publish({
|
|
||||||
variant: "danger",
|
|
||||||
text: t("nameRequired", { ns: "config" }),
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (description.trim() === "") {
|
|
||||||
publish({
|
|
||||||
variant: "danger",
|
|
||||||
text: t("descriptionRequired", { ns: "config" }),
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
createBrain();
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
handleCreateBrain,
|
|
||||||
isBrainCreationPending,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,24 +0,0 @@
|
|||||||
import { useFormContext } from "react-hook-form";
|
|
||||||
|
|
||||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
||||||
export const useBrainKnowledgeStep = () => {
|
|
||||||
const { watch } = useFormContext<CreateBrainProps>();
|
|
||||||
const brainType = watch("brain_type");
|
|
||||||
const url = watch("brain_definition.url");
|
|
||||||
const compositeBrainConnections = watch("connected_brains_ids") ?? [];
|
|
||||||
const isApiBrain = brainType === "api";
|
|
||||||
const isCompositeBrain = brainType === "composite";
|
|
||||||
|
|
||||||
const isApiBrainDefinitionsFilled = url !== "";
|
|
||||||
|
|
||||||
const isSubmitButtonDisabled =
|
|
||||||
(isCompositeBrain && compositeBrainConnections.length === 0) ||
|
|
||||||
(isApiBrain && !isApiBrainDefinitionsFilled);
|
|
||||||
|
|
||||||
return {
|
|
||||||
brainType,
|
|
||||||
isSubmitButtonDisabled,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,96 +0,0 @@
|
|||||||
import { Fragment } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { FaArrowLeft, FaArrowRight } from "react-icons/fa";
|
|
||||||
|
|
||||||
import Button from "@/lib/components/ui/Button";
|
|
||||||
import Field from "@/lib/components/ui/Field";
|
|
||||||
import { TextArea } from "@/lib/components/ui/TextArea";
|
|
||||||
|
|
||||||
import { PublicAccessConfirmationModal } from "./components/PublicAccessConfirmationModal";
|
|
||||||
import { useBrainParamsStep } from "./hooks/useBrainParamsStep";
|
|
||||||
import { usePublicAccessConfirmationModal } from "./hooks/usePublicAccessConfirmationModal";
|
|
||||||
|
|
||||||
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
|
||||||
import { useBrainTypeSelectionStep } from "../BrainTypeSelectionStep/hooks/useBrainTypeSelectionStep";
|
|
||||||
|
|
||||||
type BrainParamsStepProps = {
|
|
||||||
onCancelBrainCreation: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const BrainParamsStep = ({
|
|
||||||
onCancelBrainCreation,
|
|
||||||
}: BrainParamsStepProps): JSX.Element => {
|
|
||||||
const { goToNextStep, goToPreviousStep, currentStep } =
|
|
||||||
useBrainCreationSteps();
|
|
||||||
const { register } = useBrainTypeSelectionStep();
|
|
||||||
const { t } = useTranslation(["translation", "brain", "config"]);
|
|
||||||
const { isNextButtonDisabled } = useBrainParamsStep();
|
|
||||||
const {
|
|
||||||
isPublicAccessConfirmationModalOpened,
|
|
||||||
onCancelPublicAccess,
|
|
||||||
onConfirmPublicAccess,
|
|
||||||
} = usePublicAccessConfirmationModal();
|
|
||||||
|
|
||||||
if (currentStep !== "BRAIN_PARAMS") {
|
|
||||||
return <Fragment />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Field
|
|
||||||
label={t("brainName", { ns: "brain" })}
|
|
||||||
autoFocus
|
|
||||||
placeholder={t("brainNamePlaceholder", { ns: "brain" })}
|
|
||||||
autoComplete="off"
|
|
||||||
className="flex-1"
|
|
||||||
required
|
|
||||||
{...register("name")}
|
|
||||||
/>
|
|
||||||
<TextArea
|
|
||||||
label={t("brainDescription", { ns: "brain" })}
|
|
||||||
placeholder={t("brainDescriptionPlaceholder", { ns: "brain" })}
|
|
||||||
autoComplete="off"
|
|
||||||
className="flex-1 m-3"
|
|
||||||
required
|
|
||||||
{...register("description")}
|
|
||||||
/>
|
|
||||||
<div className="flex flex-row justify-between items-center flex-1 mt-10 w-full">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="tertiary"
|
|
||||||
onClick={onCancelBrainCreation}
|
|
||||||
className="text-primary"
|
|
||||||
>
|
|
||||||
{t("cancel", { ns: "translation" })}
|
|
||||||
</Button>
|
|
||||||
<div className="flex gap-4">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="secondary"
|
|
||||||
onClick={goToPreviousStep}
|
|
||||||
className="py-2 border-primary text-primary"
|
|
||||||
>
|
|
||||||
<FaArrowLeft className="text-xl" size={16} />
|
|
||||||
{t("previous", { ns: "translation" })}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
className="bg-primary text-white py-2 border-none"
|
|
||||||
type="button"
|
|
||||||
onClick={goToNextStep}
|
|
||||||
disabled={isNextButtonDisabled}
|
|
||||||
>
|
|
||||||
{t("next", { ns: "translation" })}
|
|
||||||
<FaArrowRight className="text-xl" size={16} />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<PublicAccessConfirmationModal
|
|
||||||
opened={isPublicAccessConfirmationModalOpened}
|
|
||||||
onClose={onCancelPublicAccess}
|
|
||||||
onCancel={onCancelPublicAccess}
|
|
||||||
onConfirm={onConfirmPublicAccess}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,42 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import Button from "@/lib/components/ui/Button";
|
|
||||||
import { Modal } from "@/lib/components/ui/Modal/Modal";
|
|
||||||
|
|
||||||
type PublicAccessConfirmationModalProps = {
|
|
||||||
opened: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
onConfirm: () => void;
|
|
||||||
onCancel: () => void;
|
|
||||||
};
|
|
||||||
export const PublicAccessConfirmationModal = ({
|
|
||||||
opened,
|
|
||||||
onClose,
|
|
||||||
onConfirm,
|
|
||||||
onCancel,
|
|
||||||
}: PublicAccessConfirmationModalProps): JSX.Element => {
|
|
||||||
const { t } = useTranslation(["brain"]);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Modal isOpen={opened} setOpen={onClose} CloseTrigger={<div />}>
|
|
||||||
<div
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: t("set_brain_status_to_public_modal_title"),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: t("set_brain_status_to_public_modal_description"),
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<div className="flex flex-row justify-between pt-10 px-10 items-center">
|
|
||||||
<Button type="button" onClick={onConfirm} variant="secondary">
|
|
||||||
{t("confirm_set_brain_status_to_public")}
|
|
||||||
</Button>
|
|
||||||
<Button type="button" onClick={onCancel}>
|
|
||||||
{t("cancel_set_brain_status_to_public")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Modal>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,16 +0,0 @@
|
|||||||
import { useFormContext } from "react-hook-form";
|
|
||||||
|
|
||||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
||||||
export const useBrainParamsStep = () => {
|
|
||||||
const { watch } = useFormContext<CreateBrainProps>();
|
|
||||||
const brainName = watch("name");
|
|
||||||
const description = watch("description");
|
|
||||||
|
|
||||||
const isNextButtonDisabled = brainName === "" || description === "";
|
|
||||||
|
|
||||||
return {
|
|
||||||
isNextButtonDisabled,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,25 +0,0 @@
|
|||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import { BrainStatus } from "@/lib/types/BrainConfig";
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
||||||
export const useBrainStatusOptions = () => {
|
|
||||||
const { t } = useTranslation(["translation", "brain", "config"]);
|
|
||||||
|
|
||||||
const brainStatusOptions: {
|
|
||||||
label: string;
|
|
||||||
value: BrainStatus;
|
|
||||||
}[] = [
|
|
||||||
{
|
|
||||||
label: t("private_brain_label", { ns: "brain" }),
|
|
||||||
value: "private",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t("public_brain_label", { ns: "brain" }),
|
|
||||||
value: "public",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
return {
|
|
||||||
brainStatusOptions,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,43 +0,0 @@
|
|||||||
import { useEffect, useState } from "react";
|
|
||||||
import { useFormContext } from "react-hook-form";
|
|
||||||
|
|
||||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
||||||
export const usePublicAccessConfirmationModal = () => {
|
|
||||||
const {
|
|
||||||
watch,
|
|
||||||
setValue,
|
|
||||||
formState: { dirtyFields },
|
|
||||||
} = useFormContext<CreateBrainProps>();
|
|
||||||
|
|
||||||
const [
|
|
||||||
isPublicAccessConfirmationModalOpened,
|
|
||||||
setIsPublicAccessConfirmationModalOpened,
|
|
||||||
] = useState(false);
|
|
||||||
|
|
||||||
const status = watch("status");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (status === "public" && dirtyFields.status === true) {
|
|
||||||
setIsPublicAccessConfirmationModalOpened(true);
|
|
||||||
}
|
|
||||||
}, [dirtyFields.status, status]);
|
|
||||||
|
|
||||||
const onConfirmPublicAccess = () => {
|
|
||||||
setIsPublicAccessConfirmationModalOpened(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCancelPublicAccess = () => {
|
|
||||||
setValue("status", "private", {
|
|
||||||
shouldDirty: true,
|
|
||||||
});
|
|
||||||
setIsPublicAccessConfirmationModalOpened(false);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
isPublicAccessConfirmationModalOpened,
|
|
||||||
onConfirmPublicAccess,
|
|
||||||
onCancelPublicAccess,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,56 +0,0 @@
|
|||||||
import { Fragment } from "react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
import { FaArrowRight } from "react-icons/fa";
|
|
||||||
|
|
||||||
import Button from "@/lib/components/ui/Button";
|
|
||||||
import { Radio } from "@/lib/components/ui/Radio";
|
|
||||||
|
|
||||||
import { useBrainTypeSelectionStep } from "./hooks/useBrainTypeSelectionStep";
|
|
||||||
import { useKnowledgeSourceLabel } from "./hooks/useKnowledgeSourceLabel";
|
|
||||||
|
|
||||||
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
|
||||||
|
|
||||||
type BrainTypeSelectionStepProps = {
|
|
||||||
onCancelBrainCreation: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const BrainTypeSelectionStep = ({
|
|
||||||
onCancelBrainCreation,
|
|
||||||
}: BrainTypeSelectionStepProps): JSX.Element => {
|
|
||||||
const { knowledgeSourceOptions } = useKnowledgeSourceLabel();
|
|
||||||
const { register } = useBrainTypeSelectionStep();
|
|
||||||
const { goToNextStep, currentStep } = useBrainCreationSteps();
|
|
||||||
const { t } = useTranslation(["translation"]);
|
|
||||||
if (currentStep !== "BRAIN_TYPE") {
|
|
||||||
return <Fragment />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<Radio
|
|
||||||
items={knowledgeSourceOptions}
|
|
||||||
className="flex-1 justify-between"
|
|
||||||
{...register("brain_type")}
|
|
||||||
/>
|
|
||||||
<div className="flex flex-row flex-1 justify-center w-full gap-48 mt-10">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
variant="tertiary"
|
|
||||||
onClick={onCancelBrainCreation}
|
|
||||||
>
|
|
||||||
{t("cancel")}
|
|
||||||
</Button>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
className="bg-primary text-white py-2 border-none"
|
|
||||||
type="button"
|
|
||||||
data-testid="create-brain-submit-button"
|
|
||||||
onClick={goToNextStep}
|
|
||||||
>
|
|
||||||
{t("next")}
|
|
||||||
<FaArrowRight className="text-xl" size={16} />
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,21 +0,0 @@
|
|||||||
import { useEffect } from "react";
|
|
||||||
import { useFormContext } from "react-hook-form";
|
|
||||||
|
|
||||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
||||||
export const useBrainTypeSelectionStep = () => {
|
|
||||||
const { register, watch, reset, setValue } =
|
|
||||||
useFormContext<CreateBrainProps>();
|
|
||||||
const brainType = watch("brain_type");
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const currentBrainType = brainType;
|
|
||||||
reset();
|
|
||||||
setValue("brain_type", currentBrainType);
|
|
||||||
}, [brainType, reset, setValue]);
|
|
||||||
|
|
||||||
return {
|
|
||||||
register,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,35 +0,0 @@
|
|||||||
import { useFeatureIsOn } from "@growthbook/growthbook-react";
|
|
||||||
import { useTranslation } from "react-i18next";
|
|
||||||
|
|
||||||
import { BrainType } from "@/lib/types/BrainConfig";
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
||||||
export const useKnowledgeSourceLabel = () => {
|
|
||||||
const { t } = useTranslation(["translation", "brain", "config"]);
|
|
||||||
const isCompositeBrainActivated = useFeatureIsOn("agent-brain");
|
|
||||||
|
|
||||||
const knowledgeSourceOptions: {
|
|
||||||
label: string;
|
|
||||||
value: BrainType;
|
|
||||||
}[] = [
|
|
||||||
{
|
|
||||||
label: t("knowledge_source_doc", { ns: "brain" }),
|
|
||||||
value: "doc",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: t("knowledge_source_api", { ns: "brain" }),
|
|
||||||
value: "api",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
if (isCompositeBrainActivated) {
|
|
||||||
knowledgeSourceOptions.push({
|
|
||||||
label: t("knowledge_source_composite_brain", { ns: "brain" }),
|
|
||||||
value: "composite",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
knowledgeSourceOptions,
|
|
||||||
};
|
|
||||||
};
|
|
@ -1,31 +0,0 @@
|
|||||||
import { Fragment } from "react";
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
|
|
||||||
import { Step } from "./components/Step";
|
|
||||||
|
|
||||||
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
|
||||||
|
|
||||||
export const Stepper = (): JSX.Element => {
|
|
||||||
const { currentStep, steps } = useBrainCreationSteps();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-row justify-between w-full px-12 mb-12">
|
|
||||||
{steps.map((step, index) => (
|
|
||||||
<Fragment key={step.value}>
|
|
||||||
<Step index={index} step={step} />
|
|
||||||
{index < steps.length - 1 && ( // Add horizontal line for all but the last step
|
|
||||||
<hr
|
|
||||||
className={cn(
|
|
||||||
"flex-grow border-t-2 border-primary m-4",
|
|
||||||
step.value === currentStep
|
|
||||||
? "border-primary"
|
|
||||||
: "border-gray-300"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</Fragment>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1,39 +0,0 @@
|
|||||||
import { FaCheckCircle } from "react-icons/fa";
|
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
|
||||||
|
|
||||||
import { useBrainCreationSteps } from "../../../hooks/useBrainCreationSteps";
|
|
||||||
import { Step as StepType } from "../../../types";
|
|
||||||
|
|
||||||
type StepProps = {
|
|
||||||
index: number;
|
|
||||||
step: StepType;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Step = ({ index, step }: StepProps): JSX.Element => {
|
|
||||||
const { currentStep, currentStepIndex } = useBrainCreationSteps();
|
|
||||||
const isStepDone = index < currentStepIndex;
|
|
||||||
const stepContent = isStepDone ? <FaCheckCircle /> : index + 1;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={step.label}
|
|
||||||
className="flex flex-row justify-center items-center flex-1"
|
|
||||||
>
|
|
||||||
<div className="flex flex-col justify-center items-center">
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"h-[40px] w-[40px] border-solid rounded-full flex flex-row items-center justify-center mb-2 border-primary border-2 text-primary",
|
|
||||||
isStepDone ? "bg-primary text-white" : "",
|
|
||||||
step.value === currentStep ? "bg-primary text-white" : ""
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{stepContent}
|
|
||||||
</div>
|
|
||||||
<span key={step.label} className="text-xs text-center">
|
|
||||||
{step.label}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1 +0,0 @@
|
|||||||
export * from "./AddBrainSteps";
|
|
@ -1,6 +0,0 @@
|
|||||||
import { BrainCreationStep } from "../../types";
|
|
||||||
|
|
||||||
export type Step = {
|
|
||||||
label: string;
|
|
||||||
value: BrainCreationStep;
|
|
||||||
};
|
|
@ -0,0 +1,19 @@
|
|||||||
|
@use "@/styles/Spacings.module.scss";
|
||||||
|
@use "@/styles/Typography.module.scss";
|
||||||
|
|
||||||
|
.brain_knowledge_wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-inline: Spacings.$spacing08;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include Typography.H2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons_wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,51 @@
|
|||||||
|
import { KnowledgeToFeed } from "@/app/chat/[chatId]/components/ActionsBar/components";
|
||||||
|
import QuivrButton from "@/lib/components/ui/QuivrButton/QuivrButton";
|
||||||
|
|
||||||
|
import styles from "./BrainKnowledgeStep.module.scss";
|
||||||
|
import { useBrainCreationApi } from "./hooks/useBrainCreationApi";
|
||||||
|
|
||||||
|
import { useBrainCreationContext } from "../../brainCreation-provider";
|
||||||
|
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
||||||
|
|
||||||
|
export const BrainKnowledgeStep = (): JSX.Element => {
|
||||||
|
const { currentStepIndex, goToPreviousStep } = useBrainCreationSteps();
|
||||||
|
const { createBrain } = useBrainCreationApi();
|
||||||
|
const { creating, setCreating } = useBrainCreationContext();
|
||||||
|
|
||||||
|
const previous = (): void => {
|
||||||
|
goToPreviousStep();
|
||||||
|
};
|
||||||
|
|
||||||
|
const feed = (): void => {
|
||||||
|
setCreating(true);
|
||||||
|
createBrain();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (currentStepIndex !== 2) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.brain_knowledge_wrapper}>
|
||||||
|
<div>
|
||||||
|
<span className={styles.title}>Feed your brain</span>
|
||||||
|
<KnowledgeToFeed hideBrainSelector={true} />
|
||||||
|
</div>
|
||||||
|
<div className={styles.buttons_wrapper}>
|
||||||
|
<QuivrButton
|
||||||
|
label="Previous step"
|
||||||
|
color="primary"
|
||||||
|
iconName="chevronLeft"
|
||||||
|
onClick={previous}
|
||||||
|
/>
|
||||||
|
<QuivrButton
|
||||||
|
label="Create brain"
|
||||||
|
color="primary"
|
||||||
|
iconName="add"
|
||||||
|
onClick={feed}
|
||||||
|
isLoading={creating}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -2,7 +2,7 @@ import { CheckedState } from "@radix-ui/react-checkbox";
|
|||||||
import { UUID } from "crypto";
|
import { UUID } from "crypto";
|
||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types/types";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export const useConnectableBrain = () => {
|
export const useConnectableBrain = () => {
|
@ -4,22 +4,17 @@ import { useFormContext } from "react-hook-form";
|
|||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { PUBLIC_BRAINS_KEY } from "@/lib/api/brain/config";
|
import { PUBLIC_BRAINS_KEY } from "@/lib/api/brain/config";
|
||||||
import { useBrainApi } from "@/lib/api/brain/useBrainApi";
|
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types/types";
|
||||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
|
||||||
import { useKnowledgeToFeedInput } from "@/lib/components/KnowledgeToFeedInput/hooks/useKnowledgeToFeedInput.ts";
|
import { useKnowledgeToFeedInput } from "@/lib/components/KnowledgeToFeedInput/hooks/useKnowledgeToFeedInput.ts";
|
||||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||||
import { useKnowledgeToFeedContext } from "@/lib/context/KnowledgeToFeedProvider/hooks/useKnowledgeToFeedContext";
|
import { useKnowledgeToFeedContext } from "@/lib/context/KnowledgeToFeedProvider/hooks/useKnowledgeToFeedContext";
|
||||||
import { useToast } from "@/lib/hooks";
|
import { useToast } from "@/lib/hooks";
|
||||||
import { useKnowledgeToFeedFilesAndUrls } from "@/lib/hooks/useKnowledgeToFeed";
|
import { useKnowledgeToFeedFilesAndUrls } from "@/lib/hooks/useKnowledgeToFeed";
|
||||||
|
|
||||||
type UseBrainCreationHandler = {
|
import { useBrainCreationContext } from "../../../brainCreation-provider";
|
||||||
closeBrainCreationModal: () => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export const useBrainCreationApi = ({
|
export const useBrainCreationApi = () => {
|
||||||
closeBrainCreationModal,
|
|
||||||
}: UseBrainCreationHandler) => {
|
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const { publish } = useToast();
|
const { publish } = useToast();
|
||||||
const { t } = useTranslation(["brain", "config"]);
|
const { t } = useTranslation(["brain", "config"]);
|
||||||
@ -27,8 +22,9 @@ export const useBrainCreationApi = ({
|
|||||||
const { getValues, reset } = useFormContext<CreateBrainProps>();
|
const { getValues, reset } = useFormContext<CreateBrainProps>();
|
||||||
const { setKnowledgeToFeed } = useKnowledgeToFeedContext();
|
const { setKnowledgeToFeed } = useKnowledgeToFeedContext();
|
||||||
const { createBrain: createBrainApi, setCurrentBrainId } = useBrainContext();
|
const { createBrain: createBrainApi, setCurrentBrainId } = useBrainContext();
|
||||||
const { setAsDefaultBrain } = useBrainApi();
|
|
||||||
const { crawlWebsiteHandler, uploadFileHandler } = useKnowledgeToFeedInput();
|
const { crawlWebsiteHandler, uploadFileHandler } = useKnowledgeToFeedInput();
|
||||||
|
const { setIsBrainCreationModalOpened, setCreating } =
|
||||||
|
useBrainCreationContext();
|
||||||
|
|
||||||
const handleFeedBrain = async (brainId: UUID): Promise<void> => {
|
const handleFeedBrain = async (brainId: UUID): Promise<void> => {
|
||||||
const uploadPromises = files.map((file) =>
|
const uploadPromises = files.map((file) =>
|
||||||
@ -44,7 +40,6 @@ export const useBrainCreationApi = ({
|
|||||||
const {
|
const {
|
||||||
name,
|
name,
|
||||||
description,
|
description,
|
||||||
setDefault,
|
|
||||||
brain_definition,
|
brain_definition,
|
||||||
brain_secrets_values,
|
brain_secrets_values,
|
||||||
status,
|
status,
|
||||||
@ -76,12 +71,11 @@ export const useBrainCreationApi = ({
|
|||||||
void handleFeedBrain(createdBrainId);
|
void handleFeedBrain(createdBrainId);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (setDefault) {
|
|
||||||
await setAsDefaultBrain(createdBrainId);
|
|
||||||
}
|
|
||||||
setCurrentBrainId(createdBrainId);
|
setCurrentBrainId(createdBrainId);
|
||||||
closeBrainCreationModal();
|
setIsBrainCreationModalOpened(false);
|
||||||
|
setCreating(false);
|
||||||
reset();
|
reset();
|
||||||
|
|
||||||
void queryClient.invalidateQueries({
|
void queryClient.invalidateQueries({
|
||||||
queryKey: [PUBLIC_BRAINS_KEY],
|
queryKey: [PUBLIC_BRAINS_KEY],
|
||||||
});
|
});
|
@ -0,0 +1,25 @@
|
|||||||
|
@use "@/styles/Spacings.module.scss";
|
||||||
|
@use "@/styles/Typography.module.scss";
|
||||||
|
|
||||||
|
.brain_main_infos_wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 100%;
|
||||||
|
padding-inline: Spacings.$spacing08;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include Typography.H2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inputs_wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: Spacings.$spacing05;
|
||||||
|
}
|
||||||
|
|
||||||
|
.buttons_wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
import { Controller, useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
|
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types/types";
|
||||||
|
import QuivrButton from "@/lib/components/ui/QuivrButton/QuivrButton";
|
||||||
|
import { TextAreaInput } from "@/lib/components/ui/TextAreaInput/TextAreaInput";
|
||||||
|
import { TextInput } from "@/lib/components/ui/TextInput/TextInput";
|
||||||
|
|
||||||
|
import styles from "./BrainMainInfosStep.module.scss";
|
||||||
|
|
||||||
|
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
||||||
|
|
||||||
|
export const BrainMainInfosStep = (): JSX.Element => {
|
||||||
|
const { currentStepIndex, goToNextStep, goToPreviousStep } =
|
||||||
|
useBrainCreationSteps();
|
||||||
|
|
||||||
|
const { watch } = useFormContext<CreateBrainProps>();
|
||||||
|
const name = watch("name");
|
||||||
|
const description = watch("description");
|
||||||
|
|
||||||
|
const isDisabled = !name || !description;
|
||||||
|
|
||||||
|
const next = (): void => {
|
||||||
|
goToNextStep();
|
||||||
|
};
|
||||||
|
|
||||||
|
const previous = (): void => {
|
||||||
|
goToPreviousStep();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (currentStepIndex !== 1) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.brain_main_infos_wrapper}>
|
||||||
|
<div className={styles.inputs_wrapper}>
|
||||||
|
<span className={styles.title}>Define brain identity</span>
|
||||||
|
<Controller
|
||||||
|
name="name"
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextInput
|
||||||
|
label="Name"
|
||||||
|
inputValue={field.value as string} // Explicitly specify the type as string
|
||||||
|
setInputValue={field.onChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Controller
|
||||||
|
name="description"
|
||||||
|
render={({ field }) => (
|
||||||
|
<TextAreaInput
|
||||||
|
label="Description"
|
||||||
|
inputValue={field.value as string}
|
||||||
|
setInputValue={field.onChange}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className={styles.buttons_wrapper}>
|
||||||
|
<QuivrButton
|
||||||
|
color="primary"
|
||||||
|
label="Previous Step"
|
||||||
|
onClick={() => previous()}
|
||||||
|
iconName="chevronLeft"
|
||||||
|
/>
|
||||||
|
<QuivrButton
|
||||||
|
color="primary"
|
||||||
|
label="Next Step"
|
||||||
|
onClick={() => next()}
|
||||||
|
iconName="chevronRight"
|
||||||
|
disabled={isDisabled}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,47 @@
|
|||||||
|
@use "@/styles/Colors.module.scss";
|
||||||
|
@use "@/styles/Radius.module.scss";
|
||||||
|
@use "@/styles/Spacings.module.scss";
|
||||||
|
@use "@/styles/Typography.module.scss";
|
||||||
|
|
||||||
|
.brain_type_wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: Spacings.$spacing05;
|
||||||
|
padding: Spacings.$spacing05;
|
||||||
|
border-radius: Radius.$big;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
pointer-events: none;
|
||||||
|
background-color: Colors.$lightest-grey;
|
||||||
|
opacity: 0.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.first_line_wrapper {
|
||||||
|
display: flex;
|
||||||
|
gap: Spacings.$spacing03;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
@include Typography.H3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: Colors.$dark-grey;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover,
|
||||||
|
&.selected {
|
||||||
|
border-color: Colors.$primary;
|
||||||
|
|
||||||
|
.name {
|
||||||
|
color: Colors.$primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.description {
|
||||||
|
color: Colors.$black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import { BrainType } from "@/lib/components/AddBrainModal/types/types";
|
||||||
|
import Icon from "@/lib/components/ui/Icon/Icon";
|
||||||
|
|
||||||
|
import styles from "./BrainTypeSelection.module.scss";
|
||||||
|
|
||||||
|
export const BrainTypeSelection = ({
|
||||||
|
brainType,
|
||||||
|
onClick,
|
||||||
|
selected,
|
||||||
|
}: {
|
||||||
|
brainType: BrainType;
|
||||||
|
onClick: () => void;
|
||||||
|
selected: boolean;
|
||||||
|
}): JSX.Element => {
|
||||||
|
const [isHovered, setIsHovered] = useState<boolean>(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
${styles.brain_type_wrapper}
|
||||||
|
${brainType.disabled && styles.disabled}
|
||||||
|
${selected && styles.selected}
|
||||||
|
`}
|
||||||
|
onMouseEnter={() => setIsHovered(true)}
|
||||||
|
onMouseLeave={() => setIsHovered(false)}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<div className={styles.first_line_wrapper}>
|
||||||
|
<Icon
|
||||||
|
name={brainType.iconName}
|
||||||
|
size="normal"
|
||||||
|
color={isHovered || selected ? "primary" : "black"}
|
||||||
|
/>
|
||||||
|
<span className={styles.name}>{brainType.name}</span>
|
||||||
|
</div>
|
||||||
|
<span className={styles.description}>{brainType.description}</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,31 @@
|
|||||||
|
@use "@/styles/ScreenSizes.module.scss";
|
||||||
|
@use "@/styles/Spacings.module.scss";
|
||||||
|
@use "@/styles/Typography.module.scss";
|
||||||
|
|
||||||
|
.brain_types_wrapper {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 100%;
|
||||||
|
padding-inline: Spacings.$spacing08;
|
||||||
|
height: 100%;
|
||||||
|
gap: Spacings.$spacing05;
|
||||||
|
|
||||||
|
@media (max-width: ScreenSizes.$small) {
|
||||||
|
padding-inline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main_wrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: Spacings.$spacing05;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@include Typography.H2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
import { BrainType } from "@/lib/components/AddBrainModal/types/types";
|
||||||
|
import QuivrButton from "@/lib/components/ui/QuivrButton/QuivrButton";
|
||||||
|
|
||||||
|
import { BrainTypeSelection } from "./BrainTypeSelection/BrainTypeSelection";
|
||||||
|
import styles from "./BrainTypeSelectionStep.module.scss";
|
||||||
|
|
||||||
|
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
||||||
|
|
||||||
|
export const BrainTypeSelectionStep = (): JSX.Element => {
|
||||||
|
const [selectedIndex, setSelectedIndex] = useState<number>(0);
|
||||||
|
const { goToNextStep, currentStepIndex } = useBrainCreationSteps();
|
||||||
|
|
||||||
|
const brainTypes: BrainType[] = [
|
||||||
|
{
|
||||||
|
name: "Core Brain",
|
||||||
|
description: "Upload documents or website links to feed your brain.",
|
||||||
|
iconName: "feed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Sync Brain - Coming soon!",
|
||||||
|
description:
|
||||||
|
"Connect to your tools and applications to interact with your data.",
|
||||||
|
iconName: "software",
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Custom Brain - Coming soon!",
|
||||||
|
description:
|
||||||
|
"Explore your databases, converse with your APIs, and much more!",
|
||||||
|
iconName: "custom",
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const next = (): void => {
|
||||||
|
goToNextStep();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (currentStepIndex !== 0) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.brain_types_wrapper}>
|
||||||
|
<div className={styles.main_wrapper}>
|
||||||
|
<span className={styles.title}>Choose a type of brain</span>
|
||||||
|
{brainTypes.map((brainType, index) => (
|
||||||
|
<div key={index}>
|
||||||
|
<BrainTypeSelection
|
||||||
|
brainType={brainType}
|
||||||
|
selected={index === selectedIndex}
|
||||||
|
onClick={() => setSelectedIndex(index)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<div className={styles.button}>
|
||||||
|
<QuivrButton
|
||||||
|
label="Next Step"
|
||||||
|
iconName="chevronRight"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => next()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -0,0 +1,105 @@
|
|||||||
|
@use "@/styles/Colors.module.scss";
|
||||||
|
@use "@/styles/Radius.module.scss";
|
||||||
|
@use "@/styles/Spacings.module.scss";
|
||||||
|
@use "@/styles/Typography.module.scss";
|
||||||
|
|
||||||
|
.stepper_wrapper {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.step {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
border-radius: Radius.$circle;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.circle {
|
||||||
|
width: 2.5rem;
|
||||||
|
height: 2.5rem;
|
||||||
|
background-color: Colors.$primary;
|
||||||
|
border-radius: Radius.$circle;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.inside_circle {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: Radius.$circle;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.done_step {
|
||||||
|
.circle {
|
||||||
|
background-color: Colors.$success;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step_info {
|
||||||
|
.step_status {
|
||||||
|
color: Colors.$success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.current_step {
|
||||||
|
.circle {
|
||||||
|
background-color: Colors.$white;
|
||||||
|
border: 1px solid Colors.$primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inside_circle {
|
||||||
|
background-color: Colors.$primary;
|
||||||
|
width: 70%;
|
||||||
|
height: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step_info {
|
||||||
|
.step_status {
|
||||||
|
color: Colors.$primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.pending_step {
|
||||||
|
.circle {
|
||||||
|
background-color: Colors.$primary-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
.step_info {
|
||||||
|
.step_status {
|
||||||
|
color: Colors.$normal-grey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.step_info {
|
||||||
|
margin-top: Spacings.$spacing03;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
font-size: Typography.$tiny;
|
||||||
|
width: 2.5rem;
|
||||||
|
|
||||||
|
.step_index {
|
||||||
|
white-space: nowrap;
|
||||||
|
color: Colors.$normal-grey;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bar {
|
||||||
|
flex-grow: 1;
|
||||||
|
height: 4px;
|
||||||
|
border-radius: Radius.$big;
|
||||||
|
background-color: Colors.$primary-light;
|
||||||
|
margin: 0 8px;
|
||||||
|
margin-top: Spacings.$spacing05;
|
||||||
|
|
||||||
|
&.done {
|
||||||
|
background-color: Colors.$success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,58 @@
|
|||||||
|
import { Icon } from "@/lib/components/ui/Icon/Icon";
|
||||||
|
|
||||||
|
import styles from "./Stepper.module.scss";
|
||||||
|
|
||||||
|
import { useBrainCreationSteps } from "../../hooks/useBrainCreationSteps";
|
||||||
|
|
||||||
|
export const Stepper = (): JSX.Element => {
|
||||||
|
const { currentStep, steps } = useBrainCreationSteps();
|
||||||
|
|
||||||
|
const currentStepIndex = steps.findIndex(
|
||||||
|
(step) => step.value === currentStep
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.stepper_wrapper}>
|
||||||
|
{steps.map((step, index) => (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
className={`${styles.step} ${
|
||||||
|
index === currentStepIndex
|
||||||
|
? styles.current_step
|
||||||
|
: index < currentStepIndex
|
||||||
|
? styles.done_step
|
||||||
|
: styles.pending_step
|
||||||
|
}`}
|
||||||
|
key={step.value}
|
||||||
|
>
|
||||||
|
<div className={styles.circle}>
|
||||||
|
<div className={styles.inside_circle}>
|
||||||
|
{index < currentStepIndex && (
|
||||||
|
<Icon name="check" size="normal" color="white" />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className={styles.step_info}>
|
||||||
|
<span className={styles.step_index}>STEP {index + 1}</span>
|
||||||
|
<span className={styles.step_status}>
|
||||||
|
{index === currentStepIndex
|
||||||
|
? "Progress"
|
||||||
|
: index < currentStepIndex
|
||||||
|
? "Completed"
|
||||||
|
: "Pending"}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{index < steps.length - 1 && (
|
||||||
|
<div
|
||||||
|
className={`
|
||||||
|
${styles.bar}
|
||||||
|
${index < currentStepIndex ? styles.done : ""}
|
||||||
|
`}
|
||||||
|
></div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -1 +0,0 @@
|
|||||||
export * from "./AddBrainSteps";
|
|
@ -1,9 +1,10 @@
|
|||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
import {
|
||||||
|
CreateBrainProps,
|
||||||
import { Step } from "../types";
|
Step,
|
||||||
|
} from "@/lib/components/AddBrainModal/types/types";
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||||
export const useBrainCreationSteps = () => {
|
export const useBrainCreationSteps = () => {
|
@ -1,4 +1,5 @@
|
|||||||
import { CreateBrainInput } from "@/lib/api/brain/types";
|
import { CreateBrainInput } from "@/lib/api/brain/types";
|
||||||
|
import { iconList } from "@/lib/helpers/iconList";
|
||||||
|
|
||||||
const brainCreationSteps = ["BRAIN_TYPE", "BRAIN_PARAMS", "KNOWLEDGE"] as const;
|
const brainCreationSteps = ["BRAIN_TYPE", "BRAIN_PARAMS", "KNOWLEDGE"] as const;
|
||||||
|
|
||||||
@ -8,3 +9,16 @@ export type CreateBrainProps = CreateBrainInput & {
|
|||||||
setDefault: boolean;
|
setDefault: boolean;
|
||||||
brainCreationStep: BrainCreationStep;
|
brainCreationStep: BrainCreationStep;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface BrainType {
|
||||||
|
name: string;
|
||||||
|
description: string;
|
||||||
|
iconName: keyof typeof iconList;
|
||||||
|
disabled?: boolean;
|
||||||
|
onClick?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Step = {
|
||||||
|
label: string;
|
||||||
|
value: BrainCreationStep;
|
||||||
|
};
|
@ -1,6 +1,6 @@
|
|||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types/types";
|
||||||
|
|
||||||
import { defaultParamDefinitionRow } from "../config";
|
import { defaultParamDefinitionRow } from "../config";
|
||||||
import { mapApiBrainDefinitionSchemaToParameterDefinition } from "../utils/mapApiBrainDefinitionSchemaToParameterDefinition";
|
import { mapApiBrainDefinitionSchemaToParameterDefinition } from "../utils/mapApiBrainDefinitionSchemaToParameterDefinition";
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useFormContext } from "react-hook-form";
|
import { useFormContext } from "react-hook-form";
|
||||||
|
|
||||||
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types";
|
import { CreateBrainProps } from "@/lib/components/AddBrainModal/types/types";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
brainSecretsValueKeyInForm,
|
brainSecretsValueKeyInForm,
|
||||||
|
@ -26,7 +26,7 @@
|
|||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
|
||||||
&.big_modal {
|
&.big_modal {
|
||||||
width: 50vw;
|
width: 40vw;
|
||||||
height: 90vh;
|
height: 90vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
@use "@/styles/Colors.module.scss";
|
||||||
|
@use "@/styles/Radius.module.scss";
|
||||||
|
@use "@/styles/Spacings.module.scss";
|
||||||
|
|
||||||
|
.text_area_input_container {
|
||||||
|
display: flex;
|
||||||
|
border: 1px solid Colors.$lighter-grey;
|
||||||
|
gap: Spacings.$spacing03;
|
||||||
|
padding-block: Spacings.$spacing02;
|
||||||
|
padding-inline: Spacings.$spacing03;
|
||||||
|
border-radius: Radius.$big;
|
||||||
|
align-items: center;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&.simple {
|
||||||
|
border: none;
|
||||||
|
padding: 0;
|
||||||
|
|
||||||
|
.text_input {
|
||||||
|
background-color: transparent;
|
||||||
|
width: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_area_input {
|
||||||
|
caret-color: Colors.$accent;
|
||||||
|
border: none;
|
||||||
|
flex: 1;
|
||||||
|
resize: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
frontend/lib/components/ui/TextAreaInput/TextAreaInput.tsx
Normal file
32
frontend/lib/components/ui/TextAreaInput/TextAreaInput.tsx
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import styles from "./TextAreaInput.module.scss";
|
||||||
|
|
||||||
|
type TextAreaInputProps = {
|
||||||
|
label: string;
|
||||||
|
inputValue: string;
|
||||||
|
setInputValue: (value: string) => void;
|
||||||
|
onSubmit?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const TextAreaInput = ({
|
||||||
|
label,
|
||||||
|
inputValue,
|
||||||
|
setInputValue,
|
||||||
|
onSubmit,
|
||||||
|
}: TextAreaInputProps): JSX.Element => {
|
||||||
|
return (
|
||||||
|
<div className={styles.text_area_input_container}>
|
||||||
|
<textarea
|
||||||
|
className={styles.text_area_input}
|
||||||
|
value={inputValue}
|
||||||
|
rows={5}
|
||||||
|
onChange={(e) => setInputValue(e.target.value)}
|
||||||
|
placeholder={label}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === "Enter" && onSubmit) {
|
||||||
|
onSubmit();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -1,5 +1,6 @@
|
|||||||
import { AiOutlineLoading3Quarters } from "react-icons/ai";
|
import { AiOutlineLoading3Quarters } from "react-icons/ai";
|
||||||
import { BsArrowRightShort, BsChatLeftText } from "react-icons/bs";
|
import { BsArrowRightShort, BsChatLeftText } from "react-icons/bs";
|
||||||
|
import { CgSoftwareDownload } from "react-icons/cg";
|
||||||
import { CiFlag1 } from "react-icons/ci";
|
import { CiFlag1 } from "react-icons/ci";
|
||||||
import {
|
import {
|
||||||
FaCheck,
|
FaCheck,
|
||||||
@ -21,6 +22,7 @@ import {
|
|||||||
LuBrain,
|
LuBrain,
|
||||||
LuBrainCircuit,
|
LuBrainCircuit,
|
||||||
LuChevronDown,
|
LuChevronDown,
|
||||||
|
LuChevronLeft,
|
||||||
LuChevronRight,
|
LuChevronRight,
|
||||||
LuCopy,
|
LuCopy,
|
||||||
LuFile,
|
LuFile,
|
||||||
@ -29,7 +31,9 @@ import {
|
|||||||
} from "react-icons/lu";
|
} from "react-icons/lu";
|
||||||
import {
|
import {
|
||||||
MdAlternateEmail,
|
MdAlternateEmail,
|
||||||
|
MdDashboardCustomize,
|
||||||
MdDeleteOutline,
|
MdDeleteOutline,
|
||||||
|
MdDynamicFeed,
|
||||||
MdHistory,
|
MdHistory,
|
||||||
MdOutlineModeEditOutline,
|
MdOutlineModeEditOutline,
|
||||||
MdUploadFile,
|
MdUploadFile,
|
||||||
@ -47,12 +51,15 @@ export const iconList: { [name: string]: IconType } = {
|
|||||||
check: FaCheck,
|
check: FaCheck,
|
||||||
checkCircle: FaCheckCircle,
|
checkCircle: FaCheckCircle,
|
||||||
chevronDown: LuChevronDown,
|
chevronDown: LuChevronDown,
|
||||||
|
chevronLeft: LuChevronLeft,
|
||||||
chevronRight: LuChevronRight,
|
chevronRight: LuChevronRight,
|
||||||
close: IoMdClose,
|
close: IoMdClose,
|
||||||
copy: LuCopy,
|
copy: LuCopy,
|
||||||
|
custom: MdDashboardCustomize,
|
||||||
delete: MdDeleteOutline,
|
delete: MdDeleteOutline,
|
||||||
edit: MdOutlineModeEditOutline,
|
edit: MdOutlineModeEditOutline,
|
||||||
email: MdAlternateEmail,
|
email: MdAlternateEmail,
|
||||||
|
feed: MdDynamicFeed,
|
||||||
file: LuFile,
|
file: LuFile,
|
||||||
flag: CiFlag1,
|
flag: CiFlag1,
|
||||||
followUp: IoArrowUpCircleOutline,
|
followUp: IoArrowUpCircleOutline,
|
||||||
@ -66,6 +73,7 @@ export const iconList: { [name: string]: IconType } = {
|
|||||||
redirection: BsArrowRightShort,
|
redirection: BsArrowRightShort,
|
||||||
search: LuSearch,
|
search: LuSearch,
|
||||||
settings: IoSettingsSharp,
|
settings: IoSettingsSharp,
|
||||||
|
software: CgSoftwareDownload,
|
||||||
star: FaRegStar,
|
star: FaRegStar,
|
||||||
unlock: FaUnlock,
|
unlock: FaUnlock,
|
||||||
upload: FiUpload,
|
upload: FiUpload,
|
||||||
|
@ -8,7 +8,7 @@ import { useOnboarding } from "./useOnboarding";
|
|||||||
import { useOnboardingTracker } from "./useOnboardingTracker";
|
import { useOnboardingTracker } from "./useOnboardingTracker";
|
||||||
import { useToast } from "./useToast";
|
import { useToast } from "./useToast";
|
||||||
|
|
||||||
import { useBrainCreationContext } from "../components/AddBrainModal/components/AddBrainSteps/brainCreation-provider";
|
import { useBrainCreationContext } from "../components/AddBrainModal/brainCreation-provider";
|
||||||
import { useKnowledgeToFeedContext } from "../context/KnowledgeToFeedProvider/hooks/useKnowledgeToFeedContext";
|
import { useKnowledgeToFeedContext } from "../context/KnowledgeToFeedProvider/hooks/useKnowledgeToFeedContext";
|
||||||
import { acceptedFormats } from "../helpers/acceptedFormats";
|
import { acceptedFormats } from "../helpers/acceptedFormats";
|
||||||
import { cloneFileWithSanitizedName } from "../helpers/cloneFileWithSanitizedName";
|
import { cloneFileWithSanitizedName } from "../helpers/cloneFileWithSanitizedName";
|
||||||
|
@ -30,3 +30,6 @@ $gold: #b8860b;
|
|||||||
// ERROR
|
// ERROR
|
||||||
$dangerous-dark: #e30c17;
|
$dangerous-dark: #e30c17;
|
||||||
$dangerous: #9b373c;
|
$dangerous: #9b373c;
|
||||||
|
|
||||||
|
// SUCCESS
|
||||||
|
$success: #47a455;
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
$circle: 50%;
|
||||||
$big: 12px;
|
$big: 12px;
|
||||||
$normal: 5px;
|
$normal: 5px;
|
||||||
$small: 2px;
|
$small: 2px;
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$tiny: 12px;
|
||||||
$small: 14px;
|
$small: 14px;
|
||||||
$medium: 16px;
|
$medium: 16px;
|
||||||
$large: 18px;
|
$large: 18px;
|
||||||
|
Loading…
Reference in New Issue
Block a user