{
+ setIsHovered(true);
+ onPreview();
+ }}
+ onMouseLeave={() => {
+ setIsHovered(false);
+ }}
+ className={cn(
+ 'relative rounded-[8px] border-[1.5px]',
+ isSelected ? 'border-border-theme-thick' : 'border-border-primary',
+ )}
+ >
+
e.preventDefault()}
+ onClick={onPreview}
+ >
+
+ {prompt.name}
+
+
+ {prompt.content}
+
+
+ {isHovered && (
+
setIsUsePromptHovered(true)}
+ onMouseLeave={() => setIsUsePromptHovered(false)}
+ onClick={onUsePrompt}
+ >
+ {t('customPrompt.usePrompt')}
+
+ )}
+
+ );
+}
diff --git a/src/components/chat/components/chat-input/prompt-modal/prompt-category.tsx b/src/components/chat/components/chat-input/prompt-modal/prompt-category.tsx
new file mode 100644
index 00000000..f460179b
--- /dev/null
+++ b/src/components/chat/components/chat-input/prompt-modal/prompt-category.tsx
@@ -0,0 +1,115 @@
+import { Button } from '../../ui/button';
+import { cn } from '../../../lib/utils';
+import { AiPromptCategory } from '../../../types/prompt';
+import { Separator } from '../../ui/separator';
+import { useMemo } from 'react';
+import { useTranslation } from '../../../i18n';
+
+export function PromptCategory({
+ isFeaturedSelected,
+ isCustomSelected,
+ selectedCatecory,
+ setIsFeaturedSelected,
+ setIsCustomSelected,
+ setSelectedCategory,
+}: {
+ isFeaturedSelected: boolean;
+ isCustomSelected: boolean;
+ selectedCatecory: AiPromptCategory | null;
+ setIsFeaturedSelected: (value: boolean) => void;
+ setIsCustomSelected: (value: boolean) => void;
+ setSelectedCategory: (category: AiPromptCategory | null) => void;
+}) {
+ const { t } = useTranslation();
+
+ const isAllSelected = useMemo(() => {
+ return !isCustomSelected && !isFeaturedSelected && !selectedCatecory;
+ }, [isCustomSelected, isFeaturedSelected, selectedCatecory]);
+
+ const categoryList = useMemo(() => {
+ const categories = Object.values(AiPromptCategory);
+
+ const withoutOthers = categories.filter(
+ (category) => category !== 'others',
+ );
+
+ const sorted = withoutOthers
+ .slice()
+ .sort((a, b) =>
+ t(`customPrompt.${a}`).localeCompare(t(`customPrompt.${b}`)),
+ );
+
+ return [...sorted, 'others'];
+ }, [t]);
+
+ return (
+
+
+
+
+
+
+
+ {categoryList.map((category) => (
+
+ ))}
+
+ );
+}
diff --git a/src/components/chat/components/chat-input/prompt-modal/prompt-database/index.tsx b/src/components/chat/components/chat-input/prompt-modal/prompt-database/index.tsx
new file mode 100644
index 00000000..bb19a356
--- /dev/null
+++ b/src/components/chat/components/chat-input/prompt-modal/prompt-database/index.tsx
@@ -0,0 +1,314 @@
+import { Button } from '../../../ui/button';
+import { Dialog, DialogContent, DialogTitle } from '../../../ui/dialog';
+import {
+ DropdownMenu,
+ DropdownMenuContent,
+ DropdownMenuRadioGroup,
+ DropdownMenuRadioItem,
+ DropdownMenuTrigger,
+} from '../../../ui/dropdown-menu';
+import { useTranslation } from '../../../../i18n';
+import { cn } from '../../../../lib/utils';
+import { usePromptModal } from '../../../../provider/prompt-modal-provider';
+import { ChevronDown } from 'lucide-react';
+import { useCallback, useEffect, useRef, useState } from 'react';
+import { PromptDatabaseViews } from './prompt-database-views';
+import { ReactComponent as CloseCircle } from '../../../../assets/icons/close_circle.svg';
+import { InvalidDatabaseDialog } from './invalid-database-dialog';
+import { useViewLoader } from '../../../../provider/view-loader-provider';
+
+export function PromptDatabaseModal({
+ isOpen,
+ closeModal,
+}: {
+ isOpen: boolean;
+ closeModal: () => void;
+}) {
+ const { getView } = useViewLoader();
+
+ const { t } = useTranslation();
+
+ const {
+ databaseConfig,
+ fields,
+ saveDatabaseConfig,
+ testDatabasePromptConfig,
+ } = usePromptModal();
+
+ const [currentDatabaseConfig, setCurrentDatabaseConfig] =
+ useState(databaseConfig);
+ const [currentFields, setCurrentFields] = useState(fields || []);
+ const [isInvalidModalOpen, setIsInvalidModalOpen] = useState(false);
+
+ useEffect(() => {
+ setCurrentDatabaseConfig(databaseConfig);
+ setCurrentFields(fields || []);
+ }, [databaseConfig, fields]);
+
+ const [viewName, setViewName] = useState