θΏ™ζ˜―indexlocζδΎ›ηš„ζœεŠ‘οΌŒδΈθ¦θΎ“ε…₯任何密码
Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 120 additions & 0 deletions frontend/src/pages/GeneralSettings/Appearance/CustomLogo/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import useLogo from "@/hooks/useLogo";
import System from "@/models/system";
import showToast from "@/utils/toast";
import { useEffect, useState } from "react";
import AnythingLLM from "@/media/logo/anything-llm.png";
import { Plus } from "@phosphor-icons/react";

export default function CustomLogo() {
const { logo: _initLogo, setLogo: _setLogo } = useLogo();
const [logo, setLogo] = useState("");
const [isDefaultLogo, setIsDefaultLogo] = useState(true);

useEffect(() => {
async function logoInit() {
setLogo(_initLogo || "");
const _isDefaultLogo = await System.isDefaultLogo();
setIsDefaultLogo(_isDefaultLogo);
}
logoInit();
}, [_initLogo]);

const handleFileUpload = async (event) => {
const file = event.target.files[0];
if (!file) return false;

const objectURL = URL.createObjectURL(file);
setLogo(objectURL);

const formData = new FormData();
formData.append("logo", file);
const { success, error } = await System.uploadLogo(formData);
if (!success) {
showToast(`Failed to upload logo: ${error}`, "error");
setLogo(_initLogo);
return;
}

const logoURL = await System.fetchLogo();
_setLogo(logoURL);

showToast("Image uploaded successfully.", "success");
setIsDefaultLogo(false);
};

const handleRemoveLogo = async () => {
setLogo("");
setIsDefaultLogo(true);

const { success, error } = await System.removeCustomLogo();
if (!success) {
console.error("Failed to remove logo:", error);
showToast(`Failed to remove logo: ${error}`, "error");
const logoURL = await System.fetchLogo();
setLogo(logoURL);
setIsDefaultLogo(false);
return;
}

const logoURL = await System.fetchLogo();
_setLogo(logoURL);

showToast("Image successfully removed.", "success");
};

return (
<div className="my-6">
<div className="flex flex-col gap-y-2">
<h2 className="leading-tight font-medium text-white">Custom Logo</h2>
<p className="text-sm font-base text-white/60">
Upload your custom logo to make your chatbot yours.
</p>
</div>
<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">
<label
className="mt-5 transition-all duration-300 hover:opacity-60"
hidden={!isDefaultLogo}
>
<input
id="logo-upload"
type="file"
accept="image/*"
className="hidden"
onChange={handleFileUpload}
/>
<div
className="w-80 py-4 bg-zinc-900/50 rounded-2xl border-2 border-dashed border-white border-opacity-60 justify-center items-center inline-flex cursor-pointer"
htmlFor="logo-upload"
>
<div className="flex flex-col items-center justify-center">
<div className="rounded-full bg-white/40">
<Plus className="w-6 h-6 text-black/80 m-2" />
</div>
<div className="text-white text-opacity-80 text-sm font-semibold py-1">
Add a custom logo
</div>
<div className="text-white text-opacity-60 text-xs font-medium py-1">
Recommended size: 800 x 200
</div>
</div>
</div>
</label>
<button
onClick={handleRemoveLogo}
className="text-white text-base font-medium hover:text-opacity-60"
>
Delete
</button>
</div>
</div>
</div>
);
}
119 changes: 119 additions & 0 deletions frontend/src/pages/GeneralSettings/Appearance/CustomMessages/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import EditingChatBubble from "@/components/EditingChatBubble";
import System from "@/models/system";
import showToast from "@/utils/toast";
import { Plus } from "@phosphor-icons/react";
import { useEffect, useState } from "react";

export default function CustomMessages() {
const [hasChanges, setHasChanges] = useState(false);
const [messages, setMessages] = useState([]);

useEffect(() => {
async function fetchMessages() {
const messages = await System.getWelcomeMessages();
setMessages(messages);
}
fetchMessages();
}, []);

const addMessage = (type) => {
if (type === "user") {
setMessages([
...messages,
{ user: "Double click to edit...", response: "" },
]);
} else {
setMessages([
...messages,
{ user: "", response: "Double click to edit..." },
]);
}
};

const removeMessage = (index) => {
setHasChanges(true);
setMessages(messages.filter((_, i) => i !== index));
};

const handleMessageChange = (index, type, value) => {
setHasChanges(true);
const newMessages = [...messages];
newMessages[index][type] = value;
setMessages(newMessages);
};

const handleMessageSave = async () => {
const { success, error } = await System.setWelcomeMessages(messages);
if (!success) {
showToast(`Failed to update welcome messages: ${error}`, "error");
return;
}
showToast("Successfully updated welcome messages.", "success");
setHasChanges(false);
};

return (
<div className="mb-6">
<div className="flex flex-col gap-y-2">
<h2 className="leading-tight font-medium text-white">
Custom Messages
</h2>
<p className="text-sm font-base text-white/60">
Customize the automatic messages displayed to your users.
</p>
</div>
<div className="mt-6 flex flex-col gap-y-6 bg-zinc-900 rounded-lg px-6 pt-4 max-w-[700px]">
{messages.map((message, index) => (
<div key={index} className="flex flex-col gap-y-2">
{message.user && (
<EditingChatBubble
message={message}
index={index}
type="user"
handleMessageChange={handleMessageChange}
removeMessage={removeMessage}
/>
)}
{message.response && (
<EditingChatBubble
message={message}
index={index}
type="response"
handleMessageChange={handleMessageChange}
removeMessage={removeMessage}
/>
)}
</div>
))}
<div className="flex gap-4 mt-12 justify-between pb-7">
<button
className="self-end text-white hover:text-white/60 transition"
onClick={() => addMessage("response")}
>
<div className="flex items-center justify-start">
<Plus className="w-5 h-5 m-2" weight="fill" /> New System Message
</div>
</button>
<button
className="self-end text-sky-400 hover:text-sky-400/60 transition"
onClick={() => addMessage("user")}
>
<div className="flex items-center">
<Plus className="w-5 h-5 m-2" weight="fill" /> New User Message
</div>
</button>
</div>
</div>
{hasChanges && (
<div className="flex justify-center py-6">
<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"
onClick={handleMessageSave}
>
Save Messages
</button>
</div>
)}
</div>
);
}
Loading