Merge remote-tracking branch 'origin/dev' into test

This commit is contained in:
LinkinStars
2024-06-12 11:54:47 +08:00
16 changed files with 878 additions and 3 deletions
+199 -2
View File
@@ -17,8 +17,7 @@
* under the License.
*/
// Code generated by swaggo/swag. DO NOT EDIT.
// Package docs Code generated by swaggo/swag. DO NOT EDIT
package docs
import "github.com/swaggo/swag"
@@ -1573,6 +1572,45 @@ const docTemplate = `{
}
}
},
"/answer/admin/api/user/profile": {
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "edit user profile",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "edit user profile",
"parameters": [
{
"description": "user",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.EditUserProfileReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/user/role": {
"put": {
"security": [
@@ -2908,6 +2946,92 @@ const docTemplate = `{
}
}
},
"/answer/api/v1/meta/reaction": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "get reaction for an object",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Meta"
],
"summary": "get reaction",
"parameters": [
{
"type": "string",
"description": "object_id",
"name": "object_id",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/handler.RespBody"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.ReactionResp"
}
}
}
]
}
}
}
},
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "update reaction. if not exist, add one",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Meta"
],
"summary": "add or update reaction",
"parameters": [
{
"description": "reaction",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.UpdateReactionReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/api/v1/notification/page": {
"get": {
"security": [
@@ -7355,6 +7479,26 @@ const docTemplate = `{
}
}
},
"schema.EditUserProfileReq": {
"type": "object",
"required": [
"email",
"user_id"
],
"properties": {
"email": {
"type": "string",
"maxLength": 500
},
"user_id": {
"type": "string"
},
"username": {
"type": "string",
"maxLength": 30
}
}
},
"schema.ExternalLoginBindingUserSendEmailReq": {
"type": "object",
"required": [
@@ -8891,6 +9035,31 @@ const docTemplate = `{
}
}
},
"schema.ReactionItem": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"is_active": {
"type": "boolean"
},
"tooltip": {
"type": "string"
}
}
},
"schema.ReactionResp": {
"type": "object",
"properties": {
"reaction_summary": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/schema.ReactionItem"
}
}
}
},
"schema.ReasonItem": {
"type": "object",
"properties": {
@@ -9952,6 +10121,34 @@ const docTemplate = `{
}
}
},
"schema.UpdateReactionReq": {
"type": "object",
"required": [
"emoji",
"object_id",
"reaction"
],
"properties": {
"emoji": {
"type": "string",
"enum": [
"heart",
"smile",
"frown"
]
},
"object_id": {
"type": "string"
},
"reaction": {
"type": "string",
"enum": [
"activate",
"deactivate"
]
}
}
},
"schema.UpdateReviewReq": {
"type": "object",
"required": [
+198
View File
@@ -1542,6 +1542,45 @@
}
}
},
"/answer/admin/api/user/profile": {
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "edit user profile",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"admin"
],
"summary": "edit user profile",
"parameters": [
{
"description": "user",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.EditUserProfileReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/admin/api/user/role": {
"put": {
"security": [
@@ -2877,6 +2916,92 @@
}
}
},
"/answer/api/v1/meta/reaction": {
"get": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "get reaction for an object",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Meta"
],
"summary": "get reaction",
"parameters": [
{
"type": "string",
"description": "object_id",
"name": "object_id",
"in": "query",
"required": true
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"allOf": [
{
"$ref": "#/definitions/handler.RespBody"
},
{
"type": "object",
"properties": {
"data": {
"$ref": "#/definitions/schema.ReactionResp"
}
}
}
]
}
}
}
},
"put": {
"security": [
{
"ApiKeyAuth": []
}
],
"description": "update reaction. if not exist, add one",
"consumes": [
"application/json"
],
"produces": [
"application/json"
],
"tags": [
"Meta"
],
"summary": "add or update reaction",
"parameters": [
{
"description": "reaction",
"name": "data",
"in": "body",
"required": true,
"schema": {
"$ref": "#/definitions/schema.UpdateReactionReq"
}
}
],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/handler.RespBody"
}
}
}
}
},
"/answer/api/v1/notification/page": {
"get": {
"security": [
@@ -7324,6 +7449,26 @@
}
}
},
"schema.EditUserProfileReq": {
"type": "object",
"required": [
"email",
"user_id"
],
"properties": {
"email": {
"type": "string",
"maxLength": 500
},
"user_id": {
"type": "string"
},
"username": {
"type": "string",
"maxLength": 30
}
}
},
"schema.ExternalLoginBindingUserSendEmailReq": {
"type": "object",
"required": [
@@ -8860,6 +9005,31 @@
}
}
},
"schema.ReactionItem": {
"type": "object",
"properties": {
"count": {
"type": "integer"
},
"is_active": {
"type": "boolean"
},
"tooltip": {
"type": "string"
}
}
},
"schema.ReactionResp": {
"type": "object",
"properties": {
"reaction_summary": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/schema.ReactionItem"
}
}
}
},
"schema.ReasonItem": {
"type": "object",
"properties": {
@@ -9921,6 +10091,34 @@
}
}
},
"schema.UpdateReactionReq": {
"type": "object",
"required": [
"emoji",
"object_id",
"reaction"
],
"properties": {
"emoji": {
"type": "string",
"enum": [
"heart",
"smile",
"frown"
]
},
"object_id": {
"type": "string"
},
"reaction": {
"type": "string",
"enum": [
"activate",
"deactivate"
]
}
}
},
"schema.UpdateReviewReq": {
"type": "object",
"required": [
+125
View File
@@ -425,6 +425,20 @@ definitions:
name:
type: string
type: object
schema.EditUserProfileReq:
properties:
email:
maxLength: 500
type: string
user_id:
type: string
username:
maxLength: 30
type: string
required:
- email
- user_id
type: object
schema.ExternalLoginBindingUserSendEmailReq:
properties:
binding_key:
@@ -1506,6 +1520,22 @@ definitions:
required:
- id
type: object
schema.ReactionItem:
properties:
count:
type: integer
is_active:
type: boolean
tooltip:
type: string
type: object
schema.ReactionResp:
properties:
reaction_summary:
additionalProperties:
$ref: '#/definitions/schema.ReactionItem'
type: object
type: object
schema.ReasonItem:
properties:
content_type:
@@ -2227,6 +2257,26 @@ definitions:
required:
- level
type: object
schema.UpdateReactionReq:
properties:
emoji:
enum:
- heart
- smile
- frown
type: string
object_id:
type: string
reaction:
enum:
- activate
- deactivate
type: string
required:
- emoji
- object_id
- reaction
type: object
schema.UpdateReviewReq:
properties:
review_id:
@@ -3581,6 +3631,30 @@ paths:
summary: update user password
tags:
- admin
/answer/admin/api/user/profile:
put:
consumes:
- application/json
description: edit user profile
parameters:
- description: user
in: body
name: data
required: true
schema:
$ref: '#/definitions/schema.EditUserProfileReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handler.RespBody'
security:
- ApiKeyAuth: []
summary: edit user profile
tags:
- admin
/answer/admin/api/user/role:
put:
consumes:
@@ -4384,6 +4458,57 @@ paths:
summary: Get language options
tags:
- Lang
/answer/api/v1/meta/reaction:
get:
consumes:
- application/json
description: get reaction for an object
parameters:
- description: object_id
in: query
name: object_id
required: true
type: string
produces:
- application/json
responses:
"200":
description: OK
schema:
allOf:
- $ref: '#/definitions/handler.RespBody'
- properties:
data:
$ref: '#/definitions/schema.ReactionResp'
type: object
security:
- ApiKeyAuth: []
summary: get reaction
tags:
- Meta
put:
consumes:
- application/json
description: update reaction. if not exist, add one
parameters:
- description: reaction
in: body
name: data
required: true
schema:
$ref: '#/definitions/schema.UpdateReactionReq'
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/handler.RespBody'
security:
- ApiKeyAuth: []
summary: add or update reaction
tags:
- Meta
/answer/api/v1/notification/page:
get:
consumes:
+16
View File
@@ -152,6 +152,8 @@ backend:
admin:
cannot_update_their_password:
other: You cannot modify your password.
cannot_edit_their_profile:
other: You cannot modify your profile.
cannot_modify_self_status:
other: You cannot modify your status.
email_or_password_wrong:
@@ -1535,6 +1537,19 @@ ui:
msg: Password must be at 8-32 characters in length.
btn_cancel: Cancel
btn_submit: Submit
edit_profile_modal:
title: Edit profile
form:
fields:
username:
label: Username
msg_range: Username up to 30 characters.
email:
label: Email
msg_invalid: Invalid Email Address.
edit_success: Edited successfully
btn_cancel: Cancel
btn_submit: Submit
user_modal:
title: Add new user
form:
@@ -1580,6 +1595,7 @@ ui:
filter:
placeholder: "Filter by name, user:id"
set_new_password: Set new password
edit_profile: Edit profile
change_status: Change status
change_role: Change role
show_logs: Show logs
+1
View File
@@ -98,6 +98,7 @@ const (
NotAllowedLoginViaPassword = "error.user.not_allowed_login_via_password"
SMTPConfigFromNameCannotBeEmail = "error.smtp.config_from_name_cannot_be_email"
AdminCannotUpdateTheirPassword = "error.admin.cannot_update_their_password"
AdminCannotEditTheirProfile = "error.admin.cannot_edit_their_profile"
AdminCannotModifySelfStatus = "error.admin.cannot_modify_self_status"
UserAccessDenied = "error.user.access_denied"
UserPageAccessDenied = "error.user.page_access_denied"
@@ -23,6 +23,7 @@ import (
"github.com/apache/incubator-answer/internal/base/handler"
"github.com/apache/incubator-answer/internal/base/middleware"
"github.com/apache/incubator-answer/internal/base/reason"
"github.com/apache/incubator-answer/internal/base/translator"
"github.com/apache/incubator-answer/internal/schema"
"github.com/apache/incubator-answer/internal/service/user_admin"
"github.com/apache/incubator-answer/plugin"
@@ -152,6 +153,35 @@ func (uc *UserAdminController) UpdateUserPassword(ctx *gin.Context) {
handler.HandleResponse(ctx, err, nil)
}
// EditUserProfile edit user profile
// @Summary edit user profile
// @Description edit user profile
// @Security ApiKeyAuth
// @Tags admin
// @Accept json
// @Produce json
// @Param data body schema.EditUserProfileReq true "user"
// @Success 200 {object} handler.RespBody
// @Router /answer/admin/api/user/profile [put]
func (uc *UserAdminController) EditUserProfile(ctx *gin.Context) {
req := &schema.EditUserProfileReq{}
if handler.BindAndCheck(ctx, req) {
return
}
req.IsAdmin = middleware.GetUserIsAdminModerator(ctx)
if !req.IsAdmin {
handler.HandleResponse(ctx, errors.Forbidden(reason.ForbiddenError), nil)
return
}
errFields, err := uc.userService.EditUserProfile(ctx, req)
for _, field := range errFields {
field.ErrorMsg = translator.Tr(handler.GetLang(ctx), field.ErrorMsg)
}
handler.HandleResponse(ctx, err, errFields)
}
// GetUserPage get user page
// @Summary get user page
// @Description get user page
+4
View File
@@ -375,6 +375,10 @@ func (qr *questionRepo) GetQuestionPage(ctx context.Context, page, pageSize int,
case "newest":
session.OrderBy("question.pin desc,question.created_at DESC")
case "active":
if inDays == 0 {
session.And("question.created_at > ?", time.Now().AddDate(0, 0, -180))
}
session.And("question.post_update_time > ?", time.Now().AddDate(0, 0, -90))
session.OrderBy("question.pin desc,question.post_update_time DESC, question.updated_at DESC")
case "frequent":
session.OrderBy("question.pin desc,question.view_count DESC")
+10
View File
@@ -174,6 +174,16 @@ func (ur *userRepo) UpdateInfo(ctx context.Context, userInfo *entity.User) (err
return
}
// UpdateUserProfile update user profile
func (ur *userRepo) UpdateUserProfile(ctx context.Context, userInfo *entity.User) (err error) {
_, err = ur.data.DB.Context(ctx).Where("id = ?", userInfo.ID).
Cols("username", "e_mail", "mail_status").Update(userInfo)
if err != nil {
err = errors.InternalServer(reason.DatabaseError).WithError(err).WithStack()
}
return
}
// GetByUserID get user info by user id
func (ur *userRepo) GetByUserID(ctx context.Context, userID string) (userInfo *entity.User, exist bool, err error) {
userInfo = &entity.User{}
+1
View File
@@ -311,6 +311,7 @@ func (a *AnswerAPIRouter) RegisterAnswerAdminAPIRouter(r *gin.RouterGroup) {
r.POST("/user", a.adminUserController.AddUser)
r.POST("/users", a.adminUserController.AddUsers)
r.PUT("/user/password", a.adminUserController.UpdateUserPassword)
r.PUT("/user/profile", a.adminUserController.EditUserProfile)
// reason
r.GET("/reasons", a.reasonController.Reasons)
+9
View File
@@ -108,6 +108,15 @@ type UpdateUserRoleReq struct {
LoginUserID string `json:"-"`
}
// EditUserProfileReq edit user profile request
type EditUserProfileReq struct {
UserID string `validate:"required" json:"user_id"`
Username string `validate:"omitempty,gt=3,lte=30" json:"username"`
Email string `validate:"required,email,gt=0,lte=500" json:"email"`
LoginUserID string `json:"-"`
IsAdmin bool `json:"-"`
}
// AddUserReq add user request
type AddUserReq struct {
DisplayName string `validate:"required,gte=4,lte=30" json:"display_name"`
@@ -45,6 +45,7 @@ import (
"github.com/apache/incubator-answer/internal/service/role"
"github.com/apache/incubator-answer/internal/service/siteinfo_common"
usercommon "github.com/apache/incubator-answer/internal/service/user_common"
"github.com/apache/incubator-answer/pkg/checker"
"github.com/jinzhu/copier"
"github.com/segmentfault/pacman/errors"
"github.com/segmentfault/pacman/log"
@@ -345,6 +346,61 @@ func (us *UserAdminService) UpdateUserPassword(ctx context.Context, req *schema.
return
}
// EditUserProfile edit user profile
func (us *UserAdminService) EditUserProfile(ctx context.Context, req *schema.EditUserProfileReq) (
errFields []*validator.FormErrorField, err error) {
if req.UserID == req.LoginUserID {
return nil, errors.BadRequest(reason.AdminCannotEditTheirProfile)
}
userInfo, exist, err := us.userRepo.GetUserInfo(ctx, req.UserID)
if err != nil {
return nil, err
}
if !exist {
return nil, errors.BadRequest(reason.UserNotFound)
}
if checker.IsInvalidUsername(req.Username) || checker.IsUsersIgnorePath(req.Username) {
return append(errFields, &validator.FormErrorField{
ErrorField: "username",
ErrorMsg: reason.UsernameInvalid,
}), errors.BadRequest(reason.UsernameInvalid)
}
userInfo, exist, err = us.userCommonService.GetByUsername(ctx, req.Username)
if err != nil {
return nil, err
}
if exist && userInfo.ID != req.UserID {
return append(errFields, &validator.FormErrorField{
ErrorField: "username",
ErrorMsg: reason.UsernameDuplicate,
}), errors.BadRequest(reason.UsernameDuplicate)
}
userInfo, exist, err = us.userCommonService.GetByEmail(ctx, req.Email)
if err != nil {
return nil, err
}
if exist && userInfo.ID != req.UserID {
return append(errFields, &validator.FormErrorField{
ErrorField: "email",
ErrorMsg: reason.EmailDuplicate,
}), errors.BadRequest(reason.EmailDuplicate)
}
user := &entity.User{}
user.ID = req.UserID
user.Username = req.Username
user.EMail = req.Email
user.MailStatus = entity.EmailStatusAvailable
err = us.userCommonService.UpdateUserProfile(ctx, user)
if err != nil {
return nil, err
}
return
}
// GetUserInfo get user one
func (us *UserAdminService) GetUserInfo(ctx context.Context, userID string) (resp *schema.GetUserInfoResp, err error) {
user, exist, err := us.userRepo.GetUserInfo(ctx, userID)
+13
View File
@@ -52,6 +52,7 @@ type UserRepo interface {
UpdateUserInterface(ctx context.Context, userID, language, colorSchema string) (err error)
UpdatePass(ctx context.Context, userID, pass string) error
UpdateInfo(ctx context.Context, userInfo *entity.User) (err error)
UpdateUserProfile(ctx context.Context, userInfo *entity.User) (err error)
GetByUserID(ctx context.Context, userID string) (userInfo *entity.User, exist bool, err error)
BatchGetByID(ctx context.Context, ids []string) ([]*entity.User, error)
GetByUsername(ctx context.Context, username string) (userInfo *entity.User, exist bool, err error)
@@ -119,6 +120,18 @@ func (us *UserCommon) BatchGetUserBasicInfoByUserNames(ctx context.Context, user
return infomap, nil
}
func (us *UserCommon) GetByEmail(ctx context.Context, email string) (userInfo *entity.User, exist bool, err error) {
return us.userRepo.GetByEmail(ctx, email)
}
func (us *UserCommon) GetByUsername(ctx context.Context, username string) (userInfo *entity.User, exist bool, err error) {
return us.userRepo.GetByUsername(ctx, username)
}
func (us *UserCommon) UpdateUserProfile(ctx context.Context, userInfo *entity.User) (err error) {
return us.userRepo.UpdateUserProfile(ctx, userInfo)
}
func (us *UserCommon) UpdateAnswerCount(ctx context.Context, userID string, num int) error {
return us.userRepo.UpdateAnswerCount(ctx, userID, num)
}
+2
View File
@@ -24,6 +24,7 @@ import usePageUsers from './usePageUsers';
import useChangeUserRoleModal from './useChangeUserRoleModal';
import useUserModal from './useUserModal';
import useChangePasswordModal from './useChangePasswordModal';
import useChangeProfileModal from './useChangeProfileModal';
import usePageTags from './usePageTags';
import useLoginRedirect from './useLoginRedirect';
import usePromptWithUnload from './usePrompt';
@@ -39,6 +40,7 @@ export {
useChangeUserRoleModal,
useUserModal,
useChangePasswordModal,
useChangeProfileModal,
usePageTags,
useLoginRedirect,
usePromptWithUnload,
@@ -0,0 +1,170 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import { useLayoutEffect, useState, useRef } from 'react';
import { Modal, Button } from 'react-bootstrap';
import { useTranslation } from 'react-i18next';
import ReactDOM from 'react-dom/client';
import type * as Type from '@/common/interface';
import { SchemaForm, JSONSchema, UISchema, initFormData } from '@/components';
import { handleFormError } from '@/utils';
import pattern from '@/common/pattern';
const div = document.createElement('div');
const root = ReactDOM.createRoot(div);
interface IProps {
title?: string;
onConfirm?: (formData: any) => Promise<any>;
}
const useChangeProfileModal = (props: IProps = {}, userData) => {
const { t } = useTranslation('translation', {
keyPrefix: 'admin.edit_profile_modal',
});
const { title = t('title'), onConfirm } = props;
const [visible, setVisibleState] = useState(false);
const [userId, setUserId] = useState('');
const schema: JSONSchema = {
title: t('title'),
required: ['username', 'email'],
properties: {
username: {
type: 'string',
title: t('form.fields.username.label'),
default: userData.username,
},
email: {
type: 'string',
title: t('form.fields.email.label'),
default: userData.e_mail,
},
},
};
const uiSchema: UISchema = {
username: {
'ui:options': {
inputType: 'text',
validator: (value) => {
const MIN_LENGTH = 3;
const MAX_LENGTH = 30;
if (value.length < MIN_LENGTH || value.length > MAX_LENGTH) {
return t('form.fields.username.msg_range');
}
return true;
},
},
},
email: {
'ui:options': {
inputType: 'email',
validator: (value) => {
if (value && !pattern.email.test(value)) {
return t('form.fields.email.msg_invalid');
}
return true;
},
},
},
};
const [formData, setFormData] = useState<Type.FormDataType>(
initFormData(schema),
);
const formRef = useRef<{
validator: () => Promise<boolean>;
}>(null);
const onClose = () => {
setVisibleState(false);
};
const onShow = (user_id: string) => {
setUserId(user_id);
setVisibleState(true);
};
const handleSubmit = async (event) => {
event.preventDefault();
event.stopPropagation();
const isValid = await formRef.current?.validator();
if (!isValid) {
return;
}
if (onConfirm instanceof Function) {
onConfirm({
username: formData.username.value,
email: formData.email.value,
user_id: userId,
})
.then(() => {
setUserId('');
onClose();
})
.catch((err) => {
if (err.isError) {
const data = handleFormError(err, formData);
setFormData({ ...data });
}
});
}
};
const handleOnChange = (data) => {
setFormData(data);
};
useLayoutEffect(() => {
root.render(
<Modal show={visible} title={title} onHide={onClose}>
<Modal.Header closeButton>
<Modal.Title as="h5">{title}</Modal.Title>
</Modal.Header>
<Modal.Body>
<SchemaForm
ref={formRef}
schema={schema}
uiSchema={uiSchema}
formData={formData}
onChange={handleOnChange}
hiddenSubmit
/>
</Modal.Body>
<Modal.Footer>
<Button variant="link" onClick={() => onClose()}>
{t('btn_cancel')}
</Button>
<Button variant="primary" onClick={handleSubmit}>
{t('btn_submit')}
</Button>
</Modal.Footer>
</Modal>,
);
});
return {
onClose,
onShow,
};
};
export default useChangeProfileModal;
@@ -23,11 +23,16 @@ import { useTranslation } from 'react-i18next';
import { Modal, Icon } from '@/components';
import {
useChangeUserRoleModal,
useChangeProfileModal,
useChangePasswordModal,
useActivationEmailModal,
useToast,
} from '@/hooks';
import { updateUserPassword, changeUserStatus } from '@/services';
import {
updateUserPassword,
changeUserStatus,
updateUserProfile,
} from '@/services';
interface Props {
showActionPassword?: boolean;
@@ -71,6 +76,29 @@ const UserOperation = ({
});
},
});
const changeProfileModal = useChangeProfileModal(
{
onConfirm: (rd) => {
return new Promise((resolve, reject) => {
updateUserProfile(rd)
.then(() => {
Toast.onShow({
msg: t('edit_success', {
keyPrefix: 'admin.edit_profile_modal',
}),
variant: 'success',
});
resolve(true);
refreshUsers?.();
})
.catch((e) => {
reject(e);
});
});
},
},
userData,
);
const activationEmailModal = useActivationEmailModal();
@@ -105,6 +133,10 @@ const UserOperation = ({
changePasswordModal.onShow(user_id);
}
if (type === 'profile') {
changeProfileModal.onShow(user_id);
}
if (type === 'activation') {
activationEmailModal.onShow(user_id);
}
@@ -166,6 +198,9 @@ const UserOperation = ({
{t('set_new_password')}
</Dropdown.Item>
) : null}
<Dropdown.Item onClick={() => handleAction('profile')}>
{t('edit_profile')}
</Dropdown.Item>
{showActionRole ? (
<Dropdown.Item onClick={() => handleAction('role')}>
{t('change_role')}
+8
View File
@@ -68,6 +68,14 @@ export const updateUserPassword = (params: {
return request.put('/answer/admin/api/user/password', params);
};
export const updateUserProfile = (params: {
username: string;
email: string;
user_id: string;
}) => {
return request.put('/answer/admin/api/user/profile', params);
};
export const getUserActivation = (userId: string) => {
const apiUrl = `/answer/admin/api/user/activation`;
return request.get<{