Update translations

This commit is contained in:
2025-05-15 15:41:39 +08:00
parent 04206cf2ae
commit b270cc480e
4 changed files with 333 additions and 89 deletions

View File

@@ -39,6 +39,8 @@ export const i18nData = {
"Description cannot be empty": "Description cannot be empty", "Description cannot be empty": "Description cannot be empty",
"Loading": "Loading", "Loading": "Loading",
"Enter a search keyword to continue": "Enter a search keyword to continue", "Enter a search keyword to continue": "Enter a search keyword to continue",
"My Info": "My Info",
"Server": "Server",
// Management page translations // Management page translations
"Manage": "Manage", "Manage": "Manage",
@@ -106,6 +108,40 @@ export const i18nData = {
"URL": "URL", "URL": "URL",
"Upload a file to server, then the file will be moved to the selected storage.": "Upload a file to server, then the file will be moved to the selected storage.", "Upload a file to server, then the file will be moved to the selected storage.": "Upload a file to server, then the file will be moved to the selected storage.",
"Select Storage": "Select Storage", "Select Storage": "Select Storage",
"Resource Details": "Resource Details",
"Delete Resource": "Delete Resource",
"Are you sure you want to delete the resource": "Are you sure you want to delete the resource",
"Delete File": "Delete File",
"Are you sure you want to delete the file": "Are you sure you want to delete the file",
// New translations
"Change Avatar": "Change Avatar",
"Change Username": "Change Username",
"Change Password": "Change Password",
"New Username": "New Username",
"Enter new username": "Enter new username",
"Save": "Save",
"Current Password": "Current Password",
"Enter current password": "Enter current password",
"New Password": "New Password",
"Enter new password": "Enter new password",
"Confirm New Password": "Confirm New Password",
"Confirm new password": "Confirm new password",
"Avatar changed successfully": "Avatar changed successfully",
"Username changed successfully": "Username changed successfully",
"Password changed successfully": "Password changed successfully",
// Manage server config page translations
"Update server config successfully": "Update server config successfully",
"Max uploading size (MB)": "Max uploading size (MB)",
"Max file size (MB)": "Max file size (MB)",
"Max downloads per day for single IP": "Max downloads per day for single IP",
"Allow register": "Allow register",
"Server name": "Server name",
"Server description": "Server description",
"Cloudflare Turnstile Site Key": "Cloudflare Turnstile Site Key",
"Cloudflare Turnstile Secret Key": "Cloudflare Turnstile Secret Key",
"If the cloudflare turnstile keys are not empty, the turnstile will be used for register and download.": "If the cloudflare turnstile keys are not empty, the turnstile will be used for register and download.",
} }
}, },
"zh-CN": { "zh-CN": {
@@ -148,6 +184,8 @@ export const i18nData = {
"Description cannot be empty": "介绍不能为空", "Description cannot be empty": "介绍不能为空",
"Loading": "加载中", "Loading": "加载中",
"Enter a search keyword to continue": "输入搜索关键词以继续", "Enter a search keyword to continue": "输入搜索关键词以继续",
"My Info": "个人信息",
"Server": "服务器",
// Management page translations // Management page translations
"Manage": "管理", "Manage": "管理",
@@ -215,6 +253,40 @@ export const i18nData = {
"URL": "URL", "URL": "URL",
"Upload a file to server, then the file will be moved to the selected storage.": "将文件上传到服务器,然后文件将被移动到选定的存储中。", "Upload a file to server, then the file will be moved to the selected storage.": "将文件上传到服务器,然后文件将被移动到选定的存储中。",
"Select Storage": "选择存储", "Select Storage": "选择存储",
"Resource Details": "资源详情",
"Delete Resource": "删除资源",
"Are you sure you want to delete the resource": "您确定要删除此资源吗",
"Delete File": "删除文件",
"Are you sure you want to delete the file": "您确定要删除此文件吗",
// New translations
"Change Avatar": "更改头像",
"Change Username": "更改用户名",
"Change Password": "更改密码",
"New Username": "新用户名",
"Enter new username": "输入新用户名",
"Save": "保存",
"Current Password": "当前密码",
"Enter current password": "输入当前密码",
"New Password": "新密码",
"Enter new password": "输入新密码",
"Confirm New Password": "确认新密码",
"Confirm new password": "确认新密码",
"Avatar changed successfully": "头像更改成功",
"Username changed successfully": "用户名更改成功",
"Password changed successfully": "密码更改成功",
// Manage server config page translations
"Update server config successfully": "成功更新服务器配置",
"Max uploading size (MB)": "最大上传大小 (MB)",
"Max file size (MB)": "最大文件大小 (MB)",
"Max downloads per day for single IP": "单个IP每日最大下载次数",
"Allow register": "允许注册",
"Server name": "服务器名称",
"Server description": "服务器描述",
"Cloudflare Turnstile Site Key": "Cloudflare Turnstile 站点密钥",
"Cloudflare Turnstile Secret Key": "Cloudflare Turnstile 密钥",
"If the cloudflare turnstile keys are not empty, the turnstile will be used for register and download.": "如果设置了 Cloudflare Turnstile 密钥,将在注册和下载时启用验证",
} }
}, },
"zh-TW": { "zh-TW": {
@@ -257,6 +329,8 @@ export const i18nData = {
"Description cannot be empty": "介紹不能為空", "Description cannot be empty": "介紹不能為空",
"Loading": "載入中", "Loading": "載入中",
"Enter a search keyword to continue": "輸入搜尋關鍵字以繼續", "Enter a search keyword to continue": "輸入搜尋關鍵字以繼續",
"My Info": "個人信息",
"Server": "伺服器",
// Management page translations // Management page translations
"Manage": "管理", "Manage": "管理",
@@ -324,6 +398,40 @@ export const i18nData = {
"URL": "URL", "URL": "URL",
"Upload a file to server, then the file will be moved to the selected storage.": "將檔案上傳到伺服器,然後檔案將被移動到選定的儲存中。", "Upload a file to server, then the file will be moved to the selected storage.": "將檔案上傳到伺服器,然後檔案將被移動到選定的儲存中。",
"Select Storage": "選擇儲存", "Select Storage": "選擇儲存",
"Resource Details": "資源詳情",
"Delete Resource": "刪除資源",
"Are you sure you want to delete the resource": "您確定要刪除此資源嗎",
"Delete File": "刪除檔案",
"Are you sure you want to delete the file": "您確定要刪除此檔案嗎",
// New translations
"Change Avatar": "更改頭像",
"Change Username": "更改用戶名",
"Change Password": "更改密碼",
"New Username": "新用戶名",
"Enter new username": "輸入新用戶名",
"Save": "儲存",
"Current Password": "當前密碼",
"Enter current password": "輸入當前密碼",
"New Password": "新密碼",
"Enter new password": "輸入新密碼",
"Confirm New Password": "確認新密碼",
"Confirm new password": "確認新密碼",
"Avatar changed successfully": "頭像更改成功",
"Username changed successfully": "用戶名更改成功",
"Password changed successfully": "密碼更改成功",
// Manage server config page translations
"Update server config successfully": "成功更新伺服器配置",
"Max uploading size (MB)": "最大上傳大小 (MB)",
"Max file size (MB)": "最大檔案大小 (MB)",
"Max downloads per day for single IP": "單個IP每日最大下載次數",
"Allow register": "允許註冊",
"Server name": "伺服器名稱",
"Server description": "伺服器描述",
"Cloudflare Turnstile Site Key": "Cloudflare Turnstile 網站密鑰",
"Cloudflare Turnstile Secret Key": "Cloudflare Turnstile 密鑰",
"If the cloudflare turnstile keys are not empty, the turnstile will be used for register and download.": "如果設置了 Cloudflare Turnstile 密鑰,將在註冊和下載時啟用驗證",
} }
} }
} }

View File

@@ -408,6 +408,19 @@ class Network {
} }
} }
async deleteResource(id: number): Promise<Response<void>> {
try {
const response = await axios.delete(`${this.apiBaseUrl}/resource/${id}`)
return response.data
} catch (e: any) {
console.error(e)
return {
success: false,
message: e.toString(),
}
}
}
async createS3Storage(name: string, endPoint: string, accessKeyID: string, async createS3Storage(name: string, endPoint: string, accessKeyID: string,
secretAccessKey: string, bucketName: string, maxSizeInMB: number): Promise<Response<any>> { secretAccessKey: string, bucketName: string, maxSizeInMB: number): Promise<Response<any>> {
try { try {
@@ -587,7 +600,7 @@ class Network {
} }
} }
async deleteFile(fileId: number): Promise<Response<void>> { async deleteFile(fileId: string): Promise<Response<void>> {
try { try {
const response = await axios.delete(`${this.apiBaseUrl}/files/${fileId}`); const response = await axios.delete(`${this.apiBaseUrl}/files/${fileId}`);
return response.data; return response.data;

View File

@@ -42,6 +42,8 @@ function ChangeAvatarDialog() {
const navigator = useNavigator(); const navigator = useNavigator();
const { t } = useTranslation();
const selectAvatar = () => { const selectAvatar = () => {
const input = document.createElement("input"); const input = document.createElement("input");
input.type = "file"; input.type = "file";
@@ -67,7 +69,7 @@ function ChangeAvatarDialog() {
app.user = res.data!; app.user = res.data!;
navigator.refresh(); navigator.refresh();
showToast({ showToast({
message: "Avatar changed successfully", message: t("Avatar changed successfully"),
type: "success", type: "success",
}) })
const dialog = document.getElementById("change_avatar_dialog") as HTMLDialogElement; const dialog = document.getElementById("change_avatar_dialog") as HTMLDialogElement;
@@ -78,7 +80,7 @@ function ChangeAvatarDialog() {
} }
return <> return <>
<ListTile icon={<MdOutlineAccountCircle />} title="Change Avatar" onClick={() => { <ListTile icon={<MdOutlineAccountCircle />} title={t("Change Avatar")} onClick={() => {
const dialog = document.getElementById("change_avatar_dialog") as HTMLDialogElement; const dialog = document.getElementById("change_avatar_dialog") as HTMLDialogElement;
if (dialog) { if (dialog) {
dialog.showModal(); dialog.showModal();
@@ -86,7 +88,7 @@ function ChangeAvatarDialog() {
}} /> }} />
<dialog id="change_avatar_dialog" className="modal"> <dialog id="change_avatar_dialog" className="modal">
<div className="modal-box"> <div className="modal-box">
<h3 className="font-bold text-lg">Change Avatar</h3> <h3 className="font-bold text-lg">{t("Change Avatar")}</h3>
<div className="h-48 flex items-center justify-center"> <div className="h-48 flex items-center justify-center">
<div className="avatar"> <div className="avatar">
<div className="w-28 rounded-full cursor-pointer" onClick={selectAvatar}> <div className="w-28 rounded-full cursor-pointer" onClick={selectAvatar}>
@@ -97,9 +99,9 @@ function ChangeAvatarDialog() {
{error && <ErrorAlert message={error} className={"m-4"} />} {error && <ErrorAlert message={error} className={"m-4"} />}
<div className="modal-action"> <div className="modal-action">
<form method="dialog"> <form method="dialog">
<Button>Close</Button> <Button>{t("Close")}</Button>
</form> </form>
<Button className="btn-primary" onClick={handleSubmit} isLoading={isLoading} disabled={avatar == null}>Save</Button> <Button className="btn-primary" onClick={handleSubmit} isLoading={isLoading} disabled={avatar == null}>{t("Save")}</Button>
</div> </div>
</div> </div>
</dialog> </dialog>
@@ -112,9 +114,11 @@ function ChangeUsernameDialog() {
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const navigator = useNavigator(); const navigator = useNavigator();
const { t } = useTranslation();
const handleSubmit = async () => { const handleSubmit = async () => {
if (!newUsername.trim()) { if (!newUsername.trim()) {
setError("Username cannot be empty"); setError(t("Username cannot be empty"));
return; return;
} }
setIsLoading(true); setIsLoading(true);
@@ -126,7 +130,7 @@ function ChangeUsernameDialog() {
app.user = res.data!; app.user = res.data!;
navigator.refresh(); navigator.refresh();
showToast({ showToast({
message: "Username changed successfully", message: t("Username changed successfully"),
type: "success", type: "success",
}); });
const dialog = document.getElementById("change_username_dialog") as HTMLDialogElement; const dialog = document.getElementById("change_username_dialog") as HTMLDialogElement;
@@ -139,7 +143,7 @@ function ChangeUsernameDialog() {
}; };
return <> return <>
<ListTile icon={<MdOutlineEditNote />} title="Change Username" onClick={() => { <ListTile icon={<MdOutlineEditNote />} title={t("Change Username")} onClick={() => {
const dialog = document.getElementById("change_username_dialog") as HTMLDialogElement; const dialog = document.getElementById("change_username_dialog") as HTMLDialogElement;
if (dialog) { if (dialog) {
dialog.showModal(); dialog.showModal();
@@ -147,14 +151,14 @@ function ChangeUsernameDialog() {
}} /> }} />
<dialog id="change_username_dialog" className="modal"> <dialog id="change_username_dialog" className="modal">
<div className="modal-box"> <div className="modal-box">
<h3 className="font-bold text-lg">Change Username</h3> <h3 className="font-bold text-lg">{t("Change Username")}</h3>
<div className="input mt-4 w-full"> <div className="input mt-4 w-full">
<label className="label"> <label className="label">
New Username {t("New Username")}
</label> </label>
<input <input
type="text" type="text"
placeholder="Enter new username" placeholder={t("Enter new username")}
value={newUsername} value={newUsername}
onChange={(e) => setNewUsername(e.target.value)} onChange={(e) => setNewUsername(e.target.value)}
/> />
@@ -162,7 +166,7 @@ function ChangeUsernameDialog() {
{error && <ErrorAlert message={error} className={"mt-4"} />} {error && <ErrorAlert message={error} className={"mt-4"} />}
<div className="modal-action"> <div className="modal-action">
<form method="dialog"> <form method="dialog">
<Button>Close</Button> <Button>{t("Close")}</Button>
</form> </form>
<Button <Button
className="btn-primary" className="btn-primary"
@@ -170,7 +174,7 @@ function ChangeUsernameDialog() {
isLoading={isLoading} isLoading={isLoading}
disabled={!newUsername.trim()} disabled={!newUsername.trim()}
> >
Save {t("Save")}
</Button> </Button>
</div> </div>
</div> </div>
@@ -185,20 +189,22 @@ function ChangePasswordDialog() {
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null); const [error, setError] = useState<string | null>(null);
const { t } = useTranslation();
const handleSubmit = async () => { const handleSubmit = async () => {
// Validate input // Validate input
if (!oldPassword || !newPassword || !confirmPassword) { if (!oldPassword || !newPassword || !confirmPassword) {
setError("All fields are required"); setError(t("All fields are required"));
return; return;
} }
if (newPassword !== confirmPassword) { if (newPassword !== confirmPassword) {
setError("New passwords don't match"); setError(t("New passwords don't match"));
return; return;
} }
if (newPassword.length < 6) { if (newPassword.length < 6) {
setError("New password must be at least 6 characters long"); setError(t("New password must be at least 6 characters long"));
return; return;
} }
@@ -214,7 +220,7 @@ function ChangePasswordDialog() {
app.user = res.data!; app.user = res.data!;
showToast({ showToast({
message: "Password changed successfully", message: t("Password changed successfully"),
type: "success", type: "success",
}); });
@@ -232,7 +238,7 @@ function ChangePasswordDialog() {
}; };
return <> return <>
<ListTile icon={<MdLockOutline />} title="Change Password" onClick={() => { <ListTile icon={<MdLockOutline />} title={t("Change Password")} onClick={() => {
const dialog = document.getElementById("change_password_dialog") as HTMLDialogElement; const dialog = document.getElementById("change_password_dialog") as HTMLDialogElement;
if (dialog) { if (dialog) {
dialog.showModal(); dialog.showModal();
@@ -240,13 +246,13 @@ function ChangePasswordDialog() {
}} /> }} />
<dialog id="change_password_dialog" className="modal"> <dialog id="change_password_dialog" className="modal">
<div className="modal-box"> <div className="modal-box">
<h3 className="font-bold text-lg mb-2">Change Password</h3> <h3 className="font-bold text-lg mb-2">{t("Change Password")}</h3>
<fieldset className="fieldset w-full"> <fieldset className="fieldset w-full">
<legend className="fieldset-legend">Current Password</legend> <legend className="fieldset-legend">{t("Current Password")}</legend>
<input <input
type="password" type="password"
placeholder="Enter current password" placeholder={t("Enter current password")}
value={oldPassword} value={oldPassword}
className="input w-full" className="input w-full"
onChange={(e) => setOldPassword(e.target.value)} onChange={(e) => setOldPassword(e.target.value)}
@@ -254,10 +260,10 @@ function ChangePasswordDialog() {
</fieldset> </fieldset>
<fieldset className="fieldset w-full"> <fieldset className="fieldset w-full">
<legend className="fieldset-legend">New Password</legend> <legend className="fieldset-legend">{t("New Password")}</legend>
<input <input
type="password" type="password"
placeholder="Enter new password" placeholder={t("Enter new password")}
value={newPassword} value={newPassword}
className="input w-full" className="input w-full"
onChange={(e) => setNewPassword(e.target.value)} onChange={(e) => setNewPassword(e.target.value)}
@@ -265,10 +271,10 @@ function ChangePasswordDialog() {
</fieldset> </fieldset>
<fieldset className="fieldset w-full"> <fieldset className="fieldset w-full">
<legend className="fieldset-legend">Confirm New Password</legend> <legend className="fieldset-legend">{t("Confirm New Password")}</legend>
<input <input
type="password" type="password"
placeholder="Confirm new password" placeholder={t("Confirm new password")}
value={confirmPassword} value={confirmPassword}
className="input w-full" className="input w-full"
onChange={(e) => setConfirmPassword(e.target.value)} onChange={(e) => setConfirmPassword(e.target.value)}
@@ -279,7 +285,7 @@ function ChangePasswordDialog() {
<div className="modal-action"> <div className="modal-action">
<form method="dialog"> <form method="dialog">
<Button>Close</Button> <Button>{t("Close")}</Button>
</form> </form>
<Button <Button
className="btn-primary" className="btn-primary"
@@ -287,7 +293,7 @@ function ChangePasswordDialog() {
isLoading={isLoading} isLoading={isLoading}
disabled={!oldPassword || !newPassword || !confirmPassword} disabled={!oldPassword || !newPassword || !confirmPassword}
> >
Save {t("Save")}
</Button> </Button>
</div> </div>
</div> </div>

View File

@@ -6,7 +6,14 @@ import showToast from "../components/toast.ts";
import Markdown from "react-markdown"; import Markdown from "react-markdown";
import "../markdown.css"; import "../markdown.css";
import Loading from "../components/loading.tsx"; import Loading from "../components/loading.tsx";
import { MdAdd, MdOutlineArticle, MdOutlineComment, MdOutlineDataset, MdOutlineDownload } from "react-icons/md"; import {
MdAdd,
MdOutlineArticle,
MdOutlineComment,
MdOutlineDataset,
MdOutlineDelete,
MdOutlineDownload
} from "react-icons/md";
import {app} from "../app.ts"; import {app} from "../app.ts";
import {uploadingManager} from "../network/uploading.ts"; import {uploadingManager} from "../network/uploading.ts";
import {ErrorAlert} from "../components/alert.tsx"; import {ErrorAlert} from "../components/alert.tsx";
@@ -14,6 +21,7 @@ import { useTranslation } from "react-i18next";
import Pagination from "../components/pagination.tsx"; import Pagination from "../components/pagination.tsx";
import showPopup, {useClosePopup} from "../components/popup.tsx"; import showPopup, {useClosePopup} from "../components/popup.tsx";
import {Turnstile} from "@marsidev/react-turnstile"; import {Turnstile} from "@marsidev/react-turnstile";
import Button from "../components/button.tsx";
export default function ResourcePage() { export default function ResourcePage() {
const params = useParams() const params = useParams()
@@ -141,12 +149,66 @@ export default function ResourcePage() {
<div key={"comments"} className="tab-content p-2"> <div key={"comments"} className="tab-content p-2">
<Comments resourceId={resource.id}/> <Comments resourceId={resource.id}/>
</div> </div>
<div className={"grow"}></div>
<DeleteResourceDialog resourceId={resource.id} uploaderId={resource.author.id}/>
</div> </div>
<div className="h-4"></div> <div className="h-4"></div>
</div> </div>
</context.Provider> </context.Provider>
} }
function DeleteResourceDialog({resourceId, uploaderId}: { resourceId: number, uploaderId?: number }) {
const [isLoading, setLoading] = useState(false)
const navigate = useNavigate()
const {t} = useTranslation()
const handleDelete = async () => {
if (isLoading) {
return
}
setLoading(true)
const res = await network.deleteResource(resourceId)
const dialog = document.getElementById("delete_resource_dialog") as HTMLDialogElement
dialog.close()
if (res.success) {
showToast({message: t("Resource deleted successfully"), type: "success"})
navigate("/", {replace: true})
} else {
showToast({message: res.message, type: "error"})
}
setLoading(false)
}
if (!app.isAdmin() && app.user?.id !== uploaderId) {
return <></>
}
return <>
<Button className={"btn-error btn-ghost"} onClick={() => {
const dialog = document.getElementById("delete_resource_dialog") as HTMLDialogElement
dialog.showModal()
}}>
<MdOutlineDelete size={20} className={"inline-block"}/>
</Button>
<dialog id={`delete_resource_dialog`} className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">{t("Delete Resource")}</h3>
<p
className="py-4">{t("Are you sure you want to delete the resource")}? {t("This action cannot be undone.")}</p>
<div className="modal-action">
<form method="dialog">
<button className="btn btn-ghost">{t("Close")}</button>
</form>
<Button className="btn btn-error" isLoading={isLoading} onClick={handleDelete}>{t("Delete")}</Button>
</div>
</div>
</dialog>
</>
}
const context = createContext<() => void>(() => { const context = createContext<() => void>(() => {
}) })
@@ -176,6 +238,7 @@ function FileTile({ file }: { file: RFile }) {
}}> }}>
<MdOutlineDownload size={24}/> <MdOutlineDownload size={24}/>
</button> </button>
<DeleteFileDialog fileId={file.id}/>
</div> </div>
</div> </div>
</div> </div>
@@ -418,6 +481,8 @@ function Comments({ resourceId }: { resourceId: number }) {
const [isLoading, setLoading] = useState(false); const [isLoading, setLoading] = useState(false);
const {t} = useTranslation();
const reload = useCallback(() => { const reload = useCallback(() => {
setPage(1); setPage(1);
setMaxPage(0); setMaxPage(0);
@@ -429,14 +494,14 @@ function Comments({ resourceId }: { resourceId: number }) {
return; return;
} }
if (commentContent === "") { if (commentContent === "") {
showToast({ message: "Comment content cannot be empty", type: "error" }); showToast({message: t("Comment content cannot be empty"), type: "error"});
return; return;
} }
setLoading(true); setLoading(true);
const res = await network.createComment(resourceId, commentContent); const res = await network.createComment(resourceId, commentContent);
if (res.success) { if (res.success) {
setCommentContent(""); setCommentContent("");
showToast({ message: "Comment created successfully", type: "success" }); showToast({message: t("Comment created successfully"), type: "success"});
reload(); reload();
} else { } else {
showToast({message: res.message, type: "error"}); showToast({message: res.message, type: "error"});
@@ -446,7 +511,7 @@ function Comments({ resourceId }: { resourceId: number }) {
return <div> return <div>
<div className={"mt-4 mb-6 textarea w-full p-4 h-28 flex flex-col"}> <div className={"mt-4 mb-6 textarea w-full p-4 h-28 flex flex-col"}>
<textarea placeholder={"Write down your comment"} className={"w-full resize-none grow"} value={commentContent} <textarea placeholder={t("Write down your comment")} className={"w-full resize-none grow"} value={commentContent}
onChange={(e) => setCommentContent(e.target.value)}/> onChange={(e) => setCommentContent(e.target.value)}/>
<div className={"flex flex-row-reverse"}> <div className={"flex flex-row-reverse"}>
<button onClick={sendComment} <button onClick={sendComment}
@@ -517,3 +582,55 @@ function CommentTile({ comment }: { comment: Comment }) {
</div> </div>
</div> </div>
} }
function DeleteFileDialog({fileId}: { fileId: string }) {
const [isLoading, setLoading] = useState(false)
const id = `delete_file_dialog_${fileId}`
const reload = useContext(context)
const {t} = useTranslation();
const handleDelete = async () => {
if (isLoading) {
return
}
setLoading(true)
const res = await network.deleteFile(fileId)
const dialog = document.getElementById(id) as HTMLDialogElement
dialog.close()
if (res.success) {
showToast({message: t("File deleted successfully"), type: "success"})
reload()
} else {
showToast({message: res.message, type: "error"})
}
setLoading(false)
}
if (!app.isAdmin()) {
return <></>
}
return <>
<button className={"btn btn-error btn-ghost btn-circle ml-1"} onClick={() => {
const dialog = document.getElementById(id) as HTMLDialogElement
dialog.showModal()
}}>
<MdOutlineDelete size={20} className={"inline-block"}/>
</button>
<dialog id={id} className="modal">
<div className="modal-box">
<h3 className="font-bold text-lg">{t("Delete File")}</h3>
<p className="py-4">{t("Are you sure you want to delete the file? This action cannot be undone.")}</p>
<div className="modal-action">
<form method="dialog">
<button className="btn btn-ghost">{t("Close")}</button>
</form>
<button className="btn btn-error" onClick={handleDelete}>{t("Delete")}</button>
</div>
</div>
</dialog>
</>
}