mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2025-03-16 07:02:22 +00:00
[STYLE] Implement new settings sidebar UI (#1829)
* implement new settings sidebar v2 * store state of settings menu in localstorage to improve ux * add tailwind color * add missed admin translation * fix admin pages showing on single user * perms fix for manager role * refactor permissions for options on sidebar * minor refactor of menuoption --------- Co-authored-by: timothycarambat <rambat1010@gmail.com>
This commit is contained in:
parent
e7fe35bda9
commit
69d67401ff
8 changed files with 368 additions and 230 deletions
168
frontend/src/components/SettingsSidebar/MenuOption/index.jsx
Normal file
168
frontend/src/components/SettingsSidebar/MenuOption/index.jsx
Normal file
|
@ -0,0 +1,168 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { CaretRight } from "@phosphor-icons/react";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
export default function MenuOption({
|
||||
btnText,
|
||||
icon,
|
||||
href,
|
||||
childOptions = [],
|
||||
flex = false,
|
||||
user = null,
|
||||
roles = [],
|
||||
hidden = false,
|
||||
isChild = false,
|
||||
}) {
|
||||
const storageKey = generateStorageKey({ key: btnText });
|
||||
const location = window.location.pathname;
|
||||
const hasChildren = childOptions.length > 0;
|
||||
const hasVisibleChildren = hasVisibleOptions(user, childOptions);
|
||||
const { isExpanded, setIsExpanded } = useIsExpanded({
|
||||
storageKey,
|
||||
hasVisibleChildren,
|
||||
childOptions,
|
||||
location,
|
||||
});
|
||||
|
||||
if (hidden) return null;
|
||||
|
||||
// If this option is a parent level option
|
||||
if (!isChild) {
|
||||
// and has no children then use its flex props and roles prop directly
|
||||
if (!hasChildren) {
|
||||
if (!flex && !roles.includes(user?.role)) return null;
|
||||
if (flex && !!user && !roles.includes(user?.role)) return null;
|
||||
}
|
||||
|
||||
// if has children and no visible children - remove it.
|
||||
if (hasChildren && !hasVisibleChildren) return null;
|
||||
} else {
|
||||
// is a child so we use it's permissions
|
||||
if (!flex && !roles.includes(user?.role)) return null;
|
||||
if (flex && !!user && !roles.includes(user?.role)) return null;
|
||||
}
|
||||
|
||||
const isActive = hasChildren
|
||||
? (!isExpanded && childOptions.some((child) => child.href === location)) ||
|
||||
location === href
|
||||
: location === href;
|
||||
|
||||
const handleClick = (e) => {
|
||||
if (hasChildren) {
|
||||
e.preventDefault();
|
||||
const newExpandedState = !isExpanded;
|
||||
setIsExpanded(newExpandedState);
|
||||
localStorage.setItem(storageKey, JSON.stringify(newExpandedState));
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div
|
||||
className={`
|
||||
flex items-center justify-between w-full
|
||||
transition-all duration-300
|
||||
rounded-[6px]
|
||||
${
|
||||
isActive
|
||||
? "bg-white/5 font-medium border-outline"
|
||||
: "hover:bg-white/5"
|
||||
}
|
||||
`}
|
||||
>
|
||||
<Link
|
||||
to={href}
|
||||
className={`flex flex-grow items-center px-[12px] h-[32px] font-medium ${
|
||||
isChild ? "text-white/70 hover:text-white" : "text-white"
|
||||
}`}
|
||||
onClick={hasChildren ? handleClick : undefined}
|
||||
>
|
||||
{icon}
|
||||
<p
|
||||
className={`${
|
||||
isChild ? "text-xs" : "text-sm"
|
||||
} leading-loose whitespace-nowrap overflow-hidden ml-2 ${
|
||||
isActive ? "text-white" : ""
|
||||
} ${!icon && "pl-5"}`}
|
||||
>
|
||||
{btnText}
|
||||
</p>
|
||||
</Link>
|
||||
{hasChildren && (
|
||||
<button onClick={handleClick} className="p-2 text-white">
|
||||
<CaretRight
|
||||
size={16}
|
||||
weight="bold"
|
||||
className={`transition-transform ${
|
||||
isExpanded ? "rotate-90" : ""
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{isExpanded && hasChildren && (
|
||||
<div className="mt-1 rounded-r-lg w-full">
|
||||
{childOptions.map((childOption, index) => (
|
||||
<MenuOption
|
||||
key={index}
|
||||
{...childOption} // flex and roles go here.
|
||||
user={user}
|
||||
isChild={true}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function useIsExpanded({
|
||||
storageKey = "",
|
||||
hasVisibleChildren = false,
|
||||
childOptions = [],
|
||||
location = null,
|
||||
}) {
|
||||
const [isExpanded, setIsExpanded] = useState(() => {
|
||||
if (hasVisibleChildren) {
|
||||
const storedValue = localStorage.getItem(storageKey);
|
||||
if (storedValue !== null) {
|
||||
return JSON.parse(storedValue);
|
||||
}
|
||||
return childOptions.some((child) => child.href === location);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (hasVisibleChildren) {
|
||||
const shouldExpand = childOptions.some(
|
||||
(child) => child.href === location
|
||||
);
|
||||
if (shouldExpand && !isExpanded) {
|
||||
setIsExpanded(true);
|
||||
localStorage.setItem(storageKey, JSON.stringify(true));
|
||||
}
|
||||
}
|
||||
}, [location]);
|
||||
|
||||
return { isExpanded, setIsExpanded };
|
||||
}
|
||||
|
||||
function hasVisibleOptions(user = null, childOptions = []) {
|
||||
if (!Array.isArray(childOptions) || childOptions?.length === 0) return false;
|
||||
|
||||
function isVisible({ roles = [], user = null, flex = false }) {
|
||||
if (!flex && !roles.includes(user?.role)) return false;
|
||||
if (flex && !!user && !roles.includes(user?.role)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
return childOptions.some((opt) =>
|
||||
isVisible({ roles: opt.roles, user, flex: opt.flex })
|
||||
);
|
||||
}
|
||||
|
||||
function generateStorageKey({ key = "" }) {
|
||||
const _key = key.replace(/\s+/g, "_").toLowerCase();
|
||||
return `anything_llm_menu_${_key}_expanded`;
|
||||
}
|
|
@ -2,28 +2,15 @@ import React, { useEffect, useRef, useState } from "react";
|
|||
import paths from "@/utils/paths";
|
||||
import useLogo from "@/hooks/useLogo";
|
||||
import {
|
||||
EnvelopeSimple,
|
||||
SquaresFour,
|
||||
Users,
|
||||
BookOpen,
|
||||
ChatCenteredText,
|
||||
Eye,
|
||||
Key,
|
||||
ChatText,
|
||||
Database,
|
||||
Lock,
|
||||
House,
|
||||
List,
|
||||
FileCode,
|
||||
Notepad,
|
||||
CodeBlock,
|
||||
Barcode,
|
||||
ClosedCaptioning,
|
||||
EyeSlash,
|
||||
SplitVertical,
|
||||
Microphone,
|
||||
Robot,
|
||||
Flask,
|
||||
Gear,
|
||||
UserCircleGear,
|
||||
PencilSimpleLine,
|
||||
Nut,
|
||||
Toolbox,
|
||||
} from "@phosphor-icons/react";
|
||||
import useUser from "@/hooks/useUser";
|
||||
import { USER_BACKGROUND_COLOR } from "@/utils/constants";
|
||||
|
@ -32,6 +19,8 @@ import Footer from "../Footer";
|
|||
import { Link } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import showToast from "@/utils/toast";
|
||||
import System from "@/models/system";
|
||||
import Option from "./MenuOption";
|
||||
|
||||
export default function SettingsSidebar() {
|
||||
const { t } = useTranslation();
|
||||
|
@ -118,6 +107,17 @@ export default function SettingsSidebar() {
|
|||
<div className="h-auto md:sidebar-items md:dark:sidebar-items">
|
||||
<div className="flex flex-col gap-y-4 pb-[60px] overflow-y-scroll no-scroll">
|
||||
<SidebarOptions user={user} t={t} />
|
||||
<div className="h-[1.5px] bg-[#3D4147] mx-3 mt-[14px]" />
|
||||
<SupportEmail />
|
||||
<Link
|
||||
hidden={
|
||||
user?.hasOwnProperty("role") && user.role !== "admin"
|
||||
}
|
||||
to={paths.settings.privacy()}
|
||||
className="text-darker hover:text-white text-xs leading-[18px] mx-3"
|
||||
>
|
||||
Privacy & Data
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -156,6 +156,15 @@ export default function SettingsSidebar() {
|
|||
<div className="h-auto sidebar-items">
|
||||
<div className="flex flex-col gap-y-2 pb-[60px] overflow-y-scroll no-scroll">
|
||||
<SidebarOptions user={user} t={t} />
|
||||
<div className="h-[1.5px] bg-[#3D4147] mx-3 mt-[14px]" />
|
||||
<SupportEmail />
|
||||
<Link
|
||||
hidden={user?.hasOwnProperty("role") && user.role !== "admin"}
|
||||
to={paths.settings.privacy()}
|
||||
className="text-darker hover:text-white text-xs leading-[18px] mx-3"
|
||||
>
|
||||
Privacy & Data
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -168,233 +177,173 @@ export default function SettingsSidebar() {
|
|||
);
|
||||
}
|
||||
|
||||
const Option = ({
|
||||
btnText,
|
||||
icon,
|
||||
href,
|
||||
childLinks = [],
|
||||
flex = false,
|
||||
user = null,
|
||||
allowedRole = [],
|
||||
subOptions = null,
|
||||
hidden = false,
|
||||
}) => {
|
||||
if (hidden) return null;
|
||||
function SupportEmail() {
|
||||
const [supportEmail, setSupportEmail] = useState(paths.mailToMintplex());
|
||||
|
||||
const hasActiveChild = childLinks.includes(window.location.pathname);
|
||||
const isActive = window.location.pathname === href;
|
||||
|
||||
// Option only for multi-user
|
||||
if (!flex && !allowedRole.includes(user?.role)) return null;
|
||||
|
||||
// Option is dual-mode, but user exists, we need to check permissions
|
||||
if (flex && !!user && !allowedRole.includes(user?.role)) return null;
|
||||
useEffect(() => {
|
||||
const fetchSupportEmail = async () => {
|
||||
const supportEmail = await System.fetchSupportEmail();
|
||||
setSupportEmail(
|
||||
supportEmail?.email
|
||||
? `mailto:${supportEmail.email}`
|
||||
: paths.mailToMintplex()
|
||||
);
|
||||
};
|
||||
fetchSupportEmail();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex gap-x-2 items-center justify-between">
|
||||
<Link
|
||||
to={href}
|
||||
className={`
|
||||
transition-all duration-[200ms]
|
||||
flex flex-grow w-[75%] gap-x-2 py-[6px] px-[12px] rounded-[4px] justify-start items-center
|
||||
hover:bg-workspace-item-selected-gradient hover:text-white hover:font-medium
|
||||
${
|
||||
isActive
|
||||
? "bg-menu-item-selected-gradient font-medium border-outline text-white"
|
||||
: "hover:bg-menu-item-selected-gradient text-zinc-200"
|
||||
}
|
||||
`}
|
||||
>
|
||||
{React.cloneElement(icon, { weight: isActive ? "fill" : "regular" })}
|
||||
<p className="text-sm leading-loose whitespace-nowrap overflow-hidden ">
|
||||
{btnText}
|
||||
</p>
|
||||
</Link>
|
||||
</div>
|
||||
{!!subOptions && (isActive || hasActiveChild) && (
|
||||
<div
|
||||
className={`ml-4 ${
|
||||
hasActiveChild ? "" : "border-l-2 border-slate-400"
|
||||
} rounded-r-lg`}
|
||||
>
|
||||
{subOptions}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
<Link
|
||||
to={supportEmail}
|
||||
className="text-darker hover:text-white text-xs leading-[18px] mx-3 mt-1"
|
||||
>
|
||||
Contact Support
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
const SidebarOptions = ({ user = null, t }) => (
|
||||
<>
|
||||
<Option
|
||||
href={paths.settings.system()}
|
||||
btnText={t("settings.system")}
|
||||
icon={<SquaresFour className="h-5 w-5 flex-shrink-0" />}
|
||||
btnText={t("settings.ai-providers")}
|
||||
icon={<Gear className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
allowedRole={["admin", "manager"]}
|
||||
childOptions={[
|
||||
{
|
||||
btnText: t("settings.llm"),
|
||||
href: paths.settings.llmPreference(),
|
||||
flex: true,
|
||||
roles: ["admin"],
|
||||
},
|
||||
{
|
||||
btnText: t("settings.vector-database"),
|
||||
href: paths.settings.vectorDatabase(),
|
||||
flex: true,
|
||||
roles: ["admin"],
|
||||
},
|
||||
{
|
||||
btnText: t("settings.embedder"),
|
||||
href: paths.settings.embedder.modelPreference(),
|
||||
flex: true,
|
||||
roles: ["admin"],
|
||||
},
|
||||
{
|
||||
btnText: t("settings.text-splitting"),
|
||||
href: paths.settings.embedder.chunkingPreference(),
|
||||
flex: true,
|
||||
roles: ["admin"],
|
||||
},
|
||||
{
|
||||
btnText: "Voice & Speech",
|
||||
href: paths.settings.audioPreference(),
|
||||
flex: true,
|
||||
roles: ["admin"],
|
||||
},
|
||||
{
|
||||
btnText: t("settings.transcription"),
|
||||
href: paths.settings.transcriptionPreference(),
|
||||
flex: true,
|
||||
roles: ["admin"],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.invites()}
|
||||
btnText={t("settings.invites")}
|
||||
icon={<EnvelopeSimple className="h-5 w-5 flex-shrink-0" />}
|
||||
btnText={t("settings.admin")}
|
||||
icon={<UserCircleGear className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
allowedRole={["admin", "manager"]}
|
||||
childOptions={[
|
||||
{
|
||||
btnText: t("settings.users"),
|
||||
href: paths.settings.users(),
|
||||
roles: ["admin", "manager"],
|
||||
},
|
||||
{
|
||||
btnText: t("settings.workspaces"),
|
||||
href: paths.settings.workspaces(),
|
||||
roles: ["admin", "manager"],
|
||||
},
|
||||
{
|
||||
btnText: t("settings.workspace-chats"),
|
||||
href: paths.settings.chats(),
|
||||
flex: true,
|
||||
roles: ["admin", "manager"],
|
||||
},
|
||||
{
|
||||
btnText: t("settings.invites"),
|
||||
href: paths.settings.invites(),
|
||||
roles: ["admin", "manager"],
|
||||
},
|
||||
{
|
||||
btnText: t("settings.system"),
|
||||
href: paths.settings.system(),
|
||||
roles: ["admin", "manager"],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.users()}
|
||||
btnText={t("settings.users")}
|
||||
icon={<Users className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
allowedRole={["admin", "manager"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.workspaces()}
|
||||
btnText={t("settings.workspaces")}
|
||||
icon={<BookOpen className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
allowedRole={["admin", "manager"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.chats()}
|
||||
btnText={t("settings.workspace-chats")}
|
||||
icon={<ChatCenteredText className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin", "manager"]}
|
||||
/>
|
||||
|
||||
<Option
|
||||
href={paths.settings.agentSkills()}
|
||||
btnText="Agent Skills"
|
||||
btnText={t("settings.agent-skills")}
|
||||
icon={<Robot className="h-5 w-5 flex-shrink-0" />}
|
||||
href={paths.settings.agentSkills()}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin", "manager"]}
|
||||
roles={["admin"]}
|
||||
/>
|
||||
<Option
|
||||
btnText={t("settings.customization")}
|
||||
icon={<PencilSimpleLine className="h-5 w-5 flex-shrink-0" />}
|
||||
href={paths.settings.appearance()}
|
||||
btnText={t("settings.appearance")}
|
||||
icon={<Eye className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin", "manager"]}
|
||||
roles={["admin", "manager"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.apiKeys()}
|
||||
btnText={t("settings.api-keys")}
|
||||
icon={<Key className="h-5 w-5 flex-shrink-0" />}
|
||||
btnText={t("settings.tools")}
|
||||
icon={<Toolbox className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
childOptions={[
|
||||
{
|
||||
btnText: t("settings.embed-chats"),
|
||||
href: paths.settings.embedChats(),
|
||||
flex: true,
|
||||
roles: ["admin"],
|
||||
},
|
||||
{
|
||||
btnText: t("settings.embeds"),
|
||||
href: paths.settings.embedSetup(),
|
||||
flex: true,
|
||||
roles: ["admin"],
|
||||
},
|
||||
{
|
||||
btnText: t("settings.event-logs"),
|
||||
href: paths.settings.logs(),
|
||||
flex: true,
|
||||
roles: ["admin"],
|
||||
},
|
||||
{
|
||||
btnText: t("settings.api-keys"),
|
||||
href: paths.settings.apiKeys(),
|
||||
flex: true,
|
||||
roles: ["admin"],
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.llmPreference()}
|
||||
btnText={t("settings.llm")}
|
||||
icon={<ChatText className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.audioPreference()}
|
||||
btnText="Voice and Speech Support"
|
||||
icon={<Microphone className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.transcriptionPreference()}
|
||||
btnText={t("settings.transcription")}
|
||||
icon={<ClosedCaptioning className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.embedder.modelPreference()}
|
||||
childLinks={[paths.settings.embedder.chunkingPreference()]}
|
||||
btnText={t("settings.embedder")}
|
||||
icon={<FileCode className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
subOptions={
|
||||
<>
|
||||
<Option
|
||||
href={paths.settings.embedder.chunkingPreference()}
|
||||
btnText={t("settings.text-splitting")}
|
||||
icon={<SplitVertical className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.vectorDatabase()}
|
||||
btnText={t("settings.vector-database")}
|
||||
icon={<Database className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.embedSetup()}
|
||||
childLinks={[paths.settings.embedChats()]}
|
||||
btnText={t("settings.embeds")}
|
||||
icon={<CodeBlock className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
subOptions={
|
||||
<>
|
||||
<Option
|
||||
href={paths.settings.embedChats()}
|
||||
btnText={t("settings.embed-chats")}
|
||||
icon={<Barcode className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.security()}
|
||||
btnText={t("settings.security")}
|
||||
icon={<Lock className="h-5 w-5 flex-shrink-0" />}
|
||||
icon={<Nut className="h-5 w-5 flex-shrink-0" />}
|
||||
href={paths.settings.security()}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin", "manager"]}
|
||||
roles={["admin", "manager"]}
|
||||
hidden={user?.role}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.logs()}
|
||||
btnText={t("settings.event-logs")}
|
||||
icon={<Notepad className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
/>
|
||||
<Option
|
||||
href={paths.settings.privacy()}
|
||||
btnText={t("settings.privacy")}
|
||||
icon={<EyeSlash className="h-5 w-5 flex-shrink-0" />}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
/>
|
||||
<HoldToReveal key="exp_features">
|
||||
<Option
|
||||
href={paths.settings.experimental()}
|
||||
btnText="Experimental Features"
|
||||
icon={<Flask className="h-5 w-5 flex-shrink-0" />}
|
||||
href={paths.settings.experimental()}
|
||||
user={user}
|
||||
flex={true}
|
||||
allowedRole={["admin"]}
|
||||
roles={["admin"]}
|
||||
/>
|
||||
</HoldToReveal>
|
||||
</>
|
||||
|
|
|
@ -14,23 +14,27 @@ const TRANSLATIONS = {
|
|||
// Setting Sidebar menu items.
|
||||
settings: {
|
||||
title: "Instance Settings",
|
||||
system: "System Preferences",
|
||||
invites: "Invitation",
|
||||
system: "General Settings",
|
||||
invites: "Invites",
|
||||
users: "Users",
|
||||
workspaces: "Workspaces",
|
||||
"workspace-chats": "Workspace Chat",
|
||||
appearance: "Appearance",
|
||||
"api-keys": "API Keys",
|
||||
llm: "LLM Preference",
|
||||
transcription: "Transcription Model",
|
||||
embedder: "Embedding Preferences",
|
||||
"workspace-chats": "Workspace Chats",
|
||||
customization: "Customization",
|
||||
"api-keys": "Developer API",
|
||||
llm: "LLM",
|
||||
transcription: "Transcription",
|
||||
embedder: "Embedder",
|
||||
"text-splitting": "Text Splitter & Chunking",
|
||||
"vector-database": "Vector Database",
|
||||
embeds: "Chat Embed Widgets",
|
||||
embeds: "Chat Embed",
|
||||
"embed-chats": "Chat Embed History",
|
||||
security: "Security",
|
||||
"event-logs": "Event Logs",
|
||||
privacy: "Privacy & Data",
|
||||
"ai-providers": "AI Providers",
|
||||
"agent-skills": "Agent Skills",
|
||||
admin: "Admin",
|
||||
tools: "Tools",
|
||||
},
|
||||
|
||||
// Page Definitions
|
||||
|
|
|
@ -18,7 +18,7 @@ const TRANSLATIONS = {
|
|||
users: "Usuarios",
|
||||
workspaces: "Espacios de trabajo",
|
||||
"workspace-chats": "Chat del espacio de trabajo",
|
||||
appearance: "Apariencia",
|
||||
customization: "Apariencia",
|
||||
"api-keys": "Claves API",
|
||||
llm: "Preferencia de LLM",
|
||||
transcription: "Modelo de transcripción",
|
||||
|
@ -30,6 +30,10 @@ const TRANSLATIONS = {
|
|||
security: "Seguridad",
|
||||
"event-logs": "Registros de eventos",
|
||||
privacy: "Privacidad y datos",
|
||||
"ai-providers": "Proveedores de IA",
|
||||
"agent-skills": "Habilidades del agente",
|
||||
admin: "Administrador",
|
||||
tools: "Herramientas",
|
||||
},
|
||||
|
||||
login: {
|
||||
|
|
|
@ -19,7 +19,7 @@ const TRANSLATIONS = {
|
|||
users: "Utilisateurs",
|
||||
workspaces: "Espaces de travail",
|
||||
"workspace-chats": "Chat de l'espace de travail",
|
||||
appearance: "Apparence",
|
||||
customization: "Apparence",
|
||||
"api-keys": "Clés API",
|
||||
llm: "Préférence LLM",
|
||||
transcription: "Modèle de transcription",
|
||||
|
@ -31,6 +31,10 @@ const TRANSLATIONS = {
|
|||
security: "Sécurité",
|
||||
"event-logs": "Journaux d'événements",
|
||||
privacy: "Confidentialité et données",
|
||||
"ai-providers": "Fournisseurs d'IA",
|
||||
"agent-skills": "Compétences de l'agent",
|
||||
admin: "Admin",
|
||||
tools: "Outils",
|
||||
},
|
||||
|
||||
// Page Definitions
|
||||
|
|
|
@ -17,7 +17,7 @@ const TRANSLATIONS = {
|
|||
users: "Пользователи",
|
||||
workspaces: "Рабочие пространства",
|
||||
"workspace-chats": "Чат рабочего пространства",
|
||||
appearance: "Внешний вид",
|
||||
customization: "Внешний вид",
|
||||
"api-keys": "API ключи",
|
||||
llm: "Предпочтение LLM",
|
||||
transcription: "Модель транскрипции",
|
||||
|
@ -29,6 +29,10 @@ const TRANSLATIONS = {
|
|||
security: "Безопасность",
|
||||
"event-logs": "Журналы событий",
|
||||
privacy: "Конфиденциальность и данные",
|
||||
"ai-providers": "Поставщики ИИ",
|
||||
"agent-skills": "Навыки агента",
|
||||
admin: "Администратор",
|
||||
tools: "Инструменты",
|
||||
},
|
||||
login: {
|
||||
"multi-user": {
|
||||
|
|
|
@ -20,7 +20,7 @@ const TRANSLATIONS = {
|
|||
users: "用户",
|
||||
workspaces: "工作区",
|
||||
"workspace-chats": "对话历史记录", // "workspace-chats" should be "对话历史记录", means "chat history",or "chat history records"
|
||||
appearance: "外观",
|
||||
customization: "外观",
|
||||
"api-keys": "API 密钥",
|
||||
llm: "LLM 首选项",
|
||||
transcription: "Transcription 模型",
|
||||
|
@ -32,6 +32,10 @@ const TRANSLATIONS = {
|
|||
security: "用户与安全",
|
||||
"event-logs": "事件日志",
|
||||
privacy: "隐私与数据",
|
||||
"ai-providers": "人工智能提供商",
|
||||
"agent-skills": "代理技能",
|
||||
admin: "管理员",
|
||||
tools: "工具",
|
||||
},
|
||||
|
||||
// Page Definitions
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
darkMode: 'false',
|
||||
darkMode: "false",
|
||||
content: {
|
||||
relative: true,
|
||||
files: [
|
||||
|
@ -11,7 +11,7 @@ export default {
|
|||
"./src/utils/**/*.js",
|
||||
"./src/*.jsx",
|
||||
"./index.html",
|
||||
'./node_modules/@tremor/**/*.{js,ts,jsx,tsx}'
|
||||
"./node_modules/@tremor/**/*.{js,ts,jsx,tsx}"
|
||||
]
|
||||
},
|
||||
theme: {
|
||||
|
@ -35,7 +35,8 @@ export default {
|
|||
"dark-highlight": "#1C1E21",
|
||||
"dark-text": "#222628",
|
||||
description: "#D2D5DB",
|
||||
"x-button": "#9CA3AF"
|
||||
"x-button": "#9CA3AF",
|
||||
darker: "#F4F4F4"
|
||||
},
|
||||
backgroundImage: {
|
||||
"preference-gradient":
|
||||
|
@ -101,30 +102,30 @@ export default {
|
|||
{
|
||||
pattern:
|
||||
/^(bg-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
|
||||
variants: ['hover', 'ui-selected'],
|
||||
variants: ["hover", "ui-selected"]
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
/^(text-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
|
||||
variants: ['hover', 'ui-selected'],
|
||||
variants: ["hover", "ui-selected"]
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
/^(border-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
|
||||
variants: ['hover', 'ui-selected'],
|
||||
variants: ["hover", "ui-selected"]
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
/^(ring-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
|
||||
/^(ring-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
/^(stroke-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
|
||||
/^(stroke-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/
|
||||
},
|
||||
{
|
||||
pattern:
|
||||
/^(fill-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/,
|
||||
},
|
||||
/^(fill-(?:slate|gray|zinc|neutral|stone|red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose)-(?:50|100|200|300|400|500|600|700|800|900|950))$/
|
||||
}
|
||||
],
|
||||
plugins: []
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue