Browse Source

上传管理页面-设置管理

master
談蓝色 10 months ago
parent
commit
1c953dbb00
  1. 5
      frontend/src/App.jsx
  2. 101
      frontend/src/components/DefaultChat/index.jsx
  3. 2
      frontend/src/components/Modals/ManageWorkspace/Documents/Directory/FileRow/index.jsx
  4. 14
      frontend/src/components/Modals/ManageWorkspace/Documents/Directory/index.jsx
  5. 20
      frontend/src/components/Modals/ManageWorkspace/Documents/UploadFile/index.jsx
  6. 6
      frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx
  7. 14
      frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx
  8. 6
      frontend/src/components/Modals/ManageWorkspace/Documents/index.jsx
  9. 4
      frontend/src/components/Modals/ManageWorkspace/index.jsx
  10. 2
      frontend/src/components/Modals/NewWorkspace.jsx
  11. 4
      frontend/src/components/Modals/Password/MultiUserAuth.jsx
  12. 9
      frontend/src/components/SettingsSidebar/index.jsx
  13. 8
      frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx
  14. 4
      frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx
  15. 16
      frontend/src/components/UserMenu/UserButton/index.jsx
  16. 8
      frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx
  17. 4
      frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx
  18. 11
      frontend/src/index.css
  19. 1
      frontend/src/locales/zh/common.js
  20. 197
      frontend/src/pages/Admin/Section/index.jsx
  21. 13
      frontend/src/pages/Admin/Users/index.jsx
  22. 3
      frontend/src/utils/paths.js

5
frontend/src/App.jsx

@ -22,6 +22,7 @@ const Main = lazy(() => import("@/pages/Main"));
const InvitePage = lazy(() => import("@/pages/Invite"));
const WorkspaceChat = lazy(() => import("@/pages/WorkspaceChat"));
const AdminUsers = lazy(() => import("@/pages/Admin/Users"));
const AdminSection = lazy(() => import("@/pages/Admin/Section"));
const AdminInvites = lazy(() => import("@/pages/Admin/Invitations"));
const AdminWorkspaces = lazy(() => import("@/pages/Admin/Workspaces"));
const AdminLogs = lazy(() => import("@/pages/Admin/Logging"));
@ -213,6 +214,10 @@ export default function App() {
path="/settings/users"
element={<ManagerRoute Component={AdminUsers} />}
/>
<Route
path="/settings/section"
element={<ManagerRoute Component={AdminSection} />}
/>
<Route
path="/settings/workspaces"
element={<ManagerRoute Component={AdminWorkspaces} />}

101
frontend/src/components/DefaultChat/index.jsx

@ -45,7 +45,8 @@ export default function DefaultChatContainer() {
<MessageContainer>
<MessageContent>
<UserIcon user={{ uid: "system" }} role={"assistant"} />
<MessageText>{t("welcomeMessage.part1")}</MessageText>
{/* <MessageText>{t("welcomeMessage.part1")}</MessageText> */}
<MessageText>{'欢迎使用琛海AI,可以将任何东西转换为你可以查询和聊天的训练有素的聊天机器人'}</MessageText>
</MessageContent>
</MessageContainer>
</React.Fragment>,
@ -54,30 +55,31 @@ export default function DefaultChatContainer() {
<MessageContainer>
<MessageContent>
<UserIcon user={{ uid: "system" }} role={"assistant"} />
<MessageText>{t("welcomeMessage.part2")}</MessageText>
{/* <MessageText>{t("welcomeMessage.part2")}</MessageText> */}
<MessageText>{'琛海AI是将强大的 AI 产品,无需繁琐操作的最简单方法,可以将你的生产力提高 100 倍。'}</MessageText>
</MessageContent>
</MessageContainer>
</React.Fragment>,
<React.Fragment key="msg3">
<MessageContainer>
<MessageContent>
<UserIcon user={{ uid: "system" }} role={"assistant"} />
<div>
<MessageText>{t("welcomeMessage.part3")}</MessageText>
<a
href={paths.github()}
target="_blank"
rel="noreferrer"
className="mt-5 w-fit transition-all duration-300 border border-slate-200 px-4 py-2 rounded-lg text-white light:border-black/50 light:text-theme-text-primary text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
>
<GitMerge className="h-4 w-4" />
<p>{t("welcomeMessage.githubIssue")}</p>
</a>
</div>
</MessageContent>
</MessageContainer>
</React.Fragment>,
// <React.Fragment key="msg3">
// <MessageContainer>
// <MessageContent>
// <UserIcon user={{ uid: "system" }} role={"assistant"} />
// <div>
// <MessageText>{t("welcomeMessage.part3")}</MessageText>
// <a
// href={paths.github()}
// target="_blank"
// rel="noreferrer"
// className="mt-5 w-fit transition-all duration-300 border border-slate-200 px-4 py-2 rounded-lg text-white light:border-black/50 light:text-theme-text-primary text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
// >
// <GitMerge className="h-4 w-4" />
// <p>{t("welcomeMessage.githubIssue")}</p>
// </a>
// </div>
// </MessageContent>
// </MessageContainer>
// </React.Fragment>,
<React.Fragment key="msg4">
<MessageContainer>
@ -93,7 +95,8 @@ export default function DefaultChatContainer() {
<MessageContent>
<UserIcon user={{ uid: "system" }} role={"assistant"} />
<div>
<MessageText>{t("welcomeMessage.part4")}</MessageText>
{/* <MessageText>{t("welcomeMessage.part4")}</MessageText> */}
<MessageText>{'很简单。所有集合都组织成我们称之为“工作区”的桶。工作区是文件、文档、图像、PDF 和其他文件的存储桶,这些文件将被琛海AI转换为可以理解和在对话中使用的内容。 你可以随时添加和删除文件。'}</MessageText>
{(!user || user?.role !== "default") && (
<button
@ -109,31 +112,31 @@ export default function DefaultChatContainer() {
</MessageContainer>
</React.Fragment>,
<React.Fragment key="msg6">
<MessageContainer>
<MessageContent>
<UserIcon user={{ uid: userFromStorage()?.username }} role={"user"} />
<MessageText>{t("welcomeMessage.user2")}</MessageText>
</MessageContent>
</MessageContainer>
</React.Fragment>,
// <React.Fragment key="msg6">
// <MessageContainer>
// <MessageContent>
// <UserIcon user={{ uid: userFromStorage()?.username }} role={"user"} />
// <MessageText>{t("welcomeMessage.user2")}</MessageText>
// </MessageContent>
// </MessageContainer>
// </React.Fragment>,
<React.Fragment key="msg7">
<MessageContainer>
<MessageContent>
<UserIcon user={{ uid: "system" }} role={"assistant"} />
<MessageText>
<Trans
i18nKey="welcomeMessage.part5"
components={{
i: <i />,
br: <br />,
}}
/>
</MessageText>
</MessageContent>
</MessageContainer>
</React.Fragment>,
// <React.Fragment key="msg7">
// <MessageContainer>
// <MessageContent>
// <UserIcon user={{ uid: "system" }} role={"assistant"} />
// <MessageText>
// <Trans
// i18nKey="welcomeMessage.part5"
// components={{
// i: <i />,
// br: <br />,
// }}
// />
// </MessageText>
// </MessageContent>
// </MessageContainer>
// </React.Fragment>,
<React.Fragment key="msg8">
<MessageContainer>
@ -152,7 +155,7 @@ export default function DefaultChatContainer() {
<MessageText>{t("welcomeMessage.part6")}</MessageText>
<div className="flex flex-col md:flex-row items-start md:items-center gap-1 md:gap-4">
<a
{/* <a
href={paths.github()}
target="_blank"
rel="noreferrer"
@ -167,7 +170,7 @@ export default function DefaultChatContainer() {
>
<EnvelopeSimple className="h-4 w-4" />
<p>{t("welcomeMessage.contact")}</p>
</a>
</a> */}
</div>
</div>
</MessageContent>
@ -203,7 +206,7 @@ export default function DefaultChatContainer() {
return (
<div
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
className={`transition-all duration-500 relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-theme-bg-secondary light:border-[1px] light:border-theme-sidebar-border w-full h-full overflow-y-scroll ${
className={`transition-all duration-500 relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] w-full h-full overflow-y-scroll ${
showScrollbar ? "show-scrollbar" : "no-scroll"
}`}
>

2
frontend/src/components/Modals/ManageWorkspace/Documents/Directory/FileRow/index.jsx

@ -44,7 +44,7 @@ export default function FileRow({ item, selected, toggleSelection }) {
<div className="col-span-2 flex justify-end items-center">
{item?.cached && (
<div className="bg-theme-settings-input-active rounded-3xl">
<p className="text-xs px-2 py-0.5">Cached</p>
<p className="text-xs px-2 py-0.5">缓存</p>
</div>
)}
</div>

14
frontend/src/components/Modals/ManageWorkspace/Documents/Directory/index.jsx

@ -194,13 +194,13 @@ function Directory({
<div className="px-8 pb-8" onContextMenu={handleContextMenu}>
<div className="flex flex-col gap-y-6">
<div className="flex items-center justify-between w-[560px] px-5 relative">
<h3 className="text-white text-base font-bold">My Documents</h3>
<h3 className="text-white text-base font-bold">我的文档</h3>
<div className="relative">
<input
type="search"
placeholder="Search for document"
placeholder="搜寻文件"
onChange={handleSearch}
className="border-none search-input bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder focus:outline-primary-button active:outline-primary-button outline-none text-sm rounded-lg pl-9 pr-2.5 py-2 w-[250px] h-[32px] light:border-theme-modal-border light:border"
className="border-none search-input bg-[#ECEFF6] text-white placeholder:text-theme-settings-input-placeholder focus:outline-primary-button active:outline-primary-button outline-none text-sm rounded-lg pl-9 pr-2.5 py-2 w-[250px] h-[32px] light:border-theme-modal-border light:border"
/>
<MagnifyingGlass
size={14}
@ -218,13 +218,13 @@ function Directory({
className="text-theme-text-primary light:text-[#0ba5ec]"
/>
<div className="text-theme-text-primary light:text-[#0ba5ec] text-xs font-bold leading-[18px]">
New Folder
新文件夹
</div>
</button>
</div>
<div className="relative w-[560px] h-[310px] bg-theme-settings-input-bg rounded-2xl overflow-hidden border border-theme-modal-border">
<div className="absolute top-0 left-0 right-0 z-10 rounded-t-2xl text-theme-text-primary text-xs grid grid-cols-12 py-2 px-8 border-b border-white/20 shadow-md bg-theme-settings-input-bg">
<div className="relative w-[560px] h-[310px] bg-[#ECEFF6] rounded-2xl overflow-hidden border ">
<div className="absolute top-0 left-0 right-0 z-10 rounded-t-2xl text-theme-text-primary text-xs grid grid-cols-12 py-2 px-8 border-b border-white/20 shadow-md ">
<p className="col-span-6">Name</p>
</div>
@ -272,7 +272,7 @@ function Directory({
onMouseLeave={() => setHighlightWorkspace(false)}
className="border-none text-sm font-semibold bg-white light:bg-[#E0F2FE] h-[30px] px-2.5 rounded-lg hover:bg-neutral-800/80 hover:text-white light:text-[#026AA2] light:hover:bg-[#026AA2] light:hover:text-white"
>
Move to Workspace
移动到工作区
</button>
<div className="relative">
<button

20
frontend/src/components/Modals/ManageWorkspace/Documents/UploadFile/index.jsx

@ -80,9 +80,8 @@ export default function UploadFile({
return (
<div>
<div
className={`w-[560px] border-dashed border-[2px] border-theme-modal-border light:border-[#686C6F] rounded-2xl bg-theme-bg-primary transition-colors duration-300 p-3 ${
ready
? " light:bg-[#E0F2FE] cursor-pointer hover:bg-theme-bg-secondary light:hover:bg-transparent"
className={`w-[560px] border-dashed border-[1px] border-theme-modal-border light:border-[#686C6F] rounded-2xl bg-theme-bg-primary transition-colors duration-300 p-3 ${ready
? " light:bg-transparent cursor-pointer hover:bg-theme-bg-secondary light:hover:bg-[#E0F2FE]"
: "cursor-not-allowed"
}`}
{...getRootProps()}
@ -103,10 +102,10 @@ export default function UploadFile({
<div className="flex flex-col items-center justify-center">
<CloudArrowUp className="w-8 h-8 text-white/80 light:invert" />
<div className="text-white text-opacity-80 text-sm font-semibold py-1">
Click to upload or drag and drop
单击上传或拖放
</div>
<div className="text-white text-opacity-60 text-xs font-medium py-1">
supports text files, csv's, spreadsheets, audio files, and more!
支持文本文件csv电子表格音频文件和更多
</div>
</div>
) : (
@ -130,7 +129,7 @@ export default function UploadFile({
)}
</div>
<div className="text-center text-white text-opacity-50 text-xs font-medium w-[560px] py-2">
or submit a link
或者提交一个链接
</div>
<form onSubmit={handleSendLink} className="flex gap-x-2">
<input
@ -144,15 +143,14 @@ export default function UploadFile({
<button
disabled={fetchingUrl}
type="submit"
className="disabled:bg-white/20 disabled:text-slate-300 disabled:border-slate-400 disabled:cursor-wait bg bg-transparent hover:bg-slate-200 hover:text-slate-800 w-auto border border-white light:border-theme-modal-border text-sm text-white p-2.5 rounded-lg"
className="disabled:bg-white/20 disabled:text-slate-300 disabled:border-slate-400 disabled:cursor-wait bg bg-transparent hover:bg-[#ECEFF6] hover:text-slate-800 w-auto border border-white light:border-theme-modal-border text-sm text-white p-2.5 rounded-lg"
>
{fetchingUrl ? "Fetching..." : "Fetch website"}
{fetchingUrl ? "Fetching..." : "获取网站"}
</button>
</form>
<div className="mt-6 text-center text-white text-opacity-80 text-xs font-medium w-[560px]">
These files will be uploaded to the document processor running on this
AnythingLLM instance. These files are not sent or shared with a third
party.
这些文件将被上传到运行于此的文档处理器上
AnythingLLM实例这些文件不会发送或与第三方共享
</div>
</div>
);

6
frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/WorkspaceFileRow/index.jsx

@ -28,7 +28,7 @@ export default function WorkspaceFileRow({
setLoading(true);
try {
setLoadingMessage(`Removing file from workspace`);
setLoadingMessage(`从工作区中删除文件中`);
await Workspace.modifyEmbeddings(workspace.slug, {
adds: [],
deletes: [`${folderName}/${item.name}`],
@ -60,7 +60,7 @@ export default function WorkspaceFileRow({
!disableSelection
? "hover:bg-theme-file-picker-hover cursor-pointer"
: ""
} ${isMovedItem ? "bg-green-800/40" : "file-row"} ${
} ${isMovedItem ? "" : "file-row"} ${
selected ? "selected light:text-white" : ""
}`}
onClick={toggleRowSelection}
@ -251,7 +251,7 @@ const RemoveItemFromWorkspace = ({ item, onClick }) => {
<div>
<ArrowUUpLeft
data-tooltip-id="remove-document"
data-tooltip-content="Remove document from workspace"
data-tooltip-content="从工作区中删除文档"
onClick={onClick}
className="text-base font-bold w-4 h-4 ml-2 flex-shrink-0 cursor-pointer"
/>

14
frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx

@ -126,8 +126,8 @@ function WorkspaceDirectory({
highlightWorkspace ? "border-4 border-cyan-300/80 z-[999]" : ""
}`}
/>
<div className="relative w-full h-full bg-theme-settings-input-bg rounded-2xl overflow-hidden border border-theme-modal-border">
<div className="text-white/80 text-xs grid grid-cols-12 py-2 px-3.5 border-b border-white/20 bg-theme-settings-input-bg sticky top-0 z-10 shadow-md">
<div className="relative w-full h-full bg-[#ECEFF6] rounded-2xl overflow-hidden border border-theme-modal-border">
<div className="text-white/80 text-xs grid grid-cols-12 py-2 px-3.5 border-b border-white/20 sticky top-0 z-10 shadow-md">
<div className="col-span-10 flex items-center gap-x-[4px]">
{!hasChanges &&
files.items.some((folder) => folder.items.length > 0) ? (
@ -182,7 +182,7 @@ function WorkspaceDirectory({
) : (
<div className="w-full h-full flex items-center justify-center">
<p className="text-white text-opacity-40 text-sm font-medium">
No Documents
暂无文件
</p>
</div>
)}
@ -201,14 +201,14 @@ function WorkspaceDirectory({
(sum, folder) => sum + folder.items.length,
0
)
? "Deselect All"
: "Select All"}
? "取消选择所有"
: "选择所有"}
</button>
<button
onClick={removeSelectedItems}
className="border-none text-sm font-semibold bg-white light:bg-[#E0F2FE] h-[30px] px-2.5 rounded-lg hover:bg-neutral-800/80 hover:text-white light:text-[#026AA2] light:hover:bg-[#026AA2] light:hover:text-white"
>
Remove Selected
删除选定的
</button>
</div>
</div>
@ -237,7 +237,7 @@ function WorkspaceDirectory({
onClick={(e) => handleSaveChanges(e)}
className="border border-slate-200 px-5 py-2.5 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
>
Save and Embed
保存和嵌入
</button>
</div>
)}

6
frontend/src/components/Modals/ManageWorkspace/Documents/index.jsx

@ -87,7 +87,7 @@ export default function DocumentSettings({ workspace, systemSettings }) {
e.preventDefault();
setLoading(true);
showToast("Updating workspace...", "info", { autoClose: false });
setLoadingMessage("This may take a while for large documents");
setLoadingMessage("对于大型文档,这可能需要一段时间");
const changesToSend = {
adds: movedItems.map((item) => `${item.folderName}/${item.name}`),
@ -102,12 +102,12 @@ export default function DocumentSettings({ workspace, systemSettings }) {
showToast(`Error: ${res.message}`, "error", { clear: true });
return;
}
showToast("Workspace updated successfully.", "success", {
showToast("更新成功.", "success", {
clear: true,
});
})
.catch((error) => {
showToast(`Workspace update failed: ${error}`, "error", {
showToast(`更新失败: ${error}`, "error", {
clear: true,
});
});

4
frontend/src/components/Modals/ManageWorkspace/index.jsx

@ -125,7 +125,7 @@ const ModalTabSwitcher = ({ selectedTab, setSelectedTab }) => {
: "text-white/20 font-medium hover:text-white light:bg-white light:text-[#535862] light:hover:bg-[#E0F2FE]"
}`}
>
Documents
文档
</button>
<button
onClick={() => setSelectedTab("dataConnectors")}
@ -135,7 +135,7 @@ const ModalTabSwitcher = ({ selectedTab, setSelectedTab }) => {
: "text-white/20 font-medium hover:text-white light:bg-white light:text-[#535862] light:hover:bg-[#E0F2FE]"
}`}
>
Data Connectors
数据连接器
</button>
</div>
</div>

2
frontend/src/components/Modals/NewWorkspace.jsx

@ -74,7 +74,7 @@ export default function NewWorkspaceModal({ hideModal = noop }) {
type="submit"
className="transition-all duration-300 bg-white text-black hover:opacity-60 px-4 py-2 rounded-lg text-sm"
>
Save
保存
</button>
</div>
</form>

4
frontend/src/components/Modals/Password/MultiUserAuth.jsx

@ -292,10 +292,10 @@ export default function MultiUserAuth() {
{"琛海科技AI管理平台"}
</p>
</div>
<p className="text-sm text-theme-text-secondary text-center">
{/* <p className="text-sm text-theme-text-secondary text-center">
{t("login.sign-in.start")} {customAppName || "AnythingLLM"}{" "}
{t("login.sign-in.end")}
</p>
</p> */}
</div>
</div>
<div className="w-full px-4 md:px-12">

9
frontend/src/components/SettingsSidebar/index.jsx

@ -1,6 +1,7 @@
import React, { useEffect, useRef, useState } from "react";
import paths from "@/utils/paths";
import useLogo from "@/hooks/useLogo";
import imglog from "../../../../images/chimg.png"
import {
House,
List,
@ -140,11 +141,12 @@ export default function SettingsSidebar() {
className="flex shrink-0 max-w-[55%] items-center justify-start mx-[38px] my-[18px]"
>
<img
src={logo}
src={imglog}
alt="Logo"
className="rounded max-h-[24px]"
style={{ objectFit: "contain" }}
/>
<div className="pl-[10px]">琛海科技</div>
</Link>
<div
ref={sidebarRef}
@ -265,6 +267,11 @@ const SidebarOptions = ({ user = null, t }) => (
href: paths.settings.users(),
roles: ["admin", "manager"],
},
{
btnText: t("settings.section"),
href: paths.settings.section(),
roles: ["admin", "manager"],
},
{
btnText: t("settings.workspaces"),
href: paths.settings.workspaces(),

8
frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/ThreadItem/index.jsx

@ -196,7 +196,7 @@ function OptionsMenu({
const renameThread = async () => {
const name = window
.prompt("What would you like to rename this thread to?")
.prompt("您想将此线程重命名为什么?")
?.trim();
if (!name || name.length === 0) {
close();
@ -223,7 +223,7 @@ function OptionsMenu({
const handleDelete = async () => {
if (
!window.confirm(
"Are you sure you want to delete this thread? All of its chats will be deleted. You cannot undo this."
"您确定要删除此线程吗?所有的聊天记录都将被删除。这是无法挽回的。"
)
)
return;
@ -254,7 +254,7 @@ function OptionsMenu({
className="w-full rounded-md flex items-center p-2 gap-x-2 hover:bg-slate-500/20 text-slate-300 light:text-theme-text-primary"
>
<PencilSimple size={18} />
<p className="text-sm">Rename</p>
<p className="text-sm">重命名</p>
</button>
<button
onClick={handleDelete}
@ -262,7 +262,7 @@ function OptionsMenu({
className="w-full rounded-md flex items-center p-2 gap-x-2 hover:bg-red-500/20 text-slate-300 light:text-theme-text-primary hover:text-red-100"
>
<Trash size={18} />
<p className="text-sm">Delete Thread</p>
<p className="text-sm">删除线程</p>
</button>
</div>
);

4
frontend/src/components/Sidebar/ActiveWorkspaces/ThreadContainer/index.jsx

@ -195,11 +195,11 @@ function NewThreadButton({ workspace }) {
{loading ? (
<p className="text-left text-white light:text-theme-text-primary text-sm">
Starting Thread...
启动线程...
</p>
) : (
<p className="text-left text-white light:text-theme-text-primary text-sm">
New Thread
新线程
</p>
)}
</div>

16
frontend/src/components/UserMenu/UserButton/index.jsx

@ -67,23 +67,23 @@ export default function UserButton() {
{showMenu && (
<div
ref={menuRef}
className="w-fit rounded-lg absolute top-12 right-0 bg-theme-action-menu-bg p-2 flex items-center-justify-center"
className="w-[120px] rounded-lg absolute top-12 right-0 bg-[#fff] p-2 flex items-center-justify-center"
>
<div className="flex flex-col gap-y-2">
<div className="flex flex-col gap-y-2 text-[14px]">
{mode === "multi" && !!user && (
<button
onClick={handleOpenAccountModal}
className="border-none text-white hover:bg-theme-action-menu-item-hover w-full text-left px-4 py-1.5 rounded-md"
className="border-none text-white hover:bg-[#F0F0F5] w-full text-left px-4 py-1.5 rounded-md"
>
Account
账户
</button>
)}
<a
{/* <a
href={supportEmail}
className="text-white hover:bg-theme-action-menu-item-hover w-full text-left px-4 py-1.5 rounded-md"
>
Support
</a>
</a> */}
<button
onClick={() => {
window.localStorage.removeItem(AUTH_USER);
@ -92,9 +92,9 @@ export default function UserButton() {
window.location.replace(paths.home());
}}
type="button"
className="text-white hover:bg-theme-action-menu-item-hover w-full text-left px-4 py-1.5 rounded-md"
className="text-white hover:bg-[#F0F0F5] w-full text-left px-4 py-1.5 rounded-md"
>
Sign out
退出登录
</button>
</div>
</div>

8
frontend/src/components/WorkspaceChat/ChatContainer/ChatHistory/index.jsx

@ -179,18 +179,18 @@ export default function ChatHistory({
<div className="flex flex-col h-full md:mt-0 pb-44 md:pb-40 w-full justify-end items-center">
<div className="flex flex-col items-center md:items-start md:max-w-[600px] w-full px-4">
<p className="text-white/60 text-lg font-base py-4">
Welcome to your new workspace.
欢迎来到你的新工作空间
</p>
{!user || user.role !== "default" ? (
<p className="w-full items-center text-white/60 text-lg font-base flex flex-col md:flex-row gap-x-1">
To get started either{" "}
开始吧{" "}
<span
className="underline font-medium cursor-pointer"
onClick={showModal}
>
upload a document
上传文档
</span>
or <b className="font-medium italic">send a chat.</b>
<b className="font-medium italic">发送聊天信息.</b>
</p>
) : (
<p className="w-full items-center text-white/60 text-lg font-base flex flex-col md:flex-row gap-x-1">

4
frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/index.jsx

@ -284,8 +284,8 @@ export default function PromptInput({
type="submit"
className="border-none inline-flex justify-center rounded-2xl cursor-pointer opacity-60 hover:opacity-100 light:opacity-100 light:hover:opacity-60 ml-4"
data-tooltip-id="send-prompt"
data-tooltip-content="Send prompt message to workspace"
aria-label="Send prompt message to workspace"
data-tooltip-content="向工作区发送提示消息"
aria-label="向工作区发送提示消息"
>
<PaperPlaneRight
color="var(--theme-sidebar-footer-icon-fill)"

11
frontend/src/index.css

@ -111,9 +111,9 @@
[data-theme="light"] .text-white\/60 {
color: var(--theme-text-secondary);
}
/* 修改背景颜色 */
[data-theme="light"] .bg-theme-bg-secondary {
border: 1px solid var(--theme-sidebar-border);
/* border: 1px solid var(--theme-sidebar-border); */
}
[data-theme="light"] .border-white\/10 {
@ -775,13 +775,16 @@ dialog::backdrop {
.file-row:nth-child(even) {
@apply bg-theme-bg-primary;
background-color: var(--theme-file-row-even);
/* 修改弹框背景 */
/* background-color: var(--theme-file-row-even); */
background-color: #ECEFF6;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}
.file-row:nth-child(odd) {
@apply bg-theme-bg-secondary;
background-color: var(--theme-file-row-odd);
/* background-color: var(--theme-file-row-odd); */
background-color: #ECEFF6;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
}

1
frontend/src/locales/zh/common.js

@ -71,6 +71,7 @@ const TRANSLATIONS = {
system: "系统",
invites: "邀请",
users: "用户",
section:"部门",
workspaces: "工作区",
"workspace-chats": "对话历史记录",
customization: "外观",

197
frontend/src/pages/Admin/Section/index.jsx

@ -0,0 +1,197 @@
import { useEffect, useState } from "react";
import Sidebar from "@/components/SettingsSidebar";
import { isMobile } from "react-device-detect";
import * as Skeleton from "react-loading-skeleton";
import "react-loading-skeleton/dist/skeleton.css";
import { UserPlus } from "@phosphor-icons/react";
import Admin from "@/models/admin";
import UserRow from "../Users/UserRow";
import useUser from "@/hooks/useUser";
import NewUserModal from "../Users/NewUserModal";
import { useModal } from "@/hooks/useModal";
import ModalWrapper from "@/components/ModalWrapper";
import CTAButton from "@/components/lib/CTAButton";
export default function AdminUsers() {
const { isOpen, openModal, closeModal } = useModal();
return (
<div className="w-screen h-screen overflow-hidden bg-theme-bg-container flex">
<Sidebar />
<div
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-theme-bg-secondary w-full h-full overflow-y-scroll p-4 md:p-0"
>
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[50px] md:py-6 py-16">
<div className="w-full flex flex-col gap-y-1 pb-6 border-white/10 border-b-2">
<div className="items-center flex gap-x-4">
<p className="text-lg leading-6 font-bold text-theme-text-primary">
部门
</p>
</div>
<p className="text-xs leading-[18px] font-base text-theme-text-secondary">
部门树状结构
</p>
</div>
<div className="w-full justify-end flex">
<CTAButton
onClick={openModal}
className="mt-3 mr-0 mb-4 md:-mb-6 z-10"
>
<UserPlus className="h-4 w-4" weight="bold" /> Add user
</CTAButton>
</div>
<div className="overflow-x-auto">
<UsersContainer />
</div>
</div>
<ModalWrapper isOpen={isOpen}>
<NewUserModal closeModal={closeModal} />
</ModalWrapper>
</div>
</div>
);
}
function UsersContainer() {
const { user: currUser } = useUser();
const [loading, setLoading] = useState(true);
const [users, setUsers] = useState([]);
useEffect(() => {
async function fetchUsers() {
const _users = await Admin.users();
setUsers(_users);
setLoading(false);
}
fetchUsers();
}, []);
if (loading) {
return (
<Skeleton.default
height="80vh"
width="100%"
highlightColor="var(--theme-bg-primary)"
baseColor="var(--theme-bg-secondary)"
count={1}
className="w-full p-4 rounded-b-2xl rounded-tr-2xl rounded-tl-sm mt-8"
containerClassName="flex w-full"
/>
);
}
return (
<table className="w-full text-sm text-left rounded-lg min-w-[640px] border-spacing-0">
<thead className="text-theme-text-secondary text-xs leading-[18px] font-bold uppercase border-white/10 border-b">
<tr>
<th scope="col" className="px-6 py-3 rounded-tl-lg">
Username
</th>
<th scope="col" className="px-6 py-3">
Role
</th>
<th scope="col" className="px-6 py-3">
Date Added
</th>
<th scope="col" className="px-6 py-3 rounded-tr-lg">
{" "}
</th>
</tr>
</thead>
<tbody>
{users.map((user) => (
<UserRow key={user.id} currUser={currUser} user={user} />
))}
</tbody>
</table>
);
}
const ROLE_HINT = {
default: [
"Can only send chats with workspaces they are added to by admin or managers.",
"Cannot modify any settings at all.",
],
manager: [
"Can view, create, and delete any workspaces and modify workspace-specific settings.",
"Can create, update and invite new users to the instance.",
"Cannot modify LLM, vectorDB, embedding, or other connections.",
],
admin: [
"Highest user level privilege.",
"Can see and do everything across the system.",
],
};
export function RoleHintDisplay({ role }) {
return (
<div className="flex flex-col gap-y-1 py-1 pb-4">
<p className="text-sm font-medium text-theme-text-primary">Permissions</p>
<ul className="flex flex-col gap-y-1 list-disc px-4">
{ROLE_HINT[role ?? "default"].map((hints, i) => {
return (
<li key={i} className="text-xs text-theme-text-secondary">
{hints}
</li>
);
})}
</ul>
</div>
);
}
export function MessageLimitInput({ enabled, limit, updateState, role }) {
if (role === "admin") return null;
return (
<div className="mt-4 mb-8">
<div className="flex flex-col gap-y-1">
<div className="flex items-center gap-x-2">
<h2 className="text-base leading-6 font-bold text-white">
Limit messages per day
</h2>
<label className="relative inline-flex cursor-pointer items-center">
<input
type="checkbox"
checked={enabled}
onChange={(e) => {
updateState((prev) => ({
...prev,
enabled: e.target.checked,
}));
}}
className="peer sr-only"
/>
<div className="pointer-events-none peer h-6 w-11 rounded-full bg-[#CFCFD0] after:absolute after:left-[2px] after:top-[2px] after:h-5 after:w-5 after:rounded-full after:shadow-xl after:border-none after:bg-white after:box-shadow-md after:transition-all after:content-[''] peer-checked:bg-[#32D583] peer-checked:after:translate-x-full peer-checked:after:border-white peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-transparent"></div>
</label>
</div>
<p className="text-xs leading-[18px] font-base text-white/60">
Restrict this user to a number of successful queries or chats within a
24 hour window.
</p>
</div>
{enabled && (
<div className="mt-4">
<label className="text-white text-sm font-semibold block mb-4">
Message limit per day
</label>
<div className="relative mt-2">
<input
type="number"
onScroll={(e) => e.target.blur()}
onChange={(e) => {
updateState({
enabled: true,
limit: Number(e?.target?.value || 0),
});
}}
value={limit}
min={1}
className="border-none bg-theme-settings-input-bg text-white placeholder:text-theme-settings-input-placeholder text-sm rounded-lg focus:outline-primary-button active:outline-primary-button outline-none block w-full p-2.5"
/>
</div>
</div>
)}
</div>
);
}

13
frontend/src/pages/Admin/Users/index.jsx

@ -30,9 +30,8 @@ export default function AdminUsers() {
</p>
</div>
<p className="text-xs leading-[18px] font-base text-theme-text-secondary">
These are all the accounts which have an account on this instance.
Removing an account will instantly remove their access to this
instance.
这些是在此实例上拥有帐户的所有帐户
删除一个帐户将立即删除他们的访问权限.
</p>
</div>
<div className="w-full justify-end flex">
@ -40,7 +39,7 @@ export default function AdminUsers() {
onClick={openModal}
className="mt-3 mr-0 mb-4 md:-mb-6 z-10"
>
<UserPlus className="h-4 w-4" weight="bold" /> Add user
<UserPlus className="h-4 w-4" weight="bold" /> 添加用户
</CTAButton>
</div>
<div className="overflow-x-auto">
@ -88,13 +87,13 @@ function UsersContainer() {
<thead className="text-theme-text-secondary text-xs leading-[18px] font-bold uppercase border-white/10 border-b">
<tr>
<th scope="col" className="px-6 py-3 rounded-tl-lg">
Username
用户名
</th>
<th scope="col" className="px-6 py-3">
Role
角色
</th>
<th scope="col" className="px-6 py-3">
Date Added
添加日期
</th>
<th scope="col" className="px-6 py-3 rounded-tr-lg">
{" "}

3
frontend/src/utils/paths.js

@ -80,6 +80,9 @@ export default {
users: () => {
return `/settings/users`;
},
section: () => {
return `/settings/section`;
},
invites: () => {
return `/settings/invites`;
},

Loading…
Cancel
Save