mirror of
https://github.com/arc53/DocsGPT.git
synced 2025-12-01 17:43:15 +00:00
Frontend audit: Bug fixes and refinements (#2112)
* (fix:attachements) sep id for redux ops * (fix:ui) popups, toast, share modal * (feat:agentsPreview) stable preview, ui fixes * (fix:ui) light theme icon, sleek scroll * (chore:i18n) missin keys * (chore:i18n) missing keys * (feat:preferrenceSlice) autoclear invalid source from storage * (fix:general) delete all conv close btn * (fix:tts) play one at a time * (fix:tts) gracefully unmount * (feat:tts) audio LRU cache * (feat:tts) pointer on hovered area * (feat:tts) clean text for speach --------- Co-authored-by: GH Action - Upstream Sync <action@github.com>
This commit is contained in:
@@ -44,7 +44,7 @@ export default function AddActionModal({
|
||||
>
|
||||
<div>
|
||||
<h2 className="text-jet dark:text-bright-gray px-3 text-xl font-semibold">
|
||||
New Action
|
||||
{t('modals.addAction.title')}
|
||||
</h2>
|
||||
<div className="relative mt-6 px-3">
|
||||
<Input
|
||||
@@ -57,7 +57,7 @@ export default function AddActionModal({
|
||||
}}
|
||||
borderVariant="thin"
|
||||
labelBgClassName="bg-white dark:bg-charleston-green-2"
|
||||
placeholder="Action Name"
|
||||
placeholder={t('modals.addAction.actionNamePlaceholder')}
|
||||
required={true}
|
||||
/>
|
||||
<p
|
||||
@@ -66,8 +66,8 @@ export default function AddActionModal({
|
||||
}`}
|
||||
>
|
||||
{functionNameError
|
||||
? 'Invalid function name format. Use only letters, numbers, underscores, and hyphens.'
|
||||
: 'Use only letters, numbers, underscores, and hyphens (e.g., `get_data`, `send_report`, etc.)'}
|
||||
? t('modals.addAction.invalidFormat')
|
||||
: t('modals.addAction.formatHelp')}
|
||||
</p>
|
||||
</div>
|
||||
<div className="mt-3 flex flex-row-reverse gap-1 px-3">
|
||||
@@ -75,7 +75,7 @@ export default function AddActionModal({
|
||||
onClick={handleAddAction}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue rounded-3xl px-5 py-2 text-sm text-white transition-all"
|
||||
>
|
||||
Add
|
||||
{t('modals.addAction.addButton')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import { Agent } from '../agents/types';
|
||||
@@ -24,6 +25,7 @@ export default function AgentDetailsModal({
|
||||
modalState,
|
||||
setModalState,
|
||||
}: AgentDetailsModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const token = useSelector(selectToken);
|
||||
|
||||
const [sharedToken, setSharedToken] = useState<string | null>(
|
||||
@@ -86,13 +88,13 @@ export default function AgentDetailsModal({
|
||||
>
|
||||
<div>
|
||||
<h2 className="text-jet dark:text-bright-gray text-xl font-semibold">
|
||||
Access Details
|
||||
{t('modals.agentDetails.title')}
|
||||
</h2>
|
||||
<div className="mt-8 flex flex-col gap-6">
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-jet dark:text-bright-gray text-base font-semibold">
|
||||
Public Link
|
||||
{t('modals.agentDetails.publicLink')}
|
||||
</h2>
|
||||
</div>
|
||||
{sharedToken ? (
|
||||
@@ -117,7 +119,9 @@ export default function AgentDetailsModal({
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span className="text-sm">Learn more</span>
|
||||
<span className="text-sm">
|
||||
{t('modals.agentDetails.learnMore')}
|
||||
</span>
|
||||
<img
|
||||
src="/src/assets/external-link.svg"
|
||||
alt="External link"
|
||||
@@ -133,14 +137,14 @@ export default function AgentDetailsModal({
|
||||
{loadingStates.publicLink ? (
|
||||
<Spinner size="small" color="#976af3" />
|
||||
) : (
|
||||
'Generate'
|
||||
t('modals.agentDetails.generate')
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<h2 className="text-jet dark:text-bright-gray text-base font-semibold">
|
||||
API Key
|
||||
{t('modals.agentDetails.apiKey')}
|
||||
</h2>
|
||||
{apiKey ? (
|
||||
<div className="flex flex-col gap-2">
|
||||
@@ -162,7 +166,7 @@ export default function AgentDetailsModal({
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Test
|
||||
{t('modals.agentDetails.test')}
|
||||
<img
|
||||
src="/src/assets/external-link.svg"
|
||||
alt="External link"
|
||||
@@ -174,14 +178,14 @@ export default function AgentDetailsModal({
|
||||
</div>
|
||||
) : (
|
||||
<button className="border-purple-30 text-purple-30 hover:bg-purple-30 w-28 rounded-3xl border border-solid px-5 py-2 text-sm font-medium transition-colors hover:text-white">
|
||||
Generate
|
||||
{t('modals.agentDetails.generate')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex flex-col gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2 className="text-jet dark:text-bright-gray text-base font-semibold">
|
||||
Webhook URL
|
||||
{t('modals.agentDetails.webhookUrl')}
|
||||
</h2>
|
||||
</div>
|
||||
{webhookUrl ? (
|
||||
@@ -202,7 +206,9 @@ export default function AgentDetailsModal({
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<span className="text-sm">Learn more</span>
|
||||
<span className="text-sm">
|
||||
{t('modals.agentDetails.learnMore')}
|
||||
</span>
|
||||
<img
|
||||
src="/src/assets/external-link.svg"
|
||||
alt="External link"
|
||||
@@ -218,7 +224,7 @@ export default function AgentDetailsModal({
|
||||
{loadingStates.webhook ? (
|
||||
<Spinner size="small" color="#976af3" />
|
||||
) : (
|
||||
'Generate'
|
||||
t('modals.agentDetails.generate')
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
|
||||
@@ -66,7 +66,7 @@ export default function ConfigToolModal({
|
||||
value={customName}
|
||||
onChange={(e) => setCustomName(e.target.value)}
|
||||
borderVariant="thin"
|
||||
placeholder="Enter custom name (optional)"
|
||||
placeholder={t('modals.configTool.customNamePlaceholder')}
|
||||
labelBgClassName="bg-white dark:bg-charleston-green-2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useSelector } from 'react-redux';
|
||||
|
||||
import userService from '../api/services/userService';
|
||||
import Dropdown from '../components/Dropdown';
|
||||
import Input from '../components/Input';
|
||||
import { CreateAPIKeyModalProps, Doc } from '../models/misc';
|
||||
import { selectSourceDocs, selectToken } from '../preferences/preferenceSlice';
|
||||
import WrapperModal from './WrapperModal';
|
||||
|
||||
const embeddingsName =
|
||||
import.meta.env.VITE_EMBEDDINGS_NAME ||
|
||||
'huggingface_sentence-transformers/all-mpnet-base-v2';
|
||||
|
||||
export default function CreateAPIKeyModal({
|
||||
close,
|
||||
createAPIKey,
|
||||
}: CreateAPIKeyModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const token = useSelector(selectToken);
|
||||
const docs = useSelector(selectSourceDocs);
|
||||
|
||||
const [APIKeyName, setAPIKeyName] = React.useState<string>('');
|
||||
const [sourcePath, setSourcePath] = React.useState<{
|
||||
name: string;
|
||||
id: string;
|
||||
type: string;
|
||||
} | null>(null);
|
||||
const [prompt, setPrompt] = React.useState<{
|
||||
name: string;
|
||||
id: string;
|
||||
type: string;
|
||||
} | null>(null);
|
||||
const [activePrompts, setActivePrompts] = React.useState<
|
||||
{ name: string; id: string; type: string }[]
|
||||
>([]);
|
||||
const [chunk, setChunk] = React.useState<string>('2');
|
||||
const chunkOptions = ['0', '2', '4', '6', '8', '10'];
|
||||
|
||||
const extractDocPaths = () =>
|
||||
docs
|
||||
? docs
|
||||
.filter((doc) => doc.model === embeddingsName)
|
||||
.map((doc: Doc) => {
|
||||
if ('id' in doc) {
|
||||
return {
|
||||
name: doc.name,
|
||||
id: doc.id as string,
|
||||
type: 'local',
|
||||
};
|
||||
}
|
||||
return {
|
||||
name: doc.name,
|
||||
id: doc.id ?? 'default',
|
||||
type: doc.type ?? 'default',
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
React.useEffect(() => {
|
||||
const handleFetchPrompts = async () => {
|
||||
try {
|
||||
const response = await userService.getPrompts(token);
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch prompts');
|
||||
}
|
||||
const promptsData = await response.json();
|
||||
setActivePrompts(promptsData);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
handleFetchPrompts();
|
||||
}, []);
|
||||
return (
|
||||
<WrapperModal close={close} className="p-4">
|
||||
<div className="mb-6">
|
||||
<span className="text-jet dark:text-bright-gray text-xl">
|
||||
{t('modals.createAPIKey.label')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="relative mt-5 mb-4">
|
||||
<Input
|
||||
type="text"
|
||||
className="rounded-md"
|
||||
value={APIKeyName}
|
||||
placeholder={t('modals.createAPIKey.apiKeyName')}
|
||||
onChange={(e) => setAPIKeyName(e.target.value)}
|
||||
borderVariant="thin"
|
||||
labelBgClassName="bg-white dark:bg-charleston-green-2"
|
||||
></Input>
|
||||
</div>
|
||||
<div className="my-4">
|
||||
<Dropdown
|
||||
placeholder={t('modals.createAPIKey.sourceDoc')}
|
||||
selectedValue={sourcePath ? sourcePath.name : null}
|
||||
onSelect={(selection: { name: string; id: string; type: string }) => {
|
||||
setSourcePath(selection);
|
||||
}}
|
||||
options={extractDocPaths()}
|
||||
size="w-full"
|
||||
rounded="xl"
|
||||
border="border"
|
||||
/>
|
||||
</div>
|
||||
<div className="my-4">
|
||||
<Dropdown
|
||||
options={activePrompts}
|
||||
selectedValue={prompt ? prompt.name : null}
|
||||
placeholder={t('modals.createAPIKey.prompt')}
|
||||
onSelect={(value: { name: string; id: string; type: string }) =>
|
||||
setPrompt(value)
|
||||
}
|
||||
size="w-full"
|
||||
border="border"
|
||||
/>
|
||||
</div>
|
||||
<div className="my-4">
|
||||
<p className="text-jet dark:text-bright-gray mb-2 ml-2 font-semibold">
|
||||
{t('modals.createAPIKey.chunks')}
|
||||
</p>
|
||||
<Dropdown
|
||||
options={chunkOptions}
|
||||
selectedValue={chunk}
|
||||
onSelect={(value: string) => setChunk(value)}
|
||||
size="w-full"
|
||||
border="border"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
disabled={!sourcePath || APIKeyName.length === 0 || !prompt}
|
||||
onClick={() => {
|
||||
if (sourcePath && prompt) {
|
||||
const payload: any = {
|
||||
name: APIKeyName,
|
||||
prompt_id: prompt.id,
|
||||
chunks: chunk,
|
||||
};
|
||||
if (sourcePath.type === 'default') {
|
||||
payload.retriever = sourcePath.id;
|
||||
}
|
||||
if (sourcePath.type === 'local') {
|
||||
payload.source = sourcePath.id;
|
||||
}
|
||||
createAPIKey(payload);
|
||||
}
|
||||
}}
|
||||
className="bg-purple-30 hover:bg-violets-are-blue float-right mt-4 rounded-full px-5 py-2 text-sm text-white disabled:opacity-50"
|
||||
>
|
||||
{t('modals.createAPIKey.create')}
|
||||
</button>
|
||||
</WrapperModal>
|
||||
);
|
||||
}
|
||||
@@ -38,7 +38,7 @@ export default function DeleteConvModal({
|
||||
<ConfirmationModal
|
||||
message={t('modals.deleteConv.confirm')}
|
||||
modalState={modalState}
|
||||
setModalState={setModalState}
|
||||
setModalState={(state) => dispatch(setModalState(state))}
|
||||
submitLabel={t('modals.deleteConv.delete')}
|
||||
handleSubmit={handleSubmit}
|
||||
handleCancel={handleCancel}
|
||||
|
||||
@@ -18,14 +18,6 @@ interface MCPServerModalProps {
|
||||
onServerSaved: () => void;
|
||||
}
|
||||
|
||||
const authTypes = [
|
||||
{ label: 'No Authentication', value: 'none' },
|
||||
{ label: 'API Key', value: 'api_key' },
|
||||
{ label: 'Bearer Token', value: 'bearer' },
|
||||
{ label: 'OAuth', value: 'oauth' },
|
||||
// { label: 'Basic Authentication', value: 'basic' },
|
||||
];
|
||||
|
||||
export default function MCPServerModal({
|
||||
modalState,
|
||||
setModalState,
|
||||
@@ -36,8 +28,16 @@ export default function MCPServerModal({
|
||||
const token = useSelector(selectToken);
|
||||
const modalRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const authTypes = [
|
||||
{ label: t('settings.tools.mcp.authTypes.none'), value: 'none' },
|
||||
{ label: t('settings.tools.mcp.authTypes.apiKey'), value: 'api_key' },
|
||||
{ label: t('settings.tools.mcp.authTypes.bearer'), value: 'bearer' },
|
||||
{ label: t('settings.tools.mcp.authTypes.oauth'), value: 'oauth' },
|
||||
// { label: t('settings.tools.mcp.authTypes.basic'), value: 'basic' },
|
||||
];
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
name: server?.displayName || 'My MCP Server',
|
||||
name: server?.displayName || t('settings.tools.mcp.defaultServerName'),
|
||||
server_url: server?.server_url || '',
|
||||
auth_type: server?.auth_type || 'none',
|
||||
api_key: '',
|
||||
@@ -72,7 +72,7 @@ export default function MCPServerModal({
|
||||
|
||||
const resetForm = () => {
|
||||
setFormData({
|
||||
name: 'My MCP Server',
|
||||
name: t('settings.tools.mcp.defaultServerName'),
|
||||
server_url: '',
|
||||
auth_type: 'none',
|
||||
api_key: '',
|
||||
@@ -133,7 +133,7 @@ export default function MCPServerModal({
|
||||
typeof timeoutValue === 'number' &&
|
||||
(timeoutValue < 1 || timeoutValue > 300)
|
||||
)
|
||||
newErrors.timeout = 'Timeout must be between 1 and 300 seconds';
|
||||
newErrors.timeout = t('settings.tools.mcp.errors.timeoutRange');
|
||||
|
||||
if (authFieldChecks[formData.auth_type])
|
||||
authFieldChecks[formData.auth_type]();
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
import React from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { SaveAPIKeyModalProps } from '../models/misc';
|
||||
import WrapperModal from './WrapperModal';
|
||||
|
||||
export default function SaveAPIKeyModal({
|
||||
apiKey,
|
||||
close,
|
||||
}: SaveAPIKeyModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const [isCopied, setIsCopied] = React.useState(false);
|
||||
|
||||
const handleCopyKey = () => {
|
||||
navigator.clipboard.writeText(apiKey);
|
||||
setIsCopied(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<WrapperModal close={close}>
|
||||
<h1 className="text-jet dark:text-bright-gray my-0 text-xl font-medium">
|
||||
{t('modals.saveKey.note')}
|
||||
</h1>
|
||||
<h3 className="text-outer-space dark:text-silver text-sm font-normal">
|
||||
{t('modals.saveKey.disclaimer')}
|
||||
</h3>
|
||||
<div className="flex justify-between py-2">
|
||||
<div>
|
||||
<h2 className="text-jet dark:text-bright-gray text-base font-semibold">
|
||||
API Key
|
||||
</h2>
|
||||
<span className="text-jet dark:text-bright-gray text-sm leading-7 font-normal">
|
||||
{apiKey}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
className="border-violets-are-blue text-violets-are-blue hover:bg-violets-are-blue my-1 h-10 w-20 rounded-full border border-solid p-2 text-sm hover:text-white"
|
||||
onClick={handleCopyKey}
|
||||
>
|
||||
{isCopied ? t('modals.saveKey.copied') : t('modals.saveKey.copy')}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={close}
|
||||
className="bg-philippine-yellow rounded-full px-4 py-3 font-medium text-black hover:bg-[#E6B91A]"
|
||||
>
|
||||
{t('modals.saveKey.confirm')}
|
||||
</button>
|
||||
</WrapperModal>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user