diff --git a/backend/core/routes/subscription_routes.py b/backend/core/routes/subscription_routes.py index 4763c5430bf8..13c4f7d0179e 100644 --- a/backend/core/routes/subscription_routes.py +++ b/backend/core/routes/subscription_routes.py @@ -5,9 +5,11 @@ from fastapi import APIRouter, Depends, HTTPException from models.brains import Brain from models.brains_subscription_invitations import BrainSubscription +from models.prompt import PromptStatusEnum from models.users import User from pydantic import BaseModel from repository.brain.create_brain_user import create_brain_user +from repository.brain.get_brain_by_id import get_brain_by_id from repository.brain.get_brain_details import get_brain_details from repository.brain.get_brain_for_user import get_brain_for_user from repository.brain.update_user_rights import update_brain_user_rights @@ -17,6 +19,8 @@ from repository.brain_subscription.subscription_invitation_service import ( SubscriptionInvitationService, ) +from repository.prompt.delete_prompt_py_id import delete_prompt_by_id +from repository.prompt.get_prompt_by_id import get_prompt_by_id from repository.user.get_user_email_by_user_id import get_user_email_by_user_id from repository.user.get_user_id_by_user_email import get_user_id_by_user_email @@ -143,6 +147,14 @@ async def remove_user_subscription( ] if len(brain_other_owners) == 0: + # Delete its prompt if it's private + deleting_brain = get_brain_by_id(brain_id) + if deleting_brain and deleting_brain.prompt_id: + deleting_brain_prompt = get_prompt_by_id(deleting_brain.prompt_id) + if deleting_brain_prompt is not None and ( + deleting_brain_prompt.status == PromptStatusEnum.private + ): + delete_prompt_by_id(deleting_brain.prompt_id) brain.delete_brain(current_user.id) else: brain.delete_user_from_brain(current_user.id) diff --git a/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/SettingsTab.tsx b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/SettingsTab.tsx index 4415b0d6b6c2..30ceb26902d6 100644 --- a/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/SettingsTab.tsx +++ b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/SettingsTab.tsx @@ -10,7 +10,7 @@ import { TextArea } from "@/lib/components/ui/TextArea"; import { models, paidModels } from "@/lib/context/BrainConfigProvider/types"; import { defineMaxTokens } from "@/lib/helpers/defineMexTokens"; -import { PublicPrompts } from "./components/PublicPrompts"; +import { PublicPrompts } from "./components/PublicPrompts/PublicPrompts"; import { useSettingsTab } from "./hooks/useSettingsTab"; type SettingsTabProps = { diff --git a/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts.tsx b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts.tsx deleted file mode 100644 index 2f93e15266e5..000000000000 --- a/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import * as Accordion from "@radix-ui/react-accordion"; -import { ChangeEvent, useEffect, useState } from "react"; - -import { usePromptApi } from "@/lib/api/prompt/usePromptApi"; -import { Prompt } from "@/lib/types/Prompt"; - -type PublicPromptsProps = { - onSelect: ({ title, content }: { title: string; content: string }) => void; -}; - -export const PublicPrompts = ({ - onSelect, -}: PublicPromptsProps): JSX.Element => { - const [publicPrompts, setPublicPrompts] = useState([]); - - const { getPublicPrompts } = usePromptApi(); - - const fetchPublicPrompts = async () => { - setPublicPrompts(await getPublicPrompts()); - }; - - const handleChange = (event: ChangeEvent) => { - const selectedPrompt = publicPrompts.find( - (prompt) => prompt.id === event.target.value - ); - if (selectedPrompt) { - onSelect({ - title: selectedPrompt.title, - content: selectedPrompt.content, - }); - } - }; - - useEffect(() => { - void fetchPublicPrompts(); - }, []); - - return ( - - - Pick in public prompts - - - - - - ); -}; diff --git a/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/PublicPrompts.tsx b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/PublicPrompts.tsx new file mode 100644 index 000000000000..e99cc5635ac7 --- /dev/null +++ b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/PublicPrompts.tsx @@ -0,0 +1,22 @@ +import { PublicPromptsList } from "./components/PublicPromptsList/PublicPromptsList"; +import { usePublicPrompts } from "./hooks/usePublicPrompts"; + +type PublicPromptsProps = { + onSelect: ({ title, content }: { title: string; content: string }) => void; +}; + +export const PublicPrompts = ({ + onSelect, +}: PublicPromptsProps): JSX.Element => { + const { handleChange, publicPrompts } = usePublicPrompts({ + onSelect, + }); + + return ( + + ); +}; diff --git a/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/components/PublicPromptsList/PublicPromptsList.tsx b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/components/PublicPromptsList/PublicPromptsList.tsx new file mode 100644 index 000000000000..5a2c8a7057ce --- /dev/null +++ b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/components/PublicPromptsList/PublicPromptsList.tsx @@ -0,0 +1,53 @@ +import { ChangeEvent } from "react"; + +import { Prompt } from "@/lib/types/Prompt"; + +import { usePublicPromptsList } from "./hooks/usePublicPromptsList"; + +type PublicPromptsListProps = { + options: Prompt[]; + onChange: (event: ChangeEvent) => void; + onSelect: ({ title, content }: { title: string; content: string }) => void; +}; + +export const PublicPromptsList = ({ + options, + onChange, + onSelect, +}: PublicPromptsListProps): JSX.Element => { + const { + handleOptionClick, + isOpen, + selectRef, + selectedOption, + toggleDropdown, + } = usePublicPromptsList({ + onChange, + onSelect, + }); + + return ( +
+ + {isOpen && ( +
+ {options.map((option) => ( +
handleOptionClick(option)} + > + {option.title} +
+ ))} +
+ )} +
+ ); +}; diff --git a/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/components/PublicPromptsList/hooks/usePublicPromptsList.ts b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/components/PublicPromptsList/hooks/usePublicPromptsList.ts new file mode 100644 index 000000000000..d45a28680adf --- /dev/null +++ b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/components/PublicPromptsList/hooks/usePublicPromptsList.ts @@ -0,0 +1,59 @@ +import { ChangeEvent, useEffect, useRef, useState } from "react"; + +import { Prompt } from "@/lib/types/Prompt"; + +type UsePublicPromptsListProps = { + onChange: (event: ChangeEvent) => void; + onSelect: ({ title, content }: { title: string; content: string }) => void; +}; + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const usePublicPromptsList = ({ + onChange, + onSelect, +}: UsePublicPromptsListProps) => { + const [isOpen, setIsOpen] = useState(false); + const [selectedOption, setSelectedOption] = useState(null); + const selectRef = useRef(null); + + const toggleDropdown = () => { + setIsOpen((prevIsOpen) => !prevIsOpen); + }; + + const handleOptionClick = (option: Prompt) => { + setSelectedOption(option); + setIsOpen(false); + onChange({ + target: { value: option.id }, + } as ChangeEvent); + onSelect({ + title: option.title, + content: option.content, + }); + }; + + const handleClickOutside = (event: MouseEvent) => { + if ( + selectRef.current && + !selectRef.current.contains(event.target as Node) + ) { + setIsOpen(false); + } + }; + + useEffect(() => { + document.addEventListener("click", handleClickOutside, true); + + return () => { + document.removeEventListener("click", handleClickOutside, true); + }; + }, []); + + return { + isOpen, + selectedOption, + selectRef, + toggleDropdown, + handleOptionClick, + }; +}; diff --git a/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/components/PublicPromptsList/index.ts b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/components/PublicPromptsList/index.ts new file mode 100644 index 000000000000..4ce1d6e8aa9a --- /dev/null +++ b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/components/PublicPromptsList/index.ts @@ -0,0 +1 @@ +export * from "./PublicPromptsList"; diff --git a/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/components/index.ts b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/components/index.ts new file mode 100644 index 000000000000..4ce1d6e8aa9a --- /dev/null +++ b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/components/index.ts @@ -0,0 +1 @@ +export * from "./PublicPromptsList"; diff --git a/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/hooks/usePublicPrompts.ts b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/hooks/usePublicPrompts.ts new file mode 100644 index 000000000000..14edab8ecb12 --- /dev/null +++ b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/hooks/usePublicPrompts.ts @@ -0,0 +1,38 @@ +import { ChangeEvent, useEffect, useState } from "react"; + +import { usePromptApi } from "@/lib/api/prompt/usePromptApi"; +import { Prompt } from "@/lib/types/Prompt"; + +type UsePublicPromptsProps = { + onSelect: ({ title, content }: { title: string; content: string }) => void; +}; + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +export const usePublicPrompts = ({ onSelect }: UsePublicPromptsProps) => { + const [publicPrompts, setPublicPrompts] = useState([]); + const { getPublicPrompts } = usePromptApi(); + + useEffect(() => { + const fetchPublicPrompts = async () => { + setPublicPrompts(await getPublicPrompts()); + }; + void fetchPublicPrompts(); + }, []); + + const handleChange = (event: ChangeEvent) => { + const selectedPrompt = publicPrompts.find( + (prompt) => prompt.id === event.target.value + ); + if (selectedPrompt) { + onSelect({ + title: selectedPrompt.title, + content: selectedPrompt.content, + }); + } + }; + + return { + publicPrompts, + handleChange, + }; +}; diff --git a/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/index.ts b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/index.ts new file mode 100644 index 000000000000..519e04261bf0 --- /dev/null +++ b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/PublicPrompts/index.ts @@ -0,0 +1 @@ +export * from "./PublicPrompts"; diff --git a/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/index.ts b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/index.ts new file mode 100644 index 000000000000..519e04261bf0 --- /dev/null +++ b/frontend/app/brains-management/[brainId]/components/BrainManagementTabs/components/SettingsTab/components/index.ts @@ -0,0 +1 @@ +export * from "./PublicPrompts";