From 293504ded0b208d5ef84040e5b50b33ed7d2cd0e Mon Sep 17 00:00:00 2001 From: Haitao Yue Date: Thu, 3 May 2018 00:04:31 +0800 Subject: [PATCH] [CE-346] Implement user management page Can create/edit/delete user in user management page. Change-Id: Ibd1e91afb642528c9fe63fd2c3c0ecb94843fde1 Signed-off-by: Haitao Yue --- src/modules/user/management/create.py | 10 +- src/modules/user/management/update.py | 8 +- .../static/dashboard/src/common/router.js | 2 +- .../src/components/StandardTable/index.js | 147 +++++++ .../src/components/StandardTable/index.less | 13 + .../static/dashboard/src/locales/en.json | 24 +- .../static/dashboard/src/locales/zh.json | 24 +- .../react/static/dashboard/src/models/user.js | 89 +++- .../src/routes/UserManagement/index.js | 386 +++++++++++++++++- .../src/routes/UserManagement/style.less | 106 ++--- .../static/dashboard/src/services/user.js | 29 +- .../static/dashboard/src/utils/config.js | 7 + 12 files changed, 749 insertions(+), 96 deletions(-) create mode 100644 src/themes/react/static/dashboard/src/components/StandardTable/index.js create mode 100644 src/themes/react/static/dashboard/src/components/StandardTable/index.less diff --git a/src/modules/user/management/create.py b/src/modules/user/management/create.py index b7e3aad07..63f527cf2 100644 --- a/src/modules/user/management/create.py +++ b/src/modules/user/management/create.py @@ -27,19 +27,19 @@ user_create_parser = reqparse.RequestParser() user_create_parser.add_argument('username', required=True, - location='form', + location=['form', 'json'], help='Username for create') user_create_parser.add_argument('password', required=True, - location='form', + location=['form', 'json'], help='Password for create') user_create_parser.add_argument('role', type=int, required=True, - location='form', + location=['form', 'json'], help='User role for create') user_create_parser.add_argument('balance', type=int, default=0, - location='form', + location=['form', 'json'], help='User balance') user_create_parser.add_argument('active', required=True, - location='form', + location=['form', 'json'], help='Whether active user when create') diff --git a/src/modules/user/management/update.py b/src/modules/user/management/update.py index 3f97b7f71..bc6945e31 100644 --- a/src/modules/user/management/update.py +++ b/src/modules/user/management/update.py @@ -24,16 +24,16 @@ user_update_parser = reqparse.RequestParser() user_update_parser.add_argument('username', required=True, - location='form', + location=['form', 'json'], help='Username for create') user_update_parser.add_argument('role', type=int, required=True, - location='form', + location=['form', 'json'], help='User role for create') user_update_parser.add_argument('balance', type=int, default=0, - location='form', + location=['form', 'json'], help='User balance') user_update_parser.add_argument('active', required=True, - location='form', + location=['form', 'json'], help='Whether active user when create') diff --git a/src/themes/react/static/dashboard/src/common/router.js b/src/themes/react/static/dashboard/src/common/router.js index a029369e2..0e6728f42 100644 --- a/src/themes/react/static/dashboard/src/common/router.js +++ b/src/themes/react/static/dashboard/src/common/router.js @@ -93,7 +93,7 @@ export const getRouterData = app => { ), }, '/user-management': { - component: dynamicWrapper(app, [], () => import('../routes/UserManagement')), + component: dynamicWrapper(app, ['user'], () => import('../routes/UserManagement')), }, }; // Get name from ./menu.js or just set it in the router data. diff --git a/src/themes/react/static/dashboard/src/components/StandardTable/index.js b/src/themes/react/static/dashboard/src/components/StandardTable/index.js new file mode 100644 index 000000000..81ab07d55 --- /dev/null +++ b/src/themes/react/static/dashboard/src/components/StandardTable/index.js @@ -0,0 +1,147 @@ +import React, { PureComponent, Fragment } from 'react'; +import { Table, Alert } from 'antd'; +import { defineMessages, FormattedMessage } from 'react-intl'; +import styles from './index.less'; + +const messages = defineMessages({ + label: { + selected: { + id: 'Components.StandardTable.Label.Selected', + defaultMessage: 'Selected', + }, + total: { + id: 'Components.StandardTable.Label.Total', + defaultMessage: 'Total', + }, + item: { + id: 'Components.StandardTable.Label.Item', + defaultMessage: 'Item', + }, + }, + button: { + clear: { + id: 'Components.StandardTable.Button.Clear', + defaultMessage: 'Clear', + }, + }, +}); + +function initTotalList(columns) { + const totalList = []; + columns.forEach(column => { + if (column.needTotal) { + totalList.push({ ...column, total: 0 }); + } + }); + return totalList; +} + +class StandardTable extends PureComponent { + constructor(props) { + super(props); + const { columns } = props; + const needTotalList = initTotalList(columns); + + this.state = { + selectedRowKeys: [], + needTotalList, + }; + } + + componentWillReceiveProps(nextProps) { + // clean state + if (nextProps.selectedRows.length === 0) { + const needTotalList = initTotalList(nextProps.columns); + this.setState({ + selectedRowKeys: [], + needTotalList, + }); + } + } + + handleRowSelectChange = (selectedRowKeys, selectedRows) => { + let needTotalList = [...this.state.needTotalList]; + needTotalList = needTotalList.map(item => { + return { + ...item, + total: selectedRows.reduce((sum, val) => { + return sum + parseFloat(val[item.dataIndex], 10); + }, 0), + }; + }); + + if (this.props.onSelectRow) { + this.props.onSelectRow(selectedRows); + } + + this.setState({ selectedRowKeys, needTotalList }); + }; + + handleTableChange = (pagination, filters, sorter) => { + this.props.onChange(pagination, filters, sorter); + }; + + cleanSelectedKeys = () => { + this.handleRowSelectChange([], []); + }; + + render() { + const { selectedRowKeys, needTotalList } = this.state; + const { data: { list, pagination }, loading, columns, rowKey } = this.props; + + const paginationProps = { + showSizeChanger: true, + showQuickJumper: true, + ...pagination, + }; + + const rowSelection = { + selectedRowKeys, + onChange: this.handleRowSelectChange, + getCheckboxProps: record => ({ + disabled: record.disabled, + }), + }; + + return ( +
+
+ + {' '} + {selectedRowKeys.length}{' '} +    + {needTotalList.map(item => ( + + {item.title} +   + + {item.render ? item.render(item.total) : item.total} + + + ))} + + + + + } + type="info" + showIcon + /> +
+ + + ); + } +} + +export default StandardTable; diff --git a/src/themes/react/static/dashboard/src/components/StandardTable/index.less b/src/themes/react/static/dashboard/src/components/StandardTable/index.less new file mode 100644 index 000000000..817be991f --- /dev/null +++ b/src/themes/react/static/dashboard/src/components/StandardTable/index.less @@ -0,0 +1,13 @@ +@import '~antd/lib/style/themes/default.less'; + +.standardTable { + :global { + .ant-table-pagination { + margin-top: 24px; + } + } + + .tableAlert { + margin-bottom: 16px; + } +} diff --git a/src/themes/react/static/dashboard/src/locales/en.json b/src/themes/react/static/dashboard/src/locales/en.json index fee31ba64..0315a5103 100755 --- a/src/themes/react/static/dashboard/src/locales/en.json +++ b/src/themes/react/static/dashboard/src/locales/en.json @@ -83,5 +83,27 @@ "Chain.Create.Title": "Create New Chain", "Chain.Create.Validate.Required.Host": "Must select a host", "Chain.Create.Label.Host": "Host", - "Chain.Create.Label.ChainSize": "Chain Size" + "Chain.Create.Label.ChainSize": "Chain Size", + "Components.StandardTable.Label.Selected": "Selected", + "Components.StandardTable.Label.Total": "Total", + "Components.StandardTable.Label.Item": "Item", + "Components.StandardTable.Button.Clear": "Clear", + "UserManagement.Button.New": "New", + "UserManagement.Button.Edit": "Edit", + "UserManagement.Button.Delete": "Delete", + "UserManagement.Title.Create": "Create new user", + "UserManagement.Title.Edit": "Edit user", + "UserManagement.Label.Name": "Username", + "UserManagement.Label.Password": "Password", + "UserManagement.Label.Active": "Active", + "UserManagement.Label.Balance": "Balance", + "UserManagement.Label.Role": "Role", + "UserManagement.Label.Operate": "Operate", + "UserManagement.Validate.Required.Name": "Please input username", + "UserManagement.Validate.NameExists": "{name} already exists", + "UserManagement.Validate.Required.Password": "Please input password", + "UserManagement.Messages.Operate.Success.Create": "Create user {name} successfully", + "UserManagement.Messages.Operate.Success.Delete": "Delete user {name} successfully", + "UserManagement.Messages.Operate.Success.Update": "Update user {name} successfully", + "UserManagement.Confirm.DeleteUser": "Do you confirm to delete user {name}" } diff --git a/src/themes/react/static/dashboard/src/locales/zh.json b/src/themes/react/static/dashboard/src/locales/zh.json index ff64e10db..4237376a6 100755 --- a/src/themes/react/static/dashboard/src/locales/zh.json +++ b/src/themes/react/static/dashboard/src/locales/zh.json @@ -83,5 +83,27 @@ "Chain.Create.Title": "创建新的链", "Chain.Create.Validate.Required.Host": "必须选择一个主机", "Chain.Create.Label.Host": "主机", - "Chain.Create.Label.ChainSize": "链大小" + "Chain.Create.Label.ChainSize": "链大小", + "Components.StandardTable.Label.Selected": "已选择", + "Components.StandardTable.Label.Total": "总共", + "Components.StandardTable.Label.Item": "项", + "Components.StandardTable.Button.Clear": "清空", + "UserManagement.Button.New": "新建", + "UserManagement.Button.Edit": "编辑", + "UserManagement.Button.Delete": "删除", + "UserManagement.Title.Create": "创建新用户", + "UserManagement.Title.Edit": "编辑用户", + "UserManagement.Label.Name": "用户名", + "UserManagement.Label.Password": "密码", + "UserManagement.Label.Active": "激活", + "UserManagement.Label.Balance": "点", + "UserManagement.Label.Role": "角色", + "UserManagement.Label.Operate": "操作", + "UserManagement.Validate.Required.Name": "请输入用户名", + "UserManagement.Validate.NameExists": "{name} 已存在", + "UserManagement.Validate.Required.Password": "请输入密码", + "UserManagement.Messages.Operate.Success.Create": "创建用户 {name} 成功", + "UserManagement.Messages.Operate.Success.Delete": "删除用户 {name} 成功", + "UserManagement.Messages.Operate.Success.Update": "更新用户 {name} 成功", + "UserManagement.Confirm.DeleteUser": "是否确认删除用户 {name}" } diff --git a/src/themes/react/static/dashboard/src/models/user.js b/src/themes/react/static/dashboard/src/models/user.js index 4e9bfa6f8..9170083a1 100644 --- a/src/themes/react/static/dashboard/src/models/user.js +++ b/src/themes/react/static/dashboard/src/models/user.js @@ -1,22 +1,66 @@ /* SPDX-License-Identifier: Apache-2.0 */ -import { query as queryUsers, queryCurrent } from '../services/user'; +import { message } from 'antd'; +import { IntlProvider, defineMessages } from 'react-intl'; +import { + query as queryUsers, + queryCurrent, + createUser, + deleteUser, + updateUser, +} from '../services/user'; +import { getLocale } from '../utils/utils'; + +const currentLocale = getLocale(); +const intlProvider = new IntlProvider( + { locale: currentLocale.locale, messages: currentLocale.messages }, + {} +); +const { intl } = intlProvider.getChildContext(); + +const messages = defineMessages({ + operate: { + success: { + create: { + id: 'UserManagement.Messages.Operate.Success.Create', + defaultMessage: '创建用户 {name} 成功', + }, + edit: { + id: 'UserManagement.Messages.Operate.Success.Update', + defaultMessage: '更新用户 {name} 成功', + }, + delete: { + id: 'UserManagement.Messages.Operate.Success.Delete', + defaultMessage: '删除用户 {name} 成功', + }, + }, + }, +}); export default { namespace: 'user', state: { - list: [], + users: [], + total: 0, + pageNo: 1, + pageSize: 10, currentUser: {}, }, effects: { *fetch(_, { call, put }) { const response = yield call(queryUsers); + const { pageNo, pageSize, totalCount, result } = response.users; yield put({ type: 'save', - payload: response, + payload: { + pageNo, + pageSize, + total: totalCount, + users: result, + }, }); }, *fetchCurrent(_, { call, put }) { @@ -26,13 +70,50 @@ export default { payload: response, }); }, + *createUser({ payload }, { call, put }) { + const response = yield call(createUser, payload); + if (response.status === 'OK') { + const values = { name: payload.username }; + message.success(intl.formatMessage(messages.operate.success.create, values)); + } + yield call(payload.callback); + yield put({ + type: 'fetch', + }); + }, + *deleteUser({ payload }, { call, put }) { + const response = yield call(deleteUser, payload.id); + const jsonResponse = JSON.parse(response); + if (jsonResponse.status === 'OK') { + const values = { name: payload.name }; + message.success(intl.formatMessage(messages.operate.success.delete, values)); + } + yield put({ + type: 'fetch', + }); + }, + *updateUser({ payload }, { call, put }) { + const response = yield call(updateUser, payload); + if (response.status === 'OK') { + const values = { name: payload.username }; + message.success(intl.formatMessage(messages.operate.success.edit, values)); + } + yield call(payload.callback); + yield put({ + type: 'fetch', + }); + }, }, reducers: { save(state, action) { + const { users, pageNo, pageSize, total } = action.payload; return { ...state, - list: action.payload, + users, + pageNo, + pageSize, + total, }; }, saveCurrentUser(state, action) { diff --git a/src/themes/react/static/dashboard/src/routes/UserManagement/index.js b/src/themes/react/static/dashboard/src/routes/UserManagement/index.js index 6d4f5c837..823de4f40 100644 --- a/src/themes/react/static/dashboard/src/routes/UserManagement/index.js +++ b/src/themes/react/static/dashboard/src/routes/UserManagement/index.js @@ -1,16 +1,392 @@ /* SPDX-License-Identifier: Apache-2.0 */ -import React, { PureComponent } from 'react'; -import { Card } from 'antd'; +import React, { PureComponent, Fragment } from 'react'; +import { connect } from 'dva'; +import { stringify } from 'qs'; +import { Card, Divider, Button, Modal, Form, Input, Select, InputNumber, Switch } from 'antd'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import StandardTable from '../../components/StandardTable'; import PageHeaderLayout from '../../layouts/PageHeaderLayout'; +import request from '../../utils/request'; +import config from '../../utils/config'; -export default class Chain extends PureComponent { +import styles from './style.less'; + +const { urls } = config; +const FormItem = Form.Item; +const { Option } = Select; + +const messages = defineMessages({ + title: { + create: { + id: 'UserManagement.Title.Create', + defaultMessage: 'Create new user', + }, + edit: { + id: 'UserManagement.Title.Edit', + defaultMessage: 'Edit user', + }, + }, + button: { + new: { + id: 'UserManagement.Button.New', + defaultMessage: 'New', + }, + edit: { + id: 'UserManagement.Button.Edit', + defaultMessage: 'Edit', + }, + delete: { + id: 'UserManagement.Button.Delete', + defaultMessage: 'Delete', + }, + }, + label: { + name: { + id: 'UserManagement.Label.Name', + defaultMessage: 'Name', + }, + role: { + id: 'UserManagement.Label.Role', + defaultMessage: 'Role', + }, + balance: { + id: 'UserManagement.Label.Balance', + defaultMessage: 'Balance', + }, + operate: { + id: 'UserManagement.Label.Operate', + defaultMessage: 'Operate', + }, + password: { + id: 'UserManagement.Label.Password', + defaultMessage: 'Password', + }, + active: { + id: 'UserManagement.Label.Active', + defaultMessage: 'Active', + }, + }, + validate: { + required: { + name: { + id: 'UserManagement.Validate.Required.Name', + defaultMessage: 'Please input username', + }, + password: { + id: 'UserManagement.Validate.Required.Password', + defaultMessage: 'Please input password', + }, + }, + nameExists: { + id: 'UserManagement.Validate.NameExists', + defaultMessage: '{name} already exists.', + }, + }, + confirm: { + deleteUser: { + id: 'UserManagement.Confirm.DeleteUser', + defaultMessage: 'Do you confirm to delete user {name}', + }, + }, +}); + +const CreateForm = Form.create()(props => { + const { + modalVisible, + form, + handleAdd, + handleEdit, + userActive, + method, + currentUser, + creating, + changeUserActive, + handleModalVisible, + intl, + } = props; + const okHandle = () => { + form.validateFields((err, fieldsValue) => { + if (err) return; + if (method === 'create') { + handleAdd({ + ...fieldsValue, + active: userActive ? 'true' : 'false', + }); + } else { + handleEdit({ + ...fieldsValue, + id: currentUser.id, + active: userActive ? 'true' : 'false', + }); + } + }); + }; + const cancelHandle = () => { + handleModalVisible(); + }; + const validateUsername = (rule, value, callback) => { + if (method === 'create') { + setTimeout(() => { + request(`${urls.user.search}?${stringify({ username: value })}`).then(response => { + if (response.user_exists) { + const values = { name: value }; + callback(intl.formatMessage(messages.validate.nameExists, values)); + } + callback(); + }); + }, 100); + } else { + callback(); + } + }; + const formItemLayout = { + labelCol: { + span: 5, + }, + wrapperCol: { + span: 15, + }, + }; + const roles = [ + { + value: 1, + label: 'Operator', + }, + { + value: 0, + label: 'Admin', + }, + { + value: 2, + label: 'User', + }, + ]; + const roleOptions = roles.map(role => ); + return ( + + + {form.getFieldDecorator('username', { + initialValue: method === 'edit' ? currentUser.name : '', + rules: [ + { required: true, message: intl.formatMessage(messages.validate.required.name) }, + { validator: validateUsername }, + ], + })( + + )} + + {method === 'create' && ( + + {form.getFieldDecorator('password', { + initialValue: method === 'edit' ? currentUser.password : '', + rules: [ + { required: true, message: intl.formatMessage(messages.validate.required.password) }, + ], + })()} + + )} + + {form.getFieldDecorator('role', { + initialValue: method === 'edit' ? currentUser.role : roles[0].value, + rules: [{ required: true, message: 'Please select a role' }], + })()} + + + {form.getFieldDecorator('balance', { + initialValue: method === 'edit' ? currentUser.balance : 0, + rules: [{ required: true, message: 'Please input balance' }], + })()} + + + {form.getFieldDecorator('active', {})( + + )} + + + ); +}); + +@connect(({ user, loading }) => ({ + user, + loadingUsers: loading.effects['user/fetch'], +})) +class UserManagement extends PureComponent { + state = { + modalVisible: false, + userActive: true, + creating: false, + currentUser: {}, + method: 'create', + }; + componentDidMount() { + this.props.dispatch({ + type: 'user/fetch', + }); + } + changeUserActive = checked => { + this.setState({ + userActive: checked, + }); + }; + addUser = values => { + this.setState({ + creating: true, + }); + this.props.dispatch({ + type: 'user/createUser', + payload: { + ...values, + callback: this.handleModalVisible, + }, + }); + this.setState({ + userActive: true, + }); + }; + editUser = values => { + this.setState({ + creating: true, + }); + this.props.dispatch({ + type: 'user/updateUser', + payload: { + ...values, + callback: this.handleModalVisible, + }, + }); + this.setState({ + userActive: true, + }); + }; + handleModalVisible = visible => { + this.setState({ + creating: false, + method: 'create', + userActive: true, + modalVisible: !!visible, + }); + }; + operateItem = (item, type) => { + const { dispatch, intl } = this.props; + const values = { name: item.name }; + switch (type) { + case 'edit': + this.setState({ + creating: false, + modalVisible: true, + method: 'edit', + currentUser: item, + userActive: item.active, + }); + break; + case 'delete': + Modal.confirm({ + title: intl.formatMessage(messages.confirm.deleteUser, values), + onOk() { + dispatch({ + type: 'user/deleteUser', + payload: { + id: item.id, + name: item.name, + }, + }); + }, + }); + break; + default: + break; + } + }; render() { + const { user, loadingUsers, intl } = this.props; + const { modalVisible, userActive, creating, method, currentUser } = this.state; + const { users, pageNo, pageSize, total } = user; + const roles = ['Admin', 'Operator', 'User']; + const columns = [ + { + title: intl.formatMessage(messages.label.name), + dataIndex: 'name', + }, + { + title: intl.formatMessage(messages.label.balance), + dataIndex: 'balance', + }, + { + title: intl.formatMessage(messages.label.role), + dataIndex: 'role', + render: val => {roles[val]}, + }, + { + title: intl.formatMessage(messages.label.operate), + render: (val, item) => ( + + this.operateItem(item, 'edit')}> + + + + this.operateItem(item, 'delete')}> + + + + ), + }, + ]; + + const parentMethods = { + handleModalVisible: this.handleModalVisible, + changeUserActive: this.changeUserActive, + userActive, + creating, + method, + currentUser, + handleAdd: this.addUser, + handleEdit: this.editUser, + intl, + }; + return ( - - User management + + +
+
+ +
+ + {modalVisible && } +
+
); } } + +export default injectIntl(UserManagement); diff --git a/src/themes/react/static/dashboard/src/routes/UserManagement/style.less b/src/themes/react/static/dashboard/src/routes/UserManagement/style.less index 6b12d3c3e..60405f26c 100644 --- a/src/themes/react/static/dashboard/src/routes/UserManagement/style.less +++ b/src/themes/react/static/dashboard/src/routes/UserManagement/style.less @@ -1,90 +1,48 @@ @import '~antd/lib/style/themes/default.less'; +@import '../../utils/utils.less'; -.card { - margin-bottom: 24px; -} - -.heading { - font-size: 14px; - line-height: 22px; - margin: 0 0 16px 0; -} - -.steps:global(.ant-steps) { - max-width: 750px; - margin: 16px auto; -} - -.errorIcon { - cursor: pointer; - color: @error-color; - margin-right: 24px; - i { - margin-right: 4px; +.tableList { + .tableListOperator { + margin-bottom: 16px; + button { + margin-right: 8px; + } } } -.errorPopover { +.tableListForm { :global { - .ant-popover-inner-content { - padding: 0; - max-height: 290px; - overflow: auto; - min-width: 256px; + .ant-form-item { + margin-bottom: 24px; + margin-right: 0; + display: flex; + > .ant-form-item-label { + width: auto; + line-height: 32px; + padding-right: 8px; + } + .ant-form-item-control { + line-height: 32px; + } + } + .ant-form-item-control-wrapper { + flex: 1; } } -} - -.errorListItem { - list-style: none; - border-bottom: 1px solid @border-color-split; - padding: 8px 16px; - cursor: pointer; - transition: all 0.3s; - &:hover { - background: @primary-1; - } - &:last-child { - border: 0; - } - .errorIcon { - color: @error-color; - float: left; - margin-top: 4px; - margin-right: 12px; - padding-bottom: 22px; - } - .errorField { - font-size: 12px; - color: @text-color-secondary; - margin-top: 2px; + .submitButtons { + white-space: nowrap; + margin-bottom: 24px; } } -.editable { - td { - padding-top: 13px !important; - padding-bottom: 12.5px !important; +@media screen and (max-width: @screen-lg) { + .tableListForm :global(.ant-form-item) { + margin-right: 24px; } } -// custom footer for fixed footer toolbar -.advancedForm + div { - padding-bottom: 64px; -} - -.advancedForm { - :global { - .ant-form .ant-row:last-child .ant-form-item { - margin-bottom: 24px; - } - .ant-table td { - transition: none !important; - } +@media screen and (max-width: @screen-md) { + .tableListForm :global(.ant-form-item) { + margin-right: 8px; } } - -.optional { - color: @text-color-secondary; - font-style: normal; -} diff --git a/src/themes/react/static/dashboard/src/services/user.js b/src/themes/react/static/dashboard/src/services/user.js index 3066d5cf2..808fcbed4 100644 --- a/src/themes/react/static/dashboard/src/services/user.js +++ b/src/themes/react/static/dashboard/src/services/user.js @@ -1,12 +1,39 @@ /* SPDX-License-Identifier: Apache-2.0 */ +import { stringify } from 'qs'; import request from '../utils/request'; +import config from '../utils/config'; +const { urls } = config; export async function query() { - return request('/api/users'); + return request(urls.user.list); } export async function queryCurrent() { return request('/api/currentUser'); } + +export async function createUser(params) { + return request(urls.user.create, { + method: 'POST', + body: params, + }); +} + +export async function deleteUser(id) { + return request(`${urls.user.delete}/${id}`, { + method: 'DELETE', + }); +} + +export async function searchUser(params) { + return request(`${urls.user.search}?${stringify(params)}`); +} + +export async function updateUser(params) { + return request(`${urls.user.update}/${params.id}`, { + method: 'PUT', + body: params, + }); +} diff --git a/src/themes/react/static/dashboard/src/utils/config.js b/src/themes/react/static/dashboard/src/utils/config.js index 0365370e7..8b43f0735 100644 --- a/src/themes/react/static/dashboard/src/utils/config.js +++ b/src/themes/react/static/dashboard/src/utils/config.js @@ -12,5 +12,12 @@ export default { crud: `${apiBase}/cluster`, operate: `${apiBase}/cluster_op`, }, + user: { + list: `${apiBase}/user/list`, + create: `${apiBase}/user/create`, + delete: `${apiBase}/user/delete`, + search: `${apiBase}/user/search`, + update: `${apiBase}/user/update`, + }, }, };