mirror of
https://github.com/QuivrHQ/quivr.git
synced 2024-12-15 01:21:48 +03:00
refactor(settings tab): extract components (#1335)
# Description Extract the components from the settingsTab to increase readability of file and allow refactoring of huge useBrainSettings hook ## 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
1fe10d904d
commit
f7ae27b9cc
@ -4,9 +4,13 @@ import { useTranslation } from "react-i18next";
|
||||
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
|
||||
import { BrainTabTrigger, KnowledgeTab, PeopleTab } from "./components";
|
||||
import {
|
||||
BrainTabTrigger,
|
||||
KnowledgeTab,
|
||||
PeopleTab,
|
||||
SettingsTab,
|
||||
} from "./components";
|
||||
import { DeleteOrUnsubscribeConfirmationModal } from "./components/Modals/DeleteOrUnsubscribeConfirmationModal";
|
||||
import { SettingsTab } from "./components/SettingsTab/SettingsTab";
|
||||
import { useBrainManagementTabs } from "./hooks/useBrainManagementTabs";
|
||||
|
||||
export const BrainManagementTabs = (): JSX.Element => {
|
||||
|
@ -0,0 +1,94 @@
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import {
|
||||
BrainContextMock,
|
||||
BrainProviderMock,
|
||||
} from "@/lib/context/BrainProvider/mocks/BrainProviderMock";
|
||||
import {
|
||||
SupabaseContextMock,
|
||||
SupabaseProviderMock,
|
||||
} from "@/lib/context/SupabaseProvider/mocks/SupabaseProviderMock";
|
||||
|
||||
import { SettingsTab } from "./SettingsTab";
|
||||
|
||||
const useTranslationMock = vi.fn(() => ({
|
||||
t: (str: string): string => str,
|
||||
}));
|
||||
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => useTranslationMock(),
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/context/SupabaseProvider/supabase-provider", () => ({
|
||||
SupabaseContext: SupabaseContextMock,
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/context/BrainProvider/brain-provider", () => ({
|
||||
BrainContext: BrainContextMock,
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/api/brain/useBrainApi", () => ({
|
||||
useBrainApi: () => ({
|
||||
setAsDefaultBrain: () => [],
|
||||
updateBrain: () => [],
|
||||
}),
|
||||
}));
|
||||
|
||||
vi.mock("@tanstack/react-query", async () => {
|
||||
const actual = await vi.importActual<typeof import("@tanstack/react-query")>(
|
||||
"@tanstack/react-query"
|
||||
);
|
||||
|
||||
vi.mock("next/navigation", () => ({
|
||||
useRouter: () => ({ replace: vi.fn() }),
|
||||
}));
|
||||
|
||||
return {
|
||||
...actual,
|
||||
useQuery: () => ({
|
||||
data: {},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("@/lib/hooks", async () => {
|
||||
const actual = await vi.importActual<typeof import("@/lib/hooks")>(
|
||||
"@/lib/hooks"
|
||||
);
|
||||
|
||||
return {
|
||||
...actual,
|
||||
useAxios: () => ({
|
||||
...actual.useAxios(),
|
||||
axiosInstance: {
|
||||
get: vi.fn(() => ({
|
||||
data: [],
|
||||
})),
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe("Settings tab in brains-management", () => {
|
||||
it("should render the seettings tab correctly", () => {
|
||||
const brainId = "4adefe4e-eb08-4208-b237-";
|
||||
|
||||
render(
|
||||
<SupabaseProviderMock>
|
||||
<BrainProviderMock>
|
||||
<SettingsTab brainId={brainId} />
|
||||
</BrainProviderMock>
|
||||
</SupabaseProviderMock>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByRole("button", { name: "setDefaultBrain" })
|
||||
).toBeVisible();
|
||||
expect(screen.getByText("brainName")).toBeVisible();
|
||||
expect(screen.getByLabelText("brainDescription")).toBeVisible();
|
||||
|
||||
expect(screen.getByLabelText("promptName")).toBeVisible();
|
||||
});
|
||||
});
|
||||
2;
|
@ -1,22 +1,15 @@
|
||||
/* eslint-disable max-lines */
|
||||
/* eslint max-lines:["error", 125] */
|
||||
|
||||
import { UUID } from "crypto";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { FaSpinner } from "react-icons/fa";
|
||||
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { Chip } from "@/lib/components/ui/Chip";
|
||||
import { Divider } from "@/lib/components/ui/Divider";
|
||||
import Field from "@/lib/components/ui/Field";
|
||||
import { Radio } from "@/lib/components/ui/Radio";
|
||||
import { TextArea } from "@/lib/components/ui/TextArea";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
import { defineMaxTokens } from "@/lib/helpers/defineMaxTokens";
|
||||
import { SaveButton } from "@/shared/SaveButton";
|
||||
|
||||
import { GeneralInformation, ModelSelection, Prompt } from "./components";
|
||||
import { AccessConfirmationModal } from "./components/PrivateAccessConfirmationModal/AccessConfirmationModal";
|
||||
import { useAccessConfirmationModal } from "./components/PrivateAccessConfirmationModal/hooks/useAccessConfirmationModal";
|
||||
import { PublicPrompts } from "./components/PublicPrompts/PublicPrompts";
|
||||
import { useSettingsTab } from "./hooks/useSettingsTab";
|
||||
import { getBrainPermissions } from "../../utils/getBrainPermissions";
|
||||
|
||||
@ -38,15 +31,15 @@ export const SettingsTab = ({ brainId }: SettingsTabProps): JSX.Element => {
|
||||
isUpdating,
|
||||
isDefaultBrain,
|
||||
formRef,
|
||||
promptId,
|
||||
pickPublicPrompt,
|
||||
removeBrainPrompt,
|
||||
accessibleModels,
|
||||
brainStatusOptions,
|
||||
status,
|
||||
setValue,
|
||||
dirtyFields,
|
||||
resetField,
|
||||
pickPublicPrompt,
|
||||
promptId,
|
||||
removeBrainPrompt,
|
||||
} = useSettingsTab({ brainId });
|
||||
|
||||
const { onCancel, isAccessModalOpened, closeModal } =
|
||||
@ -75,158 +68,38 @@ export const SettingsTab = ({ brainId }: SettingsTabProps): JSX.Element => {
|
||||
className="my-10 mb-0 flex flex-col items-center gap-2"
|
||||
ref={formRef}
|
||||
>
|
||||
<div className="flex flex-row flex-1 justify-between w-full items-end">
|
||||
<div>
|
||||
<Field
|
||||
label={t("brainName", { ns: "brain" })}
|
||||
placeholder={t("brainNamePlaceholder", { ns: "brain" })}
|
||||
autoComplete="off"
|
||||
className="flex-1"
|
||||
required
|
||||
disabled={!hasEditRights}
|
||||
{...register("name")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<div className="flex flex-1 items-center flex-col">
|
||||
{isPublicBrain && !isOwnedByCurrentUser && (
|
||||
<Chip className="mb-3 bg-primary text-white w-full">
|
||||
{t("brain:public_brain_label")}
|
||||
</Chip>
|
||||
)}
|
||||
|
||||
{isDefaultBrain ? (
|
||||
<div className="border rounded-lg border-dashed border-black dark:border-white bg-white dark:bg-black text-black dark:text-white focus:bg-black dark:focus:bg-white dark dark focus:text-white dark:focus:text-black transition-colors py-2 px-4 shadow-none">
|
||||
{t("defaultBrain", { ns: "brain" })}
|
||||
</div>
|
||||
) : (
|
||||
hasEditRights && (
|
||||
<Button
|
||||
variant={"secondary"}
|
||||
isLoading={isSettingAsDefault}
|
||||
onClick={() => void setAsDefaultBrainHandler()}
|
||||
type="button"
|
||||
>
|
||||
{t("setDefaultBrain", { ns: "brain" })}
|
||||
</Button>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{isOwnedByCurrentUser && (
|
||||
<div className="w-full mt-4">
|
||||
<Radio
|
||||
items={brainStatusOptions}
|
||||
label={t("brain_status_label", { ns: "brain" })}
|
||||
value={status}
|
||||
className="flex-1 justify-between w-[50%]"
|
||||
{...register("status")}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<TextArea
|
||||
label={t("brainDescription", { ns: "brain" })}
|
||||
placeholder={t("brainDescriptionPlaceholder", { ns: "brain" })}
|
||||
autoComplete="off"
|
||||
className="flex-1 m-3"
|
||||
disabled={!hasEditRights}
|
||||
{...register("description")}
|
||||
<GeneralInformation
|
||||
brainStatusOptions={brainStatusOptions}
|
||||
hasEditRights={hasEditRights}
|
||||
isDefaultBrain={isDefaultBrain}
|
||||
isOwnedByCurrentUser={isOwnedByCurrentUser}
|
||||
isPublicBrain={isPublicBrain}
|
||||
isSettingAsDefault={isSettingAsDefault}
|
||||
register={register}
|
||||
setAsDefaultBrainHandler={setAsDefaultBrainHandler}
|
||||
/>
|
||||
<Divider text={t("modelSection", { ns: "config" })} />
|
||||
<Field
|
||||
label={t("openAiKeyLabel", { ns: "config" })}
|
||||
placeholder={t("openAiKeyPlaceholder", { ns: "config" })}
|
||||
autoComplete="off"
|
||||
className="flex-1"
|
||||
disabled={!hasEditRights}
|
||||
{...register("openAiKey")}
|
||||
<ModelSelection
|
||||
accessibleModels={accessibleModels}
|
||||
model={model}
|
||||
maxTokens={maxTokens}
|
||||
temperature={temperature}
|
||||
register={register}
|
||||
hasEditRights={hasEditRights}
|
||||
brainId={brainId}
|
||||
handleSubmit={handleSubmit}
|
||||
/>
|
||||
<fieldset className="w-full flex flex-col mt-2">
|
||||
<label className="flex-1 text-sm" htmlFor="model">
|
||||
{t("modelLabel", { ns: "config" })}
|
||||
</label>
|
||||
<select
|
||||
id="model"
|
||||
disabled={!hasEditRights}
|
||||
{...register("model")}
|
||||
className="px-5 py-2 dark:bg-gray-700 bg-gray-200 rounded-md"
|
||||
onChange={() => {
|
||||
void handleSubmit(false); // Trigger form submission
|
||||
}}
|
||||
>
|
||||
{accessibleModels.map((availableModel) => (
|
||||
<option value={availableModel} key={availableModel}>
|
||||
{availableModel}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</fieldset>
|
||||
<fieldset className="w-full flex mt-4">
|
||||
<label className="flex-1" htmlFor="temp">
|
||||
{t("temperature", { ns: "config" })}: {temperature}
|
||||
</label>
|
||||
<input
|
||||
id="temp"
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={temperature}
|
||||
disabled={!hasEditRights}
|
||||
{...register("temperature")}
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset className="w-full flex mt-4">
|
||||
<label className="flex-1" htmlFor="tokens">
|
||||
{t("maxTokens", { ns: "config" })}: {maxTokens}
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
min="10"
|
||||
max={defineMaxTokens(model)}
|
||||
value={maxTokens}
|
||||
disabled={!hasEditRights}
|
||||
{...register("maxTokens")}
|
||||
/>
|
||||
</fieldset>
|
||||
{hasEditRights && (
|
||||
<div className="flex w-full justify-end py-4">
|
||||
<SaveButton handleSubmit={handleSubmit} />
|
||||
</div>
|
||||
)}
|
||||
<Divider text={t("customPromptSection", { ns: "config" })} />
|
||||
{hasEditRights && <PublicPrompts onSelect={pickPublicPrompt} />}
|
||||
<Field
|
||||
label={t("promptName", { ns: "config" })}
|
||||
placeholder={t("promptNamePlaceholder", { ns: "config" })}
|
||||
autoComplete="off"
|
||||
className="flex-1"
|
||||
disabled={!hasEditRights}
|
||||
{...register("prompt.title")}
|
||||
<Prompt
|
||||
brainId={brainId}
|
||||
handleSubmit={handleSubmit}
|
||||
isUpdating={isUpdating}
|
||||
pickPublicPrompt={pickPublicPrompt}
|
||||
removeBrainPrompt={removeBrainPrompt}
|
||||
promptId={promptId}
|
||||
register={register}
|
||||
hasEditRights={hasEditRights}
|
||||
/>
|
||||
<TextArea
|
||||
label={t("promptContent", { ns: "config" })}
|
||||
placeholder={t("promptContentPlaceholder", { ns: "config" })}
|
||||
autoComplete="off"
|
||||
className="flex-1"
|
||||
disabled={!hasEditRights}
|
||||
{...register("prompt.content")}
|
||||
/>
|
||||
{hasEditRights && (
|
||||
<div className="flex w-full justify-end py-4">
|
||||
<SaveButton handleSubmit={handleSubmit} />
|
||||
</div>
|
||||
)}
|
||||
{hasEditRights && promptId !== "" && (
|
||||
<Button
|
||||
disabled={isUpdating}
|
||||
onClick={() => void removeBrainPrompt()}
|
||||
>
|
||||
{t("removePrompt", { ns: "config" })}
|
||||
</Button>
|
||||
)}
|
||||
<div className="flex flex-row justify-end flex-1 w-full mt-8">
|
||||
{isUpdating && <FaSpinner className="animate-spin" />}
|
||||
{isUpdating && (
|
||||
|
@ -0,0 +1,104 @@
|
||||
/* eslint max-lines:["error", 110] */
|
||||
import { UseFormRegister } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { Chip } from "@/lib/components/ui/Chip";
|
||||
import Field from "@/lib/components/ui/Field";
|
||||
import { Radio } from "@/lib/components/ui/Radio";
|
||||
import { TextArea } from "@/lib/components/ui/TextArea";
|
||||
import { BrainConfig } from "@/lib/types/brainConfig";
|
||||
|
||||
type GeneralInformationProps = {
|
||||
register: UseFormRegister<BrainConfig>;
|
||||
hasEditRights: boolean;
|
||||
isPublicBrain: boolean;
|
||||
isOwnedByCurrentUser: boolean;
|
||||
isDefaultBrain: boolean;
|
||||
isSettingAsDefault: boolean;
|
||||
setAsDefaultBrainHandler: () => Promise<void>;
|
||||
brainStatusOptions: {
|
||||
label: string;
|
||||
value: "private" | "public";
|
||||
}[];
|
||||
};
|
||||
|
||||
export const GeneralInformation = (
|
||||
props: GeneralInformationProps
|
||||
): JSX.Element => {
|
||||
const { t } = useTranslation(["translation", "brain", "config"]);
|
||||
const {
|
||||
register,
|
||||
hasEditRights,
|
||||
isPublicBrain,
|
||||
isOwnedByCurrentUser,
|
||||
isDefaultBrain,
|
||||
isSettingAsDefault,
|
||||
setAsDefaultBrainHandler,
|
||||
brainStatusOptions,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-row flex-1 justify-between w-full items-end">
|
||||
<div>
|
||||
<Field
|
||||
label={t("brainName", { ns: "brain" })}
|
||||
placeholder={t("brainNamePlaceholder", { ns: "brain" })}
|
||||
autoComplete="off"
|
||||
className="flex-1"
|
||||
required
|
||||
disabled={!hasEditRights}
|
||||
{...register("name")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-4">
|
||||
<div className="flex flex-1 items-center flex-col">
|
||||
{isPublicBrain && !isOwnedByCurrentUser && (
|
||||
<Chip className="mb-3 bg-primary text-white w-full">
|
||||
{t("brain:public_brain_label")}
|
||||
</Chip>
|
||||
)}
|
||||
|
||||
{isDefaultBrain ? (
|
||||
<div className="border rounded-lg border-dashed border-black dark:border-white bg-white dark:bg-black text-black dark:text-white focus:bg-black dark:focus:bg-white dark dark focus:text-white dark:focus:text-black transition-colors py-2 px-4 shadow-none">
|
||||
{t("defaultBrain", { ns: "brain" })}
|
||||
</div>
|
||||
) : (
|
||||
hasEditRights && (
|
||||
<Button
|
||||
variant={"secondary"}
|
||||
isLoading={isSettingAsDefault}
|
||||
onClick={() => void setAsDefaultBrainHandler()}
|
||||
type="button"
|
||||
>
|
||||
{t("setDefaultBrain", { ns: "brain" })}
|
||||
</Button>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{isOwnedByCurrentUser && (
|
||||
<div className="w-full mt-4">
|
||||
<Radio
|
||||
items={brainStatusOptions}
|
||||
label={t("brain_status_label", { ns: "brain" })}
|
||||
value={status}
|
||||
className="flex-1 justify-between w-[50%]"
|
||||
{...register("status")}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<TextArea
|
||||
label={t("brainDescription", { ns: "brain" })}
|
||||
placeholder={t("brainDescriptionPlaceholder", { ns: "brain" })}
|
||||
autoComplete="off"
|
||||
className="flex-1 m-3"
|
||||
disabled={!hasEditRights}
|
||||
{...register("description")}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1 @@
|
||||
export * from "./GeneralInformation";
|
@ -0,0 +1,98 @@
|
||||
import { UUID } from "crypto";
|
||||
import { UseFormRegister } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Field from "@/lib/components/ui/Field";
|
||||
import { defineMaxTokens } from "@/lib/helpers/defineMaxTokens";
|
||||
import { BrainConfig } from "@/lib/types/brainConfig";
|
||||
import { SaveButton } from "@/shared/SaveButton";
|
||||
|
||||
type ModelSelectionProps = {
|
||||
brainId: UUID;
|
||||
temperature: number;
|
||||
maxTokens: number;
|
||||
model: "gpt-3.5-turbo" | "gpt-3.5-turbo-16k";
|
||||
handleSubmit: (checkDirty: boolean) => Promise<void>;
|
||||
register: UseFormRegister<BrainConfig>;
|
||||
hasEditRights: boolean;
|
||||
accessibleModels: string[];
|
||||
};
|
||||
|
||||
export const ModelSelection = (props: ModelSelectionProps): JSX.Element => {
|
||||
const { t } = useTranslation(["translation", "brain", "config"]);
|
||||
const {
|
||||
handleSubmit,
|
||||
register,
|
||||
temperature,
|
||||
maxTokens,
|
||||
model,
|
||||
hasEditRights,
|
||||
accessibleModels,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Field
|
||||
label={t("openAiKeyLabel", { ns: "config" })}
|
||||
placeholder={t("openAiKeyPlaceholder", { ns: "config" })}
|
||||
autoComplete="off"
|
||||
className="flex-1"
|
||||
disabled={!hasEditRights}
|
||||
{...register("openAiKey")}
|
||||
/>
|
||||
<fieldset className="w-full flex flex-col mt-2">
|
||||
<label className="flex-1 text-sm" htmlFor="model">
|
||||
{t("modelLabel", { ns: "config" })}
|
||||
</label>
|
||||
<select
|
||||
id="model"
|
||||
disabled={!hasEditRights}
|
||||
{...register("model")}
|
||||
className="px-5 py-2 dark:bg-gray-700 bg-gray-200 rounded-md"
|
||||
onChange={() => {
|
||||
void handleSubmit(false); // Trigger form submission
|
||||
}}
|
||||
>
|
||||
{accessibleModels.map((availableModel) => (
|
||||
<option value={availableModel} key={availableModel}>
|
||||
{availableModel}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</fieldset>
|
||||
<fieldset className="w-full flex mt-4">
|
||||
<label className="flex-1" htmlFor="temp">
|
||||
{t("temperature", { ns: "config" })}: {temperature}
|
||||
</label>
|
||||
<input
|
||||
id="temp"
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={temperature}
|
||||
disabled={!hasEditRights}
|
||||
{...register("temperature")}
|
||||
/>
|
||||
</fieldset>
|
||||
<fieldset className="w-full flex mt-4">
|
||||
<label className="flex-1" htmlFor="tokens">
|
||||
{t("maxTokens", { ns: "config" })}: {maxTokens}
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
min="10"
|
||||
max={defineMaxTokens(model)}
|
||||
value={maxTokens}
|
||||
disabled={!hasEditRights}
|
||||
{...register("maxTokens")}
|
||||
/>
|
||||
</fieldset>
|
||||
{hasEditRights && (
|
||||
<div className="flex w-full justify-end py-4">
|
||||
<SaveButton handleSubmit={handleSubmit} />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1 @@
|
||||
export * from "./ModelSelection";
|
@ -0,0 +1,73 @@
|
||||
import { UUID } from "crypto";
|
||||
import { UseFormRegister } from "react-hook-form";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import Field from "@/lib/components/ui/Field";
|
||||
import { TextArea } from "@/lib/components/ui/TextArea";
|
||||
import { BrainConfig } from "@/lib/types/brainConfig";
|
||||
import { SaveButton } from "@/shared/SaveButton";
|
||||
|
||||
import { PublicPrompts } from "../PublicPrompts";
|
||||
|
||||
type PromptProps = {
|
||||
brainId: UUID;
|
||||
pickPublicPrompt: ({
|
||||
title,
|
||||
content,
|
||||
}: {
|
||||
title: string;
|
||||
content: string;
|
||||
}) => void;
|
||||
removeBrainPrompt: () => Promise<void>;
|
||||
isUpdating: boolean;
|
||||
handleSubmit: (checkDirty: boolean) => Promise<void>;
|
||||
register: UseFormRegister<BrainConfig>;
|
||||
promptId?: string;
|
||||
hasEditRights: boolean;
|
||||
};
|
||||
|
||||
export const Prompt = (props: PromptProps): JSX.Element => {
|
||||
const { t } = useTranslation(["translation", "brain", "config"]);
|
||||
const {
|
||||
pickPublicPrompt,
|
||||
removeBrainPrompt,
|
||||
isUpdating,
|
||||
handleSubmit,
|
||||
register,
|
||||
hasEditRights,
|
||||
promptId,
|
||||
} = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
{hasEditRights && <PublicPrompts onSelect={pickPublicPrompt} />}
|
||||
<Field
|
||||
label={t("promptName", { ns: "config" })}
|
||||
placeholder={t("promptNamePlaceholder", { ns: "config" })}
|
||||
autoComplete="off"
|
||||
className="flex-1"
|
||||
disabled={!hasEditRights}
|
||||
{...register("prompt.title")}
|
||||
/>
|
||||
<TextArea
|
||||
label={t("promptContent", { ns: "config" })}
|
||||
placeholder={t("promptContentPlaceholder", { ns: "config" })}
|
||||
autoComplete="off"
|
||||
className="flex-1"
|
||||
disabled={!hasEditRights}
|
||||
{...register("prompt.content")}
|
||||
/>
|
||||
{hasEditRights && (
|
||||
<div className="flex w-full justify-end py-4">
|
||||
<SaveButton handleSubmit={handleSubmit} />
|
||||
</div>
|
||||
)}
|
||||
{hasEditRights && promptId !== "" && (
|
||||
<Button disabled={isUpdating} onClick={() => void removeBrainPrompt()}>
|
||||
{t("removePrompt", { ns: "config" })}
|
||||
</Button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
@ -0,0 +1 @@
|
||||
export * from "./Prompt";
|
@ -1 +1,4 @@
|
||||
export * from "./GeneralInformation";
|
||||
export * from "./ModelSelection";
|
||||
export * from "./Prompt";
|
||||
export * from "./PublicPrompts";
|
||||
|
@ -0,0 +1 @@
|
||||
export * from "./SettingsTab";
|
@ -1,3 +1,4 @@
|
||||
export * from "./BrainTabTrigger";
|
||||
export * from "./KnowledgeTab";
|
||||
export * from "./PeopleTab";
|
||||
export * from "./SettingsTab";
|
||||
|
@ -38,6 +38,7 @@
|
||||
"@supabase/auth-ui-shared": "^0.1.6",
|
||||
"@supabase/supabase-js": "^2.22.0",
|
||||
"@tanstack/react-query": "^4.33.0",
|
||||
"@testing-library/user-event": "^14.5.1",
|
||||
"@types/dom-speech-recognition": "^0.0.1",
|
||||
"@types/draft-js": "^0.11.12",
|
||||
"@types/lodash": "^4.14.197",
|
||||
@ -96,4 +97,4 @@
|
||||
"react-icons": "^4.8.0",
|
||||
"vitest": "^0.32.2"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user