diff --git a/frontend/src/Hero.tsx b/frontend/src/Hero.tsx index 9fe965a1..a000ab2e 100644 --- a/frontend/src/Hero.tsx +++ b/frontend/src/Hero.tsx @@ -37,7 +37,7 @@ export default function Hero({ -
-

- New Action -

-
- - Action Name - - setActionName(e.target.value)} - borderVariant="thin" - placeholder={'Enter name'} - /> -

- Use only letters, numbers, underscores, and hyphens (e.g., - `get_user_data`, `send-report`). -

- {functionNameError && ( -

- Invalid function name format. Use only letters, numbers, - underscores, and hyphens. -

- )} -
-
- - -
-
+
+

+ New Action +

+
+ setActionName(e.target.value)} + borderVariant="thin" + placeholder="Enter name" + label="Action Name" + /> +

+ Use only letters, numbers, underscores, and hyphens (e.g., + `get_user_data`, `send-report`). +

+ {functionNameError && ( +

+ Invalid function name format. Use only letters, numbers, + underscores, and hyphens. +

+ )}
- -
+
+ + +
+ + ); } diff --git a/frontend/src/modals/AddToolModal.tsx b/frontend/src/modals/AddToolModal.tsx index ba555df4..c11a5043 100644 --- a/frontend/src/modals/AddToolModal.tsx +++ b/frontend/src/modals/AddToolModal.tsx @@ -2,11 +2,11 @@ import React, { useRef } from 'react'; import { useTranslation } from 'react-i18next'; import userService from '../api/services/userService'; -import Exit from '../assets/exit.svg'; import { useOutsideAlerter } from '../hooks'; import { ActiveState } from '../models/misc'; import ConfigToolModal from './ConfigToolModal'; import { AvailableToolType } from './types'; +import WrapperComponent from './WrapperModal'; export default function AddToolModal({ message, @@ -88,28 +88,12 @@ export default function AddToolModal({ return ( <> -
-
setModalState('INACTIVE')} + className="h-[85vh] w-[90vw] md:w-[75vw]" > -
- +

{t('settings.tools.selectToolSetup')} @@ -153,8 +137,8 @@ export default function AddToolModal({

-
-
+ + )} void; + tool: AvailableToolType | null; + getUserTools: () => void; +} + export default function ConfigToolModal({ modalState, setModalState, tool, getUserTools, -}: { - modalState: ActiveState; - setModalState: (state: ActiveState) => void; - tool: AvailableToolType | null; - getUserTools: () => void; -}) { +}: ConfigToolModalProps) { const { t } = useTranslation(); const [authKey, setAuthKey] = React.useState(''); @@ -36,63 +38,47 @@ export default function ConfigToolModal({ getUserTools(); }); }; + + // Only render when modal is active + if (modalState !== 'ACTIVE') return null; + return ( -
-
-
- -
-

- {t('modals.configTool.title')} -

-

- {t('modals.configTool.type')}:{' '} - {tool?.name} -

-
- - {t('modals.configTool.apiKeyLabel')} - - setAuthKey(e.target.value)} - borderVariant="thin" - placeholder={t('modals.configTool.apiKeyPlaceholder')} - > -
-
- - -
-
+ setModalState('INACTIVE')}> +
+

+ {t('modals.configTool.title')} +

+

+ {t('modals.configTool.type')}:{' '} + {tool?.name} +

+
+ setAuthKey(e.target.value)} + borderVariant="thin" + placeholder={t('modals.configTool.apiKeyPlaceholder')} + label={t('modals.configTool.apiKeyLabel')} + />
-
-
+
+ + +
+ + ); } diff --git a/frontend/src/modals/CreateAPIKeyModal.tsx b/frontend/src/modals/CreateAPIKeyModal.tsx index 5c8c75b8..128bdff6 100644 --- a/frontend/src/modals/CreateAPIKeyModal.tsx +++ b/frontend/src/modals/CreateAPIKeyModal.tsx @@ -73,21 +73,20 @@ export default function CreateAPIKeyModal({ handleFetchPrompts(); }, []); return ( - +
{t('modals.createAPIKey.label')}
- - {t('modals.createAPIKey.apiKeyName')} - setAPIKeyName(e.target.value)} + borderVariant="thin" >
diff --git a/frontend/src/modals/SaveAPIKeyModal.tsx b/frontend/src/modals/SaveAPIKeyModal.tsx index d91d0c2d..abe862cb 100644 --- a/frontend/src/modals/SaveAPIKeyModal.tsx +++ b/frontend/src/modals/SaveAPIKeyModal.tsx @@ -1,8 +1,8 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; -import Exit from '../assets/exit.svg'; import { SaveAPIKeyModalProps } from '../models/misc'; +import WrapperModal from './WrapperModal'; export default function SaveAPIKeyModal({ apiKey, @@ -15,38 +15,37 @@ export default function SaveAPIKeyModal({ navigator.clipboard.writeText(apiKey); setIsCopied(true); }; + return ( -
-
- -

- {' '} - {t('modals.saveKey.note')} -

-

- {t('modals.saveKey.disclaimer')} -

-
-
-

API Key

- {apiKey} -
- + +

+ {t('modals.saveKey.note')} +

+

+ {t('modals.saveKey.disclaimer')} +

+
+
+

+ API Key +

+ + {apiKey} +
-
+ + ); } diff --git a/frontend/src/modals/WrapperModal.tsx b/frontend/src/modals/WrapperModal.tsx index dcf1bebb..6d5d8543 100644 --- a/frontend/src/modals/WrapperModal.tsx +++ b/frontend/src/modals/WrapperModal.tsx @@ -1,12 +1,21 @@ import React, { useEffect, useRef } from 'react'; import Exit from '../assets/exit.svg'; -import { WrapperModalPropsType } from './types'; + +interface WrapperModalPropsType { + children: React.ReactNode; + close: () => void; + isPerformingTask?: boolean; + className?: string; + contentClassName?: string; +} export default function WrapperModal({ children, close, - isPerformingTask, + isPerformingTask = false, + className = '', // Default width, but can be overridden + contentClassName = '', // Default padding, but can be overridden }: WrapperModalPropsType) { const modalRef = useRef(null); @@ -40,17 +49,17 @@ export default function WrapperModal({
{!isPerformingTask && ( )} - {children} +
{children}
); diff --git a/frontend/src/modals/index.tsx b/frontend/src/modals/index.tsx deleted file mode 100644 index 1dcffd6d..00000000 --- a/frontend/src/modals/index.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import * as React from 'react'; -import { useTranslation } from 'react-i18next'; -interface ModalProps { - handleSubmit: () => void; - isCancellable: boolean; - handleCancel?: () => void; - render: () => JSX.Element; - modalState: string; - isError: boolean; - errorMessage?: string; - textDelete?: boolean; -} - -const Modal = (props: ModalProps) => { - const { t } = useTranslation(); - return ( -
- {props.render()} -
-
- - {props.isCancellable && ( - - )} -
- {props.isError && ( -

- {props.errorMessage} -

- )} -
-
- ); -}; - -export default Modal; diff --git a/frontend/src/preferences/APIKeyModal.tsx b/frontend/src/preferences/APIKeyModal.tsx deleted file mode 100644 index 43698fe1..00000000 --- a/frontend/src/preferences/APIKeyModal.tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { useRef, useState } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { ActiveState } from '../models/misc'; -import { selectApiKey, setApiKey } from './preferenceSlice'; -import { useMediaQuery, useOutsideAlerter } from './../hooks'; -import Modal from '../modals'; -import Input from '../components/Input'; - -export default function APIKeyModal({ - modalState, - setModalState, - isCancellable = true, -}: { - modalState: ActiveState; - setModalState: (val: ActiveState) => void; - isCancellable?: boolean; -}) { - const dispatch = useDispatch(); - const apiKey = useSelector(selectApiKey); - const [key, setKey] = useState(apiKey); - const [isError, setIsError] = useState(false); - const modalRef = useRef(null); - const { isMobile } = useMediaQuery(); - - useOutsideAlerter(modalRef, () => { - if (isMobile && modalState === 'ACTIVE') { - setModalState('INACTIVE'); - } - }, [modalState]); - - function handleSubmit() { - if (key.length <= 1) { - setIsError(true); - } else { - dispatch(setApiKey(key)); - setModalState('INACTIVE'); - setIsError(false); - } - } - - function handleCancel() { - setKey(apiKey); - setIsError(false); - setModalState('INACTIVE'); - } - - return ( - { - return ( -
-

OpenAI API Key

-

- Before you can start using DocsGPT we need you to provide an API - key for llm. Currently, we support only OpenAI but soon many more. - You can find it here. -

- setKey(e.target.value)} - > -
- ); - }} - /> - ); -} diff --git a/frontend/src/preferences/PromptsModal.tsx b/frontend/src/preferences/PromptsModal.tsx index 11cb0685..f2512210 100644 --- a/frontend/src/preferences/PromptsModal.tsx +++ b/frontend/src/preferences/PromptsModal.tsx @@ -1,8 +1,8 @@ import { ActiveState } from '../models/misc'; -import Exit from '../assets/exit.svg'; import Input from '../components/Input'; import React from 'react'; import { useTranslation } from 'react-i18next'; +import WrapperModal from '../modals/WrapperModal'; function AddPrompt({ setModalState, @@ -24,69 +24,52 @@ function AddPrompt({ const { t } = useTranslation(); return ( -
- -
-

- {t('modals.prompts.addPrompt')} -

-

- {t('modals.prompts.addDescription')} -

-
- - setNewPromptName(e.target.value)} - /> -
- - {t('modals.prompts.promptName')} - -
-
- - {t('modals.prompts.promptText')} - -
- - -
-
- +
+

+ {t('modals.prompts.addPrompt')} +

+

+ {t('modals.prompts.addDescription')} +

+
+ + setNewPromptName(e.target.value)} + /> +
+ + {t('modals.prompts.promptText')} +
+ + +
+
+
); @@ -114,16 +97,7 @@ function EditPrompt({ const { t } = useTranslation(); return ( -
- +

{t('modals.prompts.editPrompt')} @@ -271,15 +245,19 @@ export default function PromptsModal({ } else { view = <>; } - return ( -

{ + setModalState('INACTIVE'); + if (type === 'ADD') { + setNewPromptName(''); + setNewPromptContent(''); + } + }} + className="sm:w-[512px] mt-24" > -
- {view} -
-
- ); + {view} + + ) : null; } diff --git a/frontend/src/settings/Analytics.tsx b/frontend/src/settings/Analytics.tsx index 4d390495..53538833 100644 --- a/frontend/src/settings/Analytics.tsx +++ b/frontend/src/settings/Analytics.tsx @@ -92,8 +92,10 @@ export default function Analytics() { const [loadingMessages, setLoadingMessages] = useLoaderState(true); const [loadingTokens, setLoadingTokens] = useLoaderState(true); const [loadingFeedback, setLoadingFeedback] = useLoaderState(true); + const [loadingChatbots, setLoadingChatbots] = useLoaderState(true); const fetchChatbots = async () => { + setLoadingChatbots(true); try { const response = await userService.getAPIKeys(); if (!response.ok) { @@ -103,6 +105,8 @@ export default function Analytics() { setChatbots(chatbots); } catch (error) { console.error(error); + } finally { + setLoadingChatbots(false); } }; @@ -188,37 +192,41 @@ export default function Analytics() { return (
-
-

- {t('settings.analytics.filterByChatbot')} -

- ({ - label: chatbot.name, - value: chatbot.id, - })), - { label: t('settings.analytics.none'), value: '' }, - ]} - placeholder={t('settings.analytics.selectChatbot')} - onSelect={(chatbot: { label: string; value: string }) => { - setSelectedChatbot( - chatbots.find((item) => item.id === chatbot.value), - ); - }} - selectedValue={ - (selectedChatbot && { - label: selectedChatbot.name, - value: selectedChatbot.id, - }) || - null - } - rounded="3xl" - border="border" - borderColor="gray-700" - /> -
+ {loadingChatbots ? ( + + ) : ( +
+

+ {t('settings.analytics.filterByChatbot')} +

+ ({ + label: chatbot.name, + value: chatbot.id, + })), + { label: t('settings.analytics.none'), value: '' }, + ]} + placeholder={t('settings.analytics.selectChatbot')} + onSelect={(chatbot: { label: string; value: string }) => { + setSelectedChatbot( + chatbots.find((item) => item.id === chatbot.value), + ); + }} + selectedValue={ + (selectedChatbot && { + label: selectedChatbot.name, + value: selectedChatbot.id, + }) || + null + } + rounded="3xl" + border="border" + borderColor="gray-700" + /> +
+ )} {/* Messages Analytics */}
diff --git a/frontend/src/settings/Prompts.tsx b/frontend/src/settings/Prompts.tsx index 611b0b90..ed1d82d7 100644 --- a/frontend/src/settings/Prompts.tsx +++ b/frontend/src/settings/Prompts.tsx @@ -168,7 +168,7 @@ export default function Prompts({ />
@@ -706,7 +704,7 @@ function Upload({ } }} disabled={isUploadDisabled()} - className={`rounded-3xl px-4 py-2 font-medium ${ + className={`rounded-3xl px-4 py-2 font-medium text-[14px] ${ isUploadDisabled() ? 'cursor-not-allowed bg-gray-300 text-gray-500' : 'cursor-pointer bg-purple-30 text-white hover:bg-purple-40'