From f2e6e025773c1919de9afb322b715bee2e551503 Mon Sep 17 00:00:00 2001 From: Katherine Date: Fri, 18 Oct 2024 09:09:32 +0800 Subject: [PATCH] feat: configure edit permissions for user profiles & change password (#5422) * feat: configure edit permissions for user profiles * fix: bug * fix: bug * refactor: locale improve * refactor: currentUser --- .../core/client/src/user/ChangePassword.tsx | 10 +- packages/core/client/src/user/CurrentUser.tsx | 2 +- packages/core/client/src/user/EditProfile.tsx | 30 +++++- .../src/client/UsersManagement.tsx | 94 +++++++++++++++++-- .../plugin-users/src/client/schemas/users.ts | 51 ++++++++++ .../plugin-users/src/locale/zh-CN.json | 5 +- .../src/server/collections/systemSettings.ts | 38 ++++++++ 7 files changed, 218 insertions(+), 12 deletions(-) create mode 100644 packages/plugins/@nocobase/plugin-users/src/server/collections/systemSettings.ts diff --git a/packages/core/client/src/user/ChangePassword.tsx b/packages/core/client/src/user/ChangePassword.tsx index a290703fba..61ede07f95 100644 --- a/packages/core/client/src/user/ChangePassword.tsx +++ b/packages/core/client/src/user/ChangePassword.tsx @@ -15,6 +15,7 @@ import { useTranslation } from 'react-i18next'; import { ActionContextProvider, DropdownVisibleContext, SchemaComponent, useActionContext } from '../'; import { useAPIClient } from '../api-client'; import { useNavigate } from 'react-router-dom'; +import { useSystemSettings } from '../'; const useCloseAction = () => { const { setVisible } = useActionContext(); @@ -132,8 +133,10 @@ export const useChangePassword = () => { const ctx = useContext(DropdownVisibleContext); const [visible, setVisible] = useState(false); const { t } = useTranslation(); + const { data } = useSystemSettings(); + const { enableChangePassword } = data?.data || {}; - return useMemo(() => { + const result = useMemo(() => { return { key: 'password', eventKey: 'ChangePassword', @@ -153,4 +156,9 @@ export const useChangePassword = () => { ), }; }, [visible]); + if (enableChangePassword === false) { + return null; + } + + return result; }; diff --git a/packages/core/client/src/user/CurrentUser.tsx b/packages/core/client/src/user/CurrentUser.tsx index ee369aa697..fdf2284eaf 100644 --- a/packages/core/client/src/user/CurrentUser.tsx +++ b/packages/core/client/src/user/CurrentUser.tsx @@ -125,7 +125,7 @@ export const SettingsMenu: React.FC<{ }, editProfile, changePassword, - { + (editProfile || changePassword) && { key: 'divider_2', type: 'divider', }, diff --git a/packages/core/client/src/user/EditProfile.tsx b/packages/core/client/src/user/EditProfile.tsx index e60c78d87a..b2e24e979b 100644 --- a/packages/core/client/src/user/EditProfile.tsx +++ b/packages/core/client/src/user/EditProfile.tsx @@ -19,6 +19,7 @@ import { useActionContext, useCurrentUserContext, useRequest, + useSystemSettings, } from '../'; import { useAPIClient } from '../api-client'; @@ -82,6 +83,11 @@ const schema: ISchema = { title: "{{t('Nickname')}}", 'x-decorator': 'FormItem', 'x-component': 'Input', + 'x-reactions': (field) => { + if (field.initialValue) { + field.disabled = true; + } + }, }, username: { type: 'string', @@ -90,6 +96,11 @@ const schema: ISchema = { 'x-component': 'Input', 'x-validator': { username: true }, required: true, + 'x-reactions': (field) => { + if (field.initialValue) { + field.disabled = true; + } + }, }, email: { type: 'string', @@ -97,12 +108,22 @@ const schema: ISchema = { 'x-decorator': 'FormItem', 'x-component': 'Input', 'x-validator': 'email', + 'x-reactions': (field) => { + if (field.initialValue) { + field.disabled = true; + } + }, }, phone: { type: 'string', title: '{{t("Phone")}}', 'x-decorator': 'FormItem', 'x-component': 'Input', + 'x-reactions': (field) => { + if (field.initialValue) { + field.disabled = true; + } + }, }, footer: { 'x-component': 'Action.Drawer.Footer', @@ -134,8 +155,9 @@ export const useEditProfile = () => { const ctx = useContext(DropdownVisibleContext); const [visible, setVisible] = useState(false); const { t } = useTranslation(); - - return useMemo(() => { + const { data } = useSystemSettings(); + const { enableEditProfile } = data?.data || {}; + const result = useMemo(() => { return { key: 'profile', eventKey: 'EditProfile', @@ -158,4 +180,8 @@ export const useEditProfile = () => { ), }; }, [visible]); + if (enableEditProfile === false) { + return null; + } + return result; }; diff --git a/packages/plugins/@nocobase/plugin-users/src/client/UsersManagement.tsx b/packages/plugins/@nocobase/plugin-users/src/client/UsersManagement.tsx index 7178d7f3ce..70909498f2 100644 --- a/packages/plugins/@nocobase/plugin-users/src/client/UsersManagement.tsx +++ b/packages/plugins/@nocobase/plugin-users/src/client/UsersManagement.tsx @@ -16,14 +16,17 @@ import { useDataBlockRequest, useDataBlockResource, useSchemaComponentContext, + useRequest, + useAPIClient, } from '@nocobase/client'; -import React, { useEffect, useMemo } from 'react'; -import { usersSchema } from './schemas/users'; -import { useUsersTranslation } from './locale'; -import { PasswordField } from './PasswordField'; -import { App } from 'antd'; +import React, { createContext, useEffect, useMemo, useContext } from 'react'; +import { App, Tabs, message } from 'antd'; import { useForm } from '@formily/react'; import { createForm } from '@formily/core'; +import { css } from '@emotion/css'; +import { usersSchema, usersSettingsSchema } from './schemas/users'; +import { useUsersTranslation } from './locale'; +import { PasswordField } from './PasswordField'; const useCancelActionProps = () => { const { setVisible } = useActionContext(); @@ -49,7 +52,6 @@ const useSubmitActionProps = () => { async onClick() { await form.submit(); const values = form.values; - console.log('values:', values); if (values[collection.filterTargetKey]) { await resource.update({ values, @@ -79,7 +81,7 @@ const useEditFormProps = () => { }; }; -export const UsersManagement: React.FC = () => { +const UsersManagementTab: React.FC = () => { const { t } = useUsersTranslation(); const scCtx = useSchemaComponentContext(); return ( @@ -92,3 +94,81 @@ export const UsersManagement: React.FC = () => { ); }; +const UsersSettingsContext = createContext({}); + +const UsersSettingsProvider = (props) => { + const result = useRequest({ + url: 'systemSettings:get/1', + }); + return {props.children}; +}; + +const UsersSettingsTab: React.FC = () => { + const { t } = useUsersTranslation(); + const scCtx = useSchemaComponentContext(); + const form = useForm(); + const useFormBlockProps = () => { + const result = useContext(UsersSettingsContext); + const { enableChangePassword, enableEditProfile } = result?.data?.data || {}; + useEffect(() => { + form?.setValues({ + enableChangePassword: enableChangePassword !== false, + enableEditProfile: enableEditProfile !== false, + }); + }, [result]); + return { + form: form, + }; + }; + + const useSubmitActionProps = () => { + const api = useAPIClient(); + const form = useForm(); + return { + type: 'primary', + async onClick() { + await form.submit(); + const values = form.values; + await api.request({ url: 'systemSettings:update/1', data: values, method: 'POST' }); + message.success(t('Saved successfully')); + window.location.reload(); + }, + }; + }; + return ( + + + + ); +}; + +export const UsersManagement: React.FC = () => { + const { t } = useUsersTranslation(); + return ( + , + }, + { + label: t('Settings'), + key: 'usersSettings', + children: , + }, + ]} + /> + ); +}; diff --git a/packages/plugins/@nocobase/plugin-users/src/client/schemas/users.ts b/packages/plugins/@nocobase/plugin-users/src/client/schemas/users.ts index 4f31d6f632..0aeb6a2ddb 100644 --- a/packages/plugins/@nocobase/plugin-users/src/client/schemas/users.ts +++ b/packages/plugins/@nocobase/plugin-users/src/client/schemas/users.ts @@ -482,6 +482,57 @@ export const usersSchema: ISchema = { }, }; +export const usersSettingsSchema: ISchema = { + type: 'object', + 'x-decorator': 'UsersSettingsProvider', + properties: { + usersSettings: { + type: 'void', + 'x-component': 'CardItem', + 'x-decorator': 'UsersSettingsProvider', + properties: { + form: { + type: 'void', + 'x-component': 'FormV2', + 'x-use-component-props': 'useFormBlockProps', + properties: { + enableEditProfile: { + type: 'string', + 'x-component': 'Checkbox', + 'x-decorator': 'FormItem', + default: true, + 'x-content': '{{t("Allow edit profile")}}', + }, + enableChangePassword: { + type: 'string', + 'x-component': 'Checkbox', + 'x-decorator': 'FormItem', + default: true, + 'x-content': '{{t("Allow change password")}}', + }, + }, + }, + footer: { + type: 'void', + 'x-component': 'div', + 'x-component-props': { + style: { + float: 'right', + }, + }, + properties: { + submit: { + title: 'Submit', + 'x-component': 'Action', + 'x-use-component-props': 'useSubmitActionProps', + }, + }, + }, + }, + }, + }, +}; + export const getRoleUsersSchema = (): ISchema => ({ type: 'void', properties: { diff --git a/packages/plugins/@nocobase/plugin-users/src/locale/zh-CN.json b/packages/plugins/@nocobase/plugin-users/src/locale/zh-CN.json index e6c5678174..2153c4fe3d 100644 --- a/packages/plugins/@nocobase/plugin-users/src/locale/zh-CN.json +++ b/packages/plugins/@nocobase/plugin-users/src/locale/zh-CN.json @@ -3,5 +3,8 @@ "Add users": "添加用户", "Remove user": "移除用户", "Are you sure you want to remove it?": "你确定要移除吗?", - "Random password": "随机密码" + "Random password": "随机密码", + "Users manager": "用户管理", + "Allow edit profile": "允许修改个人资料", + "Allow change password": "允许修改密码" } diff --git a/packages/plugins/@nocobase/plugin-users/src/server/collections/systemSettings.ts b/packages/plugins/@nocobase/plugin-users/src/server/collections/systemSettings.ts new file mode 100644 index 0000000000..1d3029bc52 --- /dev/null +++ b/packages/plugins/@nocobase/plugin-users/src/server/collections/systemSettings.ts @@ -0,0 +1,38 @@ +/** + * This file is part of the NocoBase (R) project. + * Copyright (c) 2020-2024 NocoBase Co., Ltd. + * Authors: NocoBase Team. + * + * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License. + * For more information, please refer to: https://www.nocobase.com/agreement. + */ + +import { extendCollection } from '@nocobase/database'; + +export default extendCollection({ + name: 'systemSettings', + fields: [ + { + interface: 'checkbox', + type: 'boolean', + name: 'enableEditProfile', + uiSchema: { + type: 'boolean', + 'x-component': 'Checkbox', + title: '{{t("Allow edit profile")}}', + default: true, + }, + }, + { + interface: 'checkbox', + type: 'boolean', + name: 'enableChangePassword', + uiSchema: { + type: 'boolean', + 'x-component': 'Checkbox', + title: '{{t("Allow Change Password")}}', + default: true, + }, + }, + ], +});