mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2024-10-02 08:50:11 +02:00
[FEAT] Settings pages UI updates (#833)
* WIP main sidebar designs * hover states and active states for main sidebar * main and settings sidebar UI updates & improve performance using Link instead of <a> * update borders to match rest of UI in all pages * update borders of all containers to match rest of UI * remove unneeded conditional * custom messages component redesign and appearance settings layout changes * improve UX of custom logo file uploader component to match designs * fix sizing on custom logo upload field * WIP footer customization new UI * implement new UI for custom footer icon selection * update workspace chats to match new settings UI * update system preferences to match new settings UI * update export workspace chats button border * update invitations settings page to match new settings UI * update instance workspaces settings page to match new settings UI * update instance workspaces to match new settings UI * update api keys settings to match new settings UI * update LLM preferences settings to match new settings UI * update embedding preferences settings to match new settings UI * update vector db preferences settings to match new settings UI * align all buttons in settings pages * update ui for data connectors to match rest of settings ui * update UI for embed chat * updated ui for logging page * fix duplicate attributes left from merge conflicts * fix dynamic class to use ternary * remove transition classes where it is not needed
This commit is contained in:
parent
0f31e43fd4
commit
d9fce5f65e
@ -13,27 +13,27 @@ export default function EditingChatBubble({
|
|||||||
const isUser = type === "user";
|
const isUser = type === "user";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div>
|
||||||
|
<p className={`text-xs text-[#D3D4D4] ${isUser ? "text-right" : ""}`}>
|
||||||
|
{isUser ? "User" : "AnythingLLM Chat Assistant"}
|
||||||
|
</p>
|
||||||
<div
|
<div
|
||||||
className={`relative flex w-full mt-2 items-start ${
|
className={`relative flex w-full mt-2 items-start ${
|
||||||
isUser ? "justify-end" : "justify-start"
|
isUser ? "justify-end" : "justify-start"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
className={`transition-all duration-300 absolute z-10 text-white bg-neutral-700 rounded-full hover:bg-selected-preference-gradient hover:border-white border-transparent border shadow-lg ${
|
className={`transition-all duration-300 absolute z-10 text-white rounded-full hover:bg-neutral-700 hover:border-white border-transparent border shadow-lg ${
|
||||||
isUser ? "right-0 mr-2" : "ml-2"
|
isUser ? "right-0 mr-2" : "ml-2"
|
||||||
}`}
|
}`}
|
||||||
style={{ top: "-8px", [isUser ? "right" : "left"]: "255px" }}
|
style={{ top: "6px", [isUser ? "right" : "left"]: "290px" }}
|
||||||
onClick={() => removeMessage(index)}
|
onClick={() => removeMessage(index)}
|
||||||
>
|
>
|
||||||
<X className="m-0.5" size={20} />
|
<X className="m-0.5" size={20} />
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
className={`p-4 max-w-full md:w-[290px] ${
|
className={`p-2 max-w-full md:w-[290px] text-black rounded-[8px] ${
|
||||||
isUser ? "bg-sky-400 text-black" : "bg-white text-black"
|
isUser ? "bg-[#41444C] text-white" : "bg-[#2E3036] text-white"
|
||||||
} ${
|
|
||||||
isUser
|
|
||||||
? "rounded-tr-[40px] rounded-tl-[40px] rounded-bl-[40px]"
|
|
||||||
: "rounded-br-[40px] rounded-tl-[40px] rounded-tr-[40px]"
|
|
||||||
}
|
}
|
||||||
}`}
|
}`}
|
||||||
onDoubleClick={() => setIsEditing(true)}
|
onDoubleClick={() => setIsEditing(true)}
|
||||||
@ -47,16 +47,19 @@ export default function EditingChatBubble({
|
|||||||
setIsEditing(false);
|
setIsEditing(false);
|
||||||
}}
|
}}
|
||||||
autoFocus
|
autoFocus
|
||||||
className="w-full"
|
className={`w-full ${
|
||||||
|
isUser ? "bg-[#41444C] text-white" : "bg-[#2E3036] text-white"
|
||||||
|
}`}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
tempMessage && (
|
tempMessage && (
|
||||||
<p className="text-black font-[500] md:font-semibold text-sm md:text-base break-words">
|
<p className=" font-[500] md:font-semibold text-sm md:text-base break-words">
|
||||||
{tempMessage}
|
{tempMessage}
|
||||||
</p>
|
</p>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -149,11 +149,13 @@ export default function SettingsSidebar() {
|
|||||||
<SidebarOptions user={user} />
|
<SidebarOptions user={user} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="mb-2">
|
||||||
<Footer />
|
<Footer />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,11 +92,11 @@ export default function ActiveWorkspaces() {
|
|||||||
className={`
|
className={`
|
||||||
transition-all duration-[200ms]
|
transition-all duration-[200ms]
|
||||||
flex flex-grow w-[75%] gap-x-2 py-[6px] px-[12px] rounded-[4px] text-white justify-start items-center
|
flex flex-grow w-[75%] gap-x-2 py-[6px] px-[12px] rounded-[4px] text-white justify-start items-center
|
||||||
hover:bg-workspace-item-selected-gradient border-outline
|
hover:bg-workspace-item-selected-gradient hover:font-bold border-2 border-outline
|
||||||
${
|
${
|
||||||
isActive
|
isActive
|
||||||
? "bg-workspace-item-selected-gradient font-medium border-none"
|
? "bg-workspace-item-selected-gradient font-bold"
|
||||||
: "border-[1px]"
|
: ""
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex flex-row justify-between w-full">
|
<div className="flex flex-row justify-between w-full">
|
||||||
|
@ -38,10 +38,10 @@ export default function Sidebar() {
|
|||||||
<div
|
<div
|
||||||
ref={sidebarRef}
|
ref={sidebarRef}
|
||||||
style={{ height: "calc(100% - 76px)" }}
|
style={{ height: "calc(100% - 76px)" }}
|
||||||
className="transition-all pt-[11px] px-[10px] duration-500 relative m-[16px] rounded-[16px] bg-sidebar border-2 border-outline min-w-[250px]"
|
className="relative m-[16px] rounded-[16px] bg-sidebar border-2 border-outline min-w-[250px] p-[10px]"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col h-full overflow-x-hidden">
|
<div className="flex flex-col h-full overflow-x-hidden">
|
||||||
<div className="flex-grow flex flex-col w-[235px]">
|
<div className="flex-grow flex flex-col min-w-[235px]">
|
||||||
<div className="flex flex-col gap-y-2 pb-8 overflow-y-scroll no-scroll">
|
<div className="flex flex-col gap-y-2 pb-8 overflow-y-scroll no-scroll">
|
||||||
<div className="flex gap-x-2 items-center justify-between">
|
<div className="flex gap-x-2 items-center justify-between">
|
||||||
{(!user || user?.role !== "default") && (
|
{(!user || user?.role !== "default") && (
|
||||||
@ -144,9 +144,11 @@ export function SidebarMobileHeader() {
|
|||||||
style={{ objectFit: "contain" }}
|
style={{ objectFit: "contain" }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-x-2 items-center text-slate-500 shrink-0">
|
{(!user || user?.role !== "default") && (
|
||||||
|
<div className="flex gap-x-2 items-center text-slate-500 shink-0">
|
||||||
<SettingsButton />
|
<SettingsButton />
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Primary Body */}
|
{/* Primary Body */}
|
||||||
|
@ -13,25 +13,29 @@ import ModalWrapper from "@/components/ModalWrapper";
|
|||||||
|
|
||||||
export default function AdminInvites() {
|
export default function AdminInvites() {
|
||||||
const { isOpen, openModal, closeModal } = useModal();
|
const { isOpen, openModal, closeModal } = useModal();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
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-main-gradient w-full h-full overflow-y-scroll border-2 border-outline"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="items-center flex gap-x-4">
|
||||||
<p className="text-2xl font-semibold text-white">Invitations</p>
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
|
Invitations
|
||||||
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={openModal}
|
onClick={openModal}
|
||||||
className="border border-slate-200 px-4 py-1 rounded-lg text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800"
|
className="flex items-center gap-x-2 px-4 py-2 rounded-lg bg-[#2C2F36] text-white text-sm hover:bg-[#3D4147] shadow-md border border-[#3D4147]"
|
||||||
>
|
>
|
||||||
<EnvelopeSimple className="h-4 w-4" /> Create Invite Link
|
<EnvelopeSimple className="h-4 w-4" />
|
||||||
|
Create Invite Link
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
Create invitation links for people in your organization to accept
|
Create invitation links for people in your organization to accept
|
||||||
and sign up with. Invitations can only be used by a single user.
|
and sign up with. Invitations can only be used by a single user.
|
||||||
</p>
|
</p>
|
||||||
@ -50,6 +54,7 @@ function InvitationsContainer() {
|
|||||||
const darkMode = usePrefersDarkMode();
|
const darkMode = usePrefersDarkMode();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [invites, setInvites] = useState([]);
|
const [invites, setInvites] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchInvites() {
|
async function fetchInvites() {
|
||||||
const _invites = await Admin.invites();
|
const _invites = await Admin.invites();
|
||||||
@ -74,13 +79,13 @@ function InvitationsContainer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table className="md:w-3/4 w-full text-sm text-left rounded-lg mt-5">
|
<table className="w-full text-sm text-left rounded-lg mt-6">
|
||||||
<thead className="text-white text-opacity-80 text-sm font-bold uppercase border-white border-b border-opacity-60">
|
<thead className="text-white text-opacity-80 text-xs leading-[18px] font-bold uppercase border-white border-b border-opacity-60">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" className="px-6 py-3">
|
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
||||||
Status
|
Status
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
<th scope="col" className="px-6 py-3">
|
||||||
Accepted By
|
Accepted By
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" className="px-6 py-3">
|
<th scope="col" className="px-6 py-3">
|
||||||
|
@ -30,20 +30,22 @@ export default function AdminLogs() {
|
|||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
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-main-gradient w-full h-full overflow-y-scroll border-2 border-outline"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="flex gap-x-4 items-center">
|
||||||
<p className="text-2xl font-semibold text-white">Event Logs</p>
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
|
Event Logs
|
||||||
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={handleResetLogs}
|
onClick={handleResetLogs}
|
||||||
className="px-4 py-1 rounded-lg text-slate-200/50 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800"
|
className="flex items-center gap-x-2 px-4 py-2 rounded-lg bg-[#2C2F36] text-white text-sm hover:bg-[#3D4147] shadow-md border border-[#3D4147]"
|
||||||
>
|
>
|
||||||
Clear event logs
|
Clear event logs
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
View all actions and events happening on this instance for
|
View all actions and events happening on this instance for
|
||||||
monitoring.
|
monitoring.
|
||||||
</p>
|
</p>
|
||||||
@ -95,10 +97,10 @@ function LogsContainer() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<table className="md:w-5/6 w-full text-sm text-left rounded-lg mt-5">
|
<table className="w-full text-sm text-left rounded-lg mt-6">
|
||||||
<thead className="text-white text-opacity-80 text-sm font-bold uppercase border-white border-b border-opacity-60">
|
<thead className="text-white text-opacity-80 text-xs leading-[18px] font-bold uppercase border-white border-b border-opacity-60">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" className="px-6 py-3">
|
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
||||||
Event Type
|
Event Type
|
||||||
</th>
|
</th>
|
||||||
<th scope="col" className="px-6 py-3">
|
<th scope="col" className="px-6 py-3">
|
||||||
@ -116,7 +118,7 @@ function LogsContainer() {
|
|||||||
{!!logs && logs.map((log) => <LogRow key={log.id} log={log} />)}
|
{!!logs && logs.map((log) => <LogRow key={log.id} log={log} />)}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div className="flex w-full justify-between items-center">
|
<div className="flex w-full justify-between items-center mt-6">
|
||||||
<button
|
<button
|
||||||
onClick={handlePrevious}
|
onClick={handlePrevious}
|
||||||
className="px-4 py-2 rounded-lg border border-slate-200 text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 disabled:invisible"
|
className="px-4 py-2 rounded-lg border border-slate-200 text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 disabled:invisible"
|
||||||
|
@ -12,6 +12,7 @@ export default function AdminSystem() {
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
limit: 10,
|
limit: 10,
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
@ -43,46 +44,35 @@ export default function AdminSystem() {
|
|||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
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-main-gradient w-full h-full overflow-y-scroll border-2 border-outline"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
onChange={() => setHasChanges(true)}
|
onChange={() => setHasChanges(true)}
|
||||||
className="flex w-full"
|
className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="items-center">
|
||||||
<p className="text-2xl font-semibold text-white">
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
System Preferences
|
System Preferences
|
||||||
</p>
|
</p>
|
||||||
{hasChanges && (
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
disabled={saving}
|
|
||||||
className="border border-slate-200 px-4 py-1 rounded-lg text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800"
|
|
||||||
>
|
|
||||||
{saving ? "Saving..." : "Save changes"}
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
These are the overall settings and configurations of your
|
These are the overall settings and configurations of your
|
||||||
instance.
|
instance.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="my-5">
|
<div className="mt-6 mb-8">
|
||||||
<div className="flex flex-col gap-y-2 mb-2.5">
|
<div className="flex flex-col gap-y-1">
|
||||||
<label className="leading-tight font-semibold text-white">
|
<h2 className="text-base leading-6 font-bold text-white">
|
||||||
Users can delete workspaces
|
Users can delete workspaces
|
||||||
</label>
|
</h2>
|
||||||
<p className="leading-tight text-sm text-white text-opacity-60 w-96">
|
<p className="text-xs leading-[18px] font-base text-white/60">
|
||||||
Allow non-admin users to delete workspaces that they are a
|
Allow non-admin users to delete workspaces that they are a part
|
||||||
part of. This would delete the workspace for everyone.
|
of. This would delete the workspace for everyone.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<label className="relative inline-flex cursor-pointer items-center mt-2">
|
||||||
<label className="relative inline-flex cursor-pointer items-center">
|
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
name="users_can_delete_workspaces"
|
name="users_can_delete_workspaces"
|
||||||
@ -94,18 +84,19 @@ export default function AdminSystem() {
|
|||||||
<span className="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300"></span>
|
<span className="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="my-4">
|
|
||||||
<div className="flex flex-col gap-y-2 mb-2.5">
|
|
||||||
<label className="leading-tight font-medium text-black dark:text-white">
|
|
||||||
Limit messages per user per day
|
|
||||||
</label>
|
|
||||||
<p className="leading-tight text-sm text-white text-opacity-60 w-96">
|
|
||||||
Restrict non-admin users to a number of successful queries or
|
|
||||||
chats within a 24 hour window. Enable this to prevent users
|
|
||||||
from running up OpenAI costs.
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="mb-8">
|
||||||
|
<div className="flex flex-col gap-y-1">
|
||||||
|
<h2 className="text-base leading-6 font-bold text-white">
|
||||||
|
Limit messages per user per day
|
||||||
|
</h2>
|
||||||
|
<p className="text-xs leading-[18px] font-base text-white/60">
|
||||||
|
Restrict non-admin users to a number of successful queries or
|
||||||
|
chats within a 24 hour window. Enable this to prevent users from
|
||||||
|
running up OpenAI costs.
|
||||||
|
</p>
|
||||||
|
<div className="mt-2">
|
||||||
<label className="relative inline-flex cursor-pointer items-center">
|
<label className="relative inline-flex cursor-pointer items-center">
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
@ -124,12 +115,13 @@ export default function AdminSystem() {
|
|||||||
<span className="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300"></span>
|
<span className="ml-3 text-sm font-medium text-gray-900 dark:text-gray-300"></span>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
{messageLimit.enabled && (
|
{messageLimit.enabled && (
|
||||||
<div className="mb-4">
|
<div className="mt-4">
|
||||||
<label className=" block flex items-center gap-x-1 font-medium text-black dark:text-white">
|
<label className="block text-sm font-medium text-white">
|
||||||
Message limit per day
|
Message limit per day
|
||||||
</label>
|
</label>
|
||||||
<div className="relative">
|
<div className="relative mt-2">
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
name="message_limit"
|
name="message_limit"
|
||||||
@ -143,12 +135,24 @@ export default function AdminSystem() {
|
|||||||
value={messageLimit.limit}
|
value={messageLimit.limit}
|
||||||
min={1}
|
min={1}
|
||||||
max={300}
|
max={300}
|
||||||
className="w-1/3 my-2 rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-gray-800 dark:text-slate-200 outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary"
|
className="w-1/3 rounded-lg border border-stroke bg-transparent py-4 pl-6 pr-10 text-gray-800 dark:text-slate-200 outline-none focus:border-primary focus-visible:shadow-none dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{hasChanges && (
|
||||||
|
<div className="flex justify-start">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={saving}
|
||||||
|
className="flex items-center gap-x-2 px-4 py-2 rounded-lg bg-[#2C2F36] text-white text-sm hover:bg-[#3D4147] shadow-md border border-[#3D4147]"
|
||||||
|
>
|
||||||
|
{saving ? "Saving..." : "Save changes"}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -13,25 +13,26 @@ import ModalWrapper from "@/components/ModalWrapper";
|
|||||||
|
|
||||||
export default function AdminUsers() {
|
export default function AdminUsers() {
|
||||||
const { isOpen, openModal, closeModal } = useModal();
|
const { isOpen, openModal, closeModal } = useModal();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
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-main-gradient w-full h-full overflow-y-scroll border-2 border-outline"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="items-center flex gap-x-4">
|
||||||
<p className="text-2xl font-semibold text-white">Users</p>
|
<p className="text-lg leading-6 font-bold text-white">Users</p>
|
||||||
<button
|
<button
|
||||||
onClick={openModal}
|
onClick={openModal}
|
||||||
className="border border-slate-200 px-4 py-1 rounded-lg text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800"
|
className="flex items-center gap-x-2 px-4 py-2 rounded-lg bg-[#2C2F36] text-white text-sm hover:bg-[#3D4147] shadow-md border border-[#3D4147]"
|
||||||
>
|
>
|
||||||
<UserPlus className="h-4 w-4" /> Add user
|
<UserPlus className="h-4 w-4" /> Add user
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
These are all the accounts which have an account on this instance.
|
These are all the accounts which have an account on this instance.
|
||||||
Removing an account will instantly remove their access to this
|
Removing an account will instantly remove their access to this
|
||||||
instance.
|
instance.
|
||||||
@ -51,6 +52,7 @@ function UsersContainer() {
|
|||||||
const { user: currUser } = useUser();
|
const { user: currUser } = useUser();
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [users, setUsers] = useState([]);
|
const [users, setUsers] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchUsers() {
|
async function fetchUsers() {
|
||||||
const _users = await Admin.users();
|
const _users = await Admin.users();
|
||||||
@ -75,8 +77,8 @@ function UsersContainer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table className="md:w-3/4 w-full text-sm text-left rounded-lg mt-5">
|
<table className="w-full text-sm text-left rounded-lg mt-6">
|
||||||
<thead className="text-white text-opacity-80 text-sm font-bold uppercase border-white border-b border-opacity-60">
|
<thead className="text-white text-opacity-80 text-xs leading-[18px] font-bold uppercase border-white border-b border-opacity-60">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
||||||
Username
|
Username
|
||||||
@ -120,7 +122,7 @@ const ROLE_HINT = {
|
|||||||
export function RoleHintDisplay({ role }) {
|
export function RoleHintDisplay({ role }) {
|
||||||
return (
|
return (
|
||||||
<div className="flex flex-col gap-y-1 py-1 pb-4">
|
<div className="flex flex-col gap-y-1 py-1 pb-4">
|
||||||
<p className="text-white/60 font-semibold text-sm">Permissions</p>
|
<p className="text-sm font-medium text-white">Permissions</p>
|
||||||
<ul className="flex flex-col gap-y-1 list-disc px-4">
|
<ul className="flex flex-col gap-y-1 list-disc px-4">
|
||||||
{ROLE_HINT[role ?? "default"].map((hints, i) => {
|
{ROLE_HINT[role ?? "default"].map((hints, i) => {
|
||||||
return (
|
return (
|
||||||
|
@ -13,27 +13,28 @@ import ModalWrapper from "@/components/ModalWrapper";
|
|||||||
|
|
||||||
export default function AdminWorkspaces() {
|
export default function AdminWorkspaces() {
|
||||||
const { isOpen, openModal, closeModal } = useModal();
|
const { isOpen, openModal, closeModal } = useModal();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
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-main-gradient w-full h-full overflow-y-scroll border-2 border-outline"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="items-center flex gap-x-4">
|
||||||
<p className="text-2xl font-semibold text-white">
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
Instance workspaces
|
Instance Workspaces
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={openModal}
|
onClick={openModal}
|
||||||
className="border border-slate-200 px-4 py-1 rounded-lg text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800"
|
className="flex items-center gap-x-2 px-4 py-2 rounded-lg bg-[#2C2F36] text-white text-sm hover:bg-[#3D4147] shadow-md border border-[#3D4147]"
|
||||||
>
|
>
|
||||||
<BookOpen className="h-4 w-4" /> New Workspace
|
<BookOpen className="h-4 w-4" /> New Workspace
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
These are all the workspaces that exist on this instance. Removing
|
These are all the workspaces that exist on this instance. Removing
|
||||||
a workspace will delete all of it's associated chats and settings.
|
a workspace will delete all of it's associated chats and settings.
|
||||||
</p>
|
</p>
|
||||||
@ -80,8 +81,8 @@ function WorkspacesContainer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table className="md:w-3/4 w-full text-sm text-left rounded-lg mt-5">
|
<table className="w-full text-sm text-left rounded-lg mt-6">
|
||||||
<thead className="text-white text-opacity-80 text-sm font-bold uppercase border-white border-b border-opacity-60">
|
<thead className="text-white text-opacity-80 text-xs leading-[18px] font-bold uppercase border-white border-b border-opacity-60">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
||||||
Name
|
Name
|
||||||
|
@ -15,25 +15,26 @@ import { useModal } from "@/hooks/useModal";
|
|||||||
|
|
||||||
export default function AdminApiKeys() {
|
export default function AdminApiKeys() {
|
||||||
const { isOpen, openModal, closeModal } = useModal();
|
const { isOpen, openModal, closeModal } = useModal();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
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-main-gradient w-full h-full overflow-y-scroll border-2 border-outline"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="items-center flex gap-x-4">
|
||||||
<p className="text-2xl font-semibold text-white">API Keys</p>
|
<p className="text-lg leading-6 font-bold text-white">API Keys</p>
|
||||||
<button
|
<button
|
||||||
onClick={openModal}
|
onClick={openModal}
|
||||||
className="border border-slate-200 px-4 py-1 rounded-lg text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800"
|
className="flex items-center gap-x-2 px-4 py-2 rounded-lg bg-[#2C2F36] text-white text-sm hover:bg-[#3D4147] shadow-md border border-[#3D4147]"
|
||||||
>
|
>
|
||||||
<PlusCircle className="h-4 w-4" /> Generate New API Key
|
<PlusCircle className="h-4 w-4" /> Generate New API Key
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
API keys allow the holder to programmatically access and manage
|
API keys allow the holder to programmatically access and manage
|
||||||
this AnythingLLM instance.
|
this AnythingLLM instance.
|
||||||
</p>
|
</p>
|
||||||
@ -41,7 +42,7 @@ export default function AdminApiKeys() {
|
|||||||
href={paths.apiDocs()}
|
href={paths.apiDocs()}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noreferrer"
|
rel="noreferrer"
|
||||||
className="text-sm font-base text-blue-300 hover:underline"
|
className="text-xs leading-[18px] font-base text-blue-300 hover:underline"
|
||||||
>
|
>
|
||||||
Read the API documentation →
|
Read the API documentation →
|
||||||
</a>
|
</a>
|
||||||
@ -59,11 +60,11 @@ export default function AdminApiKeys() {
|
|||||||
function ApiKeysContainer() {
|
function ApiKeysContainer() {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [apiKeys, setApiKeys] = useState([]);
|
const [apiKeys, setApiKeys] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchExistingKeys() {
|
async function fetchExistingKeys() {
|
||||||
const user = userFromStorage();
|
const user = userFromStorage();
|
||||||
const Model = !!user ? Admin : System;
|
const Model = !!user ? Admin : System;
|
||||||
|
|
||||||
const { apiKeys: foundKeys } = await Model.getApiKeys();
|
const { apiKeys: foundKeys } = await Model.getApiKeys();
|
||||||
setApiKeys(foundKeys);
|
setApiKeys(foundKeys);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
@ -86,8 +87,8 @@ function ApiKeysContainer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table className="md:w-3/4 w-full text-sm text-left rounded-lg mt-5">
|
<table className="w-full text-sm text-left rounded-lg mt-6">
|
||||||
<thead className="text-white text-opacity-80 text-sm font-bold uppercase border-white border-b border-opacity-60">
|
<thead className="text-white text-opacity-80 text-xs leading-[18px] font-bold uppercase border-white border-b border-opacity-60">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
||||||
API Key
|
API Key
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import useLogo from "@/hooks/useLogo";
|
import useLogo from "@/hooks/useLogo";
|
||||||
import System from "@/models/system";
|
import System from "@/models/system";
|
||||||
import showToast from "@/utils/toast";
|
import showToast from "@/utils/toast";
|
||||||
import { useEffect, useState } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import AnythingLLM from "@/media/logo/anything-llm.png";
|
import AnythingLLM from "@/media/logo/anything-llm.png";
|
||||||
import { Plus } from "@phosphor-icons/react";
|
import { Plus } from "@phosphor-icons/react";
|
||||||
|
|
||||||
@ -9,6 +9,7 @@ export default function CustomLogo() {
|
|||||||
const { logo: _initLogo, setLogo: _setLogo } = useLogo();
|
const { logo: _initLogo, setLogo: _setLogo } = useLogo();
|
||||||
const [logo, setLogo] = useState("");
|
const [logo, setLogo] = useState("");
|
||||||
const [isDefaultLogo, setIsDefaultLogo] = useState(true);
|
const [isDefaultLogo, setIsDefaultLogo] = useState(true);
|
||||||
|
const fileInputRef = useRef(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function logoInit() {
|
async function logoInit() {
|
||||||
@ -62,25 +63,25 @@ export default function CustomLogo() {
|
|||||||
showToast("Image successfully removed.", "success");
|
showToast("Image successfully removed.", "success");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const triggerFileInputClick = () => {
|
||||||
|
fileInputRef.current?.click();
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="my-6">
|
<div className="mt-6 mb-8">
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-1">
|
||||||
<h2 className="leading-tight font-medium text-white">Custom Logo</h2>
|
<h2 className="text-base leading-6 font-bold text-white">
|
||||||
<p className="text-sm font-base text-white/60">
|
Custom Logo
|
||||||
|
</h2>
|
||||||
|
<p className="text-xs leading-[18px] font-base text-white/60">
|
||||||
Upload your custom logo to make your chatbot yours.
|
Upload your custom logo to make your chatbot yours.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
{isDefaultLogo ? (
|
||||||
<div className="flex md:flex-row flex-col items-center">
|
<div className="flex md:flex-row flex-col items-center">
|
||||||
<img
|
|
||||||
src={logo}
|
|
||||||
alt="Uploaded Logo"
|
|
||||||
className="w-48 h-48 object-contain mr-6"
|
|
||||||
hidden={isDefaultLogo}
|
|
||||||
onError={(e) => (e.target.src = AnythingLLM)}
|
|
||||||
/>
|
|
||||||
<div className="flex flex-row gap-x-8">
|
<div className="flex flex-row gap-x-8">
|
||||||
<label
|
<label
|
||||||
className="mt-5 transition-all duration-300 hover:opacity-60"
|
className="mt-3 transition-all duration-300 hover:opacity-60"
|
||||||
hidden={!isDefaultLogo}
|
hidden={!isDefaultLogo}
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
@ -107,16 +108,43 @@ export default function CustomLogo() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
{!isDefaultLogo && (
|
</div>
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="flex md:flex-row flex-col items-center relative">
|
||||||
|
<div className="group w-80 h-[130px] mt-3 overflow-hidden">
|
||||||
|
<img
|
||||||
|
src={logo}
|
||||||
|
alt="Uploaded Logo"
|
||||||
|
className="w-full h-full object-cover border-2 border-white/20 border-dashed p-1 rounded-2xl"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="absolute w-80 top-0 left-0 right-0 bottom-0 flex flex-col gap-y-3 justify-center items-center rounded-2xl mt-3 bg-black bg-opacity-80 opacity-0 group-hover:opacity-100 transition-opacity duration-300 ease-in-out border-2 border-transparent hover:border-white">
|
||||||
|
<button
|
||||||
|
onClick={triggerFileInputClick}
|
||||||
|
className="text-white text-base font-medium hover:text-opacity-60 mx-2"
|
||||||
|
>
|
||||||
|
Replace
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<input
|
||||||
|
id="logo-upload"
|
||||||
|
type="file"
|
||||||
|
accept="image/*"
|
||||||
|
className="hidden"
|
||||||
|
onChange={handleFileUpload}
|
||||||
|
ref={fileInputRef}
|
||||||
|
/>
|
||||||
<button
|
<button
|
||||||
onClick={handleRemoveLogo}
|
onClick={handleRemoveLogo}
|
||||||
className="text-white text-base font-medium hover:text-opacity-60"
|
className="text-white text-base font-medium hover:text-opacity-60 mx-2"
|
||||||
>
|
>
|
||||||
Delete
|
Remove
|
||||||
</button>
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -53,16 +53,16 @@ export default function CustomMessages() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-6">
|
<div className="mb-8">
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-1">
|
||||||
<h2 className="leading-tight font-medium text-white">
|
<h2 className="text-base leading-6 font-bold text-white">
|
||||||
Custom Messages
|
Custom Messages
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm font-base text-white/60">
|
<p className="text-xs leading-[18px] font-base text-white/60">
|
||||||
Customize the automatic messages displayed to your users.
|
Customize the automatic messages displayed to your users.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 flex flex-col gap-y-6 bg-zinc-900 rounded-lg px-6 pt-4 max-w-[700px]">
|
<div className="mt-3 flex flex-col gap-y-6 bg-[#1C1E21] rounded-lg pr-[31px] pl-[12px] pt-4 max-w-[700px]">
|
||||||
{messages.map((message, index) => (
|
{messages.map((message, index) => (
|
||||||
<div key={index} className="flex flex-col gap-y-2">
|
<div key={index} className="flex flex-col gap-y-2">
|
||||||
{message.user && (
|
{message.user && (
|
||||||
@ -85,27 +85,34 @@ export default function CustomMessages() {
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
<div className="flex gap-4 mt-12 justify-between pb-7">
|
<div className="flex gap-4 mt-12 justify-between pb-[15px]">
|
||||||
<button
|
<button
|
||||||
className="self-end text-white hover:text-white/60 transition"
|
className="self-end text-white hover:text-white/60 transition"
|
||||||
onClick={() => addMessage("response")}
|
onClick={() => addMessage("response")}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-start">
|
<div className="flex items-center justify-start text-sm font-normal -ml-2">
|
||||||
<Plus className="w-5 h-5 m-2" weight="fill" /> New System Message
|
<Plus className="m-2" size={16} weight="bold" />
|
||||||
|
<span className="leading-5">
|
||||||
|
New <span className="font-bold italic mr-1">system</span>{" "}
|
||||||
|
message
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="self-end text-sky-400 hover:text-sky-400/60 transition"
|
className="self-end text-white hover:text-white/60 transition"
|
||||||
onClick={() => addMessage("user")}
|
onClick={() => addMessage("user")}
|
||||||
>
|
>
|
||||||
<div className="flex items-center">
|
<div className="flex items-center justify-start text-sm font-normal">
|
||||||
<Plus className="w-5 h-5 m-2" weight="fill" /> New User Message
|
<Plus className="m-2" size={16} weight="bold" />
|
||||||
|
<span className="leading-5">
|
||||||
|
New <span className="font-bold italic mr-1">user</span> message
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{hasChanges && (
|
{hasChanges && (
|
||||||
<div className="flex justify-center py-6">
|
<div className="flex justify-start pt-6">
|
||||||
<button
|
<button
|
||||||
className="transition-all duration-300 border border-slate-200 px-4 py-2 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
|
className="transition-all duration-300 border border-slate-200 px-4 py-2 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
|
||||||
onClick={handleMessageSave}
|
onClick={handleMessageSave}
|
||||||
|
@ -1,11 +1,20 @@
|
|||||||
import { ICON_COMPONENTS } from "@/components/Footer";
|
import { ICON_COMPONENTS } from "@/components/Footer";
|
||||||
import React, { useEffect, useRef, useState } from "react";
|
import React, { useEffect, useRef, useState } from "react";
|
||||||
|
import { Plus, X } from "@phosphor-icons/react";
|
||||||
|
|
||||||
export default function NewIconForm({ handleSubmit, showing }) {
|
export default function NewIconForm({ icon, url, onSave, onRemove }) {
|
||||||
const [selectedIcon, setSelectedIcon] = useState("Info");
|
const [selectedIcon, setSelectedIcon] = useState(icon || "Plus");
|
||||||
|
const [selectedUrl, setSelectedUrl] = useState(url || "");
|
||||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
const [isDropdownOpen, setIsDropdownOpen] = useState(false);
|
||||||
|
const [isEdited, setIsEdited] = useState(false);
|
||||||
const dropdownRef = useRef(null);
|
const dropdownRef = useRef(null);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedIcon(icon || "Plus");
|
||||||
|
setSelectedUrl(url || "");
|
||||||
|
setIsEdited(false);
|
||||||
|
}, [icon, url]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
function handleClickOutside(event) {
|
function handleClickOutside(event) {
|
||||||
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
||||||
@ -17,48 +26,55 @@ export default function NewIconForm({ handleSubmit, showing }) {
|
|||||||
return () => document.removeEventListener("mousedown", handleClickOutside);
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
||||||
}, [dropdownRef]);
|
}, [dropdownRef]);
|
||||||
|
|
||||||
if (!showing) return null;
|
const handleSubmit = (e) => {
|
||||||
return (
|
|
||||||
<form onSubmit={handleSubmit} className="flex justify-start">
|
|
||||||
<div className="mt-6 mb-6 flex flex-col bg-zinc-900 rounded-lg px-6 py-4">
|
|
||||||
<div className="flex gap-x-4 items-center">
|
|
||||||
<div
|
|
||||||
className="relative flex flex-col items-center gap-y-4"
|
|
||||||
ref={dropdownRef}
|
|
||||||
>
|
|
||||||
<input type="hidden" name="icon" value={selectedIcon} />
|
|
||||||
<label className="text-sm font-medium text-white">Icon</label>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className={`${
|
|
||||||
isDropdownOpen
|
|
||||||
? "bg-menu-item-selected-gradient border-slate-100/50"
|
|
||||||
: ""
|
|
||||||
}border-transparent transition-all duration-300 p-2 rounded-full text-white bg-sidebar-button hover:bg-menu-item-selected-gradient hover:border-slate-100 hover:border-opacity-50 border`}
|
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setIsDropdownOpen(!isDropdownOpen);
|
if (selectedIcon !== "Plus" && selectedUrl) {
|
||||||
}}
|
onSave(selectedIcon, selectedUrl);
|
||||||
|
setIsEdited(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRemove = () => {
|
||||||
|
onRemove();
|
||||||
|
setSelectedIcon("Plus");
|
||||||
|
setSelectedUrl("");
|
||||||
|
setIsEdited(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleIconChange = (iconName) => {
|
||||||
|
setSelectedIcon(iconName);
|
||||||
|
setIsDropdownOpen(false);
|
||||||
|
setIsEdited(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleUrlChange = (e) => {
|
||||||
|
setSelectedUrl(e.target.value);
|
||||||
|
setIsEdited(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<form onSubmit={handleSubmit} className="flex items-center gap-x-1.5">
|
||||||
|
<div className="relative" ref={dropdownRef}>
|
||||||
|
<div
|
||||||
|
className="h-[34px] w-[34px] bg-[#1C1E21] rounded-full flex items-center justify-center cursor-pointer"
|
||||||
|
onClick={() => setIsDropdownOpen(!isDropdownOpen)}
|
||||||
>
|
>
|
||||||
{React.createElement(ICON_COMPONENTS[selectedIcon], {
|
{React.createElement(ICON_COMPONENTS[selectedIcon] || Plus, {
|
||||||
className: "h-5 w-5 text-white",
|
className: "h-5 w-5 text-white",
|
||||||
weight: "fill",
|
weight: selectedIcon === "Plus" ? "bold" : "fill",
|
||||||
})}
|
})}
|
||||||
</button>
|
</div>
|
||||||
{isDropdownOpen && (
|
{isDropdownOpen && (
|
||||||
<div className="absolute z-10 grid grid-cols-4 gap-4 bg-zinc-800 -mt-20 ml-44 p-1 rounded-md w-56 h-28 overflow-y-auto border border-slate-100/10">
|
<div className="absolute z-10 grid grid-cols-4 bg-[#41444C] mt-2 rounded-md w-[150px] h-[78px] overflow-y-auto border border-white/20 shadow-lg">
|
||||||
{Object.keys(ICON_COMPONENTS).map((iconName) => (
|
{Object.keys(ICON_COMPONENTS).map((iconName) => (
|
||||||
<button
|
<button
|
||||||
key={iconName}
|
key={iconName}
|
||||||
type="button"
|
type="button"
|
||||||
className="flex justify-center items-center border border-transparent hover:bg-menu-item-selected-gradient hover:border-slate-100 rounded-full"
|
className="flex justify-center items-center border border-transparent hover:bg-[#1C1E21] hover:border-slate-100 rounded-full p-2"
|
||||||
onClick={() => {
|
onClick={() => handleIconChange(iconName)}
|
||||||
setSelectedIcon(iconName);
|
|
||||||
setIsDropdownOpen(false);
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
{React.createElement(ICON_COMPONENTS[iconName], {
|
{React.createElement(ICON_COMPONENTS[iconName], {
|
||||||
className: "h-5 w-5 text-white m-2.5",
|
className: "h-5 w-5 text-white",
|
||||||
weight: "fill",
|
weight: "fill",
|
||||||
})}
|
})}
|
||||||
</button>
|
</button>
|
||||||
@ -66,33 +82,34 @@ export default function NewIconForm({ handleSubmit, showing }) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-y-4">
|
|
||||||
<label className="text-sm font-medium text-white">Link</label>
|
|
||||||
<input
|
<input
|
||||||
type="url"
|
type="url"
|
||||||
name="url"
|
value={selectedUrl}
|
||||||
required={true}
|
onChange={handleUrlChange}
|
||||||
placeholder="https://example.com"
|
placeholder="https://example.com"
|
||||||
className="bg-sidebar text-white placeholder:text-white/20 rounded-md p-2"
|
className="bg-zinc-900 text-white placeholder-white/20 text-sm rounded-md p-2.5 w-[300px] h-[32px]"
|
||||||
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
{selectedIcon !== "Plus" && (
|
||||||
{selectedIcon !== "" && (
|
<>
|
||||||
<div className="flex flex-col gap-y-4">
|
{isEdited ? (
|
||||||
<label className="text-sm font-medium text-white invisible">
|
|
||||||
Submit
|
|
||||||
</label>
|
|
||||||
<div className="flex justify-center">
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
className="transition-all duration-300 border border-slate-200 px-4 py-2 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
|
className="text-sky-400 px-2 py-2 rounded-md text-sm font-bold hover:text-sky-500"
|
||||||
>
|
>
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
) : (
|
||||||
</div>
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={handleRemove}
|
||||||
|
className="hover:text-red-500 text-white/80 px-2 py-2 rounded-md text-sm font-bold"
|
||||||
|
>
|
||||||
|
<X size={20} />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
</form>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,36 +1,37 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import showToast from "@/utils/toast";
|
import showToast from "@/utils/toast";
|
||||||
import { Plus, X } from "@phosphor-icons/react";
|
|
||||||
import { ICON_COMPONENTS, MAX_ICONS } from "@/components/Footer";
|
|
||||||
import { safeJsonParse } from "@/utils/request";
|
import { safeJsonParse } from "@/utils/request";
|
||||||
import NewIconForm from "./NewIconForm";
|
import NewIconForm from "./NewIconForm";
|
||||||
import Admin from "@/models/admin";
|
import Admin from "@/models/admin";
|
||||||
import System from "@/models/system";
|
import System from "@/models/system";
|
||||||
|
|
||||||
export default function FooterCustomization() {
|
export default function FooterCustomization() {
|
||||||
const [loading, setLoading] = useState(true);
|
const [footerIcons, setFooterIcons] = useState(Array(3).fill(null));
|
||||||
const [footerIcons, setFooterIcons] = useState([]);
|
|
||||||
const [showForm, setShowForm] = useState(false);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchFooterIcons() {
|
async function fetchFooterIcons() {
|
||||||
const settings = (await Admin.systemPreferences())?.settings;
|
const settings = (await Admin.systemPreferences())?.settings;
|
||||||
if (settings && settings.footer_data) {
|
if (settings && settings.footer_data) {
|
||||||
setFooterIcons(safeJsonParse(settings.footer_data, []));
|
const parsedIcons = safeJsonParse(settings.footer_data, []);
|
||||||
|
setFooterIcons((prevIcons) => {
|
||||||
|
const updatedIcons = [...prevIcons];
|
||||||
|
parsedIcons.forEach((icon, index) => {
|
||||||
|
updatedIcons[index] = icon;
|
||||||
|
});
|
||||||
|
return updatedIcons;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
setLoading(false);
|
|
||||||
}
|
}
|
||||||
fetchFooterIcons();
|
fetchFooterIcons();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const removeFooterIcon = async (index) => {
|
const updateFooterIcons = async (updatedIcons) => {
|
||||||
const updatedIcons = footerIcons.filter((_, i) => i !== index);
|
|
||||||
const { success, error } = await Admin.updateSystemPreferences({
|
const { success, error } = await Admin.updateSystemPreferences({
|
||||||
footer_data: JSON.stringify(updatedIcons),
|
footer_data: JSON.stringify(updatedIcons.filter((icon) => icon !== null)),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!success) {
|
if (!success) {
|
||||||
showToast(`Failed to remove footer icon - ${error}`, "error", {
|
showToast(`Failed to update footer icons - ${error}`, "error", {
|
||||||
clear: true,
|
clear: true,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
@ -38,103 +39,44 @@ export default function FooterCustomization() {
|
|||||||
|
|
||||||
window.localStorage.removeItem(System.cacheKeys.footerIcons);
|
window.localStorage.removeItem(System.cacheKeys.footerIcons);
|
||||||
setFooterIcons(updatedIcons);
|
setFooterIcons(updatedIcons);
|
||||||
showToast("Successfully removed footer icon.", "success", { clear: true });
|
showToast("Successfully updated footer icons.", "success", { clear: true });
|
||||||
};
|
};
|
||||||
|
|
||||||
const onSubmit = async (e) => {
|
const handleRemoveIcon = (index) => {
|
||||||
e.preventDefault();
|
const updatedIcons = [...footerIcons];
|
||||||
const form = new FormData(e.target);
|
updatedIcons[index] = null;
|
||||||
const icon = form.get("icon");
|
updateFooterIcons(updatedIcons);
|
||||||
const url = form.get("url");
|
|
||||||
|
|
||||||
const newIcon = { icon, url };
|
|
||||||
setFooterIcons([...footerIcons, newIcon]);
|
|
||||||
|
|
||||||
const { success, error } = await Admin.updateSystemPreferences({
|
|
||||||
footer_data: JSON.stringify([...footerIcons, newIcon]),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
showToast(`Failed to add footer icon - ${error}`, "error", {
|
|
||||||
clear: true,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
window.localStorage.removeItem(System.cacheKeys.footerIcons);
|
|
||||||
|
|
||||||
setShowForm(false);
|
|
||||||
showToast("Successfully added footer icon.", "success", { clear: true });
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mb-6">
|
<div className="mb-8">
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-1">
|
||||||
<h2 className="leading-tight font-medium text-white">
|
<h2 className="text-base leading-6 font-bold text-white">
|
||||||
Custom Footer Icons
|
Custom Footer Icons
|
||||||
</h2>
|
</h2>
|
||||||
<p className="text-sm font-base text-white/60">
|
<p className="text-xs leading-[18px] font-base text-white/60">
|
||||||
Customize the footer icons displayed on the bottom of the sidebar.
|
Customize the footer icons displayed on the bottom of the sidebar.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<CurrentIcons footerIcons={footerIcons} remove={removeFooterIcon} />
|
<div className="mt-3 flex gap-x-3 font-bold text-white text-sm">
|
||||||
<NewIconForm
|
<div>Icon</div>
|
||||||
handleSubmit={onSubmit}
|
<div>Link</div>
|
||||||
showing={footerIcons.length < MAX_ICONS && showForm}
|
|
||||||
/>
|
|
||||||
<div hidden={!(!showForm && footerIcons.length < MAX_ICONS) || loading}>
|
|
||||||
<div className="flex gap-2 mt-6">
|
|
||||||
<button
|
|
||||||
onClick={() => setShowForm(true)}
|
|
||||||
className="flex gap-x-2 items-center justify-center text-white text-sm hover:text-sky-400 transition-all duration-300"
|
|
||||||
>
|
|
||||||
Add new footer icon
|
|
||||||
<Plus className="" size={24} weight="fill" />
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="mt-2 flex flex-col gap-y-[10px]">
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function CurrentIcons({ footerIcons, remove }) {
|
|
||||||
if (footerIcons.length === 0) return null;
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col w-fit gap-y-2 mt-4">
|
|
||||||
{footerIcons.map((icon, index) => (
|
{footerIcons.map((icon, index) => (
|
||||||
<div
|
<NewIconForm
|
||||||
key={index}
|
key={index}
|
||||||
className="flex items-center justify-between bg-zinc-900 p-2 rounded-md gap-x-4"
|
icon={icon?.icon}
|
||||||
>
|
url={icon?.url}
|
||||||
<div className="flex items-center gap-x-2">
|
onSave={(newIcon, newUrl) => {
|
||||||
<IconPreview symbol={icon.icon} disabled={true} />
|
const updatedIcons = [...footerIcons];
|
||||||
<span className="text-white/60">{icon.url}</span>
|
updatedIcons[index] = { icon: newIcon, url: newUrl };
|
||||||
</div>
|
updateFooterIcons(updatedIcons);
|
||||||
|
}}
|
||||||
<button
|
onRemove={() => handleRemoveIcon(index)}
|
||||||
type="button"
|
/>
|
||||||
className="transition-all duration-300 text-neutral-700 bg-transparent rounded-full hover:bg-zinc-600 hover:border-zinc-600 hover:text-white border-transparent border shadow-lg mr-2"
|
|
||||||
onClick={() => remove(index)}
|
|
||||||
>
|
|
||||||
<X className="m-[1px]" size={20} />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const IconPreview = ({ symbol, disabled = false }) => {
|
|
||||||
const IconComponent = ICON_COMPONENTS.hasOwnProperty(symbol)
|
|
||||||
? ICON_COMPONENTS[symbol]
|
|
||||||
: ICON_COMPONENTS.Info;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
disabled={disabled}
|
|
||||||
className="disabled:pointer-events-none border-transparent transition-all duration-300 p-2 rounded-full text-white bg-sidebar-button hover:bg-menu-item-selected-gradient hover:border-slate-100 hover:border-opacity-50 border mx-1"
|
|
||||||
>
|
|
||||||
<IconComponent className="h-5 w-5 text-white" weight="fill" />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
@ -53,9 +53,11 @@ export default function SupportEmail() {
|
|||||||
if (loading || !user?.role) return null;
|
if (loading || !user?.role) return null;
|
||||||
return (
|
return (
|
||||||
<form className="mb-6" onSubmit={updateSupportEmail}>
|
<form className="mb-6" onSubmit={updateSupportEmail}>
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="flex flex-col gap-y-1">
|
||||||
<h2 className="leading-tight font-medium text-white">Support Email</h2>
|
<h2 className="text-base leading-6 font-bold text-white">
|
||||||
<p className="text-sm font-base text-white/60">
|
Support Email
|
||||||
|
</h2>
|
||||||
|
<p className="text-xs leading-[18px] font-base text-white/60">
|
||||||
Set the support email address that shows up in the user menu while
|
Set the support email address that shows up in the user menu while
|
||||||
logged into this instance.
|
logged into this instance.
|
||||||
</p>
|
</p>
|
||||||
@ -64,7 +66,7 @@ export default function SupportEmail() {
|
|||||||
<input
|
<input
|
||||||
name="supportEmail"
|
name="supportEmail"
|
||||||
type="email"
|
type="email"
|
||||||
className="bg-zinc-900 mt-4 text-white placeholder:text-white/20 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 max-w-[275px]"
|
className="bg-zinc-900 mt-3 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 max-w-[275px] placeholder:text-white/20"
|
||||||
placeholder="support@mycompany.com"
|
placeholder="support@mycompany.com"
|
||||||
required={true}
|
required={true}
|
||||||
autoComplete="off"
|
autoComplete="off"
|
||||||
|
@ -11,16 +11,16 @@ export default function Appearance() {
|
|||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
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-main-gradient w-full h-full overflow-y-scroll border-2 border-outline"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="items-center">
|
||||||
<p className="text-2xl font-semibold text-white">
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
Appearance Settings
|
Appearance
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
Customize the appearance settings of your platform.
|
Customize the appearance settings of your platform.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
@ -7,7 +7,7 @@ import useQuery from "@/hooks/useQuery";
|
|||||||
import ChatRow from "./ChatRow";
|
import ChatRow from "./ChatRow";
|
||||||
import showToast from "@/utils/toast";
|
import showToast from "@/utils/toast";
|
||||||
import System from "@/models/system";
|
import System from "@/models/system";
|
||||||
import { CaretDown } from "@phosphor-icons/react";
|
import { CaretDown, Download } from "@phosphor-icons/react";
|
||||||
import { saveAs } from "file-saver";
|
import { saveAs } from "file-saver";
|
||||||
|
|
||||||
const exportOptions = {
|
const exportOptions = {
|
||||||
@ -47,11 +47,9 @@ const exportOptions = {
|
|||||||
|
|
||||||
export default function WorkspaceChats() {
|
export default function WorkspaceChats() {
|
||||||
const [showMenu, setShowMenu] = useState(false);
|
const [showMenu, setShowMenu] = useState(false);
|
||||||
const [exportType, setExportType] = useState("jsonl");
|
|
||||||
const menuRef = useRef();
|
const menuRef = useRef();
|
||||||
const openMenuButton = useRef();
|
const openMenuButton = useRef();
|
||||||
|
const handleDumpChats = async (exportType) => {
|
||||||
const handleDumpChats = async () => {
|
|
||||||
const chats = await System.exportChats(exportType);
|
const chats = await System.exportChats(exportType);
|
||||||
if (!!chats) {
|
if (!!chats) {
|
||||||
const { name, mimeType, fileExtension, filenameFunc } =
|
const { name, mimeType, fileExtension, filenameFunc } =
|
||||||
@ -90,47 +88,39 @@ export default function WorkspaceChats() {
|
|||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
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-main-gradient w-full h-full overflow-y-scroll border-2 border-outline"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="flex gap-x-4 items-center">
|
||||||
<p className="text-2xl font-semibold text-white">
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
Workspace Chats
|
Workspace Chats
|
||||||
</p>
|
</p>
|
||||||
<div className="flex gap-x-1 relative">
|
<div className="relative">
|
||||||
<button
|
|
||||||
onClick={handleDumpChats}
|
|
||||||
className="border border-slate-200 px-4 py-1 rounded-lg text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800"
|
|
||||||
>
|
|
||||||
Export as {exportOptions[exportType].name}
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
ref={openMenuButton}
|
ref={openMenuButton}
|
||||||
onClick={toggleMenu}
|
onClick={toggleMenu}
|
||||||
className={`transition-all duration-300 border border-slate-200 p-1 rounded-lg text-slate-200 text-sm items-center flex hover:bg-slate-200 hover:text-slate-800 ${
|
className="flex items-center gap-x-2 px-4 py-2 rounded-lg bg-[#2C2F36] text-white text-sm hover:bg-[#3D4147] shadow-md border border-[#3D4147]"
|
||||||
showMenu ? "bg-slate-200 text-slate-800" : ""
|
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<CaretDown weight="bold" className="h-4 w-4" />
|
<Download size={18} weight="bold" />
|
||||||
|
Export
|
||||||
|
<CaretDown size={18} weight="bold" />
|
||||||
</button>
|
</button>
|
||||||
<div
|
<div
|
||||||
ref={menuRef}
|
ref={menuRef}
|
||||||
className={`${
|
className={`${
|
||||||
showMenu ? "slide-down" : "slide-up hidden"
|
showMenu ? "slide-down" : "slide-up hidden"
|
||||||
} z-20 w-fit rounded-lg absolute top-full right-0 bg-sidebar p-4 flex items-center justify-center mt-2`}
|
} z-20 w-fit rounded-lg absolute top-full right-0 bg-[#2C2F36] mt-2 shadow-md`}
|
||||||
>
|
>
|
||||||
<div className="flex flex-col gap-y-2">
|
<div className="py-2">
|
||||||
{Object.entries(exportOptions)
|
{Object.entries(exportOptions).map(([key, data]) => (
|
||||||
.filter(([type, _]) => type !== exportType)
|
|
||||||
.map(([key, data]) => (
|
|
||||||
<button
|
<button
|
||||||
key={key}
|
key={key}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setExportType(key);
|
handleDumpChats(key);
|
||||||
setShowMenu(false);
|
setShowMenu(false);
|
||||||
}}
|
}}
|
||||||
className="text-white hover:bg-slate-200/20 w-full text-left px-4 py-1.5 rounded-md"
|
className="w-full text-left px-4 py-2 text-white text-sm hover:bg-[#3D4147]"
|
||||||
>
|
>
|
||||||
{data.name}
|
{data.name}
|
||||||
</button>
|
</button>
|
||||||
@ -139,7 +129,7 @@ export default function WorkspaceChats() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
These are all the recorded chats and messages that have been sent
|
These are all the recorded chats and messages that have been sent
|
||||||
by users ordered by their creation date.
|
by users ordered by their creation date.
|
||||||
</p>
|
</p>
|
||||||
@ -195,8 +185,8 @@ function ChatsContainer() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<table className="md:w-3/4 w-full text-sm text-left rounded-lg mt-5">
|
<table className="w-full text-sm text-left rounded-lg mt-6">
|
||||||
<thead className="text-white text-opacity-80 text-sm font-bold uppercase border-white border-b border-opacity-60">
|
<thead className="text-white text-opacity-80 text-xs leading-[18px] font-bold uppercase border-white border-b border-opacity-60">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
||||||
Id
|
Id
|
||||||
@ -228,7 +218,7 @@ function ChatsContainer() {
|
|||||||
))}
|
))}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div className="flex w-full justify-between items-center">
|
<div className="flex w-full justify-between items-center mt-6">
|
||||||
<button
|
<button
|
||||||
onClick={handlePrevious}
|
onClick={handlePrevious}
|
||||||
className="px-4 py-2 rounded-lg border border-slate-200 text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 disabled:invisible"
|
className="px-4 py-2 rounded-lg border border-slate-200 text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 disabled:invisible"
|
||||||
|
@ -67,19 +67,19 @@ export default function GithubConnectorSetup() {
|
|||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
||||||
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="flex w-full">
|
<div className="flex w-full">
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="flex w-full gap-x-4 items-center pb-6 border-white border-b-2 border-opacity-10">
|
<div className="flex w-full gap-x-4 items-center pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<img src={image} alt="Github" className="rounded-lg h-16 w-16" />
|
<img src={image} alt="Github" className="rounded-lg h-16 w-16" />
|
||||||
<div className="w-full flex flex-col gap-y-1">
|
<div className="w-full flex flex-col gap-y-1">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="items-center">
|
||||||
<p className="text-2xl font-semibold text-white">
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
Import GitHub Repository
|
Import GitHub Repository
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
Import all files from a public or private Github repository
|
Import all files from a public or private Github repository
|
||||||
and have its files be available in your workspace.
|
and have its files be available in your workspace.
|
||||||
</p>
|
</p>
|
||||||
|
@ -48,19 +48,19 @@ export default function YouTubeTranscriptConnectorSetup() {
|
|||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
||||||
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="flex w-full">
|
<div className="flex w-full">
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="flex w-full gap-x-4 items-center pb-6 border-white border-b-2 border-opacity-10">
|
<div className="flex w-full gap-x-4 items-center pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<img src={image} alt="YouTube" className="rounded-lg h-16 w-16" />
|
<img src={image} alt="YouTube" className="rounded-lg h-16 w-16" />
|
||||||
<div className="w-full flex flex-col gap-y-1">
|
<div className="w-full flex flex-col gap-y-1">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="items-center">
|
||||||
<p className="text-2xl font-semibold text-white">
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
Import YouTube transcription
|
Import YouTube transcription
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
From a youtube link, import the entire transcript of that
|
From a youtube link, import the entire transcript of that
|
||||||
video for embedding.
|
video for embedding.
|
||||||
</p>
|
</p>
|
||||||
|
@ -9,23 +9,27 @@ export default function DataConnectors() {
|
|||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
||||||
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="flex w-full">
|
<div className="flex w-full">
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="items-center">
|
||||||
<p className="text-2xl font-semibold text-white">
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
Data Connectors
|
Data Connectors
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
Verified data connectors allow you to add more content to your
|
Verified data connectors allow you to add more content to your
|
||||||
AnythingLLM workspaces with no custom code or complexity.
|
AnythingLLM workspaces with no custom code or complexity.
|
||||||
<br />
|
<br />
|
||||||
Guaranteed to work with your AnythingLLM instance.
|
Guaranteed to work with your AnythingLLM instance.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-sm font-medium text-white mt-6 mb-4">
|
||||||
|
Available Data Connectors
|
||||||
|
</div>
|
||||||
|
<div className="w-full">
|
||||||
<div className="py-4 w-full flex md:flex-wrap overflow-x-scroll gap-4 max-w-full">
|
<div className="py-4 w-full flex md:flex-wrap overflow-x-scroll gap-4 max-w-full">
|
||||||
<DataConnectorOption slug="github" />
|
<DataConnectorOption slug="github" />
|
||||||
<DataConnectorOption slug="youtube-transcript" />
|
<DataConnectorOption slug="youtube-transcript" />
|
||||||
@ -34,5 +38,6 @@ export default function DataConnectors() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -14,14 +14,16 @@ export default function EmbedChats() {
|
|||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
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-main-gradient w-full h-full overflow-y-scroll border-2 border-outline"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="flex gap-x-4 items-center">
|
||||||
<p className="text-2xl font-semibold text-white">Embed Chats</p>
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
|
Embed Chats
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
These are all the recorded chats and messages from any embed that
|
These are all the recorded chats and messages from any embed that
|
||||||
you have published.
|
you have published.
|
||||||
</p>
|
</p>
|
||||||
|
@ -12,27 +12,28 @@ import Embed from "@/models/embed";
|
|||||||
|
|
||||||
export default function EmbedConfigs() {
|
export default function EmbedConfigs() {
|
||||||
const { isOpen, openModal, closeModal } = useModal();
|
const { isOpen, openModal, closeModal } = useModal();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
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-main-gradient w-full h-full overflow-y-scroll border-2 border-outline"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="items-center flex gap-x-4">
|
||||||
<p className="text-2xl font-semibold text-white">
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
Embeddable Chat Widgets
|
Embeddable Chat Widgets
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
onClick={openModal}
|
onClick={openModal}
|
||||||
className="border border-slate-200 px-4 py-1 rounded-lg text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800"
|
className="flex items-center gap-x-2 px-4 py-2 rounded-lg bg-[#2C2F36] text-white text-sm hover:bg-[#3D4147] shadow-md border border-[#3D4147]"
|
||||||
>
|
>
|
||||||
<CodeBlock className="h-4 w-4" /> Create embed
|
<CodeBlock className="h-4 w-4" /> Create embed
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
Embeddable chat widgets are public facing chat interfaces that are
|
Embeddable chat widgets are public facing chat interfaces that are
|
||||||
tied to a single workspace. These allow you to build workspaces
|
tied to a single workspace. These allow you to build workspaces
|
||||||
that then you can publish to the world.
|
that then you can publish to the world.
|
||||||
@ -51,6 +52,7 @@ export default function EmbedConfigs() {
|
|||||||
function EmbedContainer() {
|
function EmbedContainer() {
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [embeds, setEmbeds] = useState([]);
|
const [embeds, setEmbeds] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchUsers() {
|
async function fetchUsers() {
|
||||||
const _embeds = await Embed.embeds();
|
const _embeds = await Embed.embeds();
|
||||||
@ -75,8 +77,8 @@ function EmbedContainer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<table className="md:w-3/4 w-full text-sm text-left rounded-lg mt-5">
|
<table className="w-full text-sm text-left rounded-lg mt-6">
|
||||||
<thead className="text-white text-opacity-80 text-sm font-bold uppercase border-white border-b border-opacity-60">
|
<thead className="text-white text-opacity-80 text-xs leading-[18px] font-bold uppercase border-white border-b border-opacity-60">
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
<th scope="col" className="px-6 py-3 rounded-tl-lg">
|
||||||
Workspace
|
Workspace
|
||||||
|
@ -128,18 +128,11 @@ export default function GeneralEmbeddingPreference() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<ModalWrapper isOpen={isOpen}>
|
|
||||||
<ChangeWarningModal
|
|
||||||
warningText="Switching the vector database will ignore previously embedded documents and future similarity search results. They will need to be re-added to each workspace."
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={handleSaveSettings}
|
|
||||||
/>
|
|
||||||
</ModalWrapper>
|
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
||||||
className="relative md:ml-[2px] md:mr-[8px] md:my-[16px] md:rounded-[26px] bg-main-gradient p-[18px] h-full overflow-y-scroll animate-pulse border-4 border-accent"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="w-full h-full flex justify-center items-center">
|
<div className="w-full h-full flex justify-center items-center">
|
||||||
<PreLoader />
|
<PreLoader />
|
||||||
@ -148,30 +141,30 @@ export default function GeneralEmbeddingPreference() {
|
|||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
||||||
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
id="embedding-form"
|
id="embedding-form"
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className="flex w-full"
|
className="flex w-full"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="flex gap-x-4 items-center">
|
||||||
<p className="text-2xl font-semibold text-white">
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
Embedding Preference
|
Embedding Preference
|
||||||
</p>
|
</p>
|
||||||
{hasChanges && (
|
{hasChanges && (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="border border-slate-200 px-4 py-1 rounded-lg text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800"
|
className="flex items-center gap-x-2 px-4 py-2 rounded-lg bg-[#2C2F36] text-white text-sm hover:bg-[#3D4147] shadow-md border border-[#3D4147]"
|
||||||
>
|
>
|
||||||
{saving ? "Saving..." : "Save changes"}
|
{saving ? "Saving..." : "Save changes"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
When using an LLM that does not natively support an embedding
|
When using an LLM that does not natively support an embedding
|
||||||
engine - you may need to additionally specify credentials to
|
engine - you may need to additionally specify credentials to
|
||||||
for embedding text.
|
for embedding text.
|
||||||
@ -181,15 +174,13 @@ export default function GeneralEmbeddingPreference() {
|
|||||||
format which AnythingLLM can use to process.
|
format which AnythingLLM can use to process.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="text-sm font-medium text-white mt-6 mb-4">
|
||||||
<>
|
|
||||||
<div className="text-white text-sm font-medium py-4">
|
|
||||||
Embedding Providers
|
Embedding Providers
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="w-full relative border-slate-300/20 shadow border-4 rounded-xl text-white">
|
<div className="w-full relative border-slate-300/20 shadow border-4 rounded-xl text-white">
|
||||||
<div className="w-full p-4 absolute top-0 rounded-t-lg backdrop-blur-sm">
|
<div className="w-full p-4 absolute top-0 rounded-t-lg backdrop-blur-sm">
|
||||||
<div className="w-full flex items-center sticky top-0 z-20">
|
<div className="w-full flex items-center sticky top-0">
|
||||||
<MagnifyingGlass
|
<MagnifyingGlass
|
||||||
size={16}
|
size={16}
|
||||||
weight="bold"
|
weight="bold"
|
||||||
@ -233,11 +224,17 @@ export default function GeneralEmbeddingPreference() {
|
|||||||
)?.options}
|
)?.options}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<ModalWrapper isOpen={isOpen}>
|
||||||
|
<ChangeWarningModal
|
||||||
|
warningText="Switching the vector database will ignore previously embedded documents and future similarity search results. They will need to be re-added to each workspace."
|
||||||
|
onClose={closeModal}
|
||||||
|
onConfirm={handleSaveSettings}
|
||||||
|
/>
|
||||||
|
</ModalWrapper>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ export default function GeneralLLMPreference() {
|
|||||||
{loading ? (
|
{loading ? (
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
||||||
className="relative md:ml-[2px] md:mr-[8px] md:my-[16px] md:rounded-[26px] bg-main-gradient p-[18px] h-full overflow-y-scroll animate-pulse border-4 border-accent"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="w-full h-full flex justify-center items-center">
|
<div className="w-full h-full flex justify-center items-center">
|
||||||
<PreLoader />
|
<PreLoader />
|
||||||
@ -208,33 +208,33 @@ export default function GeneralLLMPreference() {
|
|||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
||||||
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[26px] bg-main-gradient w-full h-full overflow-y-scroll border-4 border-accent"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<form onSubmit={handleSubmit} className="flex w-full">
|
<form onSubmit={handleSubmit} className="flex w-full">
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="flex gap-x-4 items-center">
|
||||||
<p className="text-2xl font-semibold text-white">
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
LLM Preference
|
LLM Preference
|
||||||
</p>
|
</p>
|
||||||
{hasChanges && (
|
{hasChanges && (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="border border-slate-200 px-4 py-1 rounded-lg text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800"
|
className="flex items-center gap-x-2 px-4 py-2 rounded-lg bg-[#2C2F36] text-white text-sm hover:bg-[#3D4147] shadow-md border border-[#3D4147]"
|
||||||
>
|
>
|
||||||
{saving ? "Saving..." : "Save changes"}
|
{saving ? "Saving..." : "Save changes"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
These are the credentials and settings for your preferred LLM
|
These are the credentials and settings for your preferred LLM
|
||||||
chat & embedding provider. Its important these keys are
|
chat & embedding provider. Its important these keys are
|
||||||
current and correct or else AnythingLLM will not function
|
current and correct or else AnythingLLM will not function
|
||||||
properly.
|
properly.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-white text-sm font-medium py-4">
|
<div className="text-sm font-medium text-white mt-6 mb-4">
|
||||||
LLM Providers
|
LLM Providers
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
|
@ -154,18 +154,11 @@ export default function GeneralVectorDatabase() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
<div className="w-screen h-screen overflow-hidden bg-sidebar flex">
|
||||||
<ModalWrapper isOpen={isOpen}>
|
|
||||||
<ChangeWarningModal
|
|
||||||
warningText="Switching the vector database will ignore previously embedded documents and future similarity search results. They will need to be re-added to each workspace."
|
|
||||||
onClose={closeModal}
|
|
||||||
onConfirm={handleSaveSettings}
|
|
||||||
/>
|
|
||||||
</ModalWrapper>
|
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
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-main-gradient w-full h-full overflow-y-scroll border-2 border-outline animate-pulse"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<div className="w-full h-full flex justify-center items-center">
|
<div className="w-full h-full flex justify-center items-center">
|
||||||
<PreLoader />
|
<PreLoader />
|
||||||
@ -174,42 +167,42 @@ export default function GeneralVectorDatabase() {
|
|||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
style={{ height: isMobile ? "100%" : "calc(100% - 32px)" }}
|
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-main-gradient w-full h-full overflow-y-scroll border-2 border-outline"
|
className="relative md:ml-[2px] md:mr-[16px] md:my-[16px] md:rounded-[16px] bg-main-gradient w-full h-full overflow-y-scroll"
|
||||||
>
|
>
|
||||||
<form
|
<form
|
||||||
id="vectordb-form"
|
id="vectordb-form"
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
className="flex w-full"
|
className="flex w-full"
|
||||||
>
|
>
|
||||||
<div className="flex flex-col w-full px-1 md:px-20 md:py-12 py-16">
|
<div className="flex flex-col w-full px-1 md:pl-6 md:pr-[86px] md:py-6 py-16">
|
||||||
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
<div className="w-full flex flex-col gap-y-1 pb-6 border-white border-b-2 border-opacity-10">
|
||||||
<div className="items-center flex gap-x-4">
|
<div className="flex items-center gap-x-4">
|
||||||
<p className="text-2xl font-semibold text-white">
|
<p className="text-lg leading-6 font-bold text-white">
|
||||||
Vector Database
|
Vector Database
|
||||||
</p>
|
</p>
|
||||||
{hasChanges && (
|
{hasChanges && (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="border border-slate-200 px-4 py-1 rounded-lg text-slate-200 text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800"
|
className="flex items-center gap-x-2 px-4 py-2 rounded-lg bg-[#2C2F36] text-white text-sm hover:bg-[#3D4147] shadow-md border border-[#3D4147]"
|
||||||
>
|
>
|
||||||
{saving ? "Saving..." : "Save changes"}
|
{saving ? "Saving..." : "Save changes"}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm font-base text-white text-opacity-60">
|
<p className="text-xs leading-[18px] font-base text-white text-opacity-60">
|
||||||
These are the credentials and settings for how your
|
These are the credentials and settings for how your
|
||||||
AnythingLLM instance will function. It's important these keys
|
AnythingLLM instance will function. It's important these keys
|
||||||
are current and correct.
|
are current and correct.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-white text-sm font-medium py-4">
|
<div className="text-sm font-medium text-white mt-6 mb-4">
|
||||||
Select your preferred vector database provider
|
Vector Database Providers
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<div className="w-full relative border-slate-300/20 shadow border-4 rounded-xl text-white">
|
<div className="w-full relative border-slate-300/20 shadow border-4 rounded-xl text-white">
|
||||||
<div className="w-full p-4 absolute top-0 rounded-t-lg backdrop-blur-sm">
|
<div className="w-full p-4 absolute top-0 rounded-t-lg backdrop-blur-sm">
|
||||||
<div className="w-full flex items-center sticky top-0 z-20">
|
<div className="w-full flex items-center sticky top-0">
|
||||||
<MagnifyingGlass
|
<MagnifyingGlass
|
||||||
size={16}
|
size={16}
|
||||||
weight="bold"
|
weight="bold"
|
||||||
@ -257,6 +250,13 @@ export default function GeneralVectorDatabase() {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<ModalWrapper isOpen={isOpen}>
|
||||||
|
<ChangeWarningModal
|
||||||
|
warningText="Switching the vector database will ignore previously embedded documents and future similarity search results. They will need to be re-added to each workspace."
|
||||||
|
onClose={closeModal}
|
||||||
|
onConfirm={handleSaveSettings}
|
||||||
|
/>
|
||||||
|
</ModalWrapper>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user