diff --git a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/__tests__/ChatDialogue.test.tsx b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/__tests__/ChatDialogue.test.tsx
index 4419e53fd..0859665d0 100644
--- a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/__tests__/ChatDialogue.test.tsx
+++ b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/__tests__/ChatDialogue.test.tsx
@@ -1,3 +1,4 @@
+import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { render } from "@testing-library/react";
import { describe, expect, it, vi } from "vitest";
@@ -19,6 +20,7 @@ vi.mock("../hooks/useChatDialogue", () => ({
chatListRef: vi.fn(),
})),
}));
+const queryClient = new QueryClient();
describe("ChatDialogue", () => {
it("should render chat messages correctly", () => {
@@ -37,14 +39,22 @@ describe("ChatDialogue", () => {
messages,
[]
);
- const { getAllByTestId } = render();
+ const { getAllByTestId } = render(
+
+
+
+ );
expect(getAllByTestId("brain-tags")).toBeDefined();
expect(getAllByTestId("prompt-tags")).toBeDefined();
expect(getAllByTestId("chat-message-text")).toBeDefined();
});
it("should render placeholder text when history is empty", () => {
- const { getByTestId } = render();
+ const { getByTestId } = render(
+
+
+
+ );
expect(getByTestId("empty-history-message")).toBeDefined();
});
diff --git a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/index.tsx b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/index.tsx
index acaf2e8c5..50d7105e2 100644
--- a/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/index.tsx
+++ b/frontend/app/chat/[chatId]/components/ChatDialogueArea/components/ChatDialogue/index.tsx
@@ -1,6 +1,7 @@
-import { useFeatureIsOn } from "@growthbook/growthbook-react";
import { useTranslation } from "react-i18next";
+import { useOnboarding } from "@/lib/hooks/useOnboarding";
+
import { ChatItem } from "./components";
import { Onboarding } from "./components/Onboarding/Onboarding";
import { useChatDialogue } from "./hooks/useChatDialogue";
@@ -21,9 +22,9 @@ export const ChatDialogue = ({
const { t } = useTranslation(["chat"]);
const { chatListRef } = useChatDialogue();
- const shouldDisplayOnboarding = useFeatureIsOn("onboarding");
+ const { shouldDisplayOnboardingAInstructions } = useOnboarding();
- if (shouldDisplayOnboarding) {
+ if (shouldDisplayOnboardingAInstructions) {
return (
diff --git a/frontend/app/chat/components/ChatsList/components/ChatsListItem/ChatsListItem.tsx b/frontend/app/chat/components/ChatsList/components/ChatsListItem/ChatsListItem.tsx
index f7205e113..6e940fe9a 100644
--- a/frontend/app/chat/components/ChatsList/components/ChatsListItem/ChatsListItem.tsx
+++ b/frontend/app/chat/components/ChatsList/components/ChatsListItem/ChatsListItem.tsx
@@ -10,9 +10,15 @@ import { useChatsListItem } from "./hooks/useChatsListItem";
interface ChatsListItemProps {
chat: ChatEntity;
+ editable?: boolean;
+ onDelete?: () => void;
}
-export const ChatsListItem = ({ chat }: ChatsListItemProps): JSX.Element => {
+export const ChatsListItem = ({
+ chat,
+ editable = true,
+ onDelete,
+}: ChatsListItemProps): JSX.Element => {
const {
setChatName,
deleteChat,
@@ -47,17 +53,19 @@ export const ChatsListItem = ({ chat }: ChatsListItemProps): JSX.Element => {
-
+ {editable && (
+
+ )}
diff --git a/frontend/lib/api/onboarding/__test__/useOnboarding.test.ts b/frontend/lib/api/onboarding/__test__/useOnboarding.test.ts
new file mode 100644
index 000000000..da286276f
--- /dev/null
+++ b/frontend/lib/api/onboarding/__test__/useOnboarding.test.ts
@@ -0,0 +1,51 @@
+import { renderHook } from "@testing-library/react";
+import { describe, expect, it, vi } from "vitest";
+
+import { Onboarding } from "@/lib/types/Onboarding";
+
+import { useOnboardingApi } from "../useOnboardingApi";
+
+const axiosGetMock = vi.fn(() => ({}));
+const axiosPutMock = vi.fn(() => ({}));
+
+vi.mock("@/lib/hooks", () => ({
+ useAxios: () => ({
+ axiosInstance: {
+ get: axiosGetMock,
+ put: axiosPutMock,
+ },
+ }),
+}));
+
+describe("useOnboarding", () => {
+ it("should call getOnboarding with the correct parameters", async () => {
+ axiosGetMock.mockReturnValue({ data: {} });
+ const {
+ result: {
+ current: { getOnboarding },
+ },
+ } = renderHook(() => useOnboardingApi());
+
+ await getOnboarding();
+
+ expect(axiosGetMock).toHaveBeenCalledTimes(1);
+ expect(axiosGetMock).toHaveBeenCalledWith("/onboarding");
+ });
+ it("should call updateOnboarding with the correct parameters", async () => {
+ const onboarding: Partial = {
+ onboarding_a: true,
+ onboarding_b1: false,
+ };
+ axiosPutMock.mockReturnValue({ data: {} });
+ const {
+ result: {
+ current: { updateOnboarding },
+ },
+ } = renderHook(() => useOnboardingApi());
+
+ await updateOnboarding(onboarding);
+
+ expect(axiosPutMock).toHaveBeenCalledTimes(1);
+ expect(axiosPutMock).toHaveBeenCalledWith("/onboarding", onboarding);
+ });
+});
diff --git a/frontend/lib/api/onboarding/config.ts b/frontend/lib/api/onboarding/config.ts
new file mode 100644
index 000000000..597f1fd26
--- /dev/null
+++ b/frontend/lib/api/onboarding/config.ts
@@ -0,0 +1 @@
+export const ONBOARDING_DATA_KEY = "onboarding";
diff --git a/frontend/lib/api/onboarding/onboarding.ts b/frontend/lib/api/onboarding/onboarding.ts
new file mode 100644
index 000000000..b35613bb6
--- /dev/null
+++ b/frontend/lib/api/onboarding/onboarding.ts
@@ -0,0 +1,16 @@
+import { AxiosInstance } from "axios";
+
+import { Onboarding } from "@/lib/types/Onboarding";
+
+export const getOnboarding = async (
+ axiosInstance: AxiosInstance
+): Promise => {
+ return (await axiosInstance.get("/onboarding")).data;
+};
+
+export const updateOnboarding = async (
+ onboarding: Partial,
+ axiosInstance: AxiosInstance
+): Promise => {
+ return (await axiosInstance.put("/onboarding", onboarding)).data;
+};
diff --git a/frontend/lib/api/onboarding/useOnboardingApi.ts b/frontend/lib/api/onboarding/useOnboardingApi.ts
new file mode 100644
index 000000000..838cb8583
--- /dev/null
+++ b/frontend/lib/api/onboarding/useOnboardingApi.ts
@@ -0,0 +1,19 @@
+import { useAxios } from "@/lib/hooks";
+import { Onboarding } from "@/lib/types/Onboarding";
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export const useOnboardingApi = () => {
+ const { axiosInstance } = useAxios();
+ const getOnboarding = async () => {
+ return (await axiosInstance.get("/onboarding")).data;
+ };
+ const updateOnboarding = async (onboarding: Partial) => {
+ return (await axiosInstance.put("/onboarding", onboarding))
+ .data;
+ };
+
+ return {
+ getOnboarding,
+ updateOnboarding,
+ };
+};
diff --git a/frontend/lib/hooks/useOnboarding.ts b/frontend/lib/hooks/useOnboarding.ts
new file mode 100644
index 000000000..dca1154ac
--- /dev/null
+++ b/frontend/lib/hooks/useOnboarding.ts
@@ -0,0 +1,46 @@
+import { useFeatureIsOn } from "@growthbook/growthbook-react";
+import { useQuery, useQueryClient } from "@tanstack/react-query";
+import { useParams } from "next/navigation";
+
+import { ONBOARDING_DATA_KEY } from "@/lib/api/onboarding/config";
+import { useOnboardingApi } from "@/lib/api/onboarding/useOnboardingApi";
+
+import { Onboarding } from "../types/Onboarding";
+
+// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
+export const useOnboarding = () => {
+ const isOnboardingFeatureActivated = useFeatureIsOn("onboarding");
+ const { getOnboarding } = useOnboardingApi();
+ const params = useParams();
+ const { updateOnboarding } = useOnboardingApi();
+ const queryClient = useQueryClient();
+
+ const chatId = params?.chatId as string | undefined;
+
+ const { data: onboarding } = useQuery({
+ queryFn: getOnboarding,
+ queryKey: [ONBOARDING_DATA_KEY],
+ });
+
+ const updateOnboardingHandler = async (
+ newOnboardingStatus: Partial
+ ) => {
+ await updateOnboarding(newOnboardingStatus);
+ await queryClient.invalidateQueries({ queryKey: [ONBOARDING_DATA_KEY] });
+ };
+
+ const shouldDisplayWelcomeChat =
+ isOnboardingFeatureActivated && onboarding?.onboarding_a === true;
+
+ const shouldDisplayOnboardingAInstructions =
+ isOnboardingFeatureActivated &&
+ chatId === undefined &&
+ shouldDisplayWelcomeChat;
+
+ return {
+ onboarding,
+ shouldDisplayOnboardingAInstructions,
+ shouldDisplayWelcomeChat,
+ updateOnboarding: updateOnboardingHandler,
+ };
+};
diff --git a/frontend/lib/types/Onboarding.ts b/frontend/lib/types/Onboarding.ts
new file mode 100644
index 000000000..efcfa02ae
--- /dev/null
+++ b/frontend/lib/types/Onboarding.ts
@@ -0,0 +1,6 @@
+export type Onboarding = {
+ onboarding_a: boolean;
+ onboarding_b1: boolean;
+ onboarding_b2: boolean;
+ onboarding_b3: boolean;
+};
diff --git a/frontend/public/locales/en/chat.json b/frontend/public/locales/en/chat.json
index 7f1cf5262..edf56d4bb 100644
--- a/frontend/public/locales/en/chat.json
+++ b/frontend/public/locales/en/chat.json
@@ -44,5 +44,6 @@
"how_to_use_quivr": "How to use Quivr ?",
"what_is_quivr": "What is Quivr ?",
"what_is_brain": "What is a brain ?"
- }
+ },
+ "welcome":"Welcome"
}
diff --git a/frontend/public/locales/es/chat.json b/frontend/public/locales/es/chat.json
index 8bc4a24a3..d11f6b761 100644
--- a/frontend/public/locales/es/chat.json
+++ b/frontend/public/locales/es/chat.json
@@ -44,5 +44,6 @@
"how_to_use_quivr": "¿Cómo usar Quivr?",
"what_is_quivr": "¿Qué es Quivr?",
"what_is_brain": "¿Qué es un cerebro?"
- }
+ },
+ "welcome": "Bienvenido"
}
diff --git a/frontend/public/locales/fr/chat.json b/frontend/public/locales/fr/chat.json
index 20bdb9d8a..2d7d4500d 100644
--- a/frontend/public/locales/fr/chat.json
+++ b/frontend/public/locales/fr/chat.json
@@ -44,5 +44,6 @@
"how_to_use_quivr": "Comment utiliser Quivr ?",
"what_is_quivr": "Qu'est-ce que Quivr ?",
"what_is_brain": "Qu'est-ce qu'un cerveau ?"
- }
+ },
+ "welcome": "Bienvenue"
}
diff --git a/frontend/public/locales/pt-br/chat.json b/frontend/public/locales/pt-br/chat.json
index 7e3419d8c..611663ece 100644
--- a/frontend/public/locales/pt-br/chat.json
+++ b/frontend/public/locales/pt-br/chat.json
@@ -44,5 +44,6 @@
"how_to_use_quivr": "Como usar o Quivr?",
"what_is_quivr": "O que é o Quivr?",
"what_is_brain": "O que é um cérebro?"
- }
+ },
+ "welcome": "Bem-vindo"
}
diff --git a/frontend/public/locales/ru/chat.json b/frontend/public/locales/ru/chat.json
index 03346534a..5d1d001f9 100644
--- a/frontend/public/locales/ru/chat.json
+++ b/frontend/public/locales/ru/chat.json
@@ -44,5 +44,6 @@
"how_to_use_quivr": "Как использовать Quivr?",
"what_is_quivr": "Что такое Quivr?",
"what_is_brain": "Что такое мозг?"
- }
+ },
+ "welcome": "Добро пожаловать"
}
diff --git a/frontend/public/locales/zh-cn/chat.json b/frontend/public/locales/zh-cn/chat.json
index c9fc6fbe8..503401779 100644
--- a/frontend/public/locales/zh-cn/chat.json
+++ b/frontend/public/locales/zh-cn/chat.json
@@ -45,5 +45,6 @@
"how_to_use_quivr": "如何使用Quivr?",
"what_is_quivr": "什么是Quivr?",
"what_is_brain": "什么是大脑?"
- }
+ },
+ "welcome": "欢迎来到"
}