From 5fde3b7ab89e097d8ee2a57b11e9a750f6cb67d8 Mon Sep 17 00:00:00 2001 From: mamadoudicko Date: Wed, 20 Sep 2023 12:00:16 +0200 Subject: [PATCH 1/5] feat: add brain library button --- .../[brainId]/components/BrainsList.tsx | 18 +++++++++++++++++- .../app/brains-management/library/page.tsx | 14 ++++++++++++++ .../components/AddBrainModal/AddBrainModal.tsx | 11 +++++++++-- frontend/public/locales/en/brain.json | 3 ++- frontend/public/locales/es/brain.json | 3 ++- frontend/public/locales/fr/brain.json | 3 ++- frontend/public/locales/pt-br/brain.json | 3 ++- frontend/public/locales/ru/brain.json | 3 ++- frontend/public/locales/zh-cn/brain.json | 3 ++- 9 files changed, 52 insertions(+), 9 deletions(-) create mode 100644 frontend/app/brains-management/library/page.tsx diff --git a/frontend/app/brains-management/[brainId]/components/BrainsList.tsx b/frontend/app/brains-management/[brainId]/components/BrainsList.tsx index 66fbaad12af6..8f4baf8beacd 100644 --- a/frontend/app/brains-management/[brainId]/components/BrainsList.tsx +++ b/frontend/app/brains-management/[brainId]/components/BrainsList.tsx @@ -1,8 +1,11 @@ "use client"; import { motion, MotionConfig } from "framer-motion"; +import Link from "next/link"; +import { useTranslation } from "react-i18next"; import { MdChevronRight } from "react-icons/md"; import { AddBrainModal } from "@/lib/components/AddBrainModal/AddBrainModal"; +import Button from "@/lib/components/ui/Button"; import { cn } from "@/lib/utils"; import { BrainListItem } from "./BrainListItem"; @@ -13,6 +16,8 @@ export const BrainsList = (): JSX.Element => { const { open, setOpen, searchQuery, setSearchQuery, brains } = useBrainsList(); + const { t } = useTranslation("brain"); + return ( { ))}
- + + + +
diff --git a/frontend/app/brains-management/library/page.tsx b/frontend/app/brains-management/library/page.tsx new file mode 100644 index 000000000000..a57c54e0830e --- /dev/null +++ b/frontend/app/brains-management/library/page.tsx @@ -0,0 +1,14 @@ +"use client"; +const BrainsLibrary = (): JSX.Element => { + return ( +
+
+
+

Brains Library

+
+
+
+ ); +}; + +export default BrainsLibrary; diff --git a/frontend/lib/components/AddBrainModal/AddBrainModal.tsx b/frontend/lib/components/AddBrainModal/AddBrainModal.tsx index 2d2c33256b37..e985ac68d9e8 100644 --- a/frontend/lib/components/AddBrainModal/AddBrainModal.tsx +++ b/frontend/lib/components/AddBrainModal/AddBrainModal.tsx @@ -9,6 +9,7 @@ import Button from "@/lib/components/ui/Button"; import Field from "@/lib/components/ui/Field"; import { Modal } from "@/lib/components/ui/Modal"; import { defineMaxTokens } from "@/lib/helpers/defineMaxTokens"; +import { cn } from "@/lib/utils"; import { PublicAccessConfirmationModal } from "./components/PublicAccessConfirmationModal"; import { useAddBrainModal } from "./hooks/useAddBrainModal"; @@ -16,7 +17,13 @@ import { Divider } from "../ui/Divider"; import { Radio } from "../ui/Radio"; import { TextArea } from "../ui/TextArea"; -export const AddBrainModal = (): JSX.Element => { +type AddBrainModalProps = { + triggerClassName?: string; +}; + +export const AddBrainModal = ({ + triggerClassName, +}: AddBrainModalProps): JSX.Element => { const { t } = useTranslation(["translation", "brain", "config"]); const { handleSubmit, @@ -43,7 +50,7 @@ export const AddBrainModal = (): JSX.Element => { + + ); +}; diff --git a/frontend/app/brains-management/library/hooks/useBrainsLibrary.tsx b/frontend/app/brains-management/library/hooks/useBrainsLibrary.tsx new file mode 100644 index 000000000000..4ffddf43420d --- /dev/null +++ b/frontend/app/brains-management/library/hooks/useBrainsLibrary.tsx @@ -0,0 +1,43 @@ +import { useQuery } from "@tanstack/react-query"; +import { useEffect, useState } from "react"; + +import { PUBLIC_BRAINS_KEY } from "@/lib/api/brain/config"; +import { useBrainApi } from "@/lib/api/brain/useBrainApi"; +import { Brain } from "@/lib/context/BrainProvider/types"; + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const useBrainsLibrary = () => { + const [searchBarText, setSearchBarText] = useState(""); + const { getPublicBrains } = useBrainApi(); + const { data: publicBrains = [] } = useQuery({ + queryKey: [PUBLIC_BRAINS_KEY], + queryFn: getPublicBrains, + }); + + const [displayingPublicBrains, setDisplayingPublicBrains] = useState( + [] + ); + + useEffect(() => { + setDisplayingPublicBrains(publicBrains); + }, [publicBrains]); + + useEffect(() => { + if (searchBarText === "") { + setDisplayingPublicBrains(publicBrains); + + return; + } + setDisplayingPublicBrains( + publicBrains.filter((brain) => + brain.name.toLowerCase().includes(searchBarText.toLowerCase()) + ) + ); + }, [publicBrains, searchBarText]); + + return { + displayingPublicBrains, + searchBarText, + setSearchBarText, + }; +}; diff --git a/frontend/app/brains-management/library/page.tsx b/frontend/app/brains-management/library/page.tsx index a57c54e0830e..f4d72de6dc48 100644 --- a/frontend/app/brains-management/library/page.tsx +++ b/frontend/app/brains-management/library/page.tsx @@ -1,11 +1,35 @@ "use client"; + +import { useTranslation } from "react-i18next"; + +import Field from "@/lib/components/ui/Field"; + +import { PublicBrainItem } from "./components/PublicBrainItem"; +import { useBrainsLibrary } from "./hooks/useBrainsLibrary"; + const BrainsLibrary = (): JSX.Element => { + const { displayingPublicBrains, searchBarText, setSearchBarText } = + useBrainsLibrary(); + const { t } = useTranslation("brain"); + return ( -
-
-
-

Brains Library

-
+
+
+ setSearchBarText(e.target.value)} + name="search" + inputClassName="w-max lg:min-w-[300px] md:min-w-[200px] min-w-[100px] mt-10 rounded-3xl bg-white" + placeholder={t("public_brains_search_bar_placeholder")} + /> +
+ +
+ {displayingPublicBrains.map((brain) => ( +
+ +
+ ))}
); diff --git a/frontend/lib/api/brain/config.ts b/frontend/lib/api/brain/config.ts index 32f7aedb78e0..d3da5381c874 100644 --- a/frontend/lib/api/brain/config.ts +++ b/frontend/lib/api/brain/config.ts @@ -1,7 +1,9 @@ -const brainDataKey = "quivr-brains"; +const BRAIN_DATA_KEY = "quivr-brains"; export const getBrainDataKey = (brainId: string): string => - `${brainDataKey}-${brainId}`; + `${BRAIN_DATA_KEY}-${brainId}`; export const getBrainKnowledgeDataKey = (brainId: string): string => - `${brainDataKey}-${brainId}-knowledge`; + `${BRAIN_DATA_KEY}-${brainId}-knowledge`; + +export const PUBLIC_BRAINS_KEY = "quivr-public-brains"; diff --git a/frontend/public/locales/en/brain.json b/frontend/public/locales/en/brain.json index 4df3b9b25f3e..83e190fe90d7 100644 --- a/frontend/public/locales/en/brain.json +++ b/frontend/public/locales/en/brain.json @@ -34,5 +34,7 @@ "set_brain_status_to_public_modal_description": "Every Quivr user will be able to:
- Subscribe to your brain in the 'brains library'.
- Use this brain and check the prompt and model configurations.

They won't have access to your uploaded files and people section.", "confirm_set_brain_status_to_public": "Yes, set as public", "cancel_set_brain_status_to_public": "No, keep it private", - "brain_library_button_label":"Brains library" + "brain_library_button_label":"Brains library", + "public_brains_search_bar_placeholder":"Search public brains", + "public_brain_subscribe_button_label":"Subscribe" } \ No newline at end of file diff --git a/frontend/public/locales/es/brain.json b/frontend/public/locales/es/brain.json index c6d7b9c06ae2..984d7ca8439c 100644 --- a/frontend/public/locales/es/brain.json +++ b/frontend/public/locales/es/brain.json @@ -34,5 +34,7 @@ "set_brain_status_to_public_modal_description": "Cada usuario de Quivr podrá:
- Suscribirse a tu cerebro en la 'biblioteca de cerebros'.
- Usar este cerebro y comprobar las configuraciones de las indicaciones y el modelo.

No tendrán acceso a tus archivos cargados ni a la sección de personas.", "confirm_set_brain_status_to_public": "Sí, establecer como público", "cancel_set_brain_status_to_public": "No, mantenerlo privado", - "brain_library_button_label": "Biblioteca de cerebros" + "brain_library_button_label": "Biblioteca de cerebros", + "public_brains_search_bar_placeholder": "Buscar cerebros públicos", + "public_brain_subscribe_button_label": "Suscribirse" } \ No newline at end of file diff --git a/frontend/public/locales/fr/brain.json b/frontend/public/locales/fr/brain.json index 6adf5eeadb9b..2b0c9e756c64 100644 --- a/frontend/public/locales/fr/brain.json +++ b/frontend/public/locales/fr/brain.json @@ -34,5 +34,7 @@ "set_brain_status_to_public_modal_description": "Chaque utilisateur de Quivr pourra :
- S'abonner à votre cerveau dans la 'bibliothèque des cerveaux'.
- Utiliser ce cerveau et vérifier les configurations de prompts et de modèles.

Ils n'auront pas accès à vos fichiers téléchargés et à la section des personnes.", "confirm_set_brain_status_to_public": "Oui, définir comme public", "cancel_set_brain_status_to_public": "Non, le garder privé", - "brain_library_button_label": "Bibliothèque des cerveaux" + "brain_library_button_label": "Bibliothèque des cerveaux", + "public_brains_search_bar_placeholder": "Rechercher des cerveaux publics", + "public_brain_subscribe_button_label": "S'abonner" } \ No newline at end of file diff --git a/frontend/public/locales/pt-br/brain.json b/frontend/public/locales/pt-br/brain.json index 6c996dd83711..2f2d2082ead7 100644 --- a/frontend/public/locales/pt-br/brain.json +++ b/frontend/public/locales/pt-br/brain.json @@ -34,5 +34,7 @@ "set_brain_status_to_public_modal_description": "Cada usuário do Quivr poderá:
- Se inscrever em seu cérebro na 'biblioteca de cérebros'.
- Usar este cérebro e verificar as configurações de prompts e modelos.

Eles não terão acesso aos seus arquivos enviados e à seção de pessoas.", "confirm_set_brain_status_to_public": "Sim, definir como público", "cancel_set_brain_status_to_public": "Não, mantê-lo privado", - "brain_library_button_label": "Biblioteca de cérebros" + "brain_library_button_label": "Biblioteca de cérebros", + "public_brains_search_bar_placeholder": "Pesquisar cérebros públicos", + "public_brain_subscribe_button_label": "Inscrever-se" } \ No newline at end of file diff --git a/frontend/public/locales/ru/brain.json b/frontend/public/locales/ru/brain.json index 4aa328ce7360..95eb98c303f8 100644 --- a/frontend/public/locales/ru/brain.json +++ b/frontend/public/locales/ru/brain.json @@ -34,5 +34,7 @@ "set_brain_status_to_public_modal_description": "Каждый пользователь Quivr сможет:
- Подписаться на ваш мозг в 'библиотеке мозгов'.
- Использовать этот мозг и проверить настройки подсказок и модели.

У них не будет доступа к вашим загруженным файлам и разделу 'люди'.", "confirm_set_brain_status_to_public": "Да, установить как публичный", "cancel_set_brain_status_to_public": "Нет, оставить приватным", - "brain_library_button_label": "Библиотека мозгов" + "brain_library_button_label": "Библиотека мозгов", + "public_brains_search_bar_placeholder": "Поиск общественных мозгов", + "public_brain_subscribe_button_label": "Подписаться" } \ No newline at end of file diff --git a/frontend/public/locales/zh-cn/brain.json b/frontend/public/locales/zh-cn/brain.json index 32a60e8a7806..42515d26b772 100644 --- a/frontend/public/locales/zh-cn/brain.json +++ b/frontend/public/locales/zh-cn/brain.json @@ -34,5 +34,7 @@ "set_brain_status_to_public_modal_description": "每个 Quivr 用户将能够:
- 在 '大脑库' 中订阅您的大脑。
- 使用此大脑并检查提示和模型配置。

他们将无法访问您上传的文件和人员部分。", "confirm_set_brain_status_to_public": "是的,设为公共", "cancel_set_brain_status_to_public": "不,保持私密", - "brain_library_button_label": "大脑库" + "brain_library_button_label": "大脑库", + "public_brains_search_bar_placeholder": "搜索公共大脑", + "public_brain_subscribe_button_label": "订阅" } \ No newline at end of file From 0241a9bfa5d1f587630aaef13bab21dd1031770d Mon Sep 17 00:00:00 2001 From: mamadoudicko Date: Wed, 20 Sep 2023 18:23:18 +0200 Subject: [PATCH 5/5] feat: add brain subscriber count --- backend/models/brain_entity.py | 1 + backend/models/databases/supabase/brains.py | 25 ++++++++++++++++----- backend/repository/brain/create_brain.py | 2 +- frontend/lib/context/BrainProvider/types.ts | 1 + 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/backend/models/brain_entity.py b/backend/models/brain_entity.py index bd9eafeefcca..a101af601076 100644 --- a/backend/models/brain_entity.py +++ b/backend/models/brain_entity.py @@ -39,3 +39,4 @@ class PublicBrain(BaseModel): id: UUID name: str description: Optional[str] + number_of_subscribers: int = 0 diff --git a/backend/models/databases/supabase/brains.py b/backend/models/databases/supabase/brains.py index 4402f47f5d8c..5389d0848dff 100644 --- a/backend/models/databases/supabase/brains.py +++ b/backend/models/databases/supabase/brains.py @@ -84,13 +84,13 @@ def get_public_brains(self) -> list[PublicBrain]: ) public_brains: list[PublicBrain] = [] for item in response.data: - public_brains.append( - PublicBrain( - id=item["id"], - name=item["name"], - description=item["description"], - ) + brain = PublicBrain( + id=item["id"], + name=item["name"], + description=item["description"], ) + brain.number_of_subscribers = self.get_brain_subscribers_count(brain.id) + public_brains.append(brain) return public_brains def get_brain_for_user(self, user_id, brain_id) -> MinimalBrainEntity | None: @@ -292,3 +292,16 @@ def get_brain_by_id(self, brain_id: UUID) -> BrainEntity | None: return None return BrainEntity(**response[0]) + + def get_brain_subscribers_count(self, brain_id: UUID) -> int: + response = ( + self.db.from_("brains_users") + .select( + "count", + ) + .filter("brain_id", "eq", str(brain_id)) + .execute() + ).data + if len(response) == 0: + raise ValueError(f"Brain with id {brain_id} does not exist.") + return response[0]["count"] diff --git a/backend/repository/brain/create_brain.py b/backend/repository/brain/create_brain.py index 84d7665aba27..e722dcdcc68e 100644 --- a/backend/repository/brain/create_brain.py +++ b/backend/repository/brain/create_brain.py @@ -1,5 +1,5 @@ -from models.databases.supabase.brains import CreateBrainProperties from models import BrainEntity, get_supabase_db +from models.databases.supabase.brains import CreateBrainProperties def create_brain(brain: CreateBrainProperties) -> BrainEntity: diff --git a/frontend/lib/context/BrainProvider/types.ts b/frontend/lib/context/BrainProvider/types.ts index f46c6e179980..ffc425e356f7 100644 --- a/frontend/lib/context/BrainProvider/types.ts +++ b/frontend/lib/context/BrainProvider/types.ts @@ -37,6 +37,7 @@ export type PublicBrain = { id: UUID; name: string; description?: string; + number_of_subscribers: number; }; export type BrainContextType = ReturnType;