mirror of
https://github.com/Mintplex-Labs/anything-llm.git
synced 2025-04-17 18:18:11 +00:00
[FEAT]: Allow user to set support email (#726)
* implement custom support email for usermenu support button * small refactor --------- Co-authored-by: timothycarambat <rambat1010@gmail.com>
This commit is contained in:
parent
b636a07e52
commit
17c1913ccc
8 changed files with 161 additions and 1 deletions
.vscode
frontend/src
components/UserMenu
models
pages/GeneralSettings/Appearance
server
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
|
@ -7,6 +7,7 @@
|
|||
"hljs",
|
||||
"Langchain",
|
||||
"Milvus",
|
||||
"Mintplex",
|
||||
"Ollama",
|
||||
"openai",
|
||||
"Qdrant",
|
||||
|
|
|
@ -52,6 +52,7 @@ function UserButton() {
|
|||
const { user } = useUser();
|
||||
const [showMenu, setShowMenu] = useState(false);
|
||||
const [showAccountSettings, setShowAccountSettings] = useState(false);
|
||||
const [supportEmail, setSupportEmail] = useState("");
|
||||
const mode = useLoginMode();
|
||||
const menuRef = useRef();
|
||||
const buttonRef = useRef();
|
||||
|
@ -77,6 +78,18 @@ function UserButton() {
|
|||
return () => document.removeEventListener("mousedown", handleClose);
|
||||
}, [showMenu]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSupportEmail = async () => {
|
||||
const supportEmail = await System.fetchSupportEmail();
|
||||
if (supportEmail.email) {
|
||||
setSupportEmail(`mailto:${supportEmail.email}`);
|
||||
} else {
|
||||
setSupportEmail(paths.mailToMintplex());
|
||||
}
|
||||
};
|
||||
fetchSupportEmail();
|
||||
}, []);
|
||||
|
||||
if (mode === null) return null;
|
||||
|
||||
return (
|
||||
|
@ -105,7 +118,7 @@ function UserButton() {
|
|||
</button>
|
||||
)}
|
||||
<a
|
||||
href={paths.mailToMintplex()}
|
||||
href={supportEmail}
|
||||
className="text-white hover:bg-slate-200/20 w-full text-left px-4 py-1.5 rounded-md"
|
||||
>
|
||||
Support
|
||||
|
|
|
@ -5,6 +5,7 @@ import DataConnector from "./dataConnector";
|
|||
const System = {
|
||||
cacheKeys: {
|
||||
footerIcons: "anythingllm_footer_links",
|
||||
supportEmail: "anythingllm_support_email",
|
||||
},
|
||||
ping: async function () {
|
||||
return await fetch(`${API_BASE}/ping`)
|
||||
|
@ -225,6 +226,36 @@ const System = {
|
|||
);
|
||||
return { footerData: newData, error: null };
|
||||
},
|
||||
fetchSupportEmail: async function () {
|
||||
const cache = window.localStorage.getItem(this.cacheKeys.supportEmail);
|
||||
const { email, lastFetched } = cache
|
||||
? safeJsonParse(cache, { email: "", lastFetched: 0 })
|
||||
: { email: "", lastFetched: 0 };
|
||||
|
||||
if (!!email && Date.now() - lastFetched < 3_600_000)
|
||||
return { email: email, error: null };
|
||||
|
||||
const { supportEmail, error } = await fetch(
|
||||
`${API_BASE}/system/support-email`,
|
||||
{
|
||||
method: "GET",
|
||||
cache: "no-cache",
|
||||
headers: baseHeaders(),
|
||||
}
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
return { email: "", error: e.message };
|
||||
});
|
||||
|
||||
if (!supportEmail || !!error) return { email: "", error: null };
|
||||
window.localStorage.setItem(
|
||||
this.cacheKeys.supportEmail,
|
||||
JSON.stringify({ email: supportEmail, lastFetched: Date.now() })
|
||||
);
|
||||
return { email: supportEmail, error: null };
|
||||
},
|
||||
fetchLogo: async function () {
|
||||
return await fetch(`${API_BASE}/system/logo`, {
|
||||
method: "GET",
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
import useUser from "@/hooks/useUser";
|
||||
import Admin from "@/models/admin";
|
||||
import System from "@/models/system";
|
||||
import showToast from "@/utils/toast";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export default function SupportEmail() {
|
||||
const { user } = useUser();
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [hasChanges, setHasChanges] = useState(false);
|
||||
const [supportEmail, setSupportEmail] = useState("");
|
||||
const [originalEmail, setOriginalEmail] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
const fetchSupportEmail = async () => {
|
||||
const supportEmail = await System.fetchSupportEmail();
|
||||
setSupportEmail(supportEmail.email || "");
|
||||
setOriginalEmail(supportEmail.email || "");
|
||||
setLoading(false);
|
||||
};
|
||||
fetchSupportEmail();
|
||||
}, []);
|
||||
|
||||
const updateSupportEmail = async (e, newValue = null) => {
|
||||
e.preventDefault();
|
||||
let support_email = newValue;
|
||||
if (newValue === null) {
|
||||
const form = new FormData(e.target);
|
||||
support_email = form.get("supportEmail");
|
||||
}
|
||||
|
||||
const { success, error } = await Admin.updateSystemPreferences({
|
||||
support_email,
|
||||
});
|
||||
|
||||
if (!success) {
|
||||
showToast(`Failed to update support email: ${error}`, "error");
|
||||
return;
|
||||
} else {
|
||||
showToast("Successfully updated support email.", "success");
|
||||
window.localStorage.removeItem(System.cacheKeys.supportEmail);
|
||||
setSupportEmail(support_email);
|
||||
setOriginalEmail(support_email);
|
||||
setHasChanges(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleChange = (e) => {
|
||||
setSupportEmail(e.target.value);
|
||||
setHasChanges(true);
|
||||
};
|
||||
|
||||
if (loading || !user?.role) return null;
|
||||
return (
|
||||
<form className="mb-6" onSubmit={updateSupportEmail}>
|
||||
<div className="flex flex-col gap-y-2">
|
||||
<h2 className="leading-tight font-medium text-white">Support Email</h2>
|
||||
<p className="text-sm font-base text-white/60">
|
||||
Set the support email address that shows up in the user menu while
|
||||
logged into this instance.
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-4">
|
||||
<input
|
||||
name="supportEmail"
|
||||
type="email"
|
||||
className="bg-zinc-900 mt-4 text-white text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 max-w-[275px]"
|
||||
placeholder="support@mycompany.com"
|
||||
required={true}
|
||||
autoComplete="off"
|
||||
onChange={handleChange}
|
||||
value={supportEmail}
|
||||
/>
|
||||
{originalEmail !== "" && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={(e) => updateSupportEmail(e, "")}
|
||||
className="mt-4 text-white text-base font-medium hover:text-opacity-60"
|
||||
>
|
||||
Clear
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{hasChanges && (
|
||||
<button
|
||||
type="submit"
|
||||
className="transition-all mt-6 w-fit duration-300 border border-slate-200 px-5 py-2.5 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800"
|
||||
>
|
||||
Save
|
||||
</button>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
}
|
|
@ -8,6 +8,7 @@ import EditingChatBubble from "@/components/EditingChatBubble";
|
|||
import showToast from "@/utils/toast";
|
||||
import { Plus } from "@phosphor-icons/react";
|
||||
import FooterCustomization from "./FooterCustomization";
|
||||
import SupportEmail from "./SupportEmail";
|
||||
|
||||
export default function Appearance() {
|
||||
const { logo: _initLogo, setLogo: _setLogo } = useLogo();
|
||||
|
@ -250,6 +251,7 @@ export default function Appearance() {
|
|||
)}
|
||||
</div>
|
||||
<FooterCustomization />
|
||||
<SupportEmail />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -307,6 +307,9 @@ function adminEndpoints(app) {
|
|||
footer_data:
|
||||
(await SystemSettings.get({ label: "footer_data" }))?.value ||
|
||||
JSON.stringify([]),
|
||||
support_email:
|
||||
(await SystemSettings.get({ label: "support_email" }))?.value ||
|
||||
null,
|
||||
};
|
||||
response.status(200).json({ settings });
|
||||
} catch (e) {
|
||||
|
|
|
@ -469,6 +469,21 @@ function systemEndpoints(app) {
|
|||
}
|
||||
});
|
||||
|
||||
app.get("/system/support-email", [validatedRequest], async (_, response) => {
|
||||
try {
|
||||
const supportEmail =
|
||||
(
|
||||
await SystemSettings.get({
|
||||
label: "support_email",
|
||||
})
|
||||
)?.value ?? null;
|
||||
response.status(200).json({ supportEmail: supportEmail });
|
||||
} catch (error) {
|
||||
console.error("Error fetching support email:", error);
|
||||
response.status(500).json({ message: "Internal server error" });
|
||||
}
|
||||
});
|
||||
|
||||
app.get(
|
||||
"/system/pfp/:id",
|
||||
[validatedRequest, flexUserRoleValid([ROLES.all])],
|
||||
|
|
|
@ -13,6 +13,7 @@ const SystemSettings = {
|
|||
"logo_filename",
|
||||
"telemetry_id",
|
||||
"footer_data",
|
||||
"support_email",
|
||||
],
|
||||
validations: {
|
||||
footer_data: (updates) => {
|
||||
|
|
Loading…
Add table
Reference in a new issue