forked from VinokurovVE/tests
Cleanup, shared create modal
This commit is contained in:
@ -17,6 +17,7 @@ import Boilers from "./pages/Boilers"
|
||||
import Servers from "./pages/Servers"
|
||||
import { Api, Assignment, Cloud, Factory, Home, Login, People, Shield, Storage } from "@mui/icons-material"
|
||||
|
||||
// Определение страниц с путями и компонентом для рендера
|
||||
export const pages = [
|
||||
{
|
||||
label: "",
|
||||
|
@ -1,125 +0,0 @@
|
||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||
import RoleService from '../../services/RoleService';
|
||||
import { Box, Button, Modal, Select, TextField, Typography } from '@mui/material';
|
||||
import { ICompany } from '../../interfaces/documents';
|
||||
import { useCompanies } from '../../hooks/swrHooks';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
setOpen: (state: boolean) => void;
|
||||
}
|
||||
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 400,
|
||||
bgcolor: 'background.paper',
|
||||
boxShadow: 24,
|
||||
borderRadius: 2,
|
||||
p: 4,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "8px"
|
||||
}
|
||||
|
||||
export default function CreateCompanyModal({
|
||||
open,
|
||||
setOpen
|
||||
}: Props) {
|
||||
const { companies } = useCompanies()
|
||||
|
||||
const { register, handleSubmit, formState: { errors } } = useForm<ICompany>({
|
||||
defaultValues: {
|
||||
name: '',
|
||||
fullname: '',
|
||||
description: '',
|
||||
}
|
||||
})
|
||||
|
||||
const onSubmit: SubmitHandler<ICompany> = async (data) => {
|
||||
try {
|
||||
await RoleService.createRole(data)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
|
||||
<Box sx={style}>
|
||||
<Typography variant="h6" component="h6" gutterBottom>
|
||||
Создание компании
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Наименование"
|
||||
required
|
||||
{...register('name', { required: 'Наименование обязательно' })}
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Полное наименование"
|
||||
required
|
||||
{...register('fullname', { required: 'Полное наименование обязательно' })}
|
||||
error={!!errors.fullname}
|
||||
helperText={errors.fullname?.message}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Описание"
|
||||
{...register('description')}
|
||||
error={!!errors.description}
|
||||
helperText={errors.description?.message}
|
||||
/>
|
||||
|
||||
{companies}
|
||||
|
||||
<Select
|
||||
label="owner_id"
|
||||
{...register('owner_id', { required: '' })}
|
||||
error={!!errors.owner_id}
|
||||
>
|
||||
{/* {companies &&
|
||||
companies.map((item, index) => {
|
||||
<MenuItem>
|
||||
{index}
|
||||
</MenuItem>
|
||||
})
|
||||
} */}
|
||||
</Select>
|
||||
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: "8px"
|
||||
}}>
|
||||
<Button type="submit" variant="contained" color="primary">
|
||||
Добавить роль
|
||||
</Button>
|
||||
|
||||
<Button type="button" variant="outlined" color="primary" onClick={() => setOpen(false)}>
|
||||
Отмена
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
@ -1,106 +0,0 @@
|
||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||
import RoleService from '../../services/RoleService';
|
||||
import { Box, Button, Modal, TextField, Typography } from '@mui/material';
|
||||
import { ICompany } from '../../interfaces/documents';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
setOpen: (state: boolean) => void;
|
||||
}
|
||||
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 400,
|
||||
bgcolor: 'background.paper',
|
||||
boxShadow: 24,
|
||||
borderRadius: 2,
|
||||
p: 4,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "8px"
|
||||
}
|
||||
|
||||
export default function CreateDepartmentModal({
|
||||
open,
|
||||
setOpen
|
||||
}: Props) {
|
||||
const { register, handleSubmit, formState: { errors } } = useForm<ICompany>({
|
||||
defaultValues: {
|
||||
name: '',
|
||||
fullname: '',
|
||||
description: '',
|
||||
}
|
||||
})
|
||||
|
||||
const onSubmit: SubmitHandler<ICompany> = async (data) => {
|
||||
try {
|
||||
await RoleService.createRole(data)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
|
||||
<Box sx={style}>
|
||||
<Typography variant="h6" component="h6" gutterBottom>
|
||||
Создание компании
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Наименование"
|
||||
required
|
||||
{...register('name', { required: 'Наименование обязательно' })}
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Полное наименование"
|
||||
required
|
||||
{...register('fullname', { required: 'Полное наименование обязательно' })}
|
||||
error={!!errors.fullname}
|
||||
helperText={errors.fullname?.message}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Описание"
|
||||
{...register('description')}
|
||||
error={!!errors.description}
|
||||
helperText={errors.description?.message}
|
||||
/>
|
||||
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: "8px"
|
||||
}}>
|
||||
<Button type="submit" variant="contained" color="primary">
|
||||
Добавить роль
|
||||
</Button>
|
||||
|
||||
<Button type="button" variant="outlined" color="primary" onClick={() => setOpen(false)}>
|
||||
Отмена
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
function CreateFromGridColModal() {
|
||||
return (
|
||||
<div>CreateFromGridColModal</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default CreateFromGridColModal
|
@ -1,94 +0,0 @@
|
||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { IRoleCreate } from '../../interfaces/role';
|
||||
import RoleService from '../../services/RoleService';
|
||||
import { Box, Button, Modal, TextField, Typography } from '@mui/material';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
setOpen: (state: boolean) => void;
|
||||
}
|
||||
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 400,
|
||||
bgcolor: 'background.paper',
|
||||
boxShadow: 24,
|
||||
borderRadius: 2,
|
||||
p: 4,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "8px"
|
||||
}
|
||||
|
||||
export default function CreateRoleModal({
|
||||
open,
|
||||
setOpen
|
||||
}: Props) {
|
||||
const { register, handleSubmit, formState: { errors } } = useForm<IRoleCreate>({
|
||||
defaultValues: {
|
||||
name: '',
|
||||
description: ''
|
||||
}
|
||||
})
|
||||
|
||||
const onSubmit: SubmitHandler<IRoleCreate> = async (data) => {
|
||||
try {
|
||||
await RoleService.createRole(data)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Box sx={style}>
|
||||
<Typography variant="h6" component="h6" gutterBottom>
|
||||
Создание роли
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Название"
|
||||
required
|
||||
{...register('name', { required: 'Название обязательно' })}
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Описание"
|
||||
{...register('description')}
|
||||
error={!!errors.description}
|
||||
helperText={errors.description?.message}
|
||||
/>
|
||||
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: "8px"
|
||||
}}>
|
||||
<Button type="submit" variant="contained" color="primary">
|
||||
Добавить роль
|
||||
</Button>
|
||||
|
||||
<Button type="button" variant="outlined" color="primary" onClick={() => setOpen(false)}>
|
||||
Отмена
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
@ -1,138 +0,0 @@
|
||||
import { Box, Button, Modal, TextField, Typography } from '@mui/material'
|
||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||
import UserService from '../../services/UserService';
|
||||
import { IUserCreate } from '../../interfaces/user';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
setOpen: (state: boolean) => void;
|
||||
}
|
||||
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 400,
|
||||
bgcolor: 'background.paper',
|
||||
boxShadow: 24,
|
||||
borderRadius: 2,
|
||||
p: 4,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "8px"
|
||||
}
|
||||
|
||||
export default function CreateUserModal({
|
||||
open,
|
||||
setOpen,
|
||||
}: Props) {
|
||||
const { register, handleSubmit, formState: { errors } } = useForm<IUserCreate>({
|
||||
defaultValues: {
|
||||
email: '',
|
||||
login: '',
|
||||
phone: '',
|
||||
name: '',
|
||||
surname: '',
|
||||
is_active: true,
|
||||
password: '',
|
||||
}
|
||||
})
|
||||
|
||||
const onSubmit: SubmitHandler<IUserCreate> = async (data) => {
|
||||
try {
|
||||
await UserService.createUser(data)
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Box sx={style}>
|
||||
<Typography variant="h6" component="h6" gutterBottom>
|
||||
Создание пользователя
|
||||
</Typography>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Email"
|
||||
required
|
||||
{...register('email', { required: 'Email обязателен' })}
|
||||
error={!!errors.email}
|
||||
helperText={errors.email?.message}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Логин"
|
||||
required
|
||||
{...register('login', { required: 'Логин обязателен' })}
|
||||
error={!!errors.login}
|
||||
helperText={errors.login?.message}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Телефон"
|
||||
{...register('phone')}
|
||||
error={!!errors.phone}
|
||||
helperText={errors.phone?.message}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Имя"
|
||||
{...register('name')}
|
||||
error={!!errors.name}
|
||||
helperText={errors.name?.message}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
label="Фамилия"
|
||||
{...register('surname')}
|
||||
error={!!errors.surname}
|
||||
helperText={errors.surname?.message}
|
||||
/>
|
||||
|
||||
<TextField
|
||||
fullWidth
|
||||
margin="normal"
|
||||
type="password"
|
||||
label="Пароль"
|
||||
required
|
||||
{...register('password', { required: 'Пароль обязателен' })}
|
||||
error={!!errors.password}
|
||||
helperText={errors.password?.message}
|
||||
/>
|
||||
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: "8px"
|
||||
}}>
|
||||
<Button type="submit" variant="contained" color="primary">
|
||||
Добавить пользователя
|
||||
</Button>
|
||||
|
||||
<Button type="button" variant="outlined" color="primary" onClick={() => setOpen(false)}>
|
||||
Отмена
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
108
frontend_reactjs/src/components/modals/ModalCreate.tsx
Normal file
108
frontend_reactjs/src/components/modals/ModalCreate.tsx
Normal file
@ -0,0 +1,108 @@
|
||||
import { Box, Button, Modal, TextField, Typography } from '@mui/material';
|
||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { CreateField } from '../../interfaces/create';
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
open: boolean;
|
||||
setOpen: (state: boolean) => void;
|
||||
submitHandler?: any;
|
||||
fields: CreateField[];
|
||||
submitButtonText?: string;
|
||||
mutateHandler?: any;
|
||||
}
|
||||
|
||||
const style = {
|
||||
position: 'absolute',
|
||||
top: '50%',
|
||||
left: '50%',
|
||||
transform: 'translate(-50%, -50%)',
|
||||
width: 400,
|
||||
bgcolor: 'background.paper',
|
||||
boxShadow: 24,
|
||||
borderRadius: 2,
|
||||
p: 4,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "8px"
|
||||
}
|
||||
|
||||
function ModalCreate({
|
||||
title = '',
|
||||
open,
|
||||
setOpen,
|
||||
submitHandler,
|
||||
fields,
|
||||
submitButtonText = 'Сохранить',
|
||||
mutateHandler
|
||||
}: Props) {
|
||||
const getDefaultValues = (fields: CreateField[]) => {
|
||||
let result: { [key: string]: string | boolean } = {}
|
||||
fields.forEach((field: CreateField) => {
|
||||
result[field.key] = field.defaultValue
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
const { register, handleSubmit, formState: { errors } } = useForm({
|
||||
defaultValues: getDefaultValues(fields)
|
||||
})
|
||||
|
||||
const onSubmit: SubmitHandler<any> = async (data) => {
|
||||
try {
|
||||
await submitHandler?.(data).then(() => {
|
||||
mutateHandler?.()
|
||||
})
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={() => setOpen(false)}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<Box sx={style}>
|
||||
<Typography variant="h6" component="h6" gutterBottom>
|
||||
{title}
|
||||
</Typography>
|
||||
|
||||
{fields.map((field: CreateField) => {
|
||||
return (
|
||||
<TextField
|
||||
key={field.key}
|
||||
fullWidth
|
||||
margin='normal'
|
||||
label={field.headerName || field.key.charAt(0).toUpperCase() + field.key.slice(1)}
|
||||
|
||||
required={field.required || false}
|
||||
{...register(field.key, { required: `${field.key} обязателен` })}
|
||||
error={!!errors[field.key]}
|
||||
/>
|
||||
)
|
||||
})}
|
||||
|
||||
<Box sx={{
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
gap: "8px"
|
||||
}}>
|
||||
<Button type="submit" variant="contained" color="primary">
|
||||
{submitButtonText}
|
||||
</Button>
|
||||
|
||||
<Button type="button" variant="outlined" color="primary" onClick={() => setOpen(false)}>
|
||||
Отмена
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</form>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default ModalCreate
|
20
frontend_reactjs/src/interfaces/create.ts
Normal file
20
frontend_reactjs/src/interfaces/create.ts
Normal file
@ -0,0 +1,20 @@
|
||||
export interface CreateFieldTypes {
|
||||
string: 'string';
|
||||
number: 'number';
|
||||
date: 'date';
|
||||
dateTime: 'dateTime';
|
||||
boolean: 'boolean';
|
||||
singleSelect: 'singleSelect';
|
||||
actions: 'actions';
|
||||
custom: 'custom';
|
||||
}
|
||||
|
||||
export type CreateFieldType = CreateFieldTypes[keyof CreateFieldTypes]
|
||||
|
||||
export interface CreateField {
|
||||
key: string;
|
||||
headerName?: string;
|
||||
type: CreateFieldType;
|
||||
required?: boolean;
|
||||
defaultValue?: any;
|
||||
}
|
@ -2,13 +2,20 @@ import { useState } from 'react'
|
||||
import { Box, Button, CircularProgress } from '@mui/material'
|
||||
import { DataGrid, GridColDef } from '@mui/x-data-grid'
|
||||
import { useRoles } from '../hooks/swrHooks'
|
||||
import CreateRoleModal from '../components/modals/CreateRoleModal'
|
||||
import { CreateField } from '../interfaces/create'
|
||||
import ModalCreate from '../components/modals/ModalCreate'
|
||||
import RoleService from '../services/RoleService'
|
||||
|
||||
export default function Roles() {
|
||||
const { roles, isError, isLoading } = useRoles()
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const createFields: CreateField[] = [
|
||||
{ key: 'name', headerName: 'Название', type: 'string', required: true, defaultValue: '' },
|
||||
{ key: 'description', headerName: 'Описание', type: 'string', required: false, defaultValue: '' },
|
||||
]
|
||||
|
||||
const columns: GridColDef[] = [
|
||||
{ field: 'id', headerName: 'ID', type: "number", width: 70 },
|
||||
{ field: 'name', headerName: 'Название', width: 90, editable: true },
|
||||
@ -30,9 +37,12 @@ export default function Roles() {
|
||||
Добавить роль
|
||||
</Button>
|
||||
|
||||
<CreateRoleModal
|
||||
<ModalCreate
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
fields={createFields}
|
||||
submitHandler={RoleService.createRole}
|
||||
title="Создание роли"
|
||||
/>
|
||||
|
||||
<DataGrid
|
||||
|
@ -3,7 +3,6 @@ import { DataGrid, GridColDef } from "@mui/x-data-grid"
|
||||
import { useRoles, useUsers } from "../hooks/swrHooks"
|
||||
import { IRole } from "../interfaces/role"
|
||||
import { useState } from "react"
|
||||
import CreateUserModal from "../components/modals/CreateUserModal"
|
||||
|
||||
export default function Settings() {
|
||||
const { users, isError, isLoading } = useUsers()
|
||||
@ -50,11 +49,6 @@ export default function Settings() {
|
||||
Добавить пользователя
|
||||
</Button>
|
||||
|
||||
<CreateUserModal
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
/>
|
||||
|
||||
<DataGrid
|
||||
autoHeight
|
||||
style={{ width: "100%" }}
|
||||
|
@ -3,7 +3,9 @@ import { DataGrid, GridColDef } from "@mui/x-data-grid"
|
||||
import { useRoles, useUsers } from "../hooks/swrHooks"
|
||||
import { IRole } from "../interfaces/role"
|
||||
import { useState } from "react"
|
||||
import CreateUserModal from "../components/modals/CreateUserModal"
|
||||
import { CreateField } from "../interfaces/create"
|
||||
import ModalCreate from "../components/modals/ModalCreate"
|
||||
import UserService from "../services/UserService"
|
||||
|
||||
export default function Users() {
|
||||
const { users, isError, isLoading } = useUsers()
|
||||
@ -12,6 +14,15 @@ export default function Users() {
|
||||
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const createFields: CreateField[] = [
|
||||
{ key: 'email', headerName: 'E-mail', type: 'string', required: true, defaultValue: '' },
|
||||
{ key: 'login', headerName: 'Логин', type: 'string', required: true, defaultValue: '' },
|
||||
{ key: 'phone', headerName: 'Телефон', type: 'string', required: false, defaultValue: '' },
|
||||
{ key: 'name', headerName: 'Имя', type: 'string', required: true, defaultValue: '' },
|
||||
{ key: 'surname', headerName: 'Фамилия', type: 'string', required: true, defaultValue: '' },
|
||||
{ key: 'password', headerName: 'Пароль', type: 'string', required: true, defaultValue: '' },
|
||||
]
|
||||
|
||||
const columns: GridColDef[] = [
|
||||
{ field: 'id', headerName: 'ID', type: "number", width: 70 },
|
||||
{ field: 'email', headerName: 'Email', width: 130, editable: true },
|
||||
@ -45,9 +56,12 @@ export default function Users() {
|
||||
Добавить пользователя
|
||||
</Button>
|
||||
|
||||
<CreateUserModal
|
||||
<ModalCreate
|
||||
open={open}
|
||||
setOpen={setOpen}
|
||||
fields={createFields}
|
||||
submitHandler={UserService.createUser}
|
||||
title="Создание пользователя"
|
||||
/>
|
||||
|
||||
<DataGrid
|
||||
|
Reference in New Issue
Block a user