diff --git a/ui/src/components/certificate/CertificateList.tsx b/ui/src/components/certificate/CertificateList.tsx index 0cf70bee..681e51d4 100644 --- a/ui/src/components/certificate/CertificateList.tsx +++ b/ui/src/components/certificate/CertificateList.tsx @@ -130,7 +130,7 @@ const CertificateList = ({ withPagination }: CertificateListProps) => { ]; const handleWorkflowClick = (id: string) => { - navigate(`/workflow/detail?id=${id}`); + navigate(`/workflows/detail?id=${id}`); }; const handleView = (id: string) => { @@ -154,7 +154,7 @@ const CertificateList = ({ withPagination }: CertificateListProps) => { size={"sm"} className="w-[120px] mt-3" onClick={() => { - navigate("/workflow/detail"); + navigate("/workflows/detail"); }} > {t("workflow.action.create")} diff --git a/ui/src/domain/version.ts b/ui/src/domain/version.ts index d925c9c3..e70c6d19 100644 --- a/ui/src/domain/version.ts +++ b/ui/src/domain/version.ts @@ -1 +1 @@ -export const version = "Certimate v0.3.0-alpha.3"; +export const version = "v0.3.0-alpha.3"; diff --git a/ui/src/i18n/locales/en/nls.access.json b/ui/src/i18n/locales/en/nls.access.json index f2514647..b86f0994 100644 --- a/ui/src/i18n/locales/en/nls.access.json +++ b/ui/src/i18n/locales/en/nls.access.json @@ -1,5 +1,5 @@ { - "access.page.title": "Authorization Management", + "access.page.title": "Authorization", "access.nodata": "Please add authorization to start deploying certificate.", diff --git a/ui/src/i18n/locales/en/nls.common.json b/ui/src/i18n/locales/en/nls.common.json index 22051c9d..d8ffa44e 100644 --- a/ui/src/i18n/locales/en/nls.common.json +++ b/ui/src/i18n/locales/en/nls.common.json @@ -34,6 +34,8 @@ "common.text.nodata": "No data available", "common.text.request_error": "Request error", + "common.menu.theme": "Change Theme", + "common.menu.locale": "Change Language", "common.menu.settings": "Settings", "common.menu.logout": "Logout", "common.menu.document": "Document", diff --git a/ui/src/i18n/locales/zh/nls.common.json b/ui/src/i18n/locales/zh/nls.common.json index 0d66a3ba..96594aab 100644 --- a/ui/src/i18n/locales/zh/nls.common.json +++ b/ui/src/i18n/locales/zh/nls.common.json @@ -34,6 +34,8 @@ "common.text.nodata": "暂无数据", "common.text.request_error": "请求错误", + "common.menu.theme": "切换主题", + "common.menu.locale": "切换语言", "common.menu.settings": "系统设置", "common.menu.logout": "退出登录", "common.menu.document": "文档", diff --git a/ui/src/pages/LoginLayout.tsx b/ui/src/pages/AuthLayout.tsx similarity index 88% rename from ui/src/pages/LoginLayout.tsx rename to ui/src/pages/AuthLayout.tsx index 6fe03e10..3c50eddf 100644 --- a/ui/src/pages/LoginLayout.tsx +++ b/ui/src/pages/AuthLayout.tsx @@ -3,7 +3,7 @@ import { Navigate, Outlet } from "react-router-dom"; import Version from "@/components/certimate/Version"; import { getPocketBase } from "@/repository/pocketbase"; -const LoginLayout = () => { +const AuthLayout = () => { const auth = getPocketBase().authStore; if (auth.isValid && auth.isAdmin) { return ; @@ -18,4 +18,4 @@ const LoginLayout = () => { ); }; -export default LoginLayout; +export default AuthLayout; diff --git a/ui/src/pages/ConsoleLayout.tsx b/ui/src/pages/ConsoleLayout.tsx new file mode 100644 index 00000000..4bc0cb30 --- /dev/null +++ b/ui/src/pages/ConsoleLayout.tsx @@ -0,0 +1,181 @@ +import { useState, useEffect } from "react"; +import { Link, Navigate, Outlet, useLocation, useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { Button, Dropdown, Layout, Menu, Tooltip, theme, type ButtonProps, type MenuProps } from "antd"; +import { + Languages as LanguagesIcon, + LogOut as LogOutIcon, + Home as HomeIcon, + Menu as MenuIcon, + Server as ServerIcon, + Settings as SettingsIcon, + ShieldCheck as ShieldCheckIcon, + Sun as SunIcon, + Workflow as WorkflowIcon, +} from "lucide-react"; + +import Version from "@/components/certimate/Version"; +import { getPocketBase } from "@/repository/pocketbase"; +import { ConfigProvider } from "@/providers/config"; + +const ConsoleLayout = () => { + const location = useLocation(); + const navigate = useNavigate(); + + const { t } = useTranslation(); + + const { + token: { colorBgContainer }, + } = theme.useToken(); + + const menuItems: Required["items"] = [ + { + key: "/", + icon: , + label: t("dashboard.page.title"), + onClick: () => navigate("/"), + }, + { + key: "/workflows", + icon: , + label: t("workflow.page.title"), + onClick: () => navigate("/workflows"), + }, + { + key: "/certificates", + icon: , + label: t("certificate.page.title"), + onClick: () => navigate("/certificates"), + }, + { + key: "/accesses", + icon: , + label: t("access.page.title"), + onClick: () => navigate("/accesses"), + }, + ]; + const [menuSelectedKey, setMenuSelectedKey] = useState(null); + + useEffect(() => { + const item = + menuItems.find((item) => item!.key === location.pathname) ?? + menuItems.find((item) => item!.key !== "/" && location.pathname.startsWith(item!.key as string)); + console.log(item); + if (item) { + setMenuSelectedKey(item.key as string); + } else { + setMenuSelectedKey(null); + } + }, [location.pathname]); + + useEffect(() => { + if (menuSelectedKey) { + navigate(menuSelectedKey); + } + }, [menuSelectedKey]); + + // TODO: 响应式侧边栏菜单 + + const handleLogoutClick = () => { + auth.clear(); + navigate("/login"); + }; + + const handleSettingsClick = () => { + navigate("/settings/account"); + }; + + const auth = getPocketBase().authStore; + if (!auth.isValid || !auth.isAdmin) { + return ; + } + + return ( + <> + + + +
+ + + Certimate + +
+ { + setMenuSelectedKey(key); + }} + /> +
+
+ +
+
+
+ + + +
+
{/*
+
+ + + + + + + +
+
+
+ + +
+ +
+
+
+
+
+ + ); +}; + +const ThemeToggleButton = ({ size }: { size?: ButtonProps["size"] }) => { + // TODO: 主题切换 + const items: Required["items"] = []; + + return ( + + - - - -
- -
-
- -
- - - - - - - - {t("common.menu.settings")} - {t("common.menu.logout")} - - - -
- -
- - - - - ); -} diff --git a/ui/src/pages/SettingLayout.tsx b/ui/src/pages/SettingsLayout.tsx similarity index 88% rename from ui/src/pages/SettingLayout.tsx rename to ui/src/pages/SettingsLayout.tsx index 0f5fc024..2097b3bc 100644 --- a/ui/src/pages/SettingLayout.tsx +++ b/ui/src/pages/SettingsLayout.tsx @@ -1,12 +1,13 @@ import { useEffect, useState } from "react"; import { Outlet, useLocation, useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; +import { Card } from "antd"; import { KeyRound, Megaphone, ShieldCheck, UserRound } from "lucide-react"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Toaster } from "@/components/ui/toaster"; -const SettingLayout = () => { +const SettingsLayout = () => { const location = useLocation(); const [tabValue, setTabValue] = useState("account"); const navigate = useNavigate(); @@ -19,7 +20,7 @@ const SettingLayout = () => { }, [location]); return ( -
+
{t("settings.page.title")}
@@ -28,7 +29,7 @@ const SettingLayout = () => { { - navigate("/setting/account"); + navigate("/settings/account"); }} className="px-5" > @@ -39,7 +40,7 @@ const SettingLayout = () => { { - navigate("/setting/password"); + navigate("/settings/password"); }} className="px-5" > @@ -50,7 +51,7 @@ const SettingLayout = () => { { - navigate("/setting/notify"); + navigate("/settings/notify"); }} className="px-5" > @@ -61,7 +62,7 @@ const SettingLayout = () => { { - navigate("/setting/ssl-provider"); + navigate("/settings/ssl-provider"); }} className="px-5" > @@ -76,8 +77,8 @@ const SettingLayout = () => {
-
+ ); }; -export default SettingLayout; +export default SettingsLayout; diff --git a/ui/src/pages/certificates/CertificateList.tsx b/ui/src/pages/certificates/CertificateList.tsx index 15cd9827..facde851 100644 --- a/ui/src/pages/certificates/CertificateList.tsx +++ b/ui/src/pages/certificates/CertificateList.tsx @@ -66,7 +66,7 @@ const CertificateList = () => { type="secondary" ellipsis onClick={() => { - navigate(`/workflow/detail?id=${workflowId}`); + navigate(`/workflows/detail?id=${workflowId}`); }} > {record.expand?.workflow?.name ?? ""} diff --git a/ui/src/pages/login/Login.tsx b/ui/src/pages/login/Login.tsx index ba26df7d..8ae9b371 100644 --- a/ui/src/pages/login/Login.tsx +++ b/ui/src/pages/login/Login.tsx @@ -21,10 +21,10 @@ const Login = () => { const formRule = createSchemaFieldRule(formSchema); const [form] = Form.useForm(); - const [submitting, setSubmitting] = useState(false); + const [isPending, setIsPending] = useState(false); const onSubmit = async (values: z.infer) => { - setSubmitting(true); + setIsPending(true); try { await getPocketBase().admins.authWithPassword(values.username, values.password); @@ -32,7 +32,7 @@ const Login = () => { } catch (err) { notificationApi.error({ message: t("common.text.request_error"), description: <>{String(err)} }); } finally { - setSubmitting(false); + setIsPending(false); } }; @@ -45,7 +45,7 @@ const Login = () => { -
+ @@ -55,7 +55,7 @@ const Login = () => { - diff --git a/ui/src/pages/setting/Account.tsx b/ui/src/pages/settings/Account.tsx similarity index 100% rename from ui/src/pages/setting/Account.tsx rename to ui/src/pages/settings/Account.tsx diff --git a/ui/src/pages/setting/Notify.tsx b/ui/src/pages/settings/Notify.tsx similarity index 100% rename from ui/src/pages/setting/Notify.tsx rename to ui/src/pages/settings/Notify.tsx diff --git a/ui/src/pages/setting/Password.tsx b/ui/src/pages/settings/Password.tsx similarity index 100% rename from ui/src/pages/setting/Password.tsx rename to ui/src/pages/settings/Password.tsx diff --git a/ui/src/pages/setting/SSLProvider.tsx b/ui/src/pages/settings/SSLProvider.tsx similarity index 100% rename from ui/src/pages/setting/SSLProvider.tsx rename to ui/src/pages/settings/SSLProvider.tsx diff --git a/ui/src/pages/workflows/WorkflowDetail.tsx b/ui/src/pages/workflows/WorkflowDetail.tsx index 034db9a1..bef32888 100644 --- a/ui/src/pages/workflows/WorkflowDetail.tsx +++ b/ui/src/pages/workflows/WorkflowDetail.tsx @@ -87,7 +87,7 @@ const WorkflowDetail = () => { } switchEnable(); if (!locId) { - navigate(`/workflow/detail?id=${workflow.id}`); + navigate(`/workflows/detail?id=${workflow.id}`); } }; @@ -102,7 +102,7 @@ const WorkflowDetail = () => { } save(); if (!locId) { - navigate(`/workflow/detail?id=${workflow.id}`); + navigate(`/workflows/detail?id=${workflow.id}`); } }; diff --git a/ui/src/pages/workflows/WorkflowList.tsx b/ui/src/pages/workflows/WorkflowList.tsx index 3d47e93b..d3546cd6 100644 --- a/ui/src/pages/workflows/WorkflowList.tsx +++ b/ui/src/pages/workflows/WorkflowList.tsx @@ -112,7 +112,7 @@ const WorkflowList = () => { type="link" icon={} onClick={() => { - navigate(`/workflow/detail?id=${record.id}`); + navigate(`/workflows/detail?id=${record.id}`); }} /> @@ -202,7 +202,7 @@ const WorkflowList = () => { }; const handleCreateClick = () => { - navigate("/workflow/detail"); + navigate("/workflows/detail"); }; // TODO: Empty 样式 diff --git a/ui/src/router.tsx b/ui/src/router.tsx index 21c3e0cb..3ac7e713 100644 --- a/ui/src/router.tsx +++ b/ui/src/router.tsx @@ -1,13 +1,13 @@ import { createHashRouter } from "react-router-dom"; -import LoginLayout from "./pages/LoginLayout"; -import DashboardLayout from "./pages/DashboardLayout"; -import SettingLayout from "./pages/SettingLayout"; +import AuthLayout from "./pages/AuthLayout"; +import ConsoleLayout from "./pages/ConsoleLayout"; +import SettingsLayout from "./pages/SettingsLayout"; import Login from "./pages/login/Login"; -import Account from "./pages/setting/Account"; -import Password from "./pages/setting/Password"; -import Notify from "./pages/setting/Notify"; -import SSLProvider from "./pages/setting/SSLProvider"; +import Account from "./pages/settings/Account"; +import Password from "./pages/settings/Password"; +import Notify from "./pages/settings/Notify"; +import SSLProvider from "./pages/settings/SSLProvider"; import Dashboard from "./pages/dashboard/Dashboard"; import AccessList from "./pages/accesses/AccessList"; import WorkflowList from "./pages/workflows/WorkflowList"; @@ -17,7 +17,7 @@ import CertificateList from "./pages/certificates/CertificateList"; export const router = createHashRouter([ { path: "/", - element: , + element: , children: [ { path: "/", @@ -36,23 +36,27 @@ export const router = createHashRouter([ element: , }, { - path: "/setting", - element: , + path: "/workflows/detail", + element: , + }, + { + path: "/settings", + element: , children: [ { - path: "/setting/password", + path: "/settings/password", element: , }, { - path: "/setting/account", + path: "/settings/account", element: , }, { - path: "/setting/notify", + path: "/settings/notify", element: , }, { - path: "/setting/ssl-provider", + path: "/settings/ssl-provider", element: , }, ], @@ -61,7 +65,7 @@ export const router = createHashRouter([ }, { path: "/login", - element: , + element: , children: [ { path: "/login", @@ -69,8 +73,4 @@ export const router = createHashRouter([ }, ], }, - { - path: "/workflow/detail", - element: , - }, ]);