diff --git a/frontend/src/Setting.tsx b/frontend/src/Setting.tsx index 172691a0..b24c4ca6 100644 --- a/frontend/src/Setting.tsx +++ b/frontend/src/Setting.tsx @@ -15,23 +15,24 @@ import { import { Doc } from './preferences/preferenceApi'; import { useDarkTheme } from './hooks'; import Dropdown from './components/Dropdown'; +import { ActiveState } from './models/misc'; +import PromptsModal from './preferences/PromptsModal'; +type PromptProps = { + prompts: { name: string; id: string; type: string }[]; + selectedPrompt: { name: string; id: string; type: string }; + onSelectPrompt: (name: string, id: string, type: string) => void; + setPrompts: (prompts: { name: string; id: string; type: string }[]) => void; + apiHost: string; +}; const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; - const embeddingsName = import.meta.env.VITE_EMBEDDINGS_NAME || 'huggingface_sentence-transformers/all-mpnet-base-v2'; -const Setting: React.FC = () => { - const tabs = ['General', 'Prompts', 'Documents', 'API Keys']; - //const tabs = ['General', 'Prompts', 'Documents', 'Widgets']; +const Setting: React.FC = () => { + const tabs = ['General', 'Documents', 'API Keys']; const [activeTab, setActiveTab] = useState('General'); - const [prompts, setPrompts] = useState< - { name: string; id: string; type: string }[] - >([]); - const selectedPrompt = useSelector(selectPrompt); - const [isAddPromptModalOpen, setAddPromptModalOpen] = useState(false); const documents = useSelector(selectSourceDocs); - const [isAddDocumentModalOpen, setAddDocumentModalOpen] = useState(false); const dispatch = useDispatch(); @@ -41,43 +42,6 @@ const Setting: React.FC = () => { setWidgetScreenshot(screenshot); }; - useEffect(() => { - const fetchPrompts = async () => { - try { - const response = await fetch(`${apiHost}/api/get_prompts`); - if (!response.ok) { - throw new Error('Failed to fetch prompts'); - } - const promptsData = await response.json(); - setPrompts(promptsData); - } catch (error) { - console.error(error); - } - }; - fetchPrompts(); - }, []); - - const onDeletePrompt = (name: string, id: string) => { - setPrompts(prompts.filter((prompt) => prompt.id !== id)); - - fetch(`${apiHost}/api/delete_prompt`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - // send id in body only - body: JSON.stringify({ id: id }), - }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to delete prompt'); - } - }) - .catch((error) => { - console.error(error); - }); - }; - const handleDeleteClick = (index: number, doc: Doc) => { const docPath = 'indexes/' + 'local' + '/' + doc.name; @@ -156,17 +120,6 @@ const Setting: React.FC = () => { switch (activeTab) { case 'General': return ; - case 'Prompts': - return ( - - dispatch(setPrompt({ name: name, id: id, type: type })) - } - setPrompts={setPrompts} - /> - ); case 'Documents': return ( { const themes = ['Light', 'Dark']; const languages = ['English']; const chunks = ['0', '2', '4', '6', '8', '10']; + const [prompts, setPrompts] = useState< + { name: string; id: string; type: string }[] + >([]); const selectedChunks = useSelector(selectChunks); const [isDarkTheme, toggleTheme] = useDarkTheme(); const [selectedTheme, setSelectedTheme] = useState( @@ -200,6 +156,24 @@ const General: React.FC = () => { ); const dispatch = useDispatch(); const [selectedLanguage, setSelectedLanguage] = useState(languages[0]); + const selectedPrompt = useSelector(selectPrompt); + const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com'; + + useEffect(() => { + const fetchPrompts = async () => { + try { + const response = await fetch(`${apiHost}/api/get_prompts`); + if (!response.ok) { + throw new Error('Failed to fetch prompts'); + } + const promptsData = await response.json(); + setPrompts(promptsData); + } catch (error) { + console.error(error); + } + }; + fetchPrompts(); + }, []); return (
@@ -211,6 +185,8 @@ const General: React.FC = () => { setSelectedTheme(option); option !== selectedTheme && toggleTheme(); }} + size="w-56" + rounded="3xl" />
@@ -221,9 +197,11 @@ const General: React.FC = () => { options={languages} selectedValue={selectedLanguage} onSelect={setSelectedLanguage} + size="w-56" + rounded="3xl" />
-
+

Chunks processed per query

@@ -231,6 +209,19 @@ const General: React.FC = () => { options={chunks} selectedValue={selectedChunks} onSelect={(value: string) => dispatch(setChunks(value))} + size="w-56" + rounded="3xl" + /> +
+
+ + dispatch(setPrompt({ name: name, id: id, type: type })) + } + setPrompts={setPrompts} + apiHost={apiHost} />
@@ -238,12 +229,6 @@ const General: React.FC = () => { }; export default Setting; -type PromptProps = { - prompts: { name: string; id: string; type: string }[]; - selectedPrompt: { name: string; id: string; type: string }; - onSelectPrompt: (name: string, id: string, type: string) => void; - setPrompts: (prompts: { name: string; id: string; type: string }[]) => void; -}; const Prompts: React.FC = ({ prompts, @@ -260,11 +245,20 @@ const Prompts: React.FC = ({ id: string; type: string; }) => { - setNewPromptName(name); + setEditPromptName(name); onSelectPrompt(name, id, type); }; - const [newPromptName, setNewPromptName] = useState(selectedPrompt.name); + const [newPromptName, setNewPromptName] = useState(''); const [newPromptContent, setNewPromptContent] = useState(''); + const [editPromptName, setEditPromptName] = useState(''); + const [editPromptContent, setEditPromptContent] = useState(''); + const [currentPromptEdit, setCurrentPromptEdit] = useState({ + id: '', + name: '', + type: '', + }); + const [modalType, setModalType] = useState<'ADD' | 'EDIT'>('ADD'); + const [modalState, setModalState] = useState('INACTIVE'); const handleAddPrompt = async () => { try { @@ -288,6 +282,7 @@ const Prompts: React.FC = ({ { name: newPromptName, id: newPrompt.id, type: 'private' }, ]); } + setModalState('INACTIVE'); onSelectPrompt(newPromptName, newPrompt.id, newPromptContent); setNewPromptName(newPromptName); } catch (error) { @@ -295,16 +290,14 @@ const Prompts: React.FC = ({ } }; - const handleDeletePrompt = () => { - setPrompts(prompts.filter((prompt) => prompt.id !== selectedPrompt.id)); - console.log('selectedPrompt.id', selectedPrompt.id); - + const handleDeletePrompt = (id: string) => { + setPrompts(prompts.filter((prompt) => prompt.id !== id)); fetch(`${apiHost}/api/delete_prompt`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, - body: JSON.stringify({ id: selectedPrompt.id }), + body: JSON.stringify({ id: id }), }) .then((response) => { if (!response.ok) { @@ -313,7 +306,6 @@ const Prompts: React.FC = ({ // get 1st prompt and set it as selected if (prompts.length > 0) { onSelectPrompt(prompts[0].name, prompts[0].id, prompts[0].type); - setNewPromptName(prompts[0].name); } }) .catch((error) => { @@ -321,50 +313,65 @@ const Prompts: React.FC = ({ }); }; - useEffect(() => { - const fetchPromptContent = async () => { - console.log('fetching prompt content'); - try { - const response = await fetch( - `${apiHost}/api/get_single_prompt?id=${selectedPrompt.id}`, - { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - }, + const fetchPromptContent = async (id: string) => { + console.log('fetching prompt content'); + try { + const response = await fetch( + `${apiHost}/api/get_single_prompt?id=${id}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', }, - ); - if (!response.ok) { - throw new Error('Failed to fetch prompt content'); - } - const promptContent = await response.json(); - setNewPromptContent(promptContent.content); - } catch (error) { - console.error(error); + }, + ); + if (!response.ok) { + throw new Error('Failed to fetch prompt content'); } - }; + const promptContent = await response.json(); + setEditPromptContent(promptContent.content); + } catch (error) { + console.error(error); + } + }; - fetchPromptContent(); - }, [selectedPrompt]); - - const handleSaveChanges = () => { + const handleSaveChanges = (id: string, type: string) => { fetch(`${apiHost}/api/update_prompt`, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ - id: selectedPrompt.id, - name: newPromptName, - content: newPromptContent, + id: id, + name: editPromptName, + content: editPromptContent, }), }) .then((response) => { if (!response.ok) { throw new Error('Failed to update prompt'); } - onSelectPrompt(newPromptName, selectedPrompt.id, selectedPrompt.type); - setNewPromptName(newPromptName); + if (setPrompts) { + const existingPromptIndex = prompts.findIndex( + (prompt) => prompt.id === id, + ); + if (existingPromptIndex === -1) { + setPrompts([ + ...prompts, + { name: editPromptName, id: id, type: type }, + ]); + } else { + const updatedPrompts = [...prompts]; + updatedPrompts[existingPromptIndex] = { + name: editPromptName, + id: id, + type: type, + }; + setPrompts(updatedPrompts); + } + } + setModalState('INACTIVE'); + onSelectPrompt(editPromptName, id, type); }) .catch((error) => { console.error(error); @@ -372,76 +379,65 @@ const Prompts: React.FC = ({ }; return ( -
-
-

Active Prompt

- + <> +
+
+
+

Active Prompt

+ { + setModalType('EDIT'); + setEditPromptName(name); + fetchPromptContent(id); + setCurrentPromptEdit({ id: id, name: name, type: type }); + setModalState('ACTIVE'); + }} + onDelete={handleDeletePrompt} + /> +
+ +
- -
-

Prompt name

{' '} -

- start by editing name -

- setNewPromptName(e.target.value)} - /> -
- -
-

Prompt content

- +
+
+ + +
+
+ ); +} + +function EditPrompt({ + setModalState, + handleEditPrompt, + editPromptName, + setEditPromptName, + editPromptContent, + setEditPromptContent, + currentPromptEdit, +}: { + setModalState: (state: ActiveState) => void; + handleEditPrompt?: (id: string, type: string) => void; + editPromptName: string; + setEditPromptName: (name: string) => void; + editPromptContent: string; + setEditPromptContent: (content: string) => void; + currentPromptEdit: { name: string; id: string; type: string }; +}) { + return ( +
+

Edit Prompt

+

+ Edit your custom prompt and save it to DocsGPT +

+
+ setEditPromptName(e.target.value)} + > +
+ + Prompt Name + +
+
+ + Prompt Text + +
+ +
+
+ + +
+
+ ); +} + +export default function PromptsModal({ + modalState, + setModalState, + type, + newPromptName, + setNewPromptName, + newPromptContent, + setNewPromptContent, + editPromptName, + setEditPromptName, + editPromptContent, + setEditPromptContent, + currentPromptEdit, + handleAddPrompt, + handleEditPrompt, +}: { + modalState: ActiveState; + setModalState: (state: ActiveState) => void; + type: 'ADD' | 'EDIT'; + newPromptName: string; + setNewPromptName: (name: string) => void; + newPromptContent: string; + setNewPromptContent: (content: string) => void; + editPromptName: string; + setEditPromptName: (name: string) => void; + editPromptContent: string; + setEditPromptContent: (content: string) => void; + currentPromptEdit: { name: string; id: string; type: string }; + handleAddPrompt?: () => void; + handleEditPrompt?: (id: string, type: string) => void; +}) { + let view; + + if (type === 'ADD') { + view = ( + + ); + } else if (type === 'EDIT') { + view = ( + + ); + } else { + view = <>; + } + return ( +
+
+ {view} +
+
+ ); +} diff --git a/frontend/src/upload/Upload.tsx b/frontend/src/upload/Upload.tsx index 45fc4e1a..15ae206b 100644 --- a/frontend/src/upload/Upload.tsx +++ b/frontend/src/upload/Upload.tsx @@ -307,6 +307,8 @@ export default function Upload({ onSelect={(value: { label: string; value: string }) => setUrlType(value) } + size="w-full" + rounded="3xl" /> {urlType.label !== 'Reddit' ? ( <>