Merge pull request #1528 from ManishMadan2882/basic-ui

Basic UI
This commit is contained in:
Alex
2025-01-13 10:58:37 +00:00
committed by GitHub
23 changed files with 619 additions and 327 deletions

View File

@@ -37,12 +37,14 @@ export default function Hero({
<Fragment key={key}>
<button
onClick={() => handleQuestion({ question: demo.query })}
className="w-full rounded-full border border-silver px-6 py-4 text-left hover:border-gray-4000 dark:hover:border-gray-3000 xl:min-w-[24vw]"
className="w-full rounded-full border border-silver px-6 py-4 text-left hover:border-gray-4000 dark:hover:border-gray-3000 xl:min-w-[24vw] bg-white dark:bg-raisin-black focus:outline-none focus:ring-2 focus:ring-purple-taupe"
>
<p className="mb-1 font-semibold text-black dark:text-silver">
<p className="mb-1 font-semibold text-black-1000 dark:text-bright-gray">
{demo.header}
</p>
<span className="text-gray-400">{demo.query}</span>
<span className="text-gray-700 dark:text-gray-300">
{demo.query}
</span>
</button>
</Fragment>
),

View File

@@ -21,11 +21,10 @@ import {
handleAbort,
} from './conversation/conversationSlice';
import ConversationTile from './conversation/ConversationTile';
import { useDarkTheme, useMediaQuery, useOutsideAlerter } from './hooks';
import { useDarkTheme, useMediaQuery } from './hooks';
import useDefaultDocument from './hooks/useDefaultDocument';
import DeleteConvModal from './modals/DeleteConvModal';
import { ActiveState, Doc } from './models/misc';
import APIKeyModal from './preferences/APIKeyModal';
import { getConversations, getDocs } from './preferences/preferenceApi';
import {
selectApiKeyStatus,
@@ -68,8 +67,6 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
const [isDocsListOpen, setIsDocsListOpen] = useState(false);
const { t } = useTranslation();
const isApiKeySet = useSelector(selectApiKeyStatus);
const [apiKeyModalState, setApiKeyModalState] =
useState<ActiveState>('INACTIVE');
const [uploadModalState, setUploadModalState] =
useState<ActiveState>('INACTIVE');
@@ -192,12 +189,6 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
console.error(err);
});
}
useOutsideAlerter(navRef, () => {
if (isMobile && navOpen && apiKeyModalState === 'INACTIVE') {
setNavOpen(false);
setIsDocsListOpen(false);
}
}, [navOpen, isDocsListOpen, apiKeyModalState]);
/*
Needed to fix bug where if mobile nav was closed and then window was resized to desktop, nav would still be closed but the button to open would be gone, as per #1 on issue #146
@@ -220,7 +211,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
>
<img
src={Expand}
alt="menu toggle"
alt="Toggle navigation menu"
className={`${
!navOpen ? 'rotate-180' : 'rotate-0'
} m-auto transition-all duration-200`}
@@ -234,7 +225,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
>
<img
src={openNewChat}
alt="open new chat icon"
alt="Start new chat"
className="cursor-pointer"
/>
</button>
@@ -263,7 +254,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
}}
>
<a href="/" className="flex gap-1.5">
<img className="mb-2 h-10" src={DocsGPT3} alt="" />
<img className="mb-2 h-10" src={DocsGPT3} alt="DocsGPT Logo" />
<p className="my-auto text-2xl font-semibold">DocsGPT</p>
</a>
</div>
@@ -275,7 +266,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
>
<img
src={Expand}
alt="menu toggle"
alt="Toggle navigation menu"
className={`${
!navOpen ? 'rotate-180' : 'rotate-0'
} m-auto transition-all duration-200`}
@@ -298,7 +289,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
>
<img
src={Add}
alt="new"
alt="Create new chat"
className="opacity-80 group-hover:opacity-100"
/>
<p className=" text-sm text-dove-gray group-hover:text-neutral-600 dark:text-chinese-silver dark:group-hover:text-bright-gray">
@@ -314,7 +305,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
<img
src={isDarkTheme ? SpinnerDark : Spinner}
className="animate-spin cursor-pointer bg-transparent"
alt="Loading..."
alt="Loading conversations"
/>
</div>
)}
@@ -365,6 +356,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
<img
className="mt-2 h-9 w-9 hover:cursor-pointer"
src={UploadIcon}
alt="Upload document"
onClick={() => {
setUploadModalState('ACTIVE');
if (isMobile) {
@@ -392,7 +384,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
>
<img
src={SettingGear}
alt="icon"
alt="Settings"
className="ml-2 w-5 filter dark:invert"
/>
<p className="my-auto text-sm text-eerie-black dark:text-white">
@@ -414,7 +406,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
>
<img
src={Discord}
alt="discord"
alt="Join Discord community"
className="m-2 w-6 self-center filter dark:invert"
/>
</NavLink>
@@ -427,7 +419,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
>
<img
src={Twitter}
alt="x"
alt="Follow us on Twitter"
className="m-2 w-5 self-center filter dark:invert"
/>
</NavLink>
@@ -440,7 +432,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
>
<img
src={Github}
alt="github"
alt="View on GitHub"
className="m-2 w-6 self-center filter dark:invert"
/>
</NavLink>
@@ -457,18 +449,13 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
>
<img
src={Hamburger}
alt="menu toggle"
alt="Toggle mobile menu"
className="w-7 filter dark:invert"
/>
</button>
<div className="text-[#949494] font-medium text-[20px]">DocsGPT</div>
</div>
</div>
<APIKeyModal
modalState={apiKeyModalState}
setModalState={setApiKeyModalState}
isCancellable={isApiKeySet}
/>
<DeleteConvModal
modalState={modalStateDeleteConv}
setModalState={setModalStateDeleteConv}

View File

@@ -59,7 +59,8 @@ const SettingsBar = ({ setActiveTab, activeTab }: SettingsBarProps) => {
<div className="md:hidden z-10">
<button
onClick={() => scrollTabs(-1)}
className="flex h-6 w-6 items-center rounded-full justify-center transition-all hover:bg-gray-100"
className="flex h-6 w-6 items-center rounded-full justify-center transition-all hover:bg-gray-200 dark:hover:bg-gray-700"
aria-label="Scroll tabs left"
>
<img src={ArrowLeft} alt="left-arrow" className="h-3" />
</button>
@@ -67,16 +68,22 @@ const SettingsBar = ({ setActiveTab, activeTab }: SettingsBarProps) => {
<div
ref={containerRef}
className="flex flex-nowrap overflow-x-auto no-scrollbar md:space-x-4 scroll-smooth snap-x"
role="tablist"
aria-label="Settings tabs"
>
{tabs.map((tab, index) => (
<button
key={index}
onClick={() => setActiveTab(tab)}
className={`snap-start h-9 rounded-3xl px-4 font-bold hover:text-neutral-600 dark:hover:text-white/60 ${
className={`snap-start h-9 rounded-3xl px-4 font-bold transition-colors ${
activeTab === tab
? 'bg-neutral-100 text-neutral-600 dark:bg-dark-charcoal dark:text-white/60'
: 'text-gray-6000'
? 'bg-neutral-200 text-neutral-900 dark:bg-dark-charcoal dark:text-white'
: 'text-neutral-700 hover:text-neutral-900 dark:text-neutral-400 dark:hover:text-white'
}`}
role="tab"
aria-selected={activeTab === tab}
aria-controls={`${tab.toLowerCase()}-panel`}
id={`${tab.toLowerCase()}-tab`}
>
{tab}
</button>
@@ -85,7 +92,8 @@ const SettingsBar = ({ setActiveTab, activeTab }: SettingsBarProps) => {
<div className="md:hidden z-10">
<button
onClick={() => scrollTabs(1)}
className="flex h-6 w-6 rounded-full items-center justify-center hover:bg-gray-100"
className="flex h-6 w-6 rounded-full items-center justify-center hover:bg-gray-200 dark:hover:bg-gray-700"
aria-label="Scroll tabs right"
>
<img src={ArrowRight} alt="right-arrow" className="h-3" />
</button>

View File

@@ -386,13 +386,19 @@ export default function Conversation() {
{...getRootProps()}
className="flex w-full items-center rounded-[40px] border border-silver bg-white dark:bg-raisin-black"
>
<input {...getInputProps()}></input>
<label htmlFor="file-upload" className="sr-only">
{t('modals.uploadDoc.label')}
</label>
<input {...getInputProps()} id="file-upload" />
<label htmlFor="message-input" className="sr-only">
{t('inputPlaceholder')}
</label>
<textarea
id="inputbox"
id="message-input"
ref={inputRef}
tabIndex={1}
placeholder={t('inputPlaceholder')}
className={`inputbox-style w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-full bg-transparent py-5 text-base leading-tight opacity-100 focus:outline-none dark:bg-transparent dark:text-bright-gray`}
className={`inputbox-style w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-full bg-transparent py-5 text-base leading-tight opacity-100 focus:outline-none dark:bg-transparent dark:text-bright-gray dark:placeholder-bright-gray dark:placeholder-opacity-50`}
onInput={handleInput}
onKeyDown={(e) => {
if (e.key === 'Enter' && !e.shiftKey) {
@@ -400,19 +406,27 @@ export default function Conversation() {
handleQuestionSubmission();
}
}}
aria-label={t('inputPlaceholder')}
></textarea>
{status === 'loading' ? (
<img
src={isDarkTheme ? SpinnerDark : Spinner}
className="relative right-[38px] bottom-[24px] -mr-[30px] animate-spin cursor-pointer self-end bg-transparent"
></img>
alt={t('loading')}
/>
) : (
<div className="mx-1 cursor-pointer rounded-full p-3 text-center hover:bg-gray-3000 dark:hover:bg-dark-charcoal">
<img
className="ml-[4px] h-6 w-6 text-white "
<button
onClick={() => handleQuestionSubmission()}
src={isDarkTheme ? SendDark : Send}
></img>
aria-label={t('send')}
className="flex items-center justify-center"
>
<img
className="ml-[4px] h-6 w-6 text-white"
src={isDarkTheme ? SendDark : Send}
alt={t('send')}
/>
</button>
</div>
)}
</div>

View File

@@ -8,6 +8,7 @@ import { vscDarkPlus } from 'react-syntax-highlighter/dist/cjs/styles/prism';
import rehypeKatex from 'rehype-katex';
import remarkGfm from 'remark-gfm';
import remarkMath from 'remark-math';
import { useTranslation } from 'react-i18next';
import DocsGPT3 from '../assets/cute_docsgpt3.svg';
import Dislike from '../assets/dislike.svg?react';
@@ -62,6 +63,7 @@ const ConversationBubble = forwardRef<
},
ref,
) {
const { t } = useTranslation();
// const bubbleRef = useRef<HTMLDivElement | null>(null);
const chunks = useSelector(selectChunks);
const selectedDocs = useSelector(selectSelectedDocs);
@@ -113,13 +115,13 @@ const ConversationBubble = forwardRef<
{isEditClicked && (
<div ref={editableQueryRef} className="w-[75%] flex flex-col">
<textarea
placeholder="Type the updated query..."
placeholder={t('conversation.edit.placeholder')}
onChange={(e) => {
setEditInputBox(e.target.value);
}}
rows={1}
value={editInputBox}
className="ml-2 mr-12 text-[15px] resize-y h-12 min-h-max rounded-3xl p-3 no-scrollbar leading-relaxed dark:border-[0.5px] dark:border-white dark:bg-raisin-black dark:text-white px-[18px] border-[1.5px] border-black"
className="ml-2 mr-12 text-[15px] resize-y h-12 min-h-max rounded-3xl p-3 no-scrollbar leading-relaxed dark:border-[0.5px] dark:border-white dark:bg-raisin-black dark:text-white px-[18px] border-[1.5px] border-black"
/>
<div
className={`flex flex-row-reverse justify-end gap-1 mt-3 text-sm font-medium`}
@@ -185,12 +187,14 @@ const ConversationBubble = forwardRef<
avatar={
<img
src={Sources}
alt="Sources"
alt={t('conversation.sources.title')}
className="h-full w-full object-fill"
/>
}
/>
<p className="text-base font-semibold">Sources</p>
<p className="text-base font-semibold">
{t('conversation.sources.title')}
</p>
</div>
<div className="grid grid-cols-2 gap-2 lg:grid-cols-4">
{Array.from({ length: 4 }).map((_, index) => (
@@ -217,12 +221,14 @@ const ConversationBubble = forwardRef<
avatar={
<img
src={Sources}
alt="Sources"
alt={t('conversation.sources.title')}
className="h-full w-full object-fill"
/>
}
/>
<p className="text-base font-semibold">Sources</p>
<p className="text-base font-semibold">
{t('conversation.sources.title')}
</p>
</div>
<div className="fade-in ml-3 mr-5 max-w-[90vw] md:max-w-[70vw] lg:max-w-[50vw]">
<div className="grid grid-cols-2 gap-2 lg:grid-cols-4">
@@ -289,9 +295,11 @@ const ConversationBubble = forwardRef<
className="flex h-28 cursor-pointer flex-col-reverse rounded-[20px] bg-gray-1000 p-4 text-purple-30 hover:bg-[#F1F1F1] hover:text-[#6D3ECC] dark:bg-gun-metal dark:hover:bg-[#2C2E3C] dark:hover:text-[#8C67D7]"
onClick={() => setIsSidebarOpen(true)}
>
<p className="ellipsis-text h-22 text-xs">{`View ${
sources?.length ? sources.length - 3 : 0
} more`}</p>
<p className="ellipsis-text h-22 text-xs">
{t('conversation.sources.view_more', {
count: sources?.length ? sources.length - 3 : 0,
})}
</p>
</div>
)}
</div>
@@ -306,12 +314,14 @@ const ConversationBubble = forwardRef<
avatar={
<img
src={DocsGPT3}
alt="DocsGPT"
alt={t('conversation.answer')}
className="h-full w-full object-cover"
/>
}
/>
<p className="text-base font-semibold">Answer</p>
<p className="text-base font-semibold">
{t('conversation.answer')}
</p>
</div>
<div
className={`fade-in-bubble ml-2 mr-5 flex max-w-[90vw] rounded-[28px] bg-gray-1000 py-[14px] px-7 dark:bg-gun-metal md:max-w-[70vw] lg:max-w-[50vw] ${
@@ -419,7 +429,7 @@ const ConversationBubble = forwardRef<
${type !== 'ERROR' ? 'group-hover:lg:visible' : 'hidden'}`}
>
<div>
<SpeakButton text={message} /> {/* Add SpeakButton here */}
<SpeakButton text={message} />
</div>
</div>
{type === 'ERROR' && (
@@ -557,7 +567,7 @@ function AllSources(sources: AllSourcesProps) {
{source.source && source.source !== 'local' ? (
<img
src={Link}
alt="Link"
alt={'Link'}
className="h-3 w-3 cursor-pointer object-fill"
onClick={() =>
window.open(source.source, '_blank', 'noopener, noreferrer')

View File

@@ -115,6 +115,19 @@ export default function ConversationTile({
setConversationsName(conversation.name);
setIsEdit(false);
}
const handleRenameKeyDown = (e: React.KeyboardEvent<HTMLInputElement>) => {
e.stopPropagation();
if (e.key === 'Enter') {
handleSaveConversation({
id: conversation.id,
name: conversationName,
});
} else if (e.key === 'Escape') {
onClear();
}
};
return (
<>
<div
@@ -144,6 +157,7 @@ export default function ConversationTile({
className="h-6 w-full bg-transparent px-1 text-sm font-normal leading-6 focus:outline-[#0075FF]"
value={conversationName}
onChange={(e) => setConversationsName(e.target.value)}
onKeyDown={handleRenameKeyDown}
/>
) : (
<p className="my-auto overflow-hidden overflow-ellipsis whitespace-nowrap text-sm font-normal leading-6 text-eerie-black dark:text-white">
@@ -239,7 +253,7 @@ export default function ConversationTile({
>
<img
src={Trash}
alt="Edit"
alt="Delete"
width={24}
height={24}
className="cursor-pointer hover:opacity-50"

View File

@@ -236,7 +236,7 @@ export const SharedConversation = () => {
</div>
</div>
<div className=" flex w-11/12 flex-col items-center gap-4 pb-2 md:w-10/12 lg:w-6/12">
<div className="flex w-11/12 flex-col items-center gap-4 pb-2 md:w-10/12 lg:w-6/12">
{apiKey ? (
<div className="flex h-full w-full items-center rounded-[40px] border border-silver bg-white py-1 dark:bg-raisin-black">
<div
@@ -272,7 +272,7 @@ export const SharedConversation = () => {
) : (
<button
onClick={() => navigate('/')}
className="w-fit rounded-full bg-purple-30 p-4 text-white shadow-xl transition-colors duration-200 hover:bg-purple-taupe"
className="w-fit rounded-full bg-purple-30 p-4 text-white shadow-xl transition-colors duration-200 hover:bg-purple-taupe mb-14 sm:mb-0"
>
{t('sharedConv.button')}
</button>

View File

@@ -41,16 +41,16 @@
"selectLanguage": "Select Language",
"chunks": "Chunks processed per query",
"prompt": "Active Prompt",
"deleteAllLabel": "Delete all Conversation",
"deleteAllBtn": "Delete all",
"deleteAllLabel": "Delete All Conversations",
"deleteAllBtn": "Delete All",
"addNew": "Add New",
"convHistory": "Conversational history",
"convHistory": "Conversation History",
"none": "None",
"low": "Low",
"medium": "Medium",
"high": "High",
"unlimited": "Unlimited",
"default": "default"
"default": "Default"
},
"documents": {
"label": "Documents",
@@ -58,7 +58,20 @@
"date": "Vector Date",
"type": "Type",
"tokenUsage": "Token Usage",
"noData": "No existing Documents"
"noData": "No existing Documents",
"searchPlaceholder": "Search...",
"addNew": "Add New",
"addNewTitle": "Add New Document",
"preLoaded": "Pre-loaded",
"private": "Private",
"sync": "Sync",
"syncFrequency": {
"never": "Never",
"daily": "Daily",
"weekly": "Weekly",
"monthly": "Monthly"
},
"actions": "Actions"
},
"apiKeys": {
"label": "Chatbots",
@@ -72,10 +85,21 @@
"label": "Analytics"
},
"logs": {
"label": "Logs"
"label": "Logs",
"filterByChatbot": "Filter by chatbot",
"none": "None",
"selectChatbotPlaceholder": "Select chatbot",
"apiGeneratedConversations": "API generated / chatbot conversations"
},
"tools": {
"label": "Tools"
"label": "Tools",
"searchPlaceholder": "Search tools...",
"addTool": "Add Tool",
"noToolsFound": "No tools found",
"settingsIconAlt": "Settings icon",
"configureToolAria": "Configure {toolName}",
"toggleToolAria": "Toggle {toolName}",
"selectToolSetup": "Select a tool to set up"
}
},
"modals": {
@@ -144,5 +168,23 @@
"delete": "Delete",
"rename": "Rename",
"deleteWarning": "Are you sure you want to delete this conversation?"
},
"conversation": {
"copy": "Copy",
"copied": "Copied",
"speak": "Speak",
"answer": "Answer",
"send": "Send message",
"loading": "Loading response",
"edit": {
"placeholder": "Type the updated query..."
},
"sources": {
"title": "Sources",
"text": "Source Text",
"link": "Source Link",
"view_more": "{{count}} more sources"
},
"retry": "Retry"
}
}

View File

@@ -1,5 +1,5 @@
{
"language": "Spanish",
"language": "Español",
"chat": "Chat",
"chats": "Chats",
"newChat": "Nuevo Chat",
@@ -8,7 +8,7 @@
"inputPlaceholder": "Escribe tu mensaje aquí...",
"tagline": "DocsGPT utiliza GenAI, por favor revisa información crítica utilizando fuentes.",
"sourceDocs": "Fuente",
"none": "Nada",
"none": "Ninguno",
"cancel": "Cancelar",
"help": "Asistencia",
"emailUs": "Envíanos un correo",
@@ -36,29 +36,41 @@
"general": {
"label": "General",
"selectTheme": "Seleccionar Tema",
"light": "de luz",
"dark": "oscura",
"light": "Claro",
"dark": "Oscuro",
"selectLanguage": "Seleccionar Idioma",
"chunks": "Trozos procesados por consulta",
"chunks": "Fragmentos procesados por consulta",
"prompt": "Prompt Activo",
"deleteAllLabel": "Eliminar toda la Conversación",
"deleteAllLabel": "Eliminar todas las conversaciones",
"deleteAllBtn": "Eliminar todo",
"addNew": "Agregar Nuevo",
"convHistory": "Historia conversacional",
"none": "ninguno",
"convHistory": "Historial de conversaciones",
"none": "Ninguno",
"low": "Bajo",
"medium": "Medio",
"high": "Alto",
"unlimited": "Ilimitado",
"default": "predeterminada"
"default": "Predeterminado"
},
"documents": {
"label": "Documentos",
"name": "Nombre del Documento",
"date": "Fecha Vector",
"date": "Fecha de Vector",
"type": "Tipo",
"tokenUsage": "Uso de Tokens",
"noData": "No hay documentos existentes"
"noData": "No hay documentos existentes",
"searchPlaceholder": "Buscar...",
"addNew": "Agregar Nuevo",
"addNewTitle": "Agregar Nuevo Documento",
"preLoaded": "Precargado",
"private": "Privado",
"sync": "Sincronizar",
"syncFrequency": {
"never": "Nunca",
"daily": "Diario",
"weekly": "Semanal",
"monthly": "Mensual"
}
},
"apiKeys": {
"label": "Chatbots",
@@ -72,21 +84,28 @@
"label": "Analítica"
},
"logs": {
"label": "Registros"
"label": "Registros",
"filterByChatbot": "Filtrar por chatbot",
"none": "Ninguno",
"selectChatbotPlaceholder": "Seleccionar chatbot",
"apiGeneratedConversations": "Conversaciones generadas por API / chatbot"
},
"tools": {
"label": "Herramientas"
}
},
"modals": {
"uploadDoc": {
"label": "Subir nuevo documento",
"select": "Elija cómo cargar su documento en DocsGPT",
"select": "Elige cómo cargar tu documento en DocsGPT",
"file": "Subir desde el dispositivo",
"back": "Atrás",
"wait": "Espere por favor ...",
"wait": "Por favor espera ...",
"remote": "Recoger desde un sitio web",
"start": "Empezar a chatear",
"start": "Comenzar a chatear",
"name": "Nombre",
"choose": "Seleccionar Archivos",
"info": "Por favor, suba archivos .pdf, .txt, .rst, .csv, .xlsx, .docx, .md, .html, .epub, .json, .pptx, .zip limitados a 25 MB",
"info": "Por favor, sube archivos .pdf, .txt, .rst, .csv, .xlsx, .docx, .md, .html, .epub, .json, .pptx, .zip limitados a 25MB",
"uploadedFiles": "Archivos Subidos",
"cancel": "Cancelar",
"train": "Entrenar",
@@ -101,45 +120,61 @@
"numberOfPosts": "Número de publicaciones"
},
"drag": {
"title": "Cargar un archivo fuente",
"description": "Suelta tu archivo aquí para agregarlo como fuente."
"title": "Carga un archivo fuente",
"description": "Suelta tu archivo aquí para añadirlo como fuente"
}
},
"createAPIKey": {
"label": "Crear Nueva Clave de API",
"apiKeyName": "Nombre de la Clave de API",
"chunks": "Fragmentos procesados por consulta",
"prompt": "Seleccione el prompt activo",
"prompt": "Selecciona el prompt activo",
"sourceDoc": "Documento Fuente",
"create": "Crear"
},
"saveKey": {
"note": "Por favor, guarde su Clave",
"disclaimer": "Esta es la única vez que se mostrará su clave.",
"note": "Por favor, guarda tu Clave",
"disclaimer": "Esta es la única vez que se mostrará tu clave.",
"copy": "Copiar",
"copied": "Copiado",
"confirm": "He guardado la Clave"
},
"deleteConv": {
"confirm": "¿Está seguro de que desea eliminar todas las conversaciones?",
"confirm": "¿Estás seguro de que deseas eliminar todas las conversaciones?",
"delete": "Eliminar"
},
"shareConv": {
"label": "Crear una página pública para compartir",
"note": "El documento original, la información personal y las conversaciones posteriores permanecerán privadas",
"note": "El documento fuente, información personal y conversaciones posteriores permanecerán privadas",
"create": "Crear",
"option": "Permitir a los usuarios realizar más consultas."
"option": "Permitir a los usuarios realizar más consultas"
}
},
"sharedConv": {
"subtitle": "Creado con",
"button": "Comienza con DocsGPT",
"meta": "DocsGPT utiliza GenAI, por favor revise la información crítica utilizando fuentes."
"meta": "DocsGPT utiliza GenAI, por favor revisa la información crítica utilizando fuentes."
},
"convTile": {
"share": "Compartir",
"delete": "Eliminar",
"rename": "Renombrar",
"deleteWarning": "¿Está seguro de que desea eliminar esta conversación?"
"deleteWarning": "¿Estás seguro de que deseas eliminar esta conversación?"
},
"conversation": {
"copy": "Copiar",
"copied": "Copiado",
"speak": "Hablar",
"answer": "Respuesta",
"edit": {
"placeholder": "Escribe la consulta actualizada..."
},
"sources": {
"title": "Fuentes",
"text": "Texto de la Fuente",
"link": "Enlace de la Fuente",
"view_more": "{{count}} más fuentes"
},
"retry": "Reintentar"
}
}

View File

@@ -1,7 +1,7 @@
{
"language": "日本語",
"chat": "チャット",
"chats": "チャット",
"chats": "チャット一覧",
"newChat": "新しいチャット",
"myPlan": "私のプラン",
"about": "について",
@@ -24,7 +24,7 @@
},
{
"header": "コードを書く",
"query": "APIリクエストのコードを/api/answerに書いてください"
"query": "APIリクエストのコードを/api/answerに書いてください"
},
{
"header": "学習支援",
@@ -58,7 +58,19 @@
"date": "ベクトル日付",
"type": "タイプ",
"tokenUsage": "トークン使用量",
"noData": "既存のドキュメントはありません"
"noData": "既存のドキュメントはありません",
"searchPlaceholder": "検索...",
"addNew": "新規追加",
"addNewTitle": "新規ドキュメントを追加",
"preLoaded": "プリロード済み",
"private": "プライベート",
"sync": "同期",
"syncFrequency": {
"never": "なし",
"daily": "毎日",
"weekly": "毎週",
"monthly": "<22><>月"
}
},
"apiKeys": {
"label": "チャットボット",
@@ -72,7 +84,14 @@
"label": "分析"
},
"logs": {
"label": "ログ"
"label": "ログ",
"filterByChatbot": "チャットボットでフィルタ",
"none": "なし",
"selectChatbotPlaceholder": "チャットボットを選択",
"apiGeneratedConversations": "API生成/チャットボット会話"
},
"tools": {
"label": "ツール"
}
},
"modals": {
@@ -86,7 +105,7 @@
"start": "チャットを開始する",
"name": "名前",
"choose": "ファイルを選択",
"info": ".pdf, .txt, .rst, .docx, .md, .json, .pptx, .zipファイルを25MBまでアップロードしてください",
"info": ".pdf, .txt, .rst, .csv, .xlsx, .docx, .md, .html, .epub, .json, .pptx, .zipファイルを25MBまでアップロードしてください",
"uploadedFiles": "アップロードされたファイル",
"cancel": "キャンセル",
"train": "トレーニング",
@@ -141,5 +160,21 @@
"delete": "削除",
"rename": "名前変更",
"deleteWarning": "この会話を削除してもよろしいですか?"
},
"conversation": {
"copy": "コピー",
"copied": "コピーしました",
"speak": "スピーク",
"answer": "回答",
"edit": {
"placeholder": "更新されたクエリを入力してください..."
},
"sources": {
"title": "ソース",
"text": "ソーステキスト",
"link": "ソースリンク",
"view_more": "さらに{{count}}個のソース"
},
"retry": "再試行"
}
}

View File

@@ -1,142 +1,180 @@
{
"language": "Русский",
"chat": "Чат",
"chats": "Чаты",
"newChat": "Новый чат",
"myPlan": "Мой план",
"about": "О",
"inputPlaceholder": "Введите свое сообщение здесь...",
"tagline": "DocsGPT использует GenAI, пожалуйста, проверьте важную информацию, используя источники.",
"sourceDocs": "Источник",
"none": "Нет",
"cancel": "Отмена",
"demo": [
{
"header": "Узнайте о DocsGPT",
"query": "Что такое DocsGPT?"
},
{
"header": "Обобщить документацию",
"query": "Обобщить текущий контекст"
},
{
"header": "Написать код",
"query": "Написать код для запроса API к /api/answer"
},
{
"header": "Помощь в обучении",
"query": "Написать потенциальные вопросы для контекста"
}
],
"settings": {
"label": "Настройки",
"general": {
"label": "Общие",
"selectTheme": "Выбрать тему",
"light": "Светлая",
"dark": "Темная",
"selectLanguage": "Выбрать язык",
"chunks": "Обработанные фрагменты на запрос",
"prompt": "Активная подсказка",
"deleteAllLabel": "Удалить все беседы",
"deleteAllBtn": "Удалить все",
"addNew": "Добавить новый",
"convHistory": "История разговоров",
"none": "Нет",
"low": "Низкий",
"medium": "Средний",
"high": "Высокий",
"unlimited": "Без ограничений",
"default": "по умолчанию"
},
"documents": {
"label": "Документы",
"name": "Название документа",
"date": "Дата вектора",
"type": "Тип",
"tokenUsage": "Использование токена",
"noData": "Нет существующих документов"
},
"apiKeys": {
"label": "Чат-боты",
"name": "Название",
"key": "Ключ API",
"sourceDoc": "Исходный документ",
"createNew": "Создать новый",
"noData": "Нет существующих чат-ботов"
},
"analytics": {
"label": "Analytics"
},
"logs": {
"label": "Журналы"
}
"language": "Русский",
"chat": "Чат",
"chats": "Чаты",
"newChat": "Новый чат",
"myPlan": "Мой план",
"about": "О",
"inputPlaceholder": "Введите свое сообщение здесь...",
"tagline": "DocsGPT использует GenAI, пожалуйста, проверьте важную информацию, используя источники.",
"sourceDocs": "Источник",
"none": "Нет",
"cancel": "Отмена",
"help": "Помощь",
"emailUs": "Напишите нам",
"documentation": "Документация",
"demo": [
{
"header": "Узнайте о DocsGPT",
"query": "Что такое DocsGPT?"
},
"modals": {
"uploadDoc": {
"label": "Загрузить новую документацию",
"select": "Выберите способ загрузки документа в DocsGPT",
"file": "Загрузить с устройства",
"back": "Назад",
"wait": "Пожалуйста, подождите ...",
"remote": "Собрать с веб-сайта",
"start": "Начать чат",
"name": "Имя",
"choose": "Выбрать файлы",
"info": "Загрузите .pdf, .txt, .rst, .csv, .xlsx, .docx, .md, .zip с ограничением до 25 МБ",
"uploadedFiles": "Загруженные файлы",
"cancel": "Отмена",
"train": "Обучение",
"link": "Ссылка",
"urlLink": "URL-ссылка",
"repoUrl": "URL-адрес репозитория",
"reddit": {
"id": "ID клиента",
"secret": "Секрет клиента",
"agent": "Агент пользователя",
"searchQueries": "Поисковые запросы",
"numberOfPosts": "Количество сообщений"
},
"drag": {
"title": "Загрузите исходный файл",
"description": "Перетащите сюда свой файл, чтобы добавить его в качестве источника."
}
},
"createAPIKey": {
"label": "Создать новый ключ API",
"apiKeyName": "Имя ключа API",
"chunks": "Обработано фрагментов на запрос",
"prompt": "Выбрать активный запрос",
"sourceDoc": "Исходный документ",
"create": "Создать"
},
"saveKey": {
"note": "Пожалуйста, сохраните свой ключ",
"disclaimer": "Это единственный раз, когда будет показан ваш ключ.",
"copy": "Копировать",
"copied": "Скопировано",
"confirm": "Я сохранил ключ"
},
"deleteConv": {
"confirm": "Вы уверены, что хотите удалить все разговоры?",
"delete": "Удалить"
},
"shareConv": {
"label": "Создать публичную страницу для общего доступа",
"note": "Исходный документ, личная информация и дальнейший разговор останутся конфиденциальными",
"create": "Создать",
"option": "Разрешить пользователям запрашивать дальнейшие действия"
}
{
"header": "Обобщить документацию",
"query": "Обобщить текущий контекст"
},
"sharedConv": {
"subtitle": "Создано с помощью",
"button": "Начать работу с DocsGPT",
"meta": "DocsGPT использует GenAI, пожалуйста, проверьте важную информацию с помощью источников."
{
"header": "Написать код",
"query": "Написать код для запроса API к /api/answer"
},
"convTile": {
"share": "Поделиться",
"delete": "Удалить",
"rename": "Переименовать",
"deleteWarning": "Вы уверены, что хотите удалить этот разговор?"
{
"header": "Помощь в обучении",
"query": "Написать потенциальные вопросы для контекста"
}
}
],
"settings": {
"label": "Настройки",
"general": {
"label": "Общие",
"selectTheme": "Выбрать тему",
"light": "Светлая",
"dark": "Темная",
"selectLanguage": "Выбрать язык",
"chunks": "Обработанные фрагменты на запрос",
"prompt": "Активная подсказка",
"deleteAllLabel": "Удалить все беседы",
"deleteAllBtn": "Удалить все",
"addNew": "Добавить новый",
"convHistory": "История разговоров",
"none": "Нет",
"low": "Низкий",
"medium": "Средний",
"high": "Высокий",
"unlimited": "Без ограничений",
"default": "По умолчанию"
},
"documents": {
"label": "Документы",
"name": "Название документа",
"date": "Дата вектора",
"type": "Тип",
"tokenUsage": "Использование токенов",
"noData": "Нет существующих документов",
"searchPlaceholder": "Поиск...",
"addNew": "Добавить новый",
"addNewTitle": "Добавить новый документ",
"preLoaded": "Предзагруженный",
"private": "Личный",
"sync": "Синхронизация",
"syncFrequency": {
"never": "Никогда",
"daily": "Ежедневно",
"weekly": "Еженедельно",
"monthly": "Ежемесячно"
}
},
"apiKeys": {
"label": "Чат-боты",
"name": "Название",
"key": "Ключ API",
"sourceDoc": "Исходный документ",
"createNew": "Создать новый",
"noData": "Нет существующих чат-ботов"
},
"analytics": {
"label": "Аналитика"
},
"logs": {
"label": "Регистры",
"filterByChatbot": "Фильтр по чат-боту",
"none": "Нет",
"selectChatbotPlaceholder": "Выберите чат-бота",
"apiGeneratedConversations": "Разговоры, сгенерированные API / чат-ботом"
},
"tools": {
"label": "Инструменты"
}
},
"modals": {
"uploadDoc": {
"label": "Загрузить новую документацию",
"select": "Выберите способ загрузки документа в DocsGPT",
"file": "Загрузить с устройства",
"back": "Назад",
"wait": "Пожалуйста, подождите ...",
"remote": "Собрать с веб-сайта",
"start": "Начать чат",
"name": "Имя",
"choose": "Выбрать файлы",
"info": "Пожалуйста, загрузите .pdf, .txt, .rst, .csv, .xlsx, .docx, .md, .html, .epub, .json, .pptx, .zip с ограничением до 25 МБ",
"uploadedFiles": "Загруженные файлы",
"cancel": "Отмена",
"train": "Обучение",
"link": "Ссылка",
"urlLink": "URL-ссылка",
"repoUrl": "URL-адрес репозитория",
"reddit": {
"id": "ID клиента",
"secret": "Секрет клиента",
"agent": "Агент пользователя",
"searchQueries": "Поисковые запросы",
"numberOfPosts": "Количество сообщений"
},
"drag": {
"title": "Загрузите исходный файл",
"description": "Перетащите сюда свой файл, чтобы добавить его в качестве источника"
}
},
"createAPIKey": {
"label": "Создать новый ключ API",
"apiKeyName": "Имя ключа API",
"chunks": "Обработано фрагментов на запрос",
"prompt": "Выбрать активную подсказку",
"sourceDoc": "Исходный документ",
"create": "Создать"
},
"saveKey": {
"note": "Пожалуйста, сохраните свой ключ",
"disclaimer": "Это единственный раз, когда будет показан ваш ключ.",
"copy": "Копировать",
"copied": "Скопировано",
"confirm": "Я сохранил ключ"
},
"deleteConv": {
"confirm": "Вы уверены, что хотите удалить все разговоры?",
"delete": "Удалить"
},
"shareConv": {
"label": "Создать публичную страницу для общего доступа",
"note": "Исходный документ, личная информация и дальнейший разговор останутся конфиденциальными",
"create": "Создать",
"option": "Разрешить пользователям продолжить диалог"
}
},
"sharedConv": {
"subtitle": "Создано с помощью",
"button": "Начать работу с DocsGPT",
"meta": "DocsGPT использует GenAI, пожалуйста, проверьте важную информацию с помощью источников."
},
"convTile": {
"share": "Поделиться",
"delete": "Удалить",
"rename": "Переименовать",
"deleteWarning": "Вы уверены, что хотите удалить этот разговор?"
},
"conversation": {
"copy": "Копировать",
"copied": "Скопировано",
"speak": "Говорить",
"answer": "Ответ",
"edit": {
"placeholder": "Введите обновленный запрос..."
},
"sources": {
"title": "Источники",
"text": "Исходный текст",
"link": "Исходная ссылка",
"view_more": "Еще {{count}} источников"
},
"retry": "Повторить"
}
}

View File

@@ -57,39 +57,64 @@
"name": "文件名稱",
"date": "向量日期",
"type": "類型",
"tokenUsage": "Token 使用量"
"tokenUsage": "Token 使用量",
"noData": "沒有現有的文件",
"searchPlaceholder": "搜尋...",
"addNewTitle": "新增文件",
"preLoaded": "預載入",
"private": "私人",
"sync": "同步",
"syncFrequency": {
"never": "永不",
"daily": "每日",
"weekly": "每週",
"monthly": "每月"
}
},
"apiKeys": {
"label": "API 金鑰",
"label": "聊天機器人",
"name": "名稱",
"key": "API 金鑰",
"sourceDoc": "來源文件",
"createNew": "新增"
"createNew": "新增",
"noData": "沒有現有的聊天機器人"
},
"analytics": {
"label": "分析"
},
"logs": {
"label": "日誌"
"label": "日誌",
"filterByChatbot": "按聊天機器人篩選",
"none": "無",
"selectChatbotPlaceholder": "選擇聊天機器人",
"apiGeneratedConversations": "API生成/聊天機器人對話"
},
"tools": {
"label": "工具"
}
},
"modals": {
"uploadDoc": {
"label": "上傳新文件",
"select": "選擇如何將文件上傳到 DocsGPT",
"file": "從檔案",
"remote": "遠端",
"back": "返回",
"wait": "請稍候...",
"remote": "從網站收集",
"start": "開始對話",
"name": "名稱",
"choose": "選擇檔案",
"info": "請上傳 .pdf, .txt, .rst, .docx, .md, .json, .pptx, .zip 檔案,大小限制為 25MB",
"info": "請上傳 .pdf, .txt, .rst, .csv, .xlsx, .docx, .md, .html, .epub, .json, .pptx, .zip 檔案,大小限制為 25MB",
"uploadedFiles": "已上傳的檔案",
"cancel": "取消",
"train": "訓練",
"link": "連結",
"urlLink": "URL 連結",
"repoUrl": "儲存庫 URL",
"reddit": {
"id": "用戶端 ID",
"secret": "用戶端金鑰",
"agent": "使用者代理(User-Agent)",
"agent": "使用者代理",
"searchQueries": "搜尋查詢",
"numberOfPosts": "貼文數量"
},
@@ -120,7 +145,8 @@
"shareConv": {
"label": "建立公開頁面以分享",
"note": "來源文件、個人資訊和後續對話將保持私密",
"create": "建立"
"create": "建立",
"option": "允許用戶進行更多查詢"
}
},
"sharedConv": {
@@ -133,5 +159,21 @@
"delete": "刪除",
"rename": "重新命名",
"deleteWarning": "您確定要刪除這個對話嗎?"
},
"conversation": {
"copy": "複製",
"copied": "已複製",
"speak": "朗讀",
"answer": "回答",
"edit": {
"placeholder": "輸入更新的查詢..."
},
"sources": {
"title": "來源",
"text": "來源文字",
"link": "來源連結",
"view_more": "更多{{count}}個來源"
},
"retry": "重試"
}
}

View File

@@ -34,7 +34,7 @@
"settings": {
"label": "设置",
"general": {
"label": "般",
"label": "般",
"selectTheme": "选择主题",
"light": "浅色",
"dark": "暗色",
@@ -58,7 +58,18 @@
"date": "向量日期",
"type": "类型",
"tokenUsage": "令牌使用",
"noData": "没有现有的文档"
"noData": "没有现有的文档",
"searchPlaceholder": "搜索...",
"addNewTitle": "添加新文档",
"preLoaded": "预加载",
"private": "私有",
"sync": "同步",
"syncFrequency": {
"never": "从不",
"daily": "每天",
"weekly": "每周",
"monthly": "每月"
}
},
"apiKeys": {
"label": "聊天机器人",
@@ -72,7 +83,14 @@
"label": "分析"
},
"logs": {
"label": "日志"
"label": "日志",
"filterByChatbot": "按聊天机器人筛选",
"none": "无",
"selectChatbotPlaceholder": "选择聊天机器人",
"apiGeneratedConversations": "API生成/聊天机器人对话"
},
"tools": {
"label": "工具"
}
},
"modals": {
@@ -86,7 +104,7 @@
"start": "开始聊天",
"name": "名称",
"choose": "选择文件",
"info": "请上传 .pdf, .txt, .rst, .csv, .xlsx, .docx, .md, .html, .epub, .json, .pptx, .zip 文件,限 25MB",
"info": "请上传 .pdf, .txt, .rst, .csv, .xlsx, .docx, .md, .html, .epub, .json, .pptx, .zip 文件,限制为 25MB",
"uploadedFiles": "已上传文件",
"cancel": "取消",
"train": "训练",
@@ -128,7 +146,7 @@
"label": "创建用于分享的公共页面",
"note": "源文档、个人信息和后续对话将保持私密",
"create": "创建",
"option": "允许用户进行更多查询"
"option": "允许用户进行更多查询"
}
},
"sharedConv": {
@@ -141,5 +159,21 @@
"delete": "删除",
"rename": "重命名",
"deleteWarning": "您确定要删除此对话吗?"
},
"conversation": {
"copy": "复制",
"copied": "已复制",
"speak": "朗读",
"answer": "回答",
"edit": {
"placeholder": "输入更新的查询..."
},
"sources": {
"title": "来源",
"text": "来源文本",
"link": "来源链接",
"view_more": "更多{{count}}个来源"
},
"retry": "重试"
}
}

View File

@@ -29,8 +29,9 @@ function AddPrompt({
setNewPromptName('');
setNewPromptContent('');
}}
aria-label="Close add prompt modal"
>
<img className="filter dark:invert" src={Exit} />
<img className="filter dark:invert" src={Exit} alt="Close modal" />
</button>
<div className="p-8">
<p className="mb-1 text-xl text-jet dark:text-bright-gray">
@@ -40,7 +41,11 @@ function AddPrompt({
Add your custom prompt and save it to DocsGPT
</p>
<div>
<label htmlFor="new-prompt-name" className="sr-only">
Prompt Name
</label>
<Input
id="new-prompt-name"
placeholder="Prompt Name"
type="text"
className="h-10 rounded-lg"
@@ -57,10 +62,15 @@ function AddPrompt({
Prompt Text
</span>
</div>
<label htmlFor="new-prompt-content" className="sr-only">
Prompt Text
</label>
<textarea
id="new-prompt-content"
className="h-56 w-full rounded-lg border-2 border-silver px-3 py-2 outline-none dark:border-silver/40 dark:bg-transparent dark:text-white"
value={newPromptContent}
onChange={(e) => setNewPromptContent(e.target.value)}
aria-label="Prompt Text"
></textarea>
</div>
<div className="mt-6 flex flex-row-reverse">
@@ -104,8 +114,9 @@ function EditPrompt({
onClick={() => {
setModalState('INACTIVE');
}}
aria-label="Close edit prompt modal"
>
<img className="filter dark:invert" src={Exit} />
<img className="filter dark:invert" src={Exit} alt="Close modal" />
</button>
<div className="p-8">
<p className="mb-1 text-xl text-jet dark:text-bright-gray">
@@ -115,13 +126,17 @@ function EditPrompt({
Edit your custom prompt and save it to DocsGPT
</p>
<div>
<label htmlFor="edit-prompt-name" className="sr-only">
Prompt Name
</label>
<Input
id="edit-prompt-name"
placeholder="Prompt Name"
type="text"
className="h-10 rounded-lg"
value={editPromptName}
onChange={(e) => setEditPromptName(e.target.value)}
></Input>
/>
<div className="relative bottom-12 left-3 mt-[-3.00px]">
<span className="bg-white px-1 text-xs text-silver dark:bg-outer-space dark:text-silver">
Prompt Name
@@ -132,10 +147,15 @@ function EditPrompt({
Prompt Text
</span>
</div>
<label htmlFor="edit-prompt-content" className="sr-only">
Prompt Text
</label>
<textarea
id="edit-prompt-content"
className="h-56 w-full rounded-lg border-2 border-silver px-3 py-2 outline-none dark:border-silver/40 dark:bg-transparent dark:text-white"
value={editPromptContent}
onChange={(e) => setEditPromptContent(e.target.value)}
aria-label="Prompt Text"
></textarea>
</div>
<div className="mt-6 flex flex-row-reverse gap-4">

View File

@@ -115,12 +115,20 @@ export default function APIKeys() {
<table className="min-w-full divide-y divide-silver dark:divide-silver/40 ">
<thead>
<tr className="text-start text-sm font-medium text-gray-700 dark:text-gray-50 uppercase">
<th className="p-2">{t('settings.apiKeys.name')}</th>
<th className="p-2">
<th scope="col" className="p-2">
{t('settings.apiKeys.name')}
</th>
<th scope="col" className="p-2">
{t('settings.apiKeys.sourceDoc')}
</th>
<th className="p-2">{t('settings.apiKeys.key')}</th>
<th></th>
<th scope="col" className="p-2">
{t('settings.apiKeys.key')}
</th>
<th
scope="col"
className="p-2"
aria-label="Actions"
></th>
</tr>
</thead>
<tbody className="divide-y divide-gray-200 dark:divide-neutral-700">
@@ -146,7 +154,7 @@ export default function APIKeys() {
<td>
<img
src={Trash}
alt="Delete"
alt={`Delete ${element.name}`}
className="h-4 w-4 cursor-pointer hover:opacity-50"
id={`img-${index}`}
onClick={() => handleDeleteKey(element.id)}

View File

@@ -164,7 +164,7 @@ export default function Analytics() {
<div className="mt-12">
<div className="flex flex-col items-start">
<div className="flex flex-col gap-3">
<p className="font-bold text-jet dark:text-bright-gray">
<p className="font-bold text-gray-800 dark:text-bright-gray">
Filter by chatbot
</p>
<Dropdown
@@ -191,6 +191,7 @@ export default function Analytics() {
}
rounded="3xl"
border="border"
borderColor="gray-700"
/>
</div>

View File

@@ -54,10 +54,10 @@ const Documents: React.FC<DocumentsProps> = ({
const [totalPages, setTotalPages] = useState<number>(1);
const currentDocuments = paginatedDocuments ?? [];
const syncOptions = [
{ label: 'Never', value: 'never' },
{ label: 'Daily', value: 'daily' },
{ label: 'Weekly', value: 'weekly' },
{ label: 'Monthly', value: 'monthly' },
{ label: t('settings.documents.syncFrequency.never'), value: 'never' },
{ label: t('settings.documents.syncFrequency.daily'), value: 'daily' },
{ label: t('settings.documents.syncFrequency.weekly'), value: 'weekly' },
{ label: t('settings.documents.syncFrequency.monthly'), value: 'monthly' },
];
const refreshDocs = useCallback(
@@ -151,9 +151,12 @@ const Documents: React.FC<DocumentsProps> = ({
<div className="z-10 w-full overflow-x-auto">
<div className="my-3 flex justify-between items-center">
<div className="p-1">
<label htmlFor="document-search-input" className="sr-only">
{t('settings.documents.searchPlaceholder')}
</label>
<Input
maxLength={256}
placeholder="Search..."
placeholder={t('settings.documents.searchPlaceholder')}
name="Document-search-input"
type="text"
id="document-search-input"
@@ -161,27 +164,24 @@ const Documents: React.FC<DocumentsProps> = ({
onChange={(e) => {
setSearchTerm(e.target.value);
setCurrentPage(1);
// refreshDocs(sortField, 1, rowsPerPage);
// do not call refreshDocs here the state is async
// so it will not have the updated value
}} // Handle search input change
}}
/>
</div>
<button
className="rounded-full w-40 bg-purple-30 px-4 py-3 text-white hover:bg-[#6F3FD1]"
title="Add New Document"
title={t('settings.documents.addNewTitle')}
onClick={() => {
setIsOnboarding(false); // Set onboarding flag if needed
setModalState('ACTIVE'); // Open the upload modal
}}
>
Add New
{t('settings.documents.addNew')}
</button>
</div>
{loading ? (
<SkeletonLoader count={1} />
) : (
<div className="flex flex-col">
<div className="flex flex-col">
<div className="flex-grow">
<div className="dark:border-silver/40 border-silver rounded-md border overflow-auto">
<table className="min-w-full divide-y divide-silver dark:divide-silver/40 text-xs sm:text-sm ">
@@ -225,8 +225,9 @@ const Documents: React.FC<DocumentsProps> = ({
<th
scope="col"
className="px-6 py-2 text-start font-medium text-gray-700 dark:text-gray-50 uppercase"
aria-label={t('settings.documents.actions')}
>
{' '}
{t('settings.documents.actions')}
</th>
</tr>
</thead>
@@ -270,7 +271,7 @@ const Documents: React.FC<DocumentsProps> = ({
{document.type !== 'remote' && (
<img
src={Trash}
alt="Delete"
alt={t('convTile.delete')}
className="h-4 w-4 cursor-pointer opacity-60 hover:opacity-100"
id={`img-${index}`}
onClick={(event) => {
@@ -282,7 +283,7 @@ const Documents: React.FC<DocumentsProps> = ({
{document.syncFrequency && (
<div className="ml-2">
<DropdownMenu
name="Sync"
name={t('settings.documents.sync')}
options={syncOptions}
onSelect={(value: string) => {
handleManageSync(document, value);

View File

@@ -21,33 +21,15 @@ export default function General() {
t,
i18n: { changeLanguage, language },
} = useTranslation();
const themes = ['Light', 'Dark'];
const themes = [t('settings.general.light'), t('settings.general.dark')];
const languageOptions = [
{
label: 'English',
value: 'en',
},
{
label: 'Spanish',
value: 'es',
},
{
label: 'Japanese',
value: 'jp',
},
{
label: 'Mandarin',
value: 'zh',
},
{
label: 'Traditional Chinese',
value: 'zhTW',
},
{
label: 'Russian',
value: 'ru',
},
{ label: 'English', value: 'en' },
{ label: 'Español', value: 'es' },
{ label: '日本語', value: 'jp' },
{ label: '普通话', value: 'zh' },
{ label: '繁體中文(臺灣)', value: 'zhTW' },
{ label: 'Русский', value: 'ru' },
];
const chunks = ['0', '2', '4', '6', '8', '10'];
const token_limits = new Map([
@@ -99,9 +81,9 @@ export default function General() {
return (
<div className="mt-12">
<div className="mb-5">
<p className="font-bold text-jet dark:text-bright-gray">
<label className="block font-bold text-jet dark:text-bright-gray">
{t('settings.general.selectTheme')}
</p>
</label>
<Dropdown
options={themes}
selectedValue={selectedTheme}
@@ -115,9 +97,9 @@ export default function General() {
/>
</div>
<div className="mb-5">
<p className="mb-2 font-bold text-jet dark:text-bright-gray">
<label className="block mb-2 font-bold text-jet dark:text-bright-gray">
{t('settings.general.selectLanguage')}
</p>
</label>
<Dropdown
options={languageOptions.filter(
(languageOption) =>
@@ -133,9 +115,9 @@ export default function General() {
/>
</div>
<div className="mb-5">
<p className="font-bold text-jet dark:text-bright-gray">
<label className="block font-bold text-jet dark:text-bright-gray">
{t('settings.general.chunks')}
</p>
</label>
<Dropdown
options={chunks}
selectedValue={selectedChunks}
@@ -146,9 +128,9 @@ export default function General() {
/>
</div>
<div className="mb-5">
<p className="mb-2 font-bold text-jet dark:text-bright-gray">
<label className="mb-2 block font-bold text-jet dark:text-bright-gray">
{t('settings.general.convHistory')}
</p>
</label>
<Dropdown
options={Array.from(token_limits, ([value, desc]) => ({
value: value,
@@ -181,16 +163,14 @@ export default function General() {
/>
</div>
<div className="w-56">
<p className="font-bold text-jet dark:text-bright-gray">
<label className="block font-bold text-jet dark:text-bright-gray">
{t('settings.general.deleteAllLabel')}
</p>
</label>
<button
className="mt-2 flex w-full cursor-pointer items-center justify-between rounded-3xl border border-solid border-red-500 px-5 py-3 text-red-500 hover:bg-red-500 hover:text-white"
className="mt-2 flex w-full cursor-pointer items-center justify-between rounded-3xl border border-solid border-red-700 px-5 py-3 text-red-700 transition-colors hover:bg-red-700 hover:text-white dark:border-red-600 dark:text-red-600 dark:hover:bg-red-600 dark:hover:text-white"
onClick={() => dispatch(setModalStateDeleteConv('ACTIVE'))}
>
<span className="overflow-hidden text-ellipsis ">
{t('settings.general.deleteAllBtn')}
</span>
{t('settings.general.deleteAllBtn')}
</button>
</div>
</div>

View File

@@ -1,4 +1,5 @@
import React, { useState, useEffect, useRef, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
import userService from '../api/services/userService';
import ChevronRight from '../assets/chevron-right.svg';
@@ -8,6 +9,7 @@ import { APIKeyData, LogData } from './types';
import CoppyButton from '../components/CopyButton';
export default function Logs() {
const { t } = useTranslation();
const [chatbots, setChatbots] = useState<APIKeyData[]>([]);
const [selectedChatbot, setSelectedChatbot] = useState<APIKeyData | null>();
const [logs, setLogs] = useState<LogData[]>([]);
@@ -65,9 +67,12 @@ export default function Logs() {
<div className="mt-12">
<div className="flex flex-col items-start">
<div className="flex flex-col gap-3">
<p className="font-bold text-jet dark:text-bright-gray">
Filter by chatbot
</p>
<label
id="chatbot-filter-label"
className="font-bold text-jet dark:text-bright-gray"
>
{t('settings.logs.filterByChatbot')}
</label>
{loadingChatbots ? (
<SkeletonLoader />
) : (
@@ -78,9 +83,9 @@ export default function Logs() {
label: chatbot.name,
value: chatbot.id,
})),
{ label: 'None', value: '' },
{ label: t('settings.logs.none'), value: '' },
]}
placeholder="Select chatbot"
placeholder={t('settings.logs.selectChatbotPlaceholder')}
onSelect={(chatbot: { label: string; value: string }) => {
setSelectedChatbot(
chatbots.find((item) => item.id === chatbot.value),
@@ -120,6 +125,7 @@ type LogsTableProps = {
};
function LogsTable({ logs, setPage }: LogsTableProps) {
const { t } = useTranslation();
const observerRef = useRef<any>();
const firstObserver = useCallback((node: HTMLDivElement) => {
if (observerRef.current) {
@@ -134,7 +140,7 @@ function LogsTable({ logs, setPage }: LogsTableProps) {
<div className="logs-table border rounded-2xl h-[55vh] w-full overflow-hidden border-silver dark:border-silver/40">
<div className="h-8 bg-black/10 dark:bg-chinese-black flex flex-col items-start justify-center">
<p className="px-3 text-xs dark:text-gray-6000">
API generated / chatbot conversations
{t('settings.logs.apiGeneratedConversations')}
</p>
</div>
<div
@@ -156,6 +162,7 @@ function LogsTable({ logs, setPage }: LogsTableProps) {
}
function Log({ log }: { log: LogData }) {
const { t } = useTranslation();
const logLevelColor = {
info: 'text-green-500',
error: 'text-red-500',

View File

@@ -168,7 +168,7 @@ export default function Prompts({
/>
</div>
<button
className="mt-[24px] rounded-3xl border border-solid border-purple-30 px-5 py-3 text-purple-30 hover:bg-purple-30 hover:text-white"
className="mt-[24px] rounded-3xl border border-solid border-purple-700 px-5 py-3 text-purple-700 transition-colors hover:bg-purple-700 hover:text-white dark:border-purple-400 dark:text-purple-400 dark:hover:bg-purple-400 dark:hover:text-white"
onClick={() => {
setModalType('ADD');
setModalState('ACTIVE');

View File

@@ -1,4 +1,5 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import userService from '../api/services/userService';
import CogwheelIcon from '../assets/cogwheel.svg';
@@ -18,6 +19,7 @@ export default function Tools() {
React.useState<ActiveState>('INACTIVE');
const [userTools, setUserTools] = React.useState<UserTool[]>([]);
const [selectedTool, setSelectedTool] = React.useState<UserTool | null>(null);
const { t } = useTranslation();
const getUserTools = () => {
userService
@@ -70,12 +72,15 @@ export default function Tools() {
<div className="flex flex-col relative">
<div className="my-3 flex justify-between items-center gap-1">
<div className="p-1">
<label htmlFor="tool-search-input" className="sr-only">
{t('settings.tools.searchPlaceholder')}
</label>
<Input
maxLength={256}
placeholder="Search..."
name="Document-search-input"
placeholder={t('settings.tools.searchPlaceholder')}
name="tool-search-input"
type="text"
id="document-search-input"
id="tool-search-input"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
/>
@@ -86,7 +91,7 @@ export default function Tools() {
setAddToolModalState('ACTIVE');
}}
>
Add Tool
{t('settings.tools.addTool')}
</button>
</div>
<div className="grid grid-cols-2 lg:grid-cols-3 gap-6">
@@ -98,10 +103,10 @@ export default function Tools() {
<div className="mt-24 col-span-2 lg:col-span-3 text-center text-gray-500 dark:text-gray-400">
<img
src={isDarkTheme ? NoFilesDarkIcon : NoFilesIcon}
alt="No tools found"
alt={t('settings.tools.noToolsFound')}
className="h-24 w-24 mx-auto mb-2"
/>
No tools found
{t('settings.tools.noToolsFound')}
</div>
) : (
userTools
@@ -119,15 +124,19 @@ export default function Tools() {
<div className="w-full flex items-center justify-between">
<img
src={`/toolIcons/tool_${tool.name}.svg`}
alt={`${tool.displayName} icon`}
className="h-8 w-8"
/>
<button
className="absolute top-3 right-3 cursor-pointer"
onClick={() => handleSettingsClick(tool)}
aria-label={t('settings.tools.configureToolAria', {
toolName: tool.displayName,
})}
>
<img
src={CogwheelIcon}
alt="settings"
alt={t('settings.tools.settingsIconAlt')}
className="h-[19px] w-[19px]"
/>
</button>
@@ -146,6 +155,11 @@ export default function Tools() {
htmlFor={`toolToggle-${index}`}
className="relative inline-block h-6 w-10 cursor-pointer rounded-full bg-gray-300 dark:bg-[#D2D5DA33]/20 transition [-webkit-tap-highlight-color:_transparent] has-[:checked]:bg-[#0C9D35CC] has-[:checked]:dark:bg-[#0C9D35CC]"
>
<span className="sr-only">
{t('settings.tools.toggleToolAria', {
toolName: tool.displayName,
})}
</span>
<input
type="checkbox"
id={`toolToggle-${index}`}
@@ -164,7 +178,7 @@ export default function Tools() {
</div>
</div>
<AddToolModal
message="Select a tool to set up"
message={t('settings.tools.selectToolSetup')}
modalState={addToolModalState}
setModalState={setAddToolModalState}
getUserTools={getUserTools}

View File

@@ -91,8 +91,8 @@ export default function Settings() {
case 'Widgets':
return (
<Widgets
widgetScreenshot={widgetScreenshot} // Add this line
onWidgetScreenshotChange={updateWidgetScreenshot} // Add this line
widgetScreenshot={widgetScreenshot}
onWidgetScreenshotChange={updateWidgetScreenshot}
/>
);
case t('settings.apiKeys.label'):