mirror of
https://github.com/QuivrHQ/quivr.git
synced 2024-12-15 09:32:22 +03:00
feat: update header and remove prompt / brain on backspace (#1052)
* feat: update header * feat: remove selected prompt / brain on backspace * feat(chat): update suggestions component * refactor: add getAxiosErrorParams
This commit is contained in:
parent
6e43e6f16f
commit
c5a7b8faef
@ -69,7 +69,7 @@ export const MentionInput = ({
|
||||
// `open` should be directly passed to the MentionSuggestions component.
|
||||
// However, it is not working as expected since we are not able to click on button in custom suggestion renderer.
|
||||
// So, we are using this hack to make it work.
|
||||
visibility: open ? "visible" : "hidden",
|
||||
opacity: open ? 1 : 0,
|
||||
}}
|
||||
>
|
||||
<MentionSuggestions
|
||||
|
@ -21,7 +21,9 @@ export const useMentionPlugin = () => {
|
||||
minWidth: "auto",
|
||||
backgroundColor: "transparent",
|
||||
padding: "0",
|
||||
marginBottom: "5",
|
||||
// We are adding a bottom margin to the suggestions container since it is overlapping with mention remove icon
|
||||
// Please, do not remove!
|
||||
marginBottom: "20px",
|
||||
};
|
||||
},
|
||||
},
|
||||
|
@ -127,10 +127,21 @@ export const useMentionInput = ({
|
||||
return getDefaultKeyBinding(e);
|
||||
}
|
||||
|
||||
if (
|
||||
(e.key === "Backspace" || e.key === "Delete") &&
|
||||
getEditorText(editorState) === ""
|
||||
) {
|
||||
if (e.key === "Backspace" || e.key === "Delete") {
|
||||
if (!(getEditorText(editorState) === "")) {
|
||||
return getDefaultKeyBinding(e);
|
||||
}
|
||||
if (currentPromptId !== null) {
|
||||
setCurrentPromptId(null);
|
||||
|
||||
return "backspace";
|
||||
}
|
||||
if (currentBrainId !== null) {
|
||||
setCurrentBrainId(null);
|
||||
|
||||
return "backspace";
|
||||
}
|
||||
|
||||
return "backspace";
|
||||
}
|
||||
|
||||
|
@ -7,7 +7,7 @@ import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useSubscriptionApi } from "@/lib/api/subscription/useSubscriptionApi";
|
||||
import { BrainRoleType } from "@/lib/components/NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleType } from "@/lib/components/BrainUsers/types";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
import { useToast } from "@/lib/hooks";
|
||||
import { useEventTracking } from "@/services/analytics/useEventTracking";
|
||||
|
@ -2,7 +2,7 @@
|
||||
import Link from "next/link";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { BrainRoleType } from "@/lib/components/NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleType } from "@/lib/components/BrainUsers/types";
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { Divider } from "@/lib/components/ui/Divider";
|
||||
import PageHeading from "@/lib/components/ui/PageHeading";
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* eslint-disable max-lines */
|
||||
import { AxiosInstance } from "axios";
|
||||
|
||||
import { BrainRoleType } from "@/lib/components/NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleType } from "@/lib/components/BrainUsers/types";
|
||||
import {
|
||||
BackendMinimalBrainForUser,
|
||||
Brain,
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BrainRoleType } from "@/lib/components/NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleType } from "@/lib/components/BrainUsers/types";
|
||||
|
||||
export type SubscriptionUpdatableProperties = {
|
||||
role: BrainRoleType | null;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BrainRoleType } from "@/lib/components/NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleType } from "@/lib/components/BrainUsers/types";
|
||||
|
||||
import { Subscription } from "../brain";
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BrainRoleType } from "@/lib/components/NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleType } from "@/lib/components/BrainUsers/types";
|
||||
|
||||
import { SubscriptionUpdatableProperties } from "../types";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { AxiosInstance } from "axios";
|
||||
import { UUID } from "crypto";
|
||||
|
||||
import { BrainRoleType } from "@/lib/components/NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleType } from "@/lib/components/BrainUsers/types";
|
||||
|
||||
export const acceptInvitation = async (
|
||||
brainId: UUID,
|
||||
|
@ -7,7 +7,7 @@ import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainConte
|
||||
|
||||
import { RemoveAccessIcon } from "./components/RemoveAccessIcon";
|
||||
import { useBrainUser } from "./hooks/useBrainUser";
|
||||
import { BrainRoleType } from "../../../NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleType } from "../../types";
|
||||
type BrainUserProps = {
|
||||
email: string;
|
||||
role: BrainRoleType;
|
||||
|
@ -1,13 +1,12 @@
|
||||
import axios, { AxiosResponse } from "axios";
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useBrainApi } from "@/lib/api/brain/useBrainApi";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
import { getAxiosErrorParams } from "@/lib/helpers/getAxiosErrorParams";
|
||||
import { useToast } from "@/lib/hooks";
|
||||
|
||||
import { BrainRoleType } from "../../../../NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
|
||||
import { BrainRoleType } from "../../../types";
|
||||
type UseBrainUserProps = {
|
||||
fetchBrainUsers: () => Promise<void>;
|
||||
role: BrainRoleType;
|
||||
@ -26,36 +25,41 @@ export const useBrainUser = ({
|
||||
const [selectedRole, setSelectedRole] = useState<BrainRoleType>(role);
|
||||
const [isRemovingAccess, setIsRemovingAccess] = useState(false);
|
||||
const { currentBrain } = useBrainContext();
|
||||
const { t } = useTranslation(['translation','brain']);
|
||||
const { t } = useTranslation(["translation", "brain"]);
|
||||
const updateSelectedRole = async (newRole: BrainRoleType) => {
|
||||
setSelectedRole(newRole);
|
||||
try {
|
||||
await updateBrainAccess(brainId, email, {
|
||||
role: newRole,
|
||||
});
|
||||
publish({ variant: "success", text: t('userRoleUpdated', { email: email, role: newRole, ns: 'brain' }) });
|
||||
publish({
|
||||
variant: "success",
|
||||
text: t("userRoleUpdated", {
|
||||
email: email,
|
||||
role: newRole,
|
||||
ns: "brain",
|
||||
}),
|
||||
});
|
||||
void fetchBrainUsers();
|
||||
} catch (e) {
|
||||
if (axios.isAxiosError(e) && e.response?.status === 403) {
|
||||
const axiosError = getAxiosErrorParams(e);
|
||||
if (axiosError !== undefined && axiosError.status === 403) {
|
||||
publish({
|
||||
variant: "danger",
|
||||
text: `${JSON.stringify(
|
||||
(
|
||||
e.response as {
|
||||
data: { detail: string };
|
||||
}
|
||||
).data.detail
|
||||
)}`,
|
||||
text: axiosError.message,
|
||||
});
|
||||
} else {
|
||||
publish({
|
||||
variant: "danger",
|
||||
text: t('userRoleUpdateFailed', { email: email, role: newRole, ns: 'brain' })
|
||||
text: t("userRoleUpdateFailed", {
|
||||
email: email,
|
||||
role: newRole,
|
||||
ns: "brain",
|
||||
}),
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const removeUserAccess = async () => {
|
||||
setIsRemovingAccess(true);
|
||||
try {
|
||||
@ -64,23 +68,20 @@ export const useBrainUser = ({
|
||||
});
|
||||
publish({
|
||||
variant: "success",
|
||||
text: t('userRemoved', { email: email, ns: 'brain' })
|
||||
text: t("userRemoved", { email: email, ns: "brain" }),
|
||||
});
|
||||
void fetchBrainUsers();
|
||||
} catch (e) {
|
||||
if (axios.isAxiosError(e) && e.response?.data !== undefined) {
|
||||
const axiosError = getAxiosErrorParams(e);
|
||||
if (axiosError !== undefined) {
|
||||
publish({
|
||||
variant: "danger",
|
||||
text: (
|
||||
e.response as AxiosResponse<{
|
||||
detail: string;
|
||||
}>
|
||||
).data.detail,
|
||||
text: axiosError.message,
|
||||
});
|
||||
} else {
|
||||
publish({
|
||||
variant: "danger",
|
||||
text: t('userRemoveFailed', { email: email, ns: 'brain' })
|
||||
text: t("userRemoveFailed", { email: email, ns: "brain" }),
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
@ -94,6 +95,6 @@ export const useBrainUser = ({
|
||||
removeUserAccess,
|
||||
updateSelectedRole,
|
||||
selectedRole,
|
||||
canRemoveAccess
|
||||
canRemoveAccess,
|
||||
};
|
||||
};
|
||||
|
@ -1,5 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import { MdSettings } from "react-icons/md";
|
||||
import { FaBrain } from "react-icons/fa";
|
||||
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
@ -14,7 +14,7 @@ export const BrainManagementButton = (): JSX.Element => {
|
||||
className="focus:outline-none text-2xl"
|
||||
aria-label="Settings"
|
||||
>
|
||||
<MdSettings />
|
||||
<FaBrain className="w-6 h-6" />
|
||||
</Button>
|
||||
</Link>
|
||||
);
|
||||
|
@ -1,89 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { FaBrain } from "react-icons/fa";
|
||||
import { MdCheck } from "react-icons/md";
|
||||
|
||||
import Field from "@/lib/components/ui/Field";
|
||||
import Popover from "@/lib/components/ui/Popover";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
import { BrainActions } from "./components/BrainActions/BrainActions";
|
||||
import { AddBrainModal } from "../../../../../AddBrainModal/AddBrainModal";
|
||||
|
||||
export const BrainsDropDown = (): JSX.Element => {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const { allBrains, isFetchingBrains, setActiveBrain, currentBrain } =
|
||||
useBrainContext();
|
||||
const { t } = useTranslation(['translation','brain']);
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* Add the brain icon and dropdown */}
|
||||
<div className="relative ml-auto px-4 py-2">
|
||||
<Popover
|
||||
Trigger={
|
||||
<button
|
||||
type="button"
|
||||
className="flex items-center focus:outline-none"
|
||||
>
|
||||
<FaBrain className="w-6 h-6" />
|
||||
</button>
|
||||
}
|
||||
ActionTrigger={<AddBrainModal />}
|
||||
CloseTrigger={false}
|
||||
>
|
||||
<div>
|
||||
<Field
|
||||
name="brainsearch"
|
||||
placeholder= {t('searchBrain',{ns:'brain'})}
|
||||
autoFocus
|
||||
autoComplete="off"
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
/>
|
||||
<div className="overflow-auto scrollbar flex flex-col h-48 mt-5">
|
||||
{/* List of brains */}
|
||||
{isFetchingBrains && (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<p className="text-gray-500">{t('loading')}</p>
|
||||
</div>
|
||||
)}
|
||||
{allBrains.map((brain) => {
|
||||
if (brain.name.includes(searchQuery)) {
|
||||
return (
|
||||
<div
|
||||
key={brain.id}
|
||||
className="relative flex group items-center"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={`flex flex-1 items-center gap-2 w-full text-left px-4 py-2 text-sm leading-5 text-gray-900 dark:text-gray-300 group-hover:bg-gray-100 dark:group-hover:bg-gray-700 group-focus:bg-gray-100 dark:group-focus:bg-gray-700 group-focus:outline-none transition-colors`}
|
||||
onClick={() => {
|
||||
setActiveBrain({ ...brain });
|
||||
setSearchQuery("");
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
<MdCheck
|
||||
style={{
|
||||
opacity: currentBrain?.id === brain.id ? 1 : 0,
|
||||
}}
|
||||
className="text-xl transition-opacity"
|
||||
width={32}
|
||||
height={32}
|
||||
/>
|
||||
</span>
|
||||
<span className="flex-1">{brain.name}</span>
|
||||
</button>
|
||||
<BrainActions brain={brain} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
</Popover>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
@ -1,17 +0,0 @@
|
||||
import { ShareBrain } from "@/lib/components/ShareBrain";
|
||||
import { MinimalBrainForUser } from "@/lib/context/BrainProvider/types";
|
||||
|
||||
import { DeleteBrain } from "./components";
|
||||
|
||||
type BrainActionsProps = {
|
||||
brain: MinimalBrainForUser;
|
||||
};
|
||||
|
||||
export const BrainActions = ({ brain }: BrainActionsProps): JSX.Element => {
|
||||
return (
|
||||
<div className="absolute right-0 flex flex-row">
|
||||
<ShareBrain brainId={brain.id} />
|
||||
<DeleteBrain brainId={brain.id} />
|
||||
</div>
|
||||
);
|
||||
};
|
@ -1,19 +0,0 @@
|
||||
import { UUID } from "crypto";
|
||||
import { MdDelete } from "react-icons/md";
|
||||
|
||||
import Button from "@/lib/components/ui/Button";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
export const DeleteBrain = ({ brainId }: { brainId: UUID }): JSX.Element => {
|
||||
const { deleteBrain } = useBrainContext();
|
||||
|
||||
return (
|
||||
<Button
|
||||
className="group-hover:visible invisible hover:text-red-500 transition-[colors,opacity] p-1"
|
||||
onClick={() => void deleteBrain(brainId)}
|
||||
variant={"tertiary"}
|
||||
>
|
||||
<MdDelete className="text-xl" />
|
||||
</Button>
|
||||
);
|
||||
};
|
@ -1 +0,0 @@
|
||||
export * from "./DeleteBrain";
|
@ -1 +0,0 @@
|
||||
export * from "./BrainActions";
|
@ -1,2 +0,0 @@
|
||||
export * from "@/lib/components/AddBrainModal/AddBrainModal";
|
||||
export * from "./BrainActions";
|
@ -1 +0,0 @@
|
||||
export * from "./BrainsDropDown";
|
@ -9,7 +9,6 @@ import { cn } from "@/lib/utils";
|
||||
|
||||
import { AuthButtons } from "./components/AuthButtons";
|
||||
import { BrainManagementButton } from "./components/BrainManagementButton";
|
||||
import { BrainsDropDown } from "./components/BrainsDropDown";
|
||||
import { DarkModeToggle } from "./components/DarkModeToggle";
|
||||
import { LanguageDropDown } from "./components/LanguageDropDown";
|
||||
import { NavLink } from "./components/NavLink";
|
||||
@ -60,7 +59,6 @@ export const NavItems = ({
|
||||
<div className="flex sm:flex-1 sm:justify-end flex-col items-center justify-center sm:flex-row gap-5 sm:gap-2">
|
||||
{isUserLoggedIn && (
|
||||
<>
|
||||
<BrainsDropDown />
|
||||
<BrainManagementButton />
|
||||
<Link aria-label="account" className="" href={"/user"}>
|
||||
<MdPerson className="text-2xl" />
|
||||
|
@ -14,7 +14,7 @@ import { Modal } from "@/lib/components/ui/Modal";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
import { useShareBrain } from "@/lib/hooks/useShareBrain";
|
||||
|
||||
import { BrainRoleType } from "../NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleType } from "../BrainUsers/types";
|
||||
|
||||
type ShareBrainModalProps = {
|
||||
brainId: UUID;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BrainRoleType } from "../../NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleType } from "../../BrainUsers/types";
|
||||
|
||||
export type SelectOptionsProps = {
|
||||
label: string;
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { BrainRoleAssignation } from "../../NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleAssignation } from "../../BrainUsers/types";
|
||||
|
||||
export const generateBrainAssignation = (): BrainRoleAssignation => {
|
||||
return {
|
||||
|
@ -6,10 +6,7 @@ import Field from "@/lib/components/ui/Field";
|
||||
import { Select } from "@/lib/components/ui/Select";
|
||||
import { useBrainContext } from "@/lib/context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
import {
|
||||
BrainRoleAssignation,
|
||||
BrainRoleType,
|
||||
} from "./NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleAssignation, BrainRoleType } from "./BrainUsers/types";
|
||||
import { userRoleToAssignableRoles } from "./ShareBrain/types";
|
||||
|
||||
type UserToInviteProps = {
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { UUID } from "crypto";
|
||||
|
||||
import { BrainRoleType } from "@/lib/components/NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
import { BrainRoleType } from "@/lib/components/BrainUsers/types";
|
||||
import { Document } from "@/lib/types/Document";
|
||||
|
||||
import { useBrainProvider } from "./hooks/useBrainProvider";
|
||||
|
22
frontend/lib/helpers/getAxiosErrorParams.ts
Normal file
22
frontend/lib/helpers/getAxiosErrorParams.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { AxiosResponse, isAxiosError } from "axios";
|
||||
|
||||
type AxiosErrorParams = {
|
||||
message: string;
|
||||
status: number;
|
||||
};
|
||||
export const getAxiosErrorParams = (
|
||||
e: unknown
|
||||
): AxiosErrorParams | undefined => {
|
||||
if (isAxiosError(e) && e.response?.data !== undefined) {
|
||||
return {
|
||||
message: (
|
||||
e.response as AxiosResponse<{
|
||||
detail: string;
|
||||
}>
|
||||
).data.detail,
|
||||
status: e.response.status,
|
||||
};
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
@ -10,7 +10,7 @@ import { useToast } from "@/lib/hooks";
|
||||
import {
|
||||
BrainRoleAssignation,
|
||||
BrainRoleType,
|
||||
} from "../components/NavBar/components/NavItems/components/BrainsDropDown/components/BrainActions/types";
|
||||
} from "../components/BrainUsers/types";
|
||||
import { generateBrainAssignation } from "../components/ShareBrain/utils/generateBrainAssignation";
|
||||
import { useBrainContext } from "../context/BrainProvider/hooks/useBrainContext";
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user