diff --git a/ui/.eslintrc.cjs b/ui/.eslintrc.cjs index 1281ed3a..9c21321d 100644 --- a/ui/.eslintrc.cjs +++ b/ui/.eslintrc.cjs @@ -13,6 +13,7 @@ module.exports = { parser: "@typescript-eslint/parser", plugins: ["react-refresh"], rules: { + "@typescript-eslint/no-explicit-any": "warn", "react-refresh/only-export-components": [ "warn", { diff --git a/ui/src/i18n/locales/en/nls.settings.json b/ui/src/i18n/locales/en/nls.settings.json index 86316757..bb299557 100644 --- a/ui/src/i18n/locales/en/nls.settings.json +++ b/ui/src/i18n/locales/en/nls.settings.json @@ -1,23 +1,19 @@ { "settings.page.title": "Settings", - "settings.account.relogin.message": "Please login again", - "settings.account.tab": "Account", "settings.account.form.email.label": "Email", "settings.account.form.email.placeholder": "Please enter email", "settings.password.tab": "Password", - "settings.password.current_password.label": "Current Password", - "settings.password.current_password.placeholder": "Please enter the current password", - "settings.password.new_password.label": "New Password", - "settings.password.new_password.placeholder": "Please enter the new password", - "settings.password.confirm_password.label": "Confirm Password", - "settings.password.confirm_password.placeholder": "Please enter the new password again", - "settings.password.password.errmsg.length": "Password should be at least 10 characters", - "settings.password.password.errmsg.not_matched": "Passwords do not match", - "settings.password.changed.message": "Password changed successfully", - "settings.password.failed.message": "Password change failed", + "settings.password.form.old_password.label": "Current Password", + "settings.password.form.old_password.placeholder": "Please enter the current password", + "settings.password.form.new_password.label": "New Password", + "settings.password.form.new_password.placeholder": "Please enter the new password", + "settings.password.form.confirm_password.label": "Confirm Password", + "settings.password.form.confirm_password.placeholder": "Please enter the new password again", + "settings.password.form.password.errmsg.invalid": "Password should be at least 10 characters", + "settings.password.form.password.errmsg.not_matched": "Passwords do not match", "settings.notification.tab": "Notification", "settings.notification.template.label": "Template", diff --git a/ui/src/i18n/locales/zh/nls.settings.json b/ui/src/i18n/locales/zh/nls.settings.json index f5c544b7..02ab610a 100644 --- a/ui/src/i18n/locales/zh/nls.settings.json +++ b/ui/src/i18n/locales/zh/nls.settings.json @@ -1,23 +1,19 @@ { "settings.page.title": "系统设置", - "settings.account.relogin.message": "请重新登录", - "settings.account.tab": "登录账号", "settings.account.form.email.label": "登录邮箱", "settings.account.form.email.placeholder": "请输入邮箱", "settings.password.tab": "登录密码", - "settings.password.password.errmsg.length": "密码至少10个字符", - "settings.password.password.errmsg.not_matched": "两次密码不一致", - "settings.password.current_password.label": "当前密码", - "settings.password.current_password.placeholder": "请输入旧密码", - "settings.password.new_password.label": "新密码", - "settings.password.new_password.placeholder": "请输入新密码", - "settings.password.confirm_password.label": "确认密码", - "settings.password.confirm_password.placeholder": "请再次输入新密码", - "settings.password.changed.message": "修改密码成功", - "settings.password.failed.message": "修改密码失败", + "settings.password.form.old_password.label": "当前密码", + "settings.password.form.old_password.placeholder": "请输入旧密码", + "settings.password.form.new_password.label": "新密码", + "settings.password.form.new_password.placeholder": "请输入新密码", + "settings.password.form.confirm_password.label": "确认密码", + "settings.password.form.confirm_password.placeholder": "请再次输入新密码", + "settings.password.form.password.errmsg.invalid": "密码至少 10 个字符", + "settings.password.form.password.errmsg.not_matched": "两次密码不一致", "settings.notification.tab": "消息推送", "settings.notification.template.label": "内容模板", diff --git a/ui/src/pages/settings/Password.tsx b/ui/src/pages/settings/Password.tsx deleted file mode 100644 index fa527627..00000000 --- a/ui/src/pages/settings/Password.tsx +++ /dev/null @@ -1,136 +0,0 @@ -import { useForm } from "react-hook-form"; -import { useNavigate } from "react-router-dom"; -import { useTranslation } from "react-i18next"; -import { z } from "zod"; -import { zodResolver } from "@hookform/resolvers/zod"; - -import { Button } from "@/components/ui/button"; -import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from "@/components/ui/form"; -import { Input } from "@/components/ui/input"; -import { useToast } from "@/components/ui/use-toast"; -import { getErrMsg } from "@/utils/error"; -import { getPocketBase } from "@/repository/pocketbase"; - -const formSchema = z - .object({ - oldPassword: z.string().min(10, { - message: "settings.password.password.errmsg.length", - }), - newPassword: z.string().min(10, { - message: "settings.password.password.errmsg.length", - }), - confirmPassword: z.string().min(10, { - message: "settings.password.password.errmsg.length", - }), - }) - .refine((data) => data.newPassword === data.confirmPassword, { - message: "settings.password.password.errmsg.not_matched", - path: ["confirmPassword"], - }); - -const Password = () => { - const { toast } = useToast(); - const navigate = useNavigate(); - const { t } = useTranslation(); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - oldPassword: "", - newPassword: "", - confirmPassword: "", - }, - }); - - const onSubmit = async (values: z.infer) => { - try { - await getPocketBase().admins.authWithPassword(getPocketBase().authStore.model?.email, values.oldPassword); - } catch (e) { - const message = getErrMsg(e); - form.setError("oldPassword", { message }); - } - - try { - await getPocketBase().admins.update(getPocketBase().authStore.model?.id, { - password: values.newPassword, - passwordConfirm: values.confirmPassword, - }); - - getPocketBase().authStore.clear(); - toast({ - title: t("settings.password.changed.message"), - description: t("settings.account.relogin.message"), - }); - setTimeout(() => { - navigate("/login"); - }, 500); - } catch (e) { - const message = getErrMsg(e); - toast({ - title: t("settings.password.failed.message"), - description: message, - variant: "destructive", - }); - } - }; - - return ( - <> -
-
- - ( - - {t("settings.password.current_password.label")} - - - - - - - )} - /> - - ( - - {t("settings.password.new_password.label")} - - - - - - - )} - /> - - ( - - {t("settings.password.confirm_password.label")} - - - - - - - )} - /> -
- -
- - -
- - ); -}; - -export default Password; diff --git a/ui/src/pages/settings/SettingsAccount.tsx b/ui/src/pages/settings/SettingsAccount.tsx index 45f78c82..f2b7fb53 100644 --- a/ui/src/pages/settings/SettingsAccount.tsx +++ b/ui/src/pages/settings/SettingsAccount.tsx @@ -17,7 +17,7 @@ const SettingsAccount = () => { const [notificationApi, NotificationContextHolder] = notification.useNotification(); const formSchema = z.object({ - username: z.string().email(t("common.errmsg.email_invalid")), + username: z.string({ message: "settings.account.form.email.placeholder" }).email({ message: t("common.errmsg.email_invalid") }), }); const formRule = createSchemaFieldRule(formSchema); const [form] = Form.useForm>(); @@ -32,12 +32,12 @@ const SettingsAccount = () => { setInitialChanged(form.getFieldValue("username") !== initialValues.username); }; - const handleFormFinish = async (values: z.infer) => { + const handleFormFinish = async (fields: z.infer) => { setFormPending(true); try { await getPocketBase().admins.update(getPocketBase().authStore.model?.id, { - email: values.username, + email: fields.username, }); messageApi.success(t("common.text.operation_succeeded")); diff --git a/ui/src/pages/settings/SettingsPassword.tsx b/ui/src/pages/settings/SettingsPassword.tsx new file mode 100644 index 00000000..01dd3a56 --- /dev/null +++ b/ui/src/pages/settings/SettingsPassword.tsx @@ -0,0 +1,99 @@ +import { useState } from "react"; +import { useNavigate } from "react-router-dom"; +import { useTranslation } from "react-i18next"; +import { Button, Form, Input, message, notification } from "antd"; +import { createSchemaFieldRule } from "antd-zod"; +import { z } from "zod"; + +import { getErrMsg } from "@/utils/error"; +import { getPocketBase } from "@/repository/pocketbase"; + +const SettingsPassword = () => { + const navigate = useNavigate(); + + const { t } = useTranslation(); + + const [messageApi, MessageContextHolder] = message.useMessage(); + const [notificationApi, NotificationContextHolder] = notification.useNotification(); + + const formSchema = z + .object({ + oldPassword: z + .string({ message: t("settings.password.form.old_password.placeholder") }) + .min(10, { message: t("settings.password.form.password.errmsg.invalid") }), + newPassword: z + .string({ message: t("settings.password.form.new_password.placeholder") }) + .min(10, { message: t("settings.password.form.password.errmsg.invalid") }), + confirmPassword: z + .string({ message: t("settings.password.form.confirm_password.placeholder") }) + .min(10, { message: t("settings.password.form.password.errmsg.invalid") }), + }) + .refine((data) => data.newPassword === data.confirmPassword, { + message: t("settings.password.form.password.errmsg.not_matched"), + path: ["confirmPassword"], + }); + const formRule = createSchemaFieldRule(formSchema); + const [form] = Form.useForm>(); + const [formPending, setFormPending] = useState(false); + + const [initialChanged, setInitialChanged] = useState(false); + + const handleInputChange = () => { + const fields = form.getFieldsValue(); + setInitialChanged(!!fields.oldPassword && !!fields.newPassword && !!fields.confirmPassword); + }; + + const handleFormFinish = async (fields: z.infer) => { + setFormPending(true); + + try { + await getPocketBase().admins.authWithPassword(getPocketBase().authStore.model?.email, fields.oldPassword); + await getPocketBase().admins.update(getPocketBase().authStore.model?.id, { + password: fields.newPassword, + passwordConfirm: fields.confirmPassword, + }); + + messageApi.success(t("common.text.operation_succeeded")); + + setTimeout(() => { + getPocketBase().authStore.clear(); + navigate("/login"); + }, 500); + } catch (err) { + notificationApi.error({ message: t("common.text.request_error"), description: <>{getErrMsg(err)} }); + } finally { + setFormPending(false); + } + }; + + return ( + <> + {MessageContextHolder} + {NotificationContextHolder} + +
+
+ + + + + + + + + + + + + + + +
+
+ + ); +}; + +export default SettingsPassword; diff --git a/ui/src/router.tsx b/ui/src/router.tsx index 6b01d1a2..3dbcb36d 100644 --- a/ui/src/router.tsx +++ b/ui/src/router.tsx @@ -10,7 +10,7 @@ import WorkflowDetail from "./pages/workflows/WorkflowDetail"; import CertificateList from "./pages/certificates/CertificateList"; import Settings from "./pages/settings/Settings"; import SettingsAccount from "./pages/settings/SettingsAccount"; -import SettingsPassword from "./pages/settings/Password"; +import SettingsPassword from "./pages/settings/SettingsPassword"; import SettingsNotification from "./pages/settings/Notification"; import SettingsSSLProvider from "./pages/settings/SSLProvider";