mirror of
https://github.com/arc53/DocsGPT
synced 2024-11-17 21:26:26 +00:00
Merge pull request #969 from ManishMadan2882/main
Internationalisation with i18next
This commit is contained in:
commit
ca4881ad51
169
docs/package-lock.json
generated
169
docs/package-lock.json
generated
@ -4266,69 +4266,6 @@
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz",
|
||||
"integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"boolbase": "^1.0.0",
|
||||
"css-what": "^6.1.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"domutils": "^3.0.1",
|
||||
"nth-check": "^2.0.1"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select/node_modules/dom-serializer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
|
||||
"integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.2",
|
||||
"entities": "^4.2.0"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select/node_modules/domhandler": {
|
||||
"version": "5.0.3",
|
||||
"resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz",
|
||||
"integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"domelementtype": "^2.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domhandler?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/css-select/node_modules/domutils": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.1.0.tgz",
|
||||
"integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"dom-serializer": "^2.0.0",
|
||||
"domelementtype": "^2.3.0",
|
||||
"domhandler": "^5.0.3"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/fb55/domutils?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/css-to-react-native": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz",
|
||||
@ -4339,20 +4276,6 @@
|
||||
"postcss-value-parser": "^4.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/css-tree": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
|
||||
"integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mdn-data": "2.0.30",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/css-what": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz",
|
||||
@ -4364,42 +4287,6 @@
|
||||
"url": "https://github.com/sponsors/fb55"
|
||||
}
|
||||
},
|
||||
"node_modules/csso": {
|
||||
"version": "5.0.5",
|
||||
"resolved": "https://registry.npmjs.org/csso/-/csso-5.0.5.tgz",
|
||||
"integrity": "sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"css-tree": "~2.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/csso/node_modules/css-tree": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.2.1.tgz",
|
||||
"integrity": "sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"mdn-data": "2.0.28",
|
||||
"source-map-js": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0",
|
||||
"npm": ">=7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/csso/node_modules/mdn-data": {
|
||||
"version": "2.0.28",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.28.tgz",
|
||||
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/csstype": {
|
||||
"version": "3.1.3",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
|
||||
@ -7078,13 +6965,6 @@
|
||||
"url": "https://opencollective.com/unified"
|
||||
}
|
||||
},
|
||||
"node_modules/mdn-data": {
|
||||
"version": "2.0.30",
|
||||
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
|
||||
"integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
|
||||
"optional": true,
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/mermaid": {
|
||||
"version": "10.6.1",
|
||||
"resolved": "https://registry.npmjs.org/mermaid/-/mermaid-10.6.1.tgz",
|
||||
@ -11728,42 +11608,6 @@
|
||||
"resolved": "https://registry.npmjs.org/svg-parser/-/svg-parser-2.0.4.tgz",
|
||||
"integrity": "sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ=="
|
||||
},
|
||||
"node_modules/svgo": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/svgo/-/svgo-3.2.0.tgz",
|
||||
"integrity": "sha512-4PP6CMW/V7l/GmKRKzsLR8xxjdHTV4IMvhTnpuHwwBazSIlw5W/5SmPjN8Dwyt7lKbSJrRDgp4t9ph0HgChFBQ==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@trysound/sax": "0.2.0",
|
||||
"commander": "^7.2.0",
|
||||
"css-select": "^5.1.0",
|
||||
"css-tree": "^2.3.1",
|
||||
"css-what": "^6.1.0",
|
||||
"csso": "^5.0.5",
|
||||
"picocolors": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"svgo": "bin/svgo"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/svgo"
|
||||
}
|
||||
},
|
||||
"node_modules/svgo/node_modules/commander": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
|
||||
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
|
||||
"optional": true,
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/term-size": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/term-size/-/term-size-2.2.1.tgz",
|
||||
@ -11868,19 +11712,6 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/typescript": {
|
||||
"version": "5.3.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz",
|
||||
"integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/unified": {
|
||||
"version": "10.1.2",
|
||||
"resolved": "https://registry.npmjs.org/unified/-/unified-10.1.2.tgz",
|
||||
|
83
frontend/package-lock.json
generated
83
frontend/package-lock.json
generated
@ -10,10 +10,12 @@
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^1.9.2",
|
||||
"@vercel/analytics": "^0.1.10",
|
||||
"i18next": "^23.11.5",
|
||||
"react": "^18.2.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-i18next": "^14.1.2",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-router-dom": "^6.8.1",
|
||||
@ -354,11 +356,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.20.13",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.13.tgz",
|
||||
"integrity": "sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==",
|
||||
"version": "7.24.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.6.tgz",
|
||||
"integrity": "sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@ -1485,7 +1488,7 @@
|
||||
"version": "18.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.0.10.tgz",
|
||||
"integrity": "sha512-E42GW/JA4Qv15wQdqJq8DL4JhNpB3prJgjgapN3qJT9K2zO5IIAQh4VXvCEDupoqAwnz0cY4RlXeC/ajX5SFHg==",
|
||||
"devOptional": true,
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
@ -4134,6 +4137,15 @@
|
||||
"react-is": "^16.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/html-parse-stringify": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz",
|
||||
"integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"void-elements": "3.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/human-signals": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-3.0.1.tgz",
|
||||
@ -4158,6 +4170,29 @@
|
||||
"url": "https://github.com/sponsors/typicode"
|
||||
}
|
||||
},
|
||||
"node_modules/i18next": {
|
||||
"version": "23.11.5",
|
||||
"resolved": "https://registry.npmjs.org/i18next/-/i18next-23.11.5.tgz",
|
||||
"integrity": "sha512-41pvpVbW9rhZPk5xjCX2TPJi2861LEig/YRhUkY+1FQ2IQPS0bKUDYnEqY8XPPbB48h1uIwLnP9iiEfuSl20CA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://locize.com"
|
||||
},
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://locize.com/i18next.html"
|
||||
},
|
||||
{
|
||||
"type": "individual",
|
||||
"url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.2"
|
||||
}
|
||||
},
|
||||
"node_modules/ignore": {
|
||||
"version": "5.2.4",
|
||||
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
||||
@ -6678,6 +6713,28 @@
|
||||
"react": ">= 16.8 || 18.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-i18next": {
|
||||
"version": "14.1.2",
|
||||
"resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-14.1.2.tgz",
|
||||
"integrity": "sha512-FSIcJy6oauJbGEXfhUgVeLzvWBhIBIS+/9c6Lj4niwKZyGaGb4V4vUbATXSlsHJDXXB+ociNxqFNiFuV1gmoqg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.23.9",
|
||||
"html-parse-stringify": "^3.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"i18next": ">= 23.2.3",
|
||||
"react": ">= 16.8.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
},
|
||||
"react-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "16.13.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||
@ -6875,9 +6932,10 @@
|
||||
}
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
|
||||
"version": "0.14.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
|
||||
"integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/regexp.prototype.flags": {
|
||||
"version": "1.4.3",
|
||||
@ -7923,6 +7981,15 @@
|
||||
"vite": "^2.6.0 || 3 || 4 || 5"
|
||||
}
|
||||
},
|
||||
"node_modules/void-elements": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
|
||||
"integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/which": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
||||
|
@ -21,10 +21,12 @@
|
||||
"dependencies": {
|
||||
"@reduxjs/toolkit": "^1.9.2",
|
||||
"@vercel/analytics": "^0.1.10",
|
||||
"i18next": "^23.11.5",
|
||||
"react": "^18.2.0",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-dropzone": "^14.2.3",
|
||||
"react-i18next": "^14.1.2",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-redux": "^8.0.5",
|
||||
"react-router-dom": "^6.8.1",
|
||||
@ -58,5 +60,4 @@
|
||||
"vite": "^5.0.13",
|
||||
"vite-plugin-svgr": "^4.2.0"
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import { inject } from '@vercel/analytics';
|
||||
import { useMediaQuery } from './hooks';
|
||||
import { useState } from 'react';
|
||||
import Setting from './settings';
|
||||
import './locale/i18n';
|
||||
|
||||
inject();
|
||||
|
||||
|
@ -1,28 +1,16 @@
|
||||
import { Fragment } from 'react';
|
||||
import DocsGPT3 from './assets/cute_docsgpt3.svg';
|
||||
const demos: { header: string; query: string }[] = [
|
||||
{
|
||||
header: 'Learn about DocsGPT',
|
||||
query: 'What is DocsGPT?',
|
||||
},
|
||||
{
|
||||
header: 'Summarise documentation',
|
||||
query: 'Summarise current context',
|
||||
},
|
||||
{
|
||||
header: 'Write Code',
|
||||
query: 'Write code for api request to /api/answer',
|
||||
},
|
||||
{
|
||||
header: 'Learning Assistance',
|
||||
query: 'Write potential questions for context',
|
||||
},
|
||||
];
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
export default function Hero({
|
||||
handleQuestion,
|
||||
}: {
|
||||
handleQuestion: (question: string) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const demos = t('demo', { returnObjects: true }) as Array<{
|
||||
header: string;
|
||||
query: string;
|
||||
}>;
|
||||
return (
|
||||
<div
|
||||
className={`mt-14 mb-4 flex w-full flex-col justify-end text-black-1000 dark:text-bright-gray sm:w-full lg:mt-6`}
|
||||
@ -36,19 +24,23 @@ export default function Hero({
|
||||
<div className="mb-4 flex flex-col items-center justify-center dark:text-white"></div>
|
||||
</div>
|
||||
<div className="grid w-full grid-cols-1 items-center gap-4 self-center text-xs sm:w-auto sm:gap-6 md:text-sm lg:grid-cols-2">
|
||||
{demos.map((demo) => (
|
||||
<>
|
||||
<button
|
||||
onClick={() => handleQuestion(demo.query)}
|
||||
className="w-full rounded-full border-2 border-silver px-6 py-4 text-left hover:border-gray-4000 dark:hover:border-gray-3000 xl:min-w-[24vw]"
|
||||
>
|
||||
<p className="mb-1 font-semibold text-black dark:text-silver">
|
||||
{demo.header}
|
||||
</p>
|
||||
<span className="text-gray-400">{demo.query}</span>
|
||||
</button>
|
||||
</>
|
||||
))}
|
||||
{demos?.map(
|
||||
(demo: { header: string; query: string }, key: number) =>
|
||||
demo.header &&
|
||||
demo.query && (
|
||||
<Fragment key={key}>
|
||||
<button
|
||||
onClick={() => handleQuestion(demo.query)}
|
||||
className="w-full rounded-full border-2 border-silver px-6 py-4 text-left hover:border-gray-4000 dark:hover:border-gray-3000 xl:min-w-[24vw]"
|
||||
>
|
||||
<p className="mb-1 font-semibold text-black dark:text-silver">
|
||||
{demo.header}
|
||||
</p>
|
||||
<span className="text-gray-400">{demo.query}</span>
|
||||
</button>
|
||||
</Fragment>
|
||||
),
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -39,7 +39,7 @@ import SelectDocsModal from './preferences/SelectDocsModal';
|
||||
import ConversationTile from './conversation/ConversationTile';
|
||||
import { useDarkTheme } from './hooks';
|
||||
import SourceDropdown from './components/SourceDropdown';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
interface NavigationProps {
|
||||
navOpen: boolean;
|
||||
setNavOpen: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
@ -70,6 +70,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
const { isMobile } = useMediaQuery();
|
||||
const [isDarkTheme] = useDarkTheme();
|
||||
const [isDocsListOpen, setIsDocsListOpen] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const isApiKeySet = useSelector(selectApiKeyStatus);
|
||||
const [apiKeyModalState, setApiKeyModalState] =
|
||||
@ -265,14 +266,14 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
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">
|
||||
New Chat
|
||||
{t('newChat')}
|
||||
</p>
|
||||
</NavLink>
|
||||
<div className="mb-auto h-[78vh] overflow-y-auto overflow-x-hidden dark:text-white">
|
||||
{conversations && conversations.length > 0 ? (
|
||||
<div>
|
||||
<div className=" my-auto mx-4 mt-2 flex h-6 items-center justify-between gap-4 rounded-3xl">
|
||||
<p className="mt-1 ml-4 text-sm font-semibold">Chats</p>
|
||||
<p className="mt-1 ml-4 text-sm font-semibold">{t('chats')}</p>
|
||||
</div>
|
||||
<div className="conversations-container">
|
||||
{conversations?.map((conversation) => (
|
||||
@ -310,7 +311,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
onClick={() => setUploadModalState('ACTIVE')}
|
||||
></img>
|
||||
</div>
|
||||
<p className="ml-5 mt-3 text-sm font-semibold">Source Docs</p>
|
||||
<p className="ml-5 mt-3 text-sm font-semibold">{t('sourceDocs')}</p>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2 border-b-[1px] py-2 dark:border-b-purple-taupe">
|
||||
<NavLink
|
||||
@ -327,7 +328,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
className="ml-2 w-5 filter dark:invert"
|
||||
/>
|
||||
<p className="my-auto text-sm text-eerie-black dark:text-white">
|
||||
Settings
|
||||
{t('settings.label')}
|
||||
</p>
|
||||
</NavLink>
|
||||
</div>
|
||||
@ -345,7 +346,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
|
||||
alt="icon"
|
||||
className="ml-2 w-5 filter dark:invert"
|
||||
/>
|
||||
<p className="my-auto mr-2 text-sm">About</p>
|
||||
<p className="my-auto pr-1 text-sm">{t('about')}</p>
|
||||
</NavLink>
|
||||
<div className="flex items-center justify-evenly gap-1 px-1">
|
||||
<NavLink
|
||||
|
@ -2,7 +2,7 @@ import Trash from '../assets/trash.svg';
|
||||
import Arrow2 from '../assets/dropdown-arrow.svg';
|
||||
import { Doc } from '../preferences/preferenceApi';
|
||||
import { useDispatch } from 'react-redux';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
type Props = {
|
||||
options: Doc[] | null;
|
||||
selectedDocs: Doc | null;
|
||||
@ -30,6 +30,8 @@ function SourceDropdown({
|
||||
setIsDocsListOpen(false);
|
||||
};
|
||||
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<div className="relative w-5/6 rounded-3xl">
|
||||
<button
|
||||
@ -104,7 +106,7 @@ function SourceDropdown({
|
||||
onClick={handleEmptyDocumentSelect}
|
||||
>
|
||||
<span className="ml-4 flex-1 overflow-hidden overflow-ellipsis whitespace-nowrap py-3">
|
||||
None
|
||||
{t('none')}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -14,9 +14,10 @@ import {
|
||||
import Send from './../assets/send.svg';
|
||||
import SendDark from './../assets/send_dark.svg';
|
||||
import Spinner from './../assets/spinner.svg';
|
||||
import SpinnerDark from './../assets/spinner-dark.svg'
|
||||
import SpinnerDark from './../assets/spinner-dark.svg';
|
||||
import { FEEDBACK, Query } from './conversationModels';
|
||||
import { sendFeedback } from './conversationApi';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ArrowDown from './../assets/arrow-down.svg';
|
||||
export default function Conversation() {
|
||||
const queries = useSelector(selectQueries);
|
||||
@ -28,6 +29,7 @@ export default function Conversation() {
|
||||
const [hasScrolledToLast, setHasScrolledToLast] = useState(true);
|
||||
const fetchStream = useRef<any>(null);
|
||||
const [eventInterrupt, setEventInterrupt] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
|
||||
const handleUserInterruption = () => {
|
||||
if (!eventInterrupt && status === 'loading') setEventInterrupt(true);
|
||||
@ -177,7 +179,7 @@ export default function Conversation() {
|
||||
id="inputbox"
|
||||
ref={inputRef}
|
||||
tabIndex={1}
|
||||
placeholder="Type your message here..."
|
||||
placeholder={t('inputPlaceholder')}
|
||||
contentEditable
|
||||
onPaste={handlePaste}
|
||||
className={`max-h-24 min-h-[3.8rem] w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-full bg-white py-2 pl-4 pr-9 text-base leading-10 opacity-100 focus:outline-none dark:bg-raisin-black dark:text-bright-gray`}
|
||||
@ -212,7 +214,7 @@ export default function Conversation() {
|
||||
)}
|
||||
</div>
|
||||
<p className="text-gray-595959 hidden w-[100vw] self-center bg-white bg-transparent p-5 text-center text-xs dark:bg-raisin-black dark:text-bright-gray md:inline md:w-full">
|
||||
DocsGPT uses GenAI, please review critial information using sources.
|
||||
{t('tagline')}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
100
frontend/src/locale/en.json
Normal file
100
frontend/src/locale/en.json
Normal file
@ -0,0 +1,100 @@
|
||||
{
|
||||
"language": "English",
|
||||
"chat": "Chat",
|
||||
"newChat": "New Chat",
|
||||
"myPlan": "My Plan",
|
||||
"about": "About",
|
||||
"inputPlaceholder": "Type your message here...",
|
||||
"tagline": "DocsGPT uses GenAI, please review critial information using sources.",
|
||||
"sourceDocs": "Source Docs",
|
||||
"none": "None",
|
||||
"cancel":"Cancel",
|
||||
"demo": [
|
||||
{
|
||||
"header": "Learn about DocsGPT",
|
||||
"query": "What is DocsGPT?"
|
||||
},
|
||||
{
|
||||
"header": "Summarise documentation",
|
||||
"query": "Summarise current context"
|
||||
},
|
||||
{
|
||||
"header": "Write Code",
|
||||
"query": "Write code for api request to /api/answer"
|
||||
},
|
||||
{
|
||||
"header": "Learning Assistance",
|
||||
"query": "Write potential questions for context"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"label": "Settings",
|
||||
"general": {
|
||||
"label": "General",
|
||||
"selectTheme": "Select Theme",
|
||||
"light": "Light",
|
||||
"dark": "Dark",
|
||||
"selectLanguage": "Select Language",
|
||||
"chunks": "Chunks processed per query",
|
||||
"prompt": "Active Prompt",
|
||||
"deleteAllLabel": "Delete all Conversation",
|
||||
"deleteAllBtn": "Delete all",
|
||||
"addNew": "Add New"
|
||||
},
|
||||
"documents": {
|
||||
"label": "Documents",
|
||||
"name": "Document Name",
|
||||
"date": "Vector Date",
|
||||
"type": "Type",
|
||||
"tokenUsage": "Token Usage"
|
||||
},
|
||||
"apiKeys": {
|
||||
"label": "API Keys",
|
||||
"name": "Name",
|
||||
"key": "API Key",
|
||||
"sourceDoc": "Source Document",
|
||||
"createNew": "Create New"
|
||||
}
|
||||
},
|
||||
"modals": {
|
||||
"uploadDoc": {
|
||||
"label": "Upload New Documentation",
|
||||
"file": "From File",
|
||||
"remote": "Remote",
|
||||
"name": "Name",
|
||||
"choose": "Choose Files",
|
||||
"info": "Please upload .pdf, .txt, .rst, .docx, .md, .zip limited to 25mb",
|
||||
"uploadedFiles": "Uploaded Files",
|
||||
"cancel": "Cancel",
|
||||
"train": "Train",
|
||||
"link": "Link",
|
||||
"urlLink": "URL Link",
|
||||
"reddit": {
|
||||
"id": "Client ID",
|
||||
"secret": "Client Secret",
|
||||
"agent": "User agent",
|
||||
"searchQueries": "Search queries",
|
||||
"numberOfPosts": "Number of posts"
|
||||
}
|
||||
},
|
||||
"createAPIKey": {
|
||||
"label": "Create New API Key",
|
||||
"apiKeyName": "API Key Name",
|
||||
"chunks": "Chunks processed per query",
|
||||
"prompt": "Select active prompt",
|
||||
"sourceDoc": "Source document",
|
||||
"create": "Create"
|
||||
},
|
||||
"saveKey": {
|
||||
"note": "Please save your Key",
|
||||
"disclaimer": "This is the only time your key will be shown.",
|
||||
"copy": "Copy",
|
||||
"copied": "Copied",
|
||||
"confirm": "I saved the Key"
|
||||
},
|
||||
"deleteConv": {
|
||||
"confirm": "Are you sure you want to delete all the conversations?",
|
||||
"delete": "Delete"
|
||||
}
|
||||
}
|
||||
}
|
100
frontend/src/locale/es.json
Normal file
100
frontend/src/locale/es.json
Normal file
@ -0,0 +1,100 @@
|
||||
{
|
||||
"language": "Spanish",
|
||||
"chat": "Chat",
|
||||
"newChat": "Nuevo Chat",
|
||||
"myPlan": "Mi Plan",
|
||||
"about": "Acerca de",
|
||||
"inputPlaceholder": "Escribe tu mensaje aquí...",
|
||||
"tagline": "DocsGPT utiliza GenAI, por favor revisa información crítica utilizando fuentes.",
|
||||
"sourceDocs": "Documentos Fuente",
|
||||
"none": "Nada",
|
||||
"cancel": "Cancelar",
|
||||
"demo": [
|
||||
{
|
||||
"header": "Aprende sobre DocsGPT",
|
||||
"query": "¿Qué es DocsGPT?"
|
||||
},
|
||||
{
|
||||
"header": "Resumir documentación",
|
||||
"query": "Resumir contexto actual"
|
||||
},
|
||||
{
|
||||
"header": "Escribir Código",
|
||||
"query": "Escribir código para solicitud de API a /api/answer"
|
||||
},
|
||||
{
|
||||
"header": "Asistencia de Aprendizaje",
|
||||
"query": "Escribe posibles preguntas para el contexto"
|
||||
}
|
||||
],
|
||||
"settings": {
|
||||
"label": "Configuración",
|
||||
"general": {
|
||||
"label": "General",
|
||||
"selectTheme": "Seleccionar Tema",
|
||||
"light": "de luz",
|
||||
"dark": "oscura",
|
||||
"selectLanguage": "Seleccionar Idioma",
|
||||
"chunks": "Trozos procesados por consulta",
|
||||
"prompt": "Prompt Activo",
|
||||
"deleteAllLabel": "Eliminar toda la Conversación",
|
||||
"deleteAllBtn": "Eliminar todo",
|
||||
"addNew": "Agregar Nuevo"
|
||||
},
|
||||
"documents": {
|
||||
"label": "Documentos",
|
||||
"name": "Nombre del Documento",
|
||||
"date": "Fecha Vector",
|
||||
"type": "Tipo",
|
||||
"tokenUsage": "Uso de Tokens"
|
||||
},
|
||||
"apiKeys": {
|
||||
"label": "Claves API",
|
||||
"name": "Nombre",
|
||||
"key": "Clave de API",
|
||||
"sourceDoc": "Documento Fuente",
|
||||
"createNew": "Crear Nuevo"
|
||||
}
|
||||
},
|
||||
"modals": {
|
||||
"uploadDoc": {
|
||||
"label": "Subir Nueva Documentación",
|
||||
"file": "Desde Archivo",
|
||||
"remote": "Remota",
|
||||
"name": "Nombre",
|
||||
"choose": "Seleccionar Archivos",
|
||||
"info": "Por favor, suba archivos .pdf, .txt, .rst, .docx, .md, .zip limitados a 25 MB",
|
||||
"uploadedFiles": "Archivos Subidos",
|
||||
"cancel": "Cancelar",
|
||||
"train": "Entrenar",
|
||||
"link": "Enlace",
|
||||
"urlLink": "Enlace URL",
|
||||
"reddit": {
|
||||
"id": "ID de Cliente",
|
||||
"secret": "Secreto de Cliente",
|
||||
"agent": "Agente de Usuario",
|
||||
"searchQueries": "Consultas de Búsqueda",
|
||||
"numberOfPosts": "Número de publicaciones"
|
||||
}
|
||||
},
|
||||
"createAPIKey": {
|
||||
"label": "Crear Nueva Clave de API",
|
||||
"apiKeyName": "Nombre de la Clave de API",
|
||||
"chunks": "Fragmentos procesados por consulta",
|
||||
"prompt": "Seleccione 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.",
|
||||
"copy": "Copiar",
|
||||
"copied": "Copiado",
|
||||
"confirm": "He guardado la Clave"
|
||||
},
|
||||
"deleteConv": {
|
||||
"confirm": "¿Está seguro de que desea eliminar todas las conversaciones?",
|
||||
"delete": "Eliminar"
|
||||
}
|
||||
}
|
||||
}
|
21
frontend/src/locale/i18n.ts
Normal file
21
frontend/src/locale/i18n.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import i18n from 'i18next';
|
||||
import { initReactI18next } from 'react-i18next';
|
||||
|
||||
import en from './en.json'; //English
|
||||
import es from './es.json'; //Spanish
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
resources: {
|
||||
en: {
|
||||
translation: en,
|
||||
},
|
||||
es: {
|
||||
translation: es,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const locale = localStorage.getItem('docsgpt-locale') ?? 'en';
|
||||
i18n.changeLanguage(locale);
|
||||
|
||||
export default i18n;
|
@ -1,6 +1,6 @@
|
||||
import Exit from '../assets/exit.svg';
|
||||
import { ActiveState } from '../models/misc';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
function ConfirmationModal({
|
||||
message,
|
||||
modalState,
|
||||
@ -18,6 +18,7 @@ function ConfirmationModal({
|
||||
cancelLabel?: string;
|
||||
handleCancel?: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<article
|
||||
className={`${
|
||||
@ -54,7 +55,7 @@ function ConfirmationModal({
|
||||
}}
|
||||
className="cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium hover:bg-gray-100 dark:bg-transparent dark:text-light-gray dark:hover:bg-[#767183]/50"
|
||||
>
|
||||
{cancelLabel ? cancelLabel : 'Cancel'}
|
||||
{cancelLabel ? cancelLabel : t('cancel')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -3,7 +3,7 @@ import { useDispatch } from 'react-redux';
|
||||
import { ActiveState } from '../models/misc';
|
||||
import { useMediaQuery, useOutsideAlerter } from '../hooks';
|
||||
import ConfirmationModal from './ConfirmationModal';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Action } from '@reduxjs/toolkit';
|
||||
|
||||
export default function DeleteConvModal({
|
||||
@ -18,7 +18,7 @@ export default function DeleteConvModal({
|
||||
const modalRef = React.useRef(null);
|
||||
const dispatch = useDispatch();
|
||||
const { isMobile } = useMediaQuery();
|
||||
|
||||
const { t } = useTranslation();
|
||||
useOutsideAlerter(
|
||||
modalRef,
|
||||
() => {
|
||||
@ -40,10 +40,10 @@ export default function DeleteConvModal({
|
||||
|
||||
return (
|
||||
<ConfirmationModal
|
||||
message="Are you sure you want to delete all the conversations?"
|
||||
message={t('modals.deleteConv.confirm')}
|
||||
modalState={modalState}
|
||||
setModalState={setModalState}
|
||||
submitLabel={'Delete'}
|
||||
submitLabel={t('modals.deleteConv.delete')}
|
||||
handleSubmit={handleSubmit}
|
||||
handleCancel={handleCancel}
|
||||
/>
|
||||
|
@ -1,5 +1,5 @@
|
||||
import * as React from 'react';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
interface ModalProps {
|
||||
handleSubmit: () => void;
|
||||
isCancellable: boolean;
|
||||
@ -12,6 +12,7 @@ interface ModalProps {
|
||||
}
|
||||
|
||||
const Modal = (props: ModalProps) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div
|
||||
className={`${
|
||||
@ -19,11 +20,11 @@ const Modal = (props: ModalProps) => {
|
||||
} absolute z-30 h-screen w-screen bg-gray-alpha`}
|
||||
>
|
||||
{props.render()}
|
||||
<div className=" mx-auto flex w-[90vw] max-w-lg flex-row-reverse rounded-b-lg bg-white dark:bg-outer-space pb-5 pr-5 shadow-lg">
|
||||
<div className=" mx-auto flex w-[90vw] max-w-lg flex-row-reverse rounded-b-lg bg-white pb-5 pr-5 shadow-lg dark:bg-outer-space">
|
||||
<div>
|
||||
<button
|
||||
onClick={() => props.handleSubmit()}
|
||||
className="ml-auto h-10 w-20 rounded-3xl bg-violet-800 text-white dark:text-silver transition-all hover:bg-violet-700"
|
||||
className="ml-auto h-10 w-20 rounded-3xl bg-violet-800 text-white transition-all hover:bg-violet-700 dark:text-silver"
|
||||
>
|
||||
{props.textDelete ? 'Delete' : 'Save'}
|
||||
</button>
|
||||
@ -32,7 +33,7 @@ const Modal = (props: ModalProps) => {
|
||||
onClick={() => props.handleCancel && props.handleCancel()}
|
||||
className="cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium hover:bg-gray-100 dark:bg-transparent dark:text-light-gray dark:hover:bg-[#767183]/50"
|
||||
>
|
||||
Cancel
|
||||
{t('cancel')}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
@ -9,13 +9,14 @@ import {
|
||||
import { selectSourceDocs } from '../preferences/preferenceSlice';
|
||||
import Exit from '../assets/exit.svg';
|
||||
import Trash from '../assets/trash.svg';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
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 APIKeys: React.FC = () => {
|
||||
const { t } = useTranslation();
|
||||
const [isCreateModalOpen, setCreateModal] = React.useState(false);
|
||||
const [isSaveKeyModalOpen, setSaveKeyModal] = React.useState(false);
|
||||
const [newKey, setNewKey] = React.useState('');
|
||||
@ -97,7 +98,7 @@ const APIKeys: React.FC = () => {
|
||||
onClick={() => setCreateModal(true)}
|
||||
className="rounded-full bg-purple-30 px-4 py-3 text-white hover:bg-[#6F3FD1]"
|
||||
>
|
||||
Create new
|
||||
{t('settings.apiKeys.createNew')}
|
||||
</button>
|
||||
</div>
|
||||
{isCreateModalOpen && (
|
||||
@ -117,11 +118,15 @@ const APIKeys: React.FC = () => {
|
||||
<table className="block w-max table-auto content-center justify-center rounded-xl border text-center dark:border-chinese-silver dark:text-bright-gray">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border-r p-4 md:w-[244px]">Name</th>
|
||||
<th className="w-[244px] border-r px-4 py-2">
|
||||
Source document
|
||||
<th className="border-r p-4 md:w-[244px]">
|
||||
{t('settings.apiKeys.name')}
|
||||
</th>
|
||||
<th className="w-[244px] border-r px-4 py-2">
|
||||
{t('settings.apiKeys.sourceDoc')}
|
||||
</th>
|
||||
<th className="w-[244px] border-r px-4 py-2">
|
||||
{t('settings.apiKeys.key')}
|
||||
</th>
|
||||
<th className="w-[244px] border-r px-4 py-2">API Key</th>
|
||||
<th className="px-4 py-2"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -216,7 +221,7 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
|
||||
};
|
||||
})
|
||||
: [];
|
||||
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="fixed top-0 left-0 z-30 flex h-screen w-screen items-center justify-center bg-gray-alpha bg-opacity-50">
|
||||
<div className="relative w-11/12 rounded-2xl bg-white p-10 dark:bg-outer-space sm:w-[512px]">
|
||||
@ -225,12 +230,12 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
|
||||
</button>
|
||||
<div className="mb-6">
|
||||
<span className="text-xl text-jet dark:text-bright-gray">
|
||||
Create New API Key
|
||||
{t('modals.createAPIKey.label')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="relative mt-5 mb-4">
|
||||
<span className="absolute left-2 -top-2 bg-white px-2 text-xs text-gray-4000 dark:bg-outer-space dark:text-silver">
|
||||
API Key Name
|
||||
{t('modals.createAPIKey.apiKeyName')}
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
@ -241,7 +246,7 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
|
||||
</div>
|
||||
<div className="my-4">
|
||||
<Dropdown
|
||||
placeholder="Source document"
|
||||
placeholder={t('modals.createAPIKey.sourceDoc')}
|
||||
selectedValue={sourcePath}
|
||||
onSelect={(selection: { label: string; value: string }) =>
|
||||
setSourcePath(selection)
|
||||
@ -255,7 +260,7 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
|
||||
<Dropdown
|
||||
options={activePrompts}
|
||||
selectedValue={prompt ? prompt.name : null}
|
||||
placeholder="Select active prompt"
|
||||
placeholder={t('modals.createAPIKey.prompt')}
|
||||
onSelect={(value: { name: string; id: string; type: string }) =>
|
||||
setPrompt(value)
|
||||
}
|
||||
@ -264,7 +269,7 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
|
||||
</div>
|
||||
<div className="my-4">
|
||||
<p className="mb-2 ml-2 font-bold text-jet dark:text-bright-gray">
|
||||
Chunks processed per query
|
||||
{t('modals.createAPIKey.chunks')}
|
||||
</p>
|
||||
<Dropdown
|
||||
options={chunkOptions}
|
||||
@ -287,7 +292,7 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
|
||||
}
|
||||
className="float-right mt-4 rounded-full bg-purple-30 px-5 py-2 text-sm text-white hover:bg-[#6F3FD1] disabled:opacity-50"
|
||||
>
|
||||
Create
|
||||
{t('modals.createAPIKey.create')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -296,6 +301,7 @@ const CreateAPIKeyModal: React.FC<CreateAPIKeyModalProps> = ({
|
||||
|
||||
const SaveAPIKeyModal: React.FC<SaveAPIKeyModalProps> = ({ apiKey, close }) => {
|
||||
const [isCopied, setIsCopied] = React.useState(false);
|
||||
const { t } = useTranslation();
|
||||
const handleCopyKey = () => {
|
||||
navigator.clipboard.writeText(apiKey);
|
||||
setIsCopied(true);
|
||||
@ -306,9 +312,12 @@ const SaveAPIKeyModal: React.FC<SaveAPIKeyModalProps> = ({ apiKey, close }) => {
|
||||
<button className="absolute top-3 right-4 m-2 w-3" onClick={close}>
|
||||
<img className="filter dark:invert" src={Exit} />
|
||||
</button>
|
||||
<h1 className="my-0 text-xl font-medium">Please save your Key</h1>
|
||||
<h1 className="my-0 text-xl font-medium">
|
||||
{' '}
|
||||
{t('modals.saveKey.note')}
|
||||
</h1>
|
||||
<h3 className="text-sm font-normal text-outer-space">
|
||||
This is the only time your key will be shown.
|
||||
{t('modals.saveKey.disclaimer')}
|
||||
</h3>
|
||||
<div className="flex justify-between py-2">
|
||||
<div>
|
||||
@ -319,14 +328,14 @@ const SaveAPIKeyModal: React.FC<SaveAPIKeyModalProps> = ({ apiKey, close }) => {
|
||||
className="my-1 h-10 w-20 rounded-full border border-solid border-purple-30 p-2 text-sm text-purple-30 hover:bg-purple-30 hover:text-white"
|
||||
onClick={handleCopyKey}
|
||||
>
|
||||
{isCopied ? 'Copied' : 'Copy'}
|
||||
{isCopied ? t('modals.saveKey.copied') : t('modals.saveKey.copy')}
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={close}
|
||||
className="rounded-full bg-philippine-yellow px-4 py-3 font-medium text-black hover:bg-[#E6B91A]"
|
||||
>
|
||||
I saved the Key
|
||||
{t('modals.saveKey.confirm')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,10 +1,12 @@
|
||||
import { DocumentsProps } from '../models/misc';
|
||||
import Trash from '../assets/trash.svg';
|
||||
import PropTypes from 'prop-types';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
const Documents: React.FC<DocumentsProps> = ({
|
||||
documents,
|
||||
handleDeleteDocument,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="mt-8">
|
||||
<div className="flex flex-col">
|
||||
@ -12,10 +14,18 @@ const Documents: React.FC<DocumentsProps> = ({
|
||||
<table className="block w-max table-auto content-center justify-center rounded-xl border text-center dark:border-chinese-silver dark:text-bright-gray">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="border-r p-4 md:w-[244px]">Document Name</th>
|
||||
<th className="w-[244px] border-r px-4 py-2">Vector Date</th>
|
||||
<th className="w-[244px] border-r px-4 py-2">Token usage</th>
|
||||
<th className="w-[244px] border-r px-4 py-2">Type</th>
|
||||
<th className="border-r p-4 md:w-[244px]">
|
||||
{t('settings.documents.name')}
|
||||
</th>
|
||||
<th className="w-[244px] border-r px-4 py-2">
|
||||
{t('settings.documents.date')}
|
||||
</th>
|
||||
<th className="w-[244px] border-r px-4 py-2">
|
||||
{t('settings.documents.tokenUsage')}
|
||||
</th>
|
||||
<th className="w-[244px] border-r px-4 py-2">
|
||||
{t('settings.documents.type')}
|
||||
</th>
|
||||
<th className="px-4 py-2"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -2,6 +2,7 @@ import React from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import Prompts from './Prompts';
|
||||
import { useDarkTheme } from '../hooks';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import Dropdown from '../components/Dropdown';
|
||||
import {
|
||||
selectPrompt,
|
||||
@ -16,8 +17,22 @@ import {
|
||||
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
|
||||
|
||||
const General: React.FC = () => {
|
||||
const {
|
||||
t,
|
||||
i18n: { changeLanguage, language },
|
||||
} = useTranslation();
|
||||
const themes = ['Light', 'Dark'];
|
||||
const languages = ['English'];
|
||||
|
||||
const languageOptions = [
|
||||
{
|
||||
label: 'English',
|
||||
value: 'en',
|
||||
},
|
||||
{
|
||||
label: 'Spanish',
|
||||
value: 'es',
|
||||
},
|
||||
];
|
||||
const chunks = ['0', '2', '4', '6', '8', '10'];
|
||||
const token_limits = new Map([
|
||||
[0, 'None'],
|
||||
@ -37,7 +52,12 @@ const General: React.FC = () => {
|
||||
isDarkTheme ? 'Dark' : 'Light',
|
||||
);
|
||||
const dispatch = useDispatch();
|
||||
const [selectedLanguage, setSelectedLanguage] = React.useState(languages[0]);
|
||||
const locale = localStorage.getItem('docsgpt-locale');
|
||||
const [selectedLanguage, setSelectedLanguage] = React.useState(
|
||||
locale
|
||||
? languageOptions.find((option) => option.value === locale)
|
||||
: languageOptions[0],
|
||||
);
|
||||
const selectedPrompt = useSelector(selectPrompt);
|
||||
|
||||
React.useEffect(() => {
|
||||
@ -59,7 +79,9 @@ const General: React.FC = () => {
|
||||
return (
|
||||
<div className="mt-[59px]">
|
||||
<div className="mb-5">
|
||||
<p className="font-bold text-jet dark:text-bright-gray">Select Theme</p>
|
||||
<p className="font-bold text-jet dark:text-bright-gray">
|
||||
{t('settings.general.selectTheme')}
|
||||
</p>
|
||||
<Dropdown
|
||||
options={themes}
|
||||
selectedValue={selectedTheme}
|
||||
@ -73,13 +95,17 @@ const General: React.FC = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<p className="font-bold text-jet dark:text-bright-gray">
|
||||
Select Language
|
||||
<p className="mb-2 font-bold text-jet dark:text-bright-gray">
|
||||
{t('settings.general.selectLanguage')}
|
||||
</p>
|
||||
<Dropdown
|
||||
options={languages}
|
||||
selectedValue={selectedLanguage}
|
||||
onSelect={setSelectedLanguage}
|
||||
options={languageOptions}
|
||||
selectedValue={selectedLanguage ?? languageOptions[0]}
|
||||
onSelect={(selectedOption: { label: string; value: string }) => {
|
||||
setSelectedLanguage(selectedOption);
|
||||
changeLanguage(selectedOption.value);
|
||||
localStorage.setItem('docsgpt-locale', selectedOption.value);
|
||||
}}
|
||||
size="w-56"
|
||||
rounded="3xl"
|
||||
border="border"
|
||||
@ -87,7 +113,7 @@ const General: React.FC = () => {
|
||||
</div>
|
||||
<div className="mb-5">
|
||||
<p className="font-bold text-jet dark:text-bright-gray">
|
||||
Chunks processed per query
|
||||
{t('settings.general.chunks')}
|
||||
</p>
|
||||
<Dropdown
|
||||
options={chunks}
|
||||
@ -136,13 +162,15 @@ const General: React.FC = () => {
|
||||
</div>
|
||||
<div className="w-56">
|
||||
<p className="font-bold text-jet dark:text-bright-gray">
|
||||
Delete all conversations
|
||||
{t('settings.general.deleteAllLabel')}
|
||||
</p>
|
||||
<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"
|
||||
onClick={() => dispatch(setModalStateDeleteConv('ACTIVE'))}
|
||||
>
|
||||
<span className="overflow-hidden text-ellipsis ">Delete all</span>
|
||||
<span className="overflow-hidden text-ellipsis ">
|
||||
{t('settings.general.deleteAllBtn')}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,9 +2,8 @@ import React from 'react';
|
||||
import { PromptProps, ActiveState } from '../models/misc';
|
||||
import Dropdown from '../components/Dropdown';
|
||||
import PromptsModal from '../preferences/PromptsModal';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
|
||||
|
||||
const Prompts: React.FC<PromptProps> = ({
|
||||
prompts,
|
||||
selectedPrompt,
|
||||
@ -34,7 +33,10 @@ const Prompts: React.FC<PromptProps> = ({
|
||||
});
|
||||
const [modalType, setModalType] = React.useState<'ADD' | 'EDIT'>('ADD');
|
||||
const [modalState, setModalState] = React.useState<ActiveState>('INACTIVE');
|
||||
|
||||
const {
|
||||
t,
|
||||
i18n: { changeLanguage, language },
|
||||
} = useTranslation();
|
||||
const handleAddPrompt = async () => {
|
||||
try {
|
||||
const response = await fetch(`${apiHost}/api/create_prompt`, {
|
||||
@ -158,7 +160,9 @@ const Prompts: React.FC<PromptProps> = ({
|
||||
<div>
|
||||
<div className="flex flex-row items-center gap-8">
|
||||
<div>
|
||||
<p className="font-semibold dark:text-bright-gray">Active Prompt</p>
|
||||
<p className="font-semibold dark:text-bright-gray">
|
||||
{t('settings.general.prompt')}
|
||||
</p>
|
||||
<Dropdown
|
||||
options={prompts}
|
||||
selectedValue={selectedPrompt.name}
|
||||
@ -193,7 +197,7 @@ const Prompts: React.FC<PromptProps> = ({
|
||||
setModalState('ACTIVE');
|
||||
}}
|
||||
>
|
||||
Add new
|
||||
{t('settings.general.addNew')}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -11,12 +11,18 @@ import {
|
||||
import { Doc } from '../preferences/preferenceApi';
|
||||
import ArrowLeft from '../assets/arrow-left.svg';
|
||||
import ArrowRight from '../assets/arrow-right.svg';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const apiHost = import.meta.env.VITE_API_HOST || 'https://docsapi.arc53.com';
|
||||
|
||||
const Settings: React.FC = () => {
|
||||
const dispatch = useDispatch();
|
||||
const tabs = ['General', 'Documents', 'API Keys'];
|
||||
const { t } = useTranslation();
|
||||
const tabs = [
|
||||
t('settings.general.label'),
|
||||
t('settings.documents.label'),
|
||||
t('settings.apiKeys.label'),
|
||||
];
|
||||
const [activeTab, setActiveTab] = React.useState('General');
|
||||
const [widgetScreenshot, setWidgetScreenshot] = React.useState<File | null>(
|
||||
null,
|
||||
@ -45,7 +51,7 @@ const Settings: React.FC = () => {
|
||||
return (
|
||||
<div className="wa p-4 pt-20 md:p-12">
|
||||
<p className="text-2xl font-bold text-eerie-black dark:text-bright-gray">
|
||||
Settings
|
||||
{t('settings.label')}
|
||||
</p>
|
||||
<div className="mt-6 flex flex-row items-center space-x-4 overflow-x-auto md:space-x-8 ">
|
||||
<div className="md:hidden">
|
||||
@ -100,9 +106,9 @@ const Settings: React.FC = () => {
|
||||
|
||||
function renderActiveTab() {
|
||||
switch (activeTab) {
|
||||
case 'General':
|
||||
case t('settings.general.label'):
|
||||
return <General />;
|
||||
case 'Documents':
|
||||
case t('settings.documents.label'):
|
||||
return (
|
||||
<Documents
|
||||
documents={documents}
|
||||
@ -116,7 +122,7 @@ const Settings: React.FC = () => {
|
||||
onWidgetScreenshotChange={updateWidgetScreenshot} // Add this line
|
||||
/>
|
||||
);
|
||||
case 'API Keys':
|
||||
case t('settings.apiKeys.label'):
|
||||
return <APIKeys />;
|
||||
default:
|
||||
return null;
|
||||
|
@ -6,7 +6,7 @@ import { ActiveState } from '../models/misc';
|
||||
import { getDocs } from '../preferences/preferenceApi';
|
||||
import { setSourceDocs } from '../preferences/preferenceSlice';
|
||||
import Dropdown from '../components/Dropdown';
|
||||
|
||||
import { useTranslation } from 'react-i18next';
|
||||
export default function Upload({
|
||||
modalState,
|
||||
setModalState,
|
||||
@ -24,6 +24,7 @@ export default function Upload({
|
||||
search_queries: [''],
|
||||
number_posts: 10,
|
||||
});
|
||||
const { t } = useTranslation();
|
||||
const urlOptions: { label: string; value: string }[] = [
|
||||
{ label: 'Crawler', value: 'crawler' },
|
||||
// { label: 'Sitemap', value: 'sitemap' },
|
||||
@ -238,7 +239,7 @@ export default function Upload({
|
||||
view = (
|
||||
<>
|
||||
<p className="text-xl text-jet dark:text-bright-gray">
|
||||
Upload New Documentation
|
||||
{t('modals.uploadDoc.label')}
|
||||
</p>
|
||||
<div>
|
||||
<button
|
||||
@ -249,7 +250,7 @@ export default function Upload({
|
||||
: 'text-sonic-silver hover:text-purple-30'
|
||||
} mr-4 rounded-full px-[20px] py-[5px] text-sm font-semibold`}
|
||||
>
|
||||
From File
|
||||
{t('modals.uploadDoc.file')}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('remote')}
|
||||
@ -259,7 +260,7 @@ export default function Upload({
|
||||
: 'text-sonic-silver hover:text-purple-30'
|
||||
} mr-4 rounded-full px-[20px] py-[5px] text-sm font-semibold`}
|
||||
>
|
||||
Remote
|
||||
{t('modals.uploadDoc.remote')}
|
||||
</button>
|
||||
</div>
|
||||
{activeTab === 'file' && (
|
||||
@ -272,21 +273,21 @@ export default function Upload({
|
||||
></input>
|
||||
<div className="relative bottom-12 left-2 mt-[-20px]">
|
||||
<span className="bg-white px-2 text-xs text-gray-4000 dark:bg-outer-space dark:text-silver">
|
||||
Name
|
||||
{t('modals.uploadDoc.name')}
|
||||
</span>
|
||||
</div>
|
||||
<div {...getRootProps()}>
|
||||
<span className="rounded-3xl border border-purple-30 px-4 py-2 font-medium text-purple-30 hover:cursor-pointer dark:bg-purple-taupe dark:text-silver">
|
||||
<input type="button" {...getInputProps()} />
|
||||
Choose Files
|
||||
{t('modals.uploadDoc.choose')}
|
||||
</span>
|
||||
</div>
|
||||
<p className="mb-0 text-xs italic text-gray-4000">
|
||||
Please upload .pdf, .txt, .rst, .docx, .md, .zip limited to 25mb
|
||||
{t('modals.uploadDoc.info')}
|
||||
</p>
|
||||
<div className="mt-0">
|
||||
<p className="mb-[14px] font-medium text-eerie-black dark:text-light-gray">
|
||||
Uploaded Files
|
||||
{t('modals.uploadDoc.uploadedFiles')}
|
||||
</p>
|
||||
{files.map((file) => (
|
||||
<p key={file.name} className="text-gray-6000">
|
||||
@ -294,7 +295,9 @@ export default function Upload({
|
||||
</p>
|
||||
))}
|
||||
{files.length === 0 && (
|
||||
<p className="text-gray-6000 dark:text-light-gray">None</p>
|
||||
<p className="text-gray-6000 dark:text-light-gray">
|
||||
{t('none')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
@ -313,7 +316,7 @@ export default function Upload({
|
||||
{urlType.label !== 'Reddit' ? (
|
||||
<>
|
||||
<input
|
||||
placeholder="Enter name"
|
||||
placeholder={`Enter ${t('modals.uploadDoc.name')}`}
|
||||
type="text"
|
||||
className="h-[42px] w-full rounded-full border-2 border-silver px-3 outline-none dark:border-silver/40 dark:bg-transparent dark:text-white"
|
||||
value={urlName}
|
||||
@ -321,11 +324,11 @@ export default function Upload({
|
||||
></input>
|
||||
<div className="relative bottom-12 left-2 mt-[-20px]">
|
||||
<span className="bg-white px-2 text-xs text-gray-4000 dark:bg-outer-space dark:text-silver">
|
||||
Name
|
||||
{t('modals.uploadDoc.name')}
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
placeholder="URL Link"
|
||||
placeholder={t('modals.uploadDoc.urlLink')}
|
||||
type="text"
|
||||
className="h-[42px] w-full rounded-full border-2 border-silver px-3 outline-none dark:border-silver/40 dark:bg-transparent dark:text-white"
|
||||
value={url}
|
||||
@ -333,7 +336,7 @@ export default function Upload({
|
||||
></input>
|
||||
<div className="relative bottom-12 left-2 mt-[-20px]">
|
||||
<span className="bg-white px-2 text-xs text-gray-4000 dark:bg-outer-space dark:text-silver">
|
||||
Link
|
||||
{t('modals.uploadDoc.link')}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
@ -349,7 +352,7 @@ export default function Upload({
|
||||
></input>
|
||||
<div className="relative bottom-12 left-2 mt-[-20px]">
|
||||
<span className="bg-white px-2 text-xs text-gray-4000 dark:bg-outer-space dark:text-silver">
|
||||
Client ID
|
||||
{t('modals.uploadDoc.reddit.id')}
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
@ -362,7 +365,7 @@ export default function Upload({
|
||||
></input>
|
||||
<div className="relative bottom-12 left-2 mt-[-20px]">
|
||||
<span className="bg-white px-2 text-xs text-gray-4000 dark:bg-outer-space dark:text-silver">
|
||||
Client secret
|
||||
{t('modals.uploadDoc.reddit.secret')}
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
@ -375,7 +378,7 @@ export default function Upload({
|
||||
></input>
|
||||
<div className="relative bottom-12 left-2 mt-[-20px]">
|
||||
<span className="bg-white px-2 text-xs text-gray-4000 dark:bg-outer-space dark:text-silver">
|
||||
User agent
|
||||
{t('modals.uploadDoc.reddit.agent')}
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
@ -388,7 +391,7 @@ export default function Upload({
|
||||
></input>
|
||||
<div className="relative bottom-12 left-2 mt-[-20px]">
|
||||
<span className="bg-white px-2 text-xs text-gray-4000 dark:bg-outer-space dark:text-silver">
|
||||
Search queries
|
||||
{t('modals.uploadDoc.reddit.searchQueries')}
|
||||
</span>
|
||||
</div>
|
||||
<input
|
||||
@ -401,7 +404,7 @@ export default function Upload({
|
||||
></input>
|
||||
<div className="relative bottom-12 left-2 mt-[-20px]">
|
||||
<span className="bg-white px-2 text-xs text-gray-4000 dark:bg-outer-space dark:text-silver">
|
||||
Number of posts
|
||||
{t('modals.uploadDoc.reddit.numberOfPosts')}
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
@ -422,14 +425,14 @@ export default function Upload({
|
||||
activeTab === 'file'
|
||||
}
|
||||
>
|
||||
Train
|
||||
{t('modals.uploadDoc.train')}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
onClick={uploadRemote}
|
||||
className={`ml-2 cursor-pointer rounded-3xl bg-purple-30 py-2 px-6 text-sm text-white hover:bg-[#6F3FD1]`}
|
||||
>
|
||||
Train
|
||||
{t('modals.uploadDoc.train')}
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
@ -440,7 +443,7 @@ export default function Upload({
|
||||
}}
|
||||
className="cursor-pointer rounded-3xl px-5 py-2 text-sm font-medium hover:bg-gray-100 dark:bg-transparent dark:text-light-gray dark:hover:bg-[#767183]/50"
|
||||
>
|
||||
Cancel
|
||||
{t('modals.uploadDoc.cancel')}
|
||||
</button>
|
||||
</div>
|
||||
</>
|
||||
|
Loading…
Reference in New Issue
Block a user