сделал управление пользователями
This commit is contained in:
parent
12c448aef9
commit
0a8e027b87
@ -1,4 +1,4 @@
|
|||||||
from typing import Optional
|
from typing import Optional, List, Sequence
|
||||||
|
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
@ -10,6 +10,11 @@ class StatusesRepository:
|
|||||||
def __init__(self, db: AsyncSession) -> None:
|
def __init__(self, db: AsyncSession) -> None:
|
||||||
self.db = db
|
self.db = db
|
||||||
|
|
||||||
|
async def get_all(self) -> Sequence[Status]:
|
||||||
|
query = select(Status)
|
||||||
|
results = await self.db.execute(query)
|
||||||
|
return results.scalars().all()
|
||||||
|
|
||||||
async def get_by_id(self, status_id: int) -> Optional[Status]:
|
async def get_by_id(self, status_id: int) -> Optional[Status]:
|
||||||
query = (
|
query = (
|
||||||
select(Status)
|
select(Status)
|
||||||
|
|||||||
26
api/app/controllers/statuses_router.py
Normal file
26
api/app/controllers/statuses_router.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
from typing import List, Optional
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends, Response
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.database.session import get_db
|
||||||
|
from app.domain.entities.statuses import StatusRead
|
||||||
|
from app.domain.models import User
|
||||||
|
from app.infrastructure.dependencies import require_auth_user
|
||||||
|
from app.infrastructure.statuses_servise import StatusesService
|
||||||
|
|
||||||
|
statuses_router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
|
@statuses_router.get(
|
||||||
|
'/',
|
||||||
|
response_model=List[StatusRead],
|
||||||
|
summary='Return all statuses',
|
||||||
|
description='Return all statuses',
|
||||||
|
)
|
||||||
|
async def get_statuses(
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
user: User = Depends(require_auth_user),
|
||||||
|
):
|
||||||
|
statuses_service = StatusesService(db)
|
||||||
|
return await statuses_service.get_all()
|
||||||
@ -72,6 +72,7 @@ async def change_password(
|
|||||||
users_service = UsersService(db)
|
users_service = UsersService(db)
|
||||||
return await users_service.change_password(user_id, password_data, user)
|
return await users_service.change_password(user_id, password_data, user)
|
||||||
|
|
||||||
|
|
||||||
@users_router.post(
|
@users_router.post(
|
||||||
'/create/',
|
'/create/',
|
||||||
response_model=Optional[UserRead],
|
response_model=Optional[UserRead],
|
||||||
|
|||||||
@ -34,8 +34,11 @@ class UserUpdate(BaseModel):
|
|||||||
first_name: str = Field(max_length=250)
|
first_name: str = Field(max_length=250)
|
||||||
last_name: str = Field(max_length=250)
|
last_name: str = Field(max_length=250)
|
||||||
patronymic: Optional[str] = Field(default=None, max_length=250)
|
patronymic: Optional[str] = Field(default=None, max_length=250)
|
||||||
|
login: str = Field(max_length=250)
|
||||||
email: Optional[EmailStr] = None
|
email: Optional[EmailStr] = None
|
||||||
birthdate: date
|
birthdate: date
|
||||||
|
role_id: Optional[int] = Field(default=None)
|
||||||
|
status_id: Optional[int] = Field(default=None)
|
||||||
|
|
||||||
|
|
||||||
class PasswordChangeRequest(BaseModel):
|
class PasswordChangeRequest(BaseModel):
|
||||||
|
|||||||
21
api/app/infrastructure/statuses_servise.py
Normal file
21
api/app/infrastructure/statuses_servise.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
from typing import List
|
||||||
|
|
||||||
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
|
from app.application.statuses_repository import StatusesRepository
|
||||||
|
from app.domain.entities.statuses import StatusRead
|
||||||
|
|
||||||
|
|
||||||
|
class StatusesService:
|
||||||
|
def __init__(self, db: AsyncSession):
|
||||||
|
self.statuses_repository = StatusesRepository(db)
|
||||||
|
|
||||||
|
async def get_all(self) -> List[StatusRead]:
|
||||||
|
statuses = await self.statuses_repository.get_all()
|
||||||
|
response = []
|
||||||
|
for status in statuses:
|
||||||
|
response.append(
|
||||||
|
StatusRead.model_validate(status)
|
||||||
|
)
|
||||||
|
|
||||||
|
return response
|
||||||
@ -42,7 +42,7 @@ class UsersService:
|
|||||||
detail='Пользователь не найден',
|
detail='Пользователь не найден',
|
||||||
)
|
)
|
||||||
|
|
||||||
if current_user.id != user_model.id and not current_user.role.title != self.settings.root_role_name:
|
if current_user.id != user_model.id and current_user.role.title != self.settings.root_role_name:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN,
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
detail='Доступ запрещен',
|
detail='Доступ запрещен',
|
||||||
@ -54,6 +54,10 @@ class UsersService:
|
|||||||
user_model.email = user.email
|
user_model.email = user.email
|
||||||
user_model.birthdate = user.birthdate
|
user_model.birthdate = user.birthdate
|
||||||
|
|
||||||
|
if current_user.role.title == self.settings.root_role_name and user.role_id is not None:
|
||||||
|
user_model.role_id = user.role_id
|
||||||
|
user_model.status_id = user.status_id
|
||||||
|
|
||||||
user_model = await self.users_repository.update(user_model)
|
user_model = await self.users_repository.update(user_model)
|
||||||
|
|
||||||
return UserRead.model_validate(user_model)
|
return UserRead.model_validate(user_model)
|
||||||
@ -68,7 +72,7 @@ class UsersService:
|
|||||||
detail='Пользователь не найден',
|
detail='Пользователь не найден',
|
||||||
)
|
)
|
||||||
|
|
||||||
if current_user.id != user_model.id and not current_user.role.title != self.settings.root_role_name:
|
if current_user.id != user_model.id and current_user.role.title != self.settings.root_role_name:
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_403_FORBIDDEN,
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
detail='Доступ запрещен',
|
detail='Доступ запрещен',
|
||||||
|
|||||||
@ -4,6 +4,7 @@ from starlette.middleware.cors import CORSMiddleware
|
|||||||
from app.controllers.auth_router import auth_router
|
from app.controllers.auth_router import auth_router
|
||||||
from app.controllers.register_router import register_router
|
from app.controllers.register_router import register_router
|
||||||
from app.controllers.roles_router import roles_router
|
from app.controllers.roles_router import roles_router
|
||||||
|
from app.controllers.statuses_router import statuses_router
|
||||||
from app.controllers.users_router import users_router
|
from app.controllers.users_router import users_router
|
||||||
from app.settings import Settings
|
from app.settings import Settings
|
||||||
|
|
||||||
@ -23,6 +24,7 @@ def start_app():
|
|||||||
api_app.include_router(auth_router, prefix=f'{settings.prefix}/auth', tags=['auth'])
|
api_app.include_router(auth_router, prefix=f'{settings.prefix}/auth', tags=['auth'])
|
||||||
api_app.include_router(register_router, prefix=f'{settings.prefix}/register', tags=['register'])
|
api_app.include_router(register_router, prefix=f'{settings.prefix}/register', tags=['register'])
|
||||||
api_app.include_router(roles_router, prefix=f'{settings.prefix}/roles', tags=['roles'])
|
api_app.include_router(roles_router, prefix=f'{settings.prefix}/roles', tags=['roles'])
|
||||||
|
api_app.include_router(statuses_router, prefix=f'{settings.prefix}/statuses', tags=['statuses'])
|
||||||
api_app.include_router(users_router, prefix=f'{settings.prefix}/users', tags=['users'])
|
api_app.include_router(users_router, prefix=f'{settings.prefix}/users', tags=['users'])
|
||||||
|
|
||||||
return api_app
|
return api_app
|
||||||
|
|||||||
20
web/src/Api/statusesApi.js
Normal file
20
web/src/Api/statusesApi.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import {createApi, fetchBaseQuery} from "@reduxjs/toolkit/query/react";
|
||||||
|
import CONFIG from "../Core/сonfig.js";
|
||||||
|
import {baseQueryWithAuth} from "./baseQuery.js";
|
||||||
|
|
||||||
|
|
||||||
|
export const statusesApi = createApi({
|
||||||
|
reducerPath: 'statusesApi',
|
||||||
|
baseQuery: baseQueryWithAuth,
|
||||||
|
tagTypes: ['Statuses'],
|
||||||
|
endpoints: (builder) => ({
|
||||||
|
getStatuses: builder.query({
|
||||||
|
query: () => '/statuses/',
|
||||||
|
providesTags: ['Statuses'],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const {
|
||||||
|
useGetStatusesQuery,
|
||||||
|
} = statusesApi;
|
||||||
@ -13,6 +13,7 @@ export const usersApi = createApi({
|
|||||||
}),
|
}),
|
||||||
getAuthenticatedUserData: builder.query({
|
getAuthenticatedUserData: builder.query({
|
||||||
query: () => "/users/me/",
|
query: () => "/users/me/",
|
||||||
|
providesTags: ["user"],
|
||||||
}),
|
}),
|
||||||
updateUser: builder.mutation({
|
updateUser: builder.mutation({
|
||||||
query: ({userId, ...data}) => ({
|
query: ({userId, ...data}) => ({
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import {ControlOutlined, PlusOutlined} from "@ant-design/icons";
|
|||||||
import useAdminPage from "./useAdminPage.js";
|
import useAdminPage from "./useAdminPage.js";
|
||||||
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
import LoadingIndicator from "../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
import CreateUserModalForm from "./CreateUserModalForm/CreateUserModalForm.jsx";
|
import CreateUserModalForm from "./CreateUserModalForm/CreateUserModalForm.jsx";
|
||||||
|
import UpdateUserModalForm from "./UpdateUserModalForm/UpdateUserModalForm.jsx";
|
||||||
|
|
||||||
const {Title} = Typography;
|
const {Title} = Typography;
|
||||||
|
|
||||||
@ -15,6 +16,7 @@ const AdminPage = () => {
|
|||||||
isLoading,
|
isLoading,
|
||||||
isError,
|
isError,
|
||||||
openCreateModal,
|
openCreateModal,
|
||||||
|
currentUser,
|
||||||
} = useAdminPage();
|
} = useAdminPage();
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
@ -47,6 +49,13 @@ const AdminPage = () => {
|
|||||||
filters: rolesData.map(role => ({text: role.title, value: role.title})),
|
filters: rolesData.map(role => ({text: role.title, value: role.title})),
|
||||||
onFilter: (value, record) => record.role.title === value,
|
onFilter: (value, record) => record.role.title === value,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "Статус",
|
||||||
|
dataIndex: ["status", "title"],
|
||||||
|
key: "status",
|
||||||
|
filters: rolesData.map(status => ({text: status.title, value: status.title})),
|
||||||
|
onFilter: (value, record) => record.status.title === value,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "Статус",
|
title: "Статус",
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
@ -56,9 +65,9 @@ const AdminPage = () => {
|
|||||||
title: "Действия",
|
title: "Действия",
|
||||||
key: "actions",
|
key: "actions",
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Button type="link" onClick={() => handleSelectUserToEdit(record)}>
|
(currentUser.id !== record.id ? (<Button type="link" onClick={() => handleSelectUserToEdit(record)}>
|
||||||
Редактировать
|
Редактировать
|
||||||
</Button>
|
</Button>) : null)
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
@ -97,6 +106,7 @@ const AdminPage = () => {
|
|||||||
</Col>
|
</Col>
|
||||||
|
|
||||||
<CreateUserModalForm/>
|
<CreateUserModalForm/>
|
||||||
|
<UpdateUserModalForm/>
|
||||||
</Row>
|
</Row>
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|||||||
@ -36,8 +36,6 @@ const useCreateUserModalForm = () => {
|
|||||||
: null,
|
: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log(payload);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await registerUser(payload).unwrap();
|
await registerUser(payload).unwrap();
|
||||||
notification.success({
|
notification.success({
|
||||||
|
|||||||
@ -0,0 +1,147 @@
|
|||||||
|
import {Button, DatePicker, Form, Input, Modal, Result, Select, Tooltip, Typography} from "antd";
|
||||||
|
import useUpdateUserModalForm from "./useUpdateUserModalForm.js";
|
||||||
|
import {CalendarOutlined, InfoCircleOutlined} from "@ant-design/icons";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import LoadingIndicator from "../../../Widgets/LoadingIndicator/LoadingIndicator.jsx";
|
||||||
|
|
||||||
|
|
||||||
|
const UpdateUserModalForm = () => {
|
||||||
|
const {
|
||||||
|
modalVisible,
|
||||||
|
handleCancel,
|
||||||
|
handleFinish,
|
||||||
|
userForm,
|
||||||
|
passwordForm,
|
||||||
|
roles,
|
||||||
|
isLoading,
|
||||||
|
isError,
|
||||||
|
isLoadingUpdate,
|
||||||
|
isErrorUpdate,
|
||||||
|
handlePasswordFinish,
|
||||||
|
statusesData,
|
||||||
|
} = useUpdateUserModalForm();
|
||||||
|
|
||||||
|
if (isLoading) {
|
||||||
|
return <LoadingIndicator/>
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isError) {
|
||||||
|
return <Result status="500" title="500" subTitle="Произошла ошибка при загрузке данных пользователя"/>
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
title="Изменить пользователя"
|
||||||
|
open={modalVisible}
|
||||||
|
onCancel={handleCancel}
|
||||||
|
footer={null}
|
||||||
|
>
|
||||||
|
<Form
|
||||||
|
form={userForm}
|
||||||
|
onFinish={handleFinish}
|
||||||
|
layout="vertical"
|
||||||
|
>
|
||||||
|
<Form.Item label="Фамилия" name="last_name" rules={[{required: true, message: "Введите фамилию"}]}>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Имя" name="first_name" rules={[{required: true, message: "Введите имя"}]}>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Отчество" name="patronymic">
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item label="Логин" name="login" rules={[{required: true, message: "Введите логин"}]}>
|
||||||
|
<Input disabled/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="email"
|
||||||
|
label="Email"
|
||||||
|
rules={[{required: true, message: "Введите email", type: "email"}]}>
|
||||||
|
<Input/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="birthdate"
|
||||||
|
label="Дата рождения"
|
||||||
|
rules={[{required: true, message: "Введите дату рождения"}]}
|
||||||
|
>
|
||||||
|
<DatePicker
|
||||||
|
suffixIcon={<CalendarOutlined/>}
|
||||||
|
format="DD.MM.YYYY"
|
||||||
|
style={{width: "100%"}}
|
||||||
|
size="large"
|
||||||
|
maxDate={dayjs()}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="role_id"
|
||||||
|
label="Роль"
|
||||||
|
rules={[{required: true, message: "Выберите роль"}]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
{roles.map((role) => (
|
||||||
|
<Select.Option key={role.id} value={role.id}>
|
||||||
|
{role.title}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="status_id"
|
||||||
|
label="Статус"
|
||||||
|
rules={[{required: true, message: "Выберите статус"}]}
|
||||||
|
>
|
||||||
|
<Select>
|
||||||
|
{statusesData.map((status) => (
|
||||||
|
<Select.Option key={status.id} value={status.id}>
|
||||||
|
{status.title}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item>
|
||||||
|
<Button type="primary" htmlType="submit" loading={isLoadingUpdate || isLoading}>
|
||||||
|
Сохранить изменения
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
<Form form={passwordForm} onFinish={handlePasswordFinish}>
|
||||||
|
<Typography.Title level={4}>Изменение пароля</Typography.Title>
|
||||||
|
<Form.Item
|
||||||
|
name="password"
|
||||||
|
label="Пароль"
|
||||||
|
rules={[{required: true, message: "Введите пароль"}]}
|
||||||
|
>
|
||||||
|
<Input.Password/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item
|
||||||
|
name="repeat_password"
|
||||||
|
label="Подтверждение пароля"
|
||||||
|
dependencies={["password"]}
|
||||||
|
rules={[
|
||||||
|
{required: true, message: "Повторите пароль"},
|
||||||
|
({getFieldValue}) => ({
|
||||||
|
validator(_, value) {
|
||||||
|
if (!value || getFieldValue("password") === value) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error("Пароли не совпадают"));
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Input.Password/>
|
||||||
|
</Form.Item>
|
||||||
|
<Form.Item>
|
||||||
|
<Button type="primary" htmlType="submit" loading={isLoadingUpdate || isLoading}>
|
||||||
|
Изменить пароль
|
||||||
|
</Button>
|
||||||
|
<Button onClick={handleCancel} style={{marginLeft: 8}}>
|
||||||
|
Отмена
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UpdateUserModalForm;
|
||||||
@ -0,0 +1,137 @@
|
|||||||
|
import {useDispatch, useSelector} from "react-redux";
|
||||||
|
import {Form, notification} from "antd";
|
||||||
|
import {setSelectedUserToUpdate} from "../../../../Redux/Slices/usersSlice.js";
|
||||||
|
import {useGetAllRolesQuery} from "../../../../Api/rolesApi.js";
|
||||||
|
import {useEffect} from "react";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
import {useUpdateUserMutation, useUpdateUserPasswordMutation} from "../../../../Api/usersApi.js";
|
||||||
|
import {useGetStatusesQuery} from "../../../../Api/statusesApi.js";
|
||||||
|
|
||||||
|
|
||||||
|
const useUpdateUserModalForm = () => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const [userForm] = Form.useForm();
|
||||||
|
const [passwordForm] = Form.useForm();
|
||||||
|
|
||||||
|
const {
|
||||||
|
selectedUserToUpdate
|
||||||
|
} = useSelector(state => state.users);
|
||||||
|
|
||||||
|
const modalVisible = selectedUserToUpdate !== null;
|
||||||
|
|
||||||
|
const handleCancel = () => {
|
||||||
|
dispatch(setSelectedUserToUpdate(null));
|
||||||
|
};
|
||||||
|
|
||||||
|
const [
|
||||||
|
updateUser,
|
||||||
|
{
|
||||||
|
isLoading: isLoadingUpdate,
|
||||||
|
isError: isErrorUpdate,
|
||||||
|
}
|
||||||
|
] = useUpdateUserMutation();
|
||||||
|
|
||||||
|
const [
|
||||||
|
changeUserPassword,
|
||||||
|
{
|
||||||
|
isLoading: isLoadingChangePassword,
|
||||||
|
isError: isErrorChangePassword,
|
||||||
|
}
|
||||||
|
] = useUpdateUserPasswordMutation();
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: statusesData = [],
|
||||||
|
isLoading: statusesIsLoading,
|
||||||
|
isError: statusesIsError,
|
||||||
|
} = useGetStatusesQuery(undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedUserToUpdate) {
|
||||||
|
const { role, ...userWithoutRole } = selectedUserToUpdate;
|
||||||
|
|
||||||
|
userForm.setFieldsValue({
|
||||||
|
...userWithoutRole,
|
||||||
|
role_id: role?.id,
|
||||||
|
birthdate: selectedUserToUpdate.birthdate
|
||||||
|
? dayjs(selectedUserToUpdate.birthdate)
|
||||||
|
: null,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [selectedUserToUpdate, userForm]);
|
||||||
|
|
||||||
|
const handlePasswordFinish = async () => {
|
||||||
|
const values = passwordForm.getFieldsValue();
|
||||||
|
const payload = {
|
||||||
|
...values,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await changeUserPassword({userId: selectedUserToUpdate.id, ...payload}).unwrap();
|
||||||
|
notification.success({
|
||||||
|
title: "Пароль изменен",
|
||||||
|
description: "Пароль успешно изменен",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
passwordForm.resetFields();
|
||||||
|
handleCancel();
|
||||||
|
} catch (error) {
|
||||||
|
notification.error({
|
||||||
|
title: "Ошибка изменения пароля",
|
||||||
|
description: error?.data?.detail || "Не удалось изменить пароль",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFinish = async () => {
|
||||||
|
const values = userForm.getFieldsValue();
|
||||||
|
const payload = {
|
||||||
|
...values,
|
||||||
|
birthdate: values.birthdate
|
||||||
|
? values.birthdate.format("YYYY-MM-DD")
|
||||||
|
: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await updateUser({userId: selectedUserToUpdate.id, ...payload}).unwrap();
|
||||||
|
notification.success({
|
||||||
|
title: "Пользователь изменен",
|
||||||
|
description: "Пользователь успешно изменен",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
userForm.resetFields();
|
||||||
|
handleCancel();
|
||||||
|
} catch (error) {
|
||||||
|
notification.error({
|
||||||
|
title: "Ошибка изменения пользователя",
|
||||||
|
description: error?.data?.detail || "Не удалось изменить пользователя",
|
||||||
|
placement: "topRight",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const {
|
||||||
|
data: roles = [],
|
||||||
|
isLoading: isLoadingRoles,
|
||||||
|
isError: isErrorRoles,
|
||||||
|
} = useGetAllRolesQuery(undefined, {
|
||||||
|
pollingInterval: 60000,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
modalVisible,
|
||||||
|
handleCancel,
|
||||||
|
handleFinish,
|
||||||
|
userForm,
|
||||||
|
passwordForm,
|
||||||
|
roles,
|
||||||
|
isLoading: isLoadingRoles | statusesIsLoading,
|
||||||
|
isError: isErrorRoles | statusesIsError,
|
||||||
|
isLoadingUpdate,
|
||||||
|
isErrorUpdate,
|
||||||
|
handlePasswordFinish,
|
||||||
|
statusesData,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useUpdateUserModalForm;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import {useGetAllUsersQuery} from "../../../Api/usersApi.js";
|
import {useGetAllUsersQuery, useGetAuthenticatedUserDataQuery} from "../../../Api/usersApi.js";
|
||||||
import {useGetAllRolesQuery} from "../../../Api/rolesApi.js";
|
import {useGetAllRolesQuery} from "../../../Api/rolesApi.js";
|
||||||
import {useMemo, useState} from "react";
|
import {useMemo, useState} from "react";
|
||||||
import {useDispatch} from "react-redux";
|
import {useDispatch} from "react-redux";
|
||||||
@ -7,6 +7,11 @@ import {setOpenModalCreateUser, setSelectedUserToUpdate} from "../../../Redux/Sl
|
|||||||
|
|
||||||
const useAdminPage = () => {
|
const useAdminPage = () => {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
const {
|
||||||
|
data: currentUser = {},
|
||||||
|
isLoading: currentUserIsLoading,
|
||||||
|
isError: currentUserIsError,
|
||||||
|
} = useGetAuthenticatedUserDataQuery(undefined);
|
||||||
|
|
||||||
const [
|
const [
|
||||||
searchString,
|
searchString,
|
||||||
@ -56,9 +61,10 @@ const useAdminPage = () => {
|
|||||||
rolesData,
|
rolesData,
|
||||||
filteredUsers,
|
filteredUsers,
|
||||||
handleSearch,
|
handleSearch,
|
||||||
isLoading: usersIsLoading | rolesIsLoading,
|
isLoading: usersIsLoading | rolesIsLoading | currentUserIsLoading,
|
||||||
isError: usersIsError | rolesIsError,
|
isError: usersIsError | rolesIsError | currentUserIsError,
|
||||||
openCreateModal,
|
openCreateModal,
|
||||||
|
currentUser,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -148,10 +148,8 @@ const ProfilePage = () => {
|
|||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Карточка смены пароля */}
|
|
||||||
<Card
|
<Card
|
||||||
title={<Title level={4}><LockOutlined/> Смена пароля</Title>}
|
title={<Title level={4}><LockOutlined/> Смена пароля</Title>}
|
||||||
bordered={false}
|
|
||||||
style={{borderRadius: 12, boxShadow: "0 4px 12px rgba(0,0,0,0.05)"}}
|
style={{borderRadius: 12, boxShadow: "0 4px 12px rgba(0,0,0,0.05)"}}
|
||||||
>
|
>
|
||||||
<Form form={passwordForm} layout="vertical" onFinish={onFinishPasswordForm}>
|
<Form form={passwordForm} layout="vertical" onFinish={onFinishPasswordForm}>
|
||||||
|
|||||||
@ -4,6 +4,7 @@ import usersReducer from "./Slices/usersSlice.js";
|
|||||||
import {authApi} from "../Api/authApi.js";
|
import {authApi} from "../Api/authApi.js";
|
||||||
import {usersApi} from "../Api/usersApi.js";
|
import {usersApi} from "../Api/usersApi.js";
|
||||||
import {rolesApi} from "../Api/rolesApi.js";
|
import {rolesApi} from "../Api/rolesApi.js";
|
||||||
|
import {statusesApi} from "../Api/statusesApi.js";
|
||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
@ -14,12 +15,15 @@ export const store = configureStore({
|
|||||||
[usersApi.reducerPath]: usersApi.reducer,
|
[usersApi.reducerPath]: usersApi.reducer,
|
||||||
|
|
||||||
[rolesApi.reducerPath]: rolesApi.reducer,
|
[rolesApi.reducerPath]: rolesApi.reducer,
|
||||||
|
|
||||||
|
[statusesApi.reducerPath]: statusesApi.reducer
|
||||||
},
|
},
|
||||||
middleware: (getDefaultMiddleware) => (
|
middleware: (getDefaultMiddleware) => (
|
||||||
getDefaultMiddleware().concat(
|
getDefaultMiddleware().concat(
|
||||||
authApi.middleware,
|
authApi.middleware,
|
||||||
usersApi.middleware,
|
usersApi.middleware,
|
||||||
rolesApi.middleware,
|
rolesApi.middleware,
|
||||||
|
statusesApi.middleware,
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user