diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 6a1fd00d..987f38e5 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -6,7 +6,7 @@ import PageNotFound from './PageNotFound'; import { inject } from '@vercel/analytics'; import { useMediaQuery } from './hooks'; import { useState } from 'react'; -import Setting from './Setting'; +import Setting from './settings'; inject(); diff --git a/frontend/src/Setting.tsx b/frontend/src/Setting.tsx deleted file mode 100644 index b24c4ca6..00000000 --- a/frontend/src/Setting.tsx +++ /dev/null @@ -1,996 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { useSelector, useDispatch } from 'react-redux'; -import ArrowLeft from './assets/arrow-left.svg'; -import ArrowRight from './assets/arrow-right.svg'; -import Exit from './assets/exit.svg'; -import Trash from './assets/trash.svg'; -import { - selectPrompt, - setPrompt, - selectSourceDocs, - setSourceDocs, - setChunks, - selectChunks, -} from './preferences/preferenceSlice'; -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', 'Documents', 'API Keys']; - const [activeTab, setActiveTab] = useState('General'); - const documents = useSelector(selectSourceDocs); - - const dispatch = useDispatch(); - - const [widgetScreenshot, setWidgetScreenshot] = useState(null); - - const updateWidgetScreenshot = (screenshot: File | null) => { - setWidgetScreenshot(screenshot); - }; - - const handleDeleteClick = (index: number, doc: Doc) => { - const docPath = 'indexes/' + 'local' + '/' + doc.name; - - fetch(`${apiHost}/api/delete_old?path=${docPath}`, { - method: 'GET', - }) - .then((response) => { - if (response.ok && documents) { - const updatedDocuments = [ - ...documents.slice(0, index), - ...documents.slice(index + 1), - ]; - dispatch(setSourceDocs(updatedDocuments)); - } - }) - .catch((error) => console.error(error)); - }; - - return ( -
-

- Settings -

-
-
- -
-
- {tabs.map((tab, index) => ( - - ))} -
-
- -
-
- {renderActiveTab()} - - {/* {activeTab === 'Widgets' && ( - - )} */} -
- ); - - function scrollTabs(direction: number) { - const container = document.querySelector('.flex-nowrap'); - if (container) { - container.scrollLeft += direction * 100; // Adjust the scroll amount as needed - } - } - - function renderActiveTab() { - switch (activeTab) { - case 'General': - return ; - case 'Documents': - return ( - - ); - case 'Widgets': - return ( - - ); - case 'API Keys': - return ; - default: - return null; - } - } -}; - -const General: React.FC = () => { - 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( - isDarkTheme ? 'Dark' : 'Light', - ); - 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 ( -
-
-

Select Theme

- { - setSelectedTheme(option); - option !== selectedTheme && toggleTheme(); - }} - size="w-56" - rounded="3xl" - /> -
-
-

- Select Language -

- -
-
-

- Chunks processed per query -

- dispatch(setChunks(value))} - size="w-56" - rounded="3xl" - /> -
-
- - dispatch(setPrompt({ name: name, id: id, type: type })) - } - setPrompts={setPrompts} - apiHost={apiHost} - /> -
-
- ); -}; - -export default Setting; - -const Prompts: React.FC = ({ - prompts, - selectedPrompt, - onSelectPrompt, - setPrompts, -}) => { - const handleSelectPrompt = ({ - name, - id, - type, - }: { - name: string; - id: string; - type: string; - }) => { - setEditPromptName(name); - onSelectPrompt(name, id, type); - }; - 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 { - const response = await fetch(`${apiHost}/api/create_prompt`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - name: newPromptName, - content: newPromptContent, - }), - }); - if (!response.ok) { - throw new Error('Failed to add prompt'); - } - const newPrompt = await response.json(); - if (setPrompts) { - setPrompts([ - ...prompts, - { name: newPromptName, id: newPrompt.id, type: 'private' }, - ]); - } - setModalState('INACTIVE'); - onSelectPrompt(newPromptName, newPrompt.id, newPromptContent); - setNewPromptName(newPromptName); - } catch (error) { - console.error(error); - } - }; - - 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: id }), - }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to delete prompt'); - } - // get 1st prompt and set it as selected - if (prompts.length > 0) { - onSelectPrompt(prompts[0].name, prompts[0].id, prompts[0].type); - } - }) - .catch((error) => { - console.error(error); - }); - }; - - 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(); - setEditPromptContent(promptContent.content); - } catch (error) { - console.error(error); - } - }; - - const handleSaveChanges = (id: string, type: string) => { - fetch(`${apiHost}/api/update_prompt`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - id: id, - name: editPromptName, - content: editPromptContent, - }), - }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to update prompt'); - } - 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); - }); - }; - - return ( - <> -
-
-
-

Active Prompt

- { - setModalType('EDIT'); - setEditPromptName(name); - fetchPromptContent(id); - setCurrentPromptEdit({ id: id, name: name, type: type }); - setModalState('ACTIVE'); - }} - onDelete={handleDeletePrompt} - /> -
- -
-
- - - ); -}; - -type AddPromptModalProps = { - newPromptName: string; - onNewPromptNameChange: (name: string) => void; - onAddPrompt: () => void; - onClose: () => void; -}; - -const AddPromptModal: React.FC = ({ - newPromptName, - onNewPromptNameChange, - onAddPrompt, - onClose, -}) => { - return ( -
-
-

Add New Prompt

- onNewPromptNameChange(e.target.value)} - className="mb-4 w-full rounded-3xl border-2 p-2 dark:border-chinese-silver" - /> - - -
-
- ); -}; -type DocumentsProps = { - documents: Doc[] | null; - handleDeleteDocument: (index: number, document: Doc) => void; -}; - -const Documents: React.FC = ({ - documents, - handleDeleteDocument, -}) => { - return ( -
-
-
- - - - - - - - - - - {documents && - documents.map((document, index) => ( - - - - - - - ))} - -
Document NameVector DateType
- {document.name} - - {document.date} - - {document.location === 'remote' - ? 'Pre-loaded' - : 'Private'} - - {document.location !== 'remote' && ( - Delete { - event.stopPropagation(); - handleDeleteDocument(index, document); - }} - /> - )} -
-
- {/* */} -
- - {/* {isAddDocumentModalOpen && ( - - )} */} -
- ); -}; - -type Document = { - name: string; - vectorDate: string; - vectorLocation: string; -}; - -// Modal for adding a new document -type AddDocumentModalProps = { - newDocument: Document; - onNewDocumentChange: (document: Document) => void; - onAddDocument: () => void; - onClose: () => void; -}; - -const AddDocumentModal: React.FC = ({ - newDocument, - onNewDocumentChange, - onAddDocument, - onClose, -}) => { - return ( -
-
-

Add New Document

- - onNewDocumentChange({ ...newDocument, name: e.target.value }) - } - className="mb-4 w-full rounded-lg border-2 p-2" - /> - - onNewDocumentChange({ ...newDocument, vectorDate: e.target.value }) - } - className="mb-4 w-full rounded-lg border-2 p-2" - /> - - onNewDocumentChange({ - ...newDocument, - vectorLocation: e.target.value, - }) - } - className="mb-4 w-full rounded-lg border-2 p-2" - /> - - -
-
- ); -}; -const APIKeys: React.FC = () => { - const [isCreateModalOpen, setCreateModal] = useState(false); - const [isSaveKeyModalOpen, setSaveKeyModal] = useState(false); - const [newKey, setNewKey] = useState(''); - const [apiKeys, setApiKeys] = useState< - { name: string; key: string; source: string; id: string }[] - >([]); - const handleDeleteKey = (id: string) => { - fetch(`${apiHost}/api/delete_api_key`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ id }), - }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to delete API Key'); - } - return response.json(); - }) - .then((data) => { - data.status === 'ok' && - setApiKeys((previous) => previous.filter((elem) => elem.id !== id)); - }) - .catch((error) => { - console.error(error); - }); - }; - useEffect(() => { - fetchAPIKeys(); - }, []); - const fetchAPIKeys = async () => { - try { - const response = await fetch(`${apiHost}/api/get_api_keys`); - if (!response.ok) { - throw new Error('Failed to fetch API Keys'); - } - const apiKeys = await response.json(); - setApiKeys(apiKeys); - } catch (error) { - console.log(error); - } - }; - const createAPIKey = (payload: { name: string; source: string }) => { - fetch(`${apiHost}/api/create_api_key`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(payload), - }) - .then((response) => { - if (!response.ok) { - throw new Error('Failed to create API Key'); - } - return response.json(); - }) - .then((data) => { - setApiKeys([...apiKeys, data]); - setCreateModal(false); //close the create key modal - setNewKey(data.key); - setSaveKeyModal(true); // render the newly created key - fetchAPIKeys(); - }) - .catch((error) => { - console.error(error); - }); - }; - return ( -
-
-
- -
- {isCreateModalOpen && ( - setCreateModal(false)} - createAPIKey={createAPIKey} - /> - )} - {isSaveKeyModalOpen && ( - setSaveKeyModal(false)} - /> - )} -
-
- - - - - - - - - - - {apiKeys?.map((element, index) => ( - - - - - - - ))} - -
Name - Source document - API Key
{element.name}{element.source}{element.key} - Delete handleDeleteKey(element.id)} - /> -
-
-
-
-
- ); -}; -type SaveAPIKeyModalProps = { - apiKey: string; - close: () => void; -}; -const SaveAPIKeyModal: React.FC = ({ apiKey, close }) => { - const [isCopied, setIsCopied] = useState(false); - const handleCopyKey = () => { - navigator.clipboard.writeText(apiKey); - setIsCopied(true); - }; - return ( -
-
- -

Please save your Key

-

- This is the only time your key will be shown. -

-
-
-

API Key

- {apiKey} -
- -
- -
-
- ); -}; - -type CreateAPIKeyModalProps = { - close: () => void; - createAPIKey: (payload: { name: string; source: string }) => void; -}; -const CreateAPIKeyModal: React.FC = ({ - close, - createAPIKey, -}) => { - const [APIKeyName, setAPIKeyName] = useState(''); - const [sourcePath, setSourcePath] = useState<{ - label: string; - value: string; - } | null>(null); - const docs = useSelector(selectSourceDocs); - const extractDocPaths = () => - docs - ? docs - .filter((doc) => doc.model === embeddingsName) - .map((doc: Doc) => { - let namePath = doc.name; - if (doc.language === namePath) { - namePath = '.project'; - } - let docPath = 'default'; - if (doc.location === 'local') { - docPath = 'local' + '/' + doc.name + '/'; - } else if (doc.location === 'remote') { - docPath = - doc.language + - '/' + - namePath + - '/' + - doc.version + - '/' + - doc.model + - '/'; - } - return { - label: doc.name, - value: docPath, - }; - }) - : []; - - return ( -
-
- - - Create New API Key - -
- - API Key Name - - setAPIKeyName(e.target.value)} - /> -
-
- - setSourcePath(selection) - } - options={extractDocPaths()} - /> -
- -
-
- ); -}; -const Widgets: React.FC<{ - widgetScreenshot: File | null; - onWidgetScreenshotChange: (screenshot: File | null) => void; -}> = ({ widgetScreenshot, onWidgetScreenshotChange }) => { - const widgetSources = ['Source 1', 'Source 2', 'Source 3']; - const widgetMethods = ['Method 1', 'Method 2', 'Method 3']; - const widgetTypes = ['Type 1', 'Type 2', 'Type 3']; - - const [selectedWidgetSource, setSelectedWidgetSource] = useState( - widgetSources[0], - ); - const [selectedWidgetMethod, setSelectedWidgetMethod] = useState( - widgetMethods[0], - ); - const [selectedWidgetType, setSelectedWidgetType] = useState(widgetTypes[0]); - - // const [widgetScreenshot, setWidgetScreenshot] = useState(null); - const [widgetCode, setWidgetCode] = useState(''); // Your widget code state - - const handleScreenshotChange = ( - event: React.ChangeEvent, - ) => { - const files = event.target.files; - - if (files && files.length > 0) { - const selectedScreenshot = files[0]; - onWidgetScreenshotChange(selectedScreenshot); // Update the screenshot in the parent component - } - }; - - const handleCopyToClipboard = () => { - // Create a new textarea element to select the text - const textArea = document.createElement('textarea'); - textArea.value = widgetCode; - document.body.appendChild(textArea); - - // Select and copy the text - textArea.select(); - document.execCommand('copy'); - - // Clean up the textarea element - document.body.removeChild(textArea); - }; - - return ( -
-
-

Widget Source

- -
-
-

Widget Method

- -
-
-

Widget Type

- -
-
-

Widget Code Snippet

-