diff --git a/ui/src/components/AppDocument.tsx b/ui/src/components/AppDocument.tsx index ef738d99..d4d2dcb2 100644 --- a/ui/src/components/AppDocument.tsx +++ b/ui/src/components/AppDocument.tsx @@ -11,14 +11,10 @@ export interface AppDocumentLinkButtonProps { } const AppDocumentLinkButton = ({ className, style, showIcon = true }: AppDocumentLinkButtonProps) => { - const { i18n, t } = useTranslation(); + const { t } = useTranslation(); const handleDocumentClick = () => { - if (i18n.language.startsWith("en")) { - window.open(APP_DOCUMENT_URL + "/en/", "_blank"); - } else { - window.open(APP_DOCUMENT_URL, "_blank"); - } + window.open(APP_DOCUMENT_URL, "_blank"); }; return ( diff --git a/ui/src/domain/app.ts b/ui/src/domain/app.ts index bc7f58e2..11bcc27f 100644 --- a/ui/src/domain/app.ts +++ b/ui/src/domain/app.ts @@ -1,8 +1,16 @@ -export const APP_REPO_URL = "https://github.com/certimate-go/certimate"; - -export const APP_DOWNLOAD_URL = APP_REPO_URL + "/releases"; - -export const APP_DOCUMENT_URL = "https://docs.certimate.me"; +import i18next from "i18next"; // fallback policy: .env > git tag > "v0.0.0-dev" export const APP_VERSION: string = import.meta.env.VITE_APP_VERSION || __APP_VERSION__ || "v0.0.0-dev"; + +export const APP_REPO_URL = "https://github.com/certimate-go/certimate"; +export const APP_DOWNLOAD_URL = APP_REPO_URL + "/releases"; +export let APP_DOCUMENT_URL = "https://docs.certimate.me"; + +i18next.on("languageChanged", (language) => { + if (language.startsWith("zh")) { + APP_DOCUMENT_URL = "https://docs.certimate.me"; + } else { + APP_DOCUMENT_URL = "https://docs.certimate.me/en/"; + } +}); diff --git a/ui/src/i18n/locales/en/nls.settings.json b/ui/src/i18n/locales/en/nls.settings.json index 642a11a0..29789404 100644 --- a/ui/src/i18n/locales/en/nls.settings.json +++ b/ui/src/i18n/locales/en/nls.settings.json @@ -57,5 +57,17 @@ "settings.diagnostics.workflow_dispatcher.title": "Workflow dispatcher", "settings.diagnostics.workflow_dispatcher.statistics.concurrency": "Concurrency", "settings.diagnostics.workflow_dispatcher.statistics.pending": "Pending", - "settings.diagnostics.workflow_dispatcher.statistics.processing": "Processing" + "settings.diagnostics.workflow_dispatcher.statistics.processing": "Processing", + + "settings.about.tab": "About", + "settings.about.version.new": "New version available", + "settings.about.socials.document": "Documentation", + "settings.about.socials.github": "GitHub", + "settings.about.socials.telegram": "Telegram", + "settings.about.socials.donate": "Donate", + "settings.about.feedback.title": "Help us improve Certimate", + "settings.about.feedback.subtitle": "Tell us how to make Certimate work better for you.", + "settings.about.feedback.button": "Give feedback", + "settings.about.contributors.title": "Contributors", + "settings.about.contributors.tips": "Thanks to all the people who have contributed to this project." } diff --git a/ui/src/i18n/locales/zh/nls.settings.json b/ui/src/i18n/locales/zh/nls.settings.json index 94d39849..a59c39da 100644 --- a/ui/src/i18n/locales/zh/nls.settings.json +++ b/ui/src/i18n/locales/zh/nls.settings.json @@ -57,5 +57,17 @@ "settings.diagnostics.workflow_dispatcher.title": "工作流调度器", "settings.diagnostics.workflow_dispatcher.statistics.concurrency": "最大并发", "settings.diagnostics.workflow_dispatcher.statistics.pending": "等待运行", - "settings.diagnostics.workflow_dispatcher.statistics.processing": "运行中" + "settings.diagnostics.workflow_dispatcher.statistics.processing": "运行中", + + "settings.about.tab": "关于", + "settings.about.version.new": "有新版本可更新", + "settings.about.socials.document": "文档", + "settings.about.socials.github": "GitHub", + "settings.about.socials.telegram": "Telegram", + "settings.about.socials.donate": "捐赠", + "settings.about.feedback.title": "帮助我们改进 Certimate", + "settings.about.feedback.subtitle": "告诉我们如何让 Certimate 为你更好地服务。", + "settings.about.feedback.button": "意见反馈", + "settings.about.contributors.title": "贡献者", + "settings.about.contributors.tips": "感谢所有贡献者对本项目做出的贡献。" } diff --git a/ui/src/pages/ConsoleLayout.tsx b/ui/src/pages/ConsoleLayout.tsx index 7d7f635f..16c88902 100644 --- a/ui/src/pages/ConsoleLayout.tsx +++ b/ui/src/pages/ConsoleLayout.tsx @@ -28,7 +28,7 @@ import { isBrowserHappy } from "@/utils/browser"; const ConsoleLayout = () => { const navigate = useNavigate(); - const { i18n, t } = useTranslation(); + const { t } = useTranslation(); const { token: themeToken } = theme.useToken(); @@ -40,11 +40,7 @@ const ConsoleLayout = () => { }; const handleDocumentClick = () => { - if (i18n.language.startsWith("zh")) { - window.open(APP_DOCUMENT_URL, "_blank"); - } else { - window.open(APP_DOCUMENT_URL + "/en/", "_blank"); - } + window.open(APP_DOCUMENT_URL, "_blank"); }; const handleGitHubClick = () => { diff --git a/ui/src/pages/settings/Settings.tsx b/ui/src/pages/settings/Settings.tsx index 038ad039..5c22351e 100644 --- a/ui/src/pages/settings/Settings.tsx +++ b/ui/src/pages/settings/Settings.tsx @@ -1,7 +1,7 @@ import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { Outlet, useLocation, useNavigate } from "react-router-dom"; -import { IconBracketsAngle, IconDatabaseCog, IconPalette, IconPlugConnected, IconUserShield } from "@tabler/icons-react"; +import { IconBracketsAngle, IconDatabaseCog, IconInfoCircle, IconPalette, IconPlugConnected, IconUserShield } from "@tabler/icons-react"; import { Menu } from "antd"; const Settings = () => { @@ -16,6 +16,7 @@ const Settings = () => { ["ssl-provider", "settings.sslprovider.tab", ], ["persistence", "settings.persistence.tab", ], ["diagnostics", "settings.diagnostics.tab", ], + ["about", "settings.about.tab", ], ] satisfies [string, string, React.ReactElement][]; const [menuKey, setMenuKey] = useState(() => location.pathname.split("/")[2]); useEffect(() => { diff --git a/ui/src/pages/settings/SettingsAbout.tsx b/ui/src/pages/settings/SettingsAbout.tsx new file mode 100644 index 00000000..b4d86e8c --- /dev/null +++ b/ui/src/pages/settings/SettingsAbout.tsx @@ -0,0 +1,88 @@ +import { useTranslation } from "react-i18next"; +import { IconBook, IconBrandGithub, IconBrandTelegram, IconCoin, IconMessageChatbot } from "@tabler/icons-react"; +import { Badge, Button, Divider, List, Tooltip, Typography } from "antd"; + +import { APP_DOCUMENT_URL, APP_DOWNLOAD_URL, APP_REPO_URL, APP_VERSION } from "@/domain/app"; +import { useVersionChecker } from "@/hooks"; + +const SettingsAbout = () => { + const { t } = useTranslation(); + + const { hasUpdate } = useVersionChecker(); + + const handleDownloadClick = () => { + window.open(APP_DOWNLOAD_URL, "_blank"); + }; + + const handleDocumentClick = () => { + window.open(APP_DOCUMENT_URL, "_blank"); + }; + + const handleGithubClick = () => { + window.open(APP_REPO_URL, "_blank"); + }; + + const handleTelegramClick = () => { + window.open("https://t.me/+ZXphsppxUg41YmVl", "_blank"); + }; + + const handleDonateClick = () => { + window.open("https://profile.ikit.fun/sponsors/", "_blank"); + }; + + const handleFeedbackClick = () => { + window.open(APP_REPO_URL + "/issues", "_blank"); + }; + + return ( + <> +

Certimate

+
+
+ Version: {APP_VERSION} + +
+
+
+ +
+ + + +

{t("settings.about.contributors.title")}

+
+ {t("settings.about.contributors.tips")} +
+
+ Contributors +
+ + + +
+ + {t("settings.about.feedback.button")}}> + } + title={t("settings.about.feedback.title")} + description={t("settings.about.feedback.subtitle")} + /> + + +
+ + ); +}; + +export default SettingsAbout; diff --git a/ui/src/pages/settings/SettingsDiagnostics.tsx b/ui/src/pages/settings/SettingsDiagnostics.tsx index b693d131..c1157e8c 100644 --- a/ui/src/pages/settings/SettingsDiagnostics.tsx +++ b/ui/src/pages/settings/SettingsDiagnostics.tsx @@ -306,7 +306,7 @@ const SettingsDiagnosticsWorkflowDispatcher = ({ className, style }: { className type Statistics = Awaited>["data"]; const [statistics, setStatistics] = useState(); - const { loading } = useRequest( + const { loading, cancel } = useRequest( () => { return getWorkflowStats(); }, @@ -318,6 +318,11 @@ const SettingsDiagnosticsWorkflowDispatcher = ({ className, style }: { className onSuccess: (res) => { setStatistics(res.data); }, + onError: () => { + if (!statistics) { + cancel(); + } + }, } ); diff --git a/ui/src/routers/index.tsx b/ui/src/routers/index.tsx index be671ebc..cabf6278 100644 --- a/ui/src/routers/index.tsx +++ b/ui/src/routers/index.tsx @@ -9,6 +9,7 @@ import Dashboard from "@/pages/dashboard/Dashboard"; import ErrorLayout from "@/pages/ErrorLayout"; import Login from "@/pages/login/Login"; import Settings from "@/pages/settings/Settings"; +import SettingsAbout from "@/pages/settings/SettingsAbout"; import SettingsAccount from "@/pages/settings/SettingsAccount"; import SettingsAppearance from "@/pages/settings/SettingsAppearance"; import SettingsDiagnostics from "@/pages/settings/SettingsDiagnostics"; @@ -87,6 +88,10 @@ export const router = createHashRouter([ path: "/settings/diagnostics", element: , }, + { + path: "/settings/about", + element: , + }, ], }, ],