forked from ddrilling/asb_cloud_front
Добавлена view для разрешений
К отображению телеметрии добавлено id Актуализирован редактор ролей Добавлена кнопка смены пароля Окно смены пароля вынесено в компонент
This commit is contained in:
parent
1b17ee2cfd
commit
e084727c72
63
src/components/ChangePassword.tsx
Normal file
63
src/components/ChangePassword.tsx
Normal file
@ -0,0 +1,63 @@
|
||||
import { memo, useState } from 'react'
|
||||
import { useForm } from 'antd/lib/form/Form'
|
||||
import { Form, Input, Modal, FormProps } from 'antd'
|
||||
|
||||
import { AuthService } from '../services/api'
|
||||
import { passwordRules } from '../utils/validationRules'
|
||||
|
||||
import LoaderPortal from './LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from './factory'
|
||||
|
||||
const formLayout: FormProps = { labelCol: { span: 11 }, wrapperCol: { span: 16 } }
|
||||
|
||||
export type ChangePasswordProps = {
|
||||
userId?: number
|
||||
visible?: boolean
|
||||
onCancel?: () => void
|
||||
onOk?: () => void
|
||||
}
|
||||
|
||||
|
||||
export const ChangePassword = memo<ChangePasswordProps>(({ userId, visible, onCancel, onOk }) => {
|
||||
const [showLoader, setShowLoader] = useState<boolean>(false)
|
||||
const [password, setPassword] = useState<string>('')
|
||||
|
||||
const [form] = useForm()
|
||||
|
||||
const onModalCancel = () => {
|
||||
form.resetFields()
|
||||
onCancel?.()
|
||||
}
|
||||
|
||||
const onFormFinish = () => invokeWebApiWrapperAsync(
|
||||
async() => {
|
||||
await AuthService.changePassword(userId ?? localStorage['userId'], `"${password}"`)
|
||||
onOk?.()
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось сменить пароль пользователя ${localStorage['login']}`
|
||||
)
|
||||
|
||||
return (
|
||||
<Modal
|
||||
centered
|
||||
title={'Сменить пароль'}
|
||||
visible={visible}
|
||||
onCancel={onModalCancel}
|
||||
onOk={() => form.submit()}
|
||||
>
|
||||
<LoaderPortal show={showLoader}>
|
||||
<Form
|
||||
{...formLayout}
|
||||
form={form}
|
||||
name={'change-password'}
|
||||
onFinish={onFormFinish}
|
||||
>
|
||||
<Form.Item label={'Новый пароль'} name={'new-password'} rules={passwordRules}>
|
||||
<Input.Password onChange={(e) => setPassword(e.target.value)} value={password} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</LoaderPortal>
|
||||
</Modal>
|
||||
)
|
||||
})
|
@ -49,6 +49,8 @@ export const EditableTable = ({
|
||||
onRowAdd, // Метод вызывается с новой добавленной записью. Если метод не определен, то кнопка добавления строки не показывается
|
||||
onRowEdit, // Метод вызывается с новой отредактированной записью. Если метод не поределен, то кнопка редактирования строки не показывается
|
||||
onRowDelete, // Метод вызывается с удаленной записью. Если метод не поределен, то кнопка удаления строки не показывается
|
||||
additionalButtons,
|
||||
buttonsWidth,
|
||||
...otherTableProps
|
||||
}) => {
|
||||
|
||||
@ -144,7 +146,7 @@ export const EditableTable = ({
|
||||
}
|
||||
|
||||
const operationColumn = {
|
||||
width: 82,
|
||||
width: buttonsWidth ?? 82,
|
||||
title: !!onRowAdd && (
|
||||
<Button
|
||||
onClick={addNewRow}
|
||||
@ -157,6 +159,7 @@ export const EditableTable = ({
|
||||
<span>
|
||||
<Button onClick={() => save(record)} icon={<SaveOutlined/>}/>
|
||||
<Button onClick={cancel} icon={<CloseCircleOutlined/>}/>
|
||||
{additionalButtons?.(record, editingKey)}
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
@ -172,6 +175,7 @@ export const EditableTable = ({
|
||||
<Button icon={<DeleteOutlined/>}/>
|
||||
</Popconfirm>
|
||||
)}
|
||||
{additionalButtons?.(record, editingKey)}
|
||||
</span>
|
||||
),
|
||||
}
|
||||
|
@ -1,9 +1,10 @@
|
||||
import { ReactNode } from 'react'
|
||||
import { InputNumber, Select, Table as RawTable } from 'antd'
|
||||
import { memo, useEffect, useState, ReactNode } from 'react'
|
||||
import { InputNumber, Select, Table as RawTable, Tag, SelectProps } from 'antd'
|
||||
import { OptionsType } from 'rc-select/lib/interface'
|
||||
import { tryAddKeys } from './EditableTable'
|
||||
import { makeNumericSorter, makeStringSorter } from './sorters'
|
||||
import { Rule } from 'rc-field-form/lib/interface'
|
||||
import { SelectValue } from 'antd/lib/select'
|
||||
export { makeDateSorter, makeNumericSorter, makeStringSorter } from './sorters'
|
||||
export { EditableTable, makeActionHandler } from './EditableTable'
|
||||
export { DatePickerWrapper } from './DatePickerWrapper'
|
||||
@ -205,13 +206,71 @@ export const makeSelectColumn = <T extends unknown = string>(
|
||||
dataIndex: string,
|
||||
options: OptionsType,
|
||||
defaultValue?: T,
|
||||
other?: columnPropsOther
|
||||
other?: columnPropsOther,
|
||||
selectOther?: SelectProps<SelectValue>
|
||||
) => makeColumn(title, dataIndex, {
|
||||
input: <Select options={options}/>,
|
||||
input: <Select options={options} {...selectOther}/>,
|
||||
render: (value) => options?.find(option => option?.value === value)?.label ?? defaultValue ?? value ?? '--',
|
||||
...other
|
||||
})
|
||||
|
||||
const makeTagInput = <T extends Record<string, any>>(id_key: string, value_key: string) => memo<{
|
||||
options: T[],
|
||||
value?: T[],
|
||||
onChange?: (values: T[]) => void
|
||||
}>(({ options, value, onChange }) => {
|
||||
const [selectOptions, setSelectOptions] = useState<OptionsType>([])
|
||||
|
||||
useEffect(() => {
|
||||
setSelectOptions(options.map((elm) => ({
|
||||
value: elm[id_key],
|
||||
label: elm[value_key]
|
||||
})))
|
||||
}, [options])
|
||||
|
||||
const onSelectChange = (rawValues?: SelectValue) => {
|
||||
let values: string[] = []
|
||||
if (typeof rawValues === 'string')
|
||||
values = rawValues.split(',')
|
||||
else if (typeof rawValues === 'number')
|
||||
values = [`${rawValues}`]
|
||||
|
||||
const objectValues: T[] = values.reduce((out: T[], id: string) => {
|
||||
const res = options.find((option) => `${option[id_key]}` === id)
|
||||
if (res) out.push(res)
|
||||
return out
|
||||
}, [])
|
||||
|
||||
onChange?.(objectValues)
|
||||
}
|
||||
|
||||
return (
|
||||
<Select
|
||||
mode={'tags'}
|
||||
options={selectOptions}
|
||||
value={value?.join(',')}
|
||||
onChange={onSelectChange}
|
||||
/>
|
||||
)
|
||||
})
|
||||
|
||||
export const makeTagColumn = <T extends Record<string, any>>(
|
||||
title: string,
|
||||
dataIndex: string,
|
||||
options: T[],
|
||||
value_key: string,
|
||||
label_key: string,
|
||||
other?: columnPropsOther
|
||||
) => {
|
||||
const InputComponent = makeTagInput<T>(value_key, label_key)
|
||||
|
||||
return makeColumn(title, dataIndex, {
|
||||
...other,
|
||||
render: (item?: T[]) => item?.map((elm: T) => <Tag key={elm[label_key]} color='blue'>{elm[label_key]}</Tag>) ?? '-',
|
||||
input: <InputComponent options={options} />,
|
||||
})
|
||||
}
|
||||
|
||||
type PaginationContainer = {
|
||||
skip?: number
|
||||
take?: number
|
||||
|
@ -1,52 +1,35 @@
|
||||
import { MouseEventHandler, useState } from 'react'
|
||||
import { Link, useHistory } from 'react-router-dom'
|
||||
import { Button, Dropdown, Menu, Modal, Form, Input, FormProps } from 'antd'
|
||||
import { useForm } from 'antd/lib/form/Form'
|
||||
import { Button, Dropdown, Menu } from 'antd'
|
||||
import { UserOutlined } from '@ant-design/icons'
|
||||
import { AuthService } from '../services/api'
|
||||
import { passwordRules } from '../utils/validationRules'
|
||||
import { invokeWebApiWrapperAsync } from './factory'
|
||||
|
||||
import { PrivateMenuItem } from './Private'
|
||||
import LoaderPortal from './LoaderPortal'
|
||||
import { ChangePassword } from './ChangePassword'
|
||||
|
||||
const handleLogout = () => {
|
||||
localStorage.removeItem('login')
|
||||
localStorage.removeItem('token')
|
||||
}
|
||||
|
||||
const formLayout: FormProps = { labelCol: { span: 11 }, wrapperCol: { span: 16 } }
|
||||
|
||||
type UserMenuProps = {
|
||||
isAdmin?: boolean
|
||||
}
|
||||
|
||||
export const UserMenu: React.FC<UserMenuProps> = ({ isAdmin }) => {
|
||||
const [form] = useForm()
|
||||
const [isModalVisible, setIsModalVisible] = useState<boolean>(false)
|
||||
const [showLoader, setShowLoader] = useState<boolean>(false)
|
||||
const [password, setPassword] = useState<string>('')
|
||||
|
||||
const history = useHistory()
|
||||
|
||||
const changePassword = () => invokeWebApiWrapperAsync(
|
||||
async() => {
|
||||
await AuthService.changePassword(localStorage['userId'], `"${password}"`)
|
||||
history.push('/login')
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось сменить пароль пользователя ${localStorage['login']}`
|
||||
)
|
||||
|
||||
const onFormCancel = () => {
|
||||
form.resetFields()
|
||||
setIsModalVisible(false)
|
||||
}
|
||||
|
||||
const onChangePasswordClick: MouseEventHandler = (e) => {
|
||||
setIsModalVisible(true)
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
const onChangePasswordOk = () => {
|
||||
setIsModalVisible(false)
|
||||
history.push('/login')
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dropdown
|
||||
@ -71,26 +54,11 @@ export const UserMenu: React.FC<UserMenuProps> = ({ isAdmin }) => {
|
||||
>
|
||||
<Button icon={<UserOutlined/>}>{localStorage['login']}</Button>
|
||||
</Dropdown>
|
||||
<Modal
|
||||
title={'Сменить пароль'}
|
||||
centered
|
||||
<ChangePassword
|
||||
visible={isModalVisible}
|
||||
onCancel={onFormCancel}
|
||||
onOk={() => form.submit()}
|
||||
>
|
||||
<LoaderPortal show={showLoader}>
|
||||
<Form
|
||||
{...formLayout}
|
||||
form={form}
|
||||
name={'change-password'}
|
||||
onFinish={changePassword}
|
||||
>
|
||||
<Form.Item label={'Новый пароль'} name={'new-password'} rules={passwordRules}>
|
||||
<Input.Password onChange={(e) => setPassword(e.target.value)} value={password} />
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</LoaderPortal>
|
||||
</Modal>
|
||||
onOk={onChangePasswordOk}
|
||||
onCancel={() => setIsModalVisible(false)}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
27
src/components/Views/PermissionView.tsx
Normal file
27
src/components/Views/PermissionView.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
import { Tooltip } from 'antd'
|
||||
import { memo } from 'react'
|
||||
import { PermissionDto } from '../../services/api'
|
||||
import { Grid, GridItem } from '../Grid'
|
||||
|
||||
export type PermissionViewProps = {
|
||||
info?: PermissionDto
|
||||
}
|
||||
|
||||
export const PermissionView = memo<PermissionViewProps>(({ info }) => info ? (
|
||||
<Tooltip overlayInnerStyle={{ width: '400px' }} title={
|
||||
<Grid>
|
||||
<GridItem row={1} col={1}>Название:</GridItem>
|
||||
<GridItem row={1} col={2}>{info.name}</GridItem>
|
||||
|
||||
<GridItem row={2} col={1}>Описание:</GridItem>
|
||||
<GridItem row={2} col={2}>{info.description}</GridItem>
|
||||
</Grid>
|
||||
}>
|
||||
{info.name}
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip title={'нет данных'}>-</Tooltip>
|
||||
))
|
||||
|
||||
|
||||
export default PermissionView
|
@ -1,6 +1,6 @@
|
||||
import { memo } from 'react'
|
||||
import { Fragment, memo } from 'react'
|
||||
import { Tooltip } from 'antd'
|
||||
import { TelemetryInfoDto } from '../../services/api'
|
||||
import { TelemetryDto, TelemetryInfoDto } from '../../services/api'
|
||||
import { Grid, GridItem } from '../Grid'
|
||||
|
||||
const lables: { [labelKey: string]: string } = {
|
||||
@ -17,25 +17,29 @@ const lables: { [labelKey: string]: string } = {
|
||||
spinPlcVersion: 'Версия Спин Мастер',
|
||||
}
|
||||
|
||||
export const getTelemetryLabel = (info?: TelemetryInfoDto) => info ? `${info.deposit} / ${info.cluster} / ${info.well}` : '---'
|
||||
export const getTelemetryLabel = (telemetry?: TelemetryDto) =>
|
||||
`${telemetry?.id ?? '-'} / ${telemetry?.info?.deposit ?? '-'} / ${telemetry?.info?.cluster ?? '-'} / ${telemetry?.info?.well ?? '-'}`
|
||||
|
||||
export type TelemetryViewProps = {
|
||||
info?: TelemetryInfoDto
|
||||
telemetry?: TelemetryDto
|
||||
}
|
||||
|
||||
export const TelemetryView = memo<TelemetryViewProps>(({ info }) => info ? (
|
||||
<Tooltip overlayInnerStyle={{ width: '400px' }} title={
|
||||
<Grid>
|
||||
{(Object.keys(info) as Array<keyof TelemetryInfoDto>).map((key, i) => (
|
||||
<>
|
||||
<GridItem row={i+1} col={1}>{lables[key] ?? key}:</GridItem>
|
||||
<GridItem row={i+1} col={2}>{info[key]}</GridItem>
|
||||
</>
|
||||
))}
|
||||
</Grid>
|
||||
}>
|
||||
{getTelemetryLabel(info)}
|
||||
export const TelemetryView = memo<TelemetryViewProps>(({ telemetry }) => telemetry?.info ? (
|
||||
<Tooltip
|
||||
overlayInnerStyle={{ width: '400px' }}
|
||||
title={
|
||||
<Grid>
|
||||
{(Object.keys(telemetry.info) as Array<keyof TelemetryInfoDto>).map((key, i) => (
|
||||
<Fragment key={i}>
|
||||
<GridItem row={i+1} col={1}>{lables[key] ?? key}:</GridItem>
|
||||
<GridItem row={i+1} col={2}>{telemetry.info?.[key]}</GridItem>
|
||||
</Fragment>
|
||||
))}
|
||||
</Grid>
|
||||
}
|
||||
>
|
||||
{getTelemetryLabel(telemetry)}
|
||||
</Tooltip>
|
||||
) : (
|
||||
<Tooltip title={'нет данных'}>-</Tooltip>
|
||||
<Tooltip title={'нет данных'}>{getTelemetryLabel()}</Tooltip>
|
||||
))
|
||||
|
@ -1,9 +1,11 @@
|
||||
export type { CompanyViewProps } from './CompanyView'
|
||||
export type { MarkViewProps } from './MarkView'
|
||||
export type { PermissionViewProps } from './PermissionView'
|
||||
export type { TelemetryViewProps } from './TelemetryView'
|
||||
export type { UserViewProps } from './UserView'
|
||||
|
||||
export { CompanyView } from './CompanyView'
|
||||
export { MarkView } from './MarkView'
|
||||
export { TelemetryView } from './TelemetryView'
|
||||
export { PermissionView } from './PermissionView'
|
||||
export { TelemetryView, getTelemetryLabel } from './TelemetryView'
|
||||
export { UserView } from './UserView'
|
||||
|
@ -1,101 +1,50 @@
|
||||
import { Button, Modal } from 'antd'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { AdminUserRoleService } from '../../services/api'
|
||||
import { Select, Tag } from 'antd'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { PermissionView } from '../../components/Views'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import { EditableTable, makeActionHandler, makeColumn, makeSelectColumn } from '../../components/Table'
|
||||
import { AdminPermissionService, AdminUserRoleService } from '../../services/api'
|
||||
import { arrayOrDefault } from '../../utils'
|
||||
|
||||
export const toHexString = (num, size) => '0x' + ('0'.repeat(size) + num.toString(16).toUpperCase()).slice(-size)
|
||||
const PermissionTag = memo(({ permissions, value, onChange }) => {
|
||||
const [options, setOptions] = useState([])
|
||||
|
||||
const columns = [
|
||||
makeColumn('Имена прав', 'name', {
|
||||
width: 400,
|
||||
editable: true,
|
||||
formItemRules: [{
|
||||
required: true,
|
||||
message: 'Пожалуйста, введите имя права'
|
||||
}],
|
||||
}),
|
||||
]
|
||||
useEffect(() => {
|
||||
setOptions(permissions.map((elm) => ({ key: Date.now(), value: `${elm.id}`, label: elm.name })))
|
||||
}, [permissions])
|
||||
|
||||
export const RolePermissions = ({ value, onChange }) => {
|
||||
const [isModalVisible, setIsModalVisible] = useState(false)
|
||||
const [list, setList] = useState([])
|
||||
console.log({ permissions, value })
|
||||
|
||||
const save = () => {
|
||||
const newValue = list.map((value) => value.name)
|
||||
if(!onChange(newValue))
|
||||
setIsModalVisible(false)
|
||||
const onSelectChange = (values) => {
|
||||
const arr = values.map((id) => permissions.find((elm) => `${elm.id}` === id))
|
||||
onChange?.(arr)
|
||||
}
|
||||
|
||||
const add = (permission) => {
|
||||
permission.key = Date.now()
|
||||
setList((prevList) => {
|
||||
if (!prevList) prevList = []
|
||||
prevList.push(permission)
|
||||
return prevList
|
||||
})
|
||||
}
|
||||
|
||||
const edit = (permission) => {
|
||||
if (!permission.key) return
|
||||
const idx = list.findIndex(v => v.key === permission.key)
|
||||
if (idx < 0) return
|
||||
setList((prevList) => {
|
||||
prevList[idx] = permission
|
||||
return prevList
|
||||
})
|
||||
}
|
||||
|
||||
const remove = (permission) => {
|
||||
if (!permission.key) return
|
||||
const idx = list.findIndex(v => v.key === permission.key)
|
||||
if (idx < 0) return
|
||||
setList((prevList) => {
|
||||
prevList.splice(idx, 1)
|
||||
return prevList
|
||||
})
|
||||
}
|
||||
const selectValue = value?.map((val) => `${val.id}`)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button type={'link'} onClick={() => setIsModalVisible(true)}>Редактировать</Button>
|
||||
<Modal
|
||||
title={'Права доступа'}
|
||||
centered
|
||||
visible={isModalVisible}
|
||||
width={750}
|
||||
onCancel={() => setIsModalVisible(false)}
|
||||
onOk={save}
|
||||
okText={'Сохранить'}
|
||||
>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={columns}
|
||||
dataSource={list}
|
||||
onRowAdd={add}
|
||||
onRowEdit={edit}
|
||||
onRowDelete={remove}
|
||||
/>
|
||||
</Modal>
|
||||
</>
|
||||
<Select
|
||||
showSearch
|
||||
mode={'tags'}
|
||||
options={options}
|
||||
value={selectValue}
|
||||
onChange={onSelectChange}
|
||||
/>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
export const RoleController = () => {
|
||||
const [permissions, setPermissions] = useState([])
|
||||
const [roles, setRoles] = useState([])
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
const [columns, setColumns] = useState([])
|
||||
|
||||
const updateTable = () => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const roles = await AdminUserRoleService.getAll()
|
||||
setRoles(roles)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список прав`
|
||||
)
|
||||
const loadRoles = async () => {
|
||||
const roles = await AdminUserRoleService.getAll()
|
||||
setRoles(Array.isArray(roles) ? roles : [])
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const options = roles?.map((r) => ({ value: r.id, label: r.caption })) ?? []
|
||||
@ -104,23 +53,39 @@ export const RoleController = () => {
|
||||
makeSelectColumn('Роль-родитель', 'idParent', options, options[0], {
|
||||
width: 200,
|
||||
editable: true
|
||||
}),
|
||||
}, { allowClear: true }),
|
||||
makeColumn('Права доступа', 'permissions', {
|
||||
width: 200,
|
||||
editable: true,
|
||||
input: <RolePermissions />,
|
||||
render: (permissions) => permissions?.join(', ') ?? '',
|
||||
})
|
||||
input: <PermissionTag permissions={permissions} />,
|
||||
render: (item) => item?.map((elm) => (
|
||||
<Tag key={elm.name} color={'blue'}>
|
||||
<PermissionView info={elm} />
|
||||
</Tag>
|
||||
)) ?? '-',
|
||||
}),
|
||||
])
|
||||
}, [roles])
|
||||
}, [roles, permissions])
|
||||
|
||||
useEffect(updateTable, [])
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const permissions = await AdminPermissionService.getAll()
|
||||
setPermissions(arrayOrDefault(permissions))
|
||||
await loadRoles()
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список прав`
|
||||
), [])
|
||||
|
||||
const handlerProps = {
|
||||
service: AdminUserRoleService,
|
||||
setLoader: setShowLoader,
|
||||
errorMsg: `Не удалось выполнить операцию`,
|
||||
onComplete: updateTable
|
||||
onComplete: async () => invokeWebApiWrapperAsync(
|
||||
loadRoles,
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список прав`
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -1,66 +1,28 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { UserSwitchOutlined } from '@ant-design/icons'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { EditableTable, makeColumn, makeSelectColumn, makeActionHandler, makeStringSorter, makeNumericSorter } from '../../components/Table'
|
||||
import { AdminCompanyService, AdminUserService } from '../../services/api'
|
||||
import { createLoginRules, nameRules, phoneRules, emailRules } from '../../utils/validationRules'
|
||||
import { arrayOrDefault } from '../../utils'
|
||||
import { Button } from 'antd'
|
||||
import { ChangePassword } from '../../components/ChangePassword'
|
||||
|
||||
export default function UserController() {
|
||||
const [companies, setCompanies] = useState([])
|
||||
const [users, setUsers] = useState([])
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
const [columns, setColumns] = useState([])
|
||||
const [selectedId, setSelectedId] = useState(null)
|
||||
|
||||
const userColumns = [
|
||||
makeColumn('Логин', 'login', {
|
||||
editable: true,
|
||||
formItemRules: [
|
||||
{ required: true },
|
||||
...createLoginRules,
|
||||
() => ({
|
||||
validator(_, value) {
|
||||
if (!value || users.findIndex((user) => user.login === value) < 0)
|
||||
return Promise.resolve()
|
||||
return Promise.reject(new Error('Логин уже занят!'))
|
||||
}
|
||||
})
|
||||
],
|
||||
sorter: makeStringSorter('login'),
|
||||
}),
|
||||
makeColumn('Фамилия', 'surname', {
|
||||
editable: true,
|
||||
formItemRules: [{ required: true }, ...nameRules],
|
||||
sorter: makeStringSorter('surname'),
|
||||
}),
|
||||
makeColumn('Имя', 'name', {
|
||||
editable: true,
|
||||
formItemRules: nameRules,
|
||||
sorter: makeStringSorter('name'),
|
||||
}),
|
||||
makeColumn('Отчество', 'patronymic', {
|
||||
editable: true,
|
||||
formItemRules: nameRules,
|
||||
sorter: makeStringSorter('patronymic'),
|
||||
}),
|
||||
makeColumn('E-mail', 'email', {
|
||||
editable: true,
|
||||
formItemRules: [{ required: true }, ...emailRules],
|
||||
sorter: makeStringSorter('email'),
|
||||
}),
|
||||
makeColumn('Номер телефона', 'phone', {
|
||||
editable: true,
|
||||
formItemRules: phoneRules,
|
||||
sorter: makeStringSorter('phone'),
|
||||
}),
|
||||
makeColumn('Должность', 'position', {
|
||||
editable: true,
|
||||
sorter: makeStringSorter('position'),
|
||||
}),
|
||||
makeSelectColumn('Компания', 'idCompany', companies, '--', {
|
||||
editable: true,
|
||||
sorter: makeNumericSorter('idCompany'),
|
||||
}),
|
||||
]
|
||||
const additionalButtons = (record, editingKey) => (
|
||||
<Button
|
||||
icon={<UserSwitchOutlined />}
|
||||
onClick={() => setSelectedId(record.id)}
|
||||
title={'Сменить пароль'}
|
||||
disabled={editingKey !== ''}
|
||||
/>
|
||||
)
|
||||
|
||||
const updateTable = () => invokeWebApiWrapperAsync(
|
||||
async() => {
|
||||
@ -75,14 +37,65 @@ export default function UserController() {
|
||||
async () => {
|
||||
let companies = arrayOrDefault(await AdminCompanyService.getAll())
|
||||
companies = companies?.map((company) => ({ value: company.id, label: company.caption }))
|
||||
setCompanies(companies)
|
||||
|
||||
const users = arrayOrDefault(await AdminUserService.getAll())
|
||||
setUsers(users)
|
||||
|
||||
setColumns([
|
||||
makeColumn('Логин', 'login', {
|
||||
editable: true,
|
||||
formItemRules: [
|
||||
{ required: true },
|
||||
...createLoginRules,
|
||||
() => ({
|
||||
validator(_, value) {
|
||||
if (!value || users.findIndex((user) => user.login === value) < 0)
|
||||
return Promise.resolve()
|
||||
return Promise.reject(new Error('Логин уже занят!'))
|
||||
}
|
||||
})
|
||||
],
|
||||
sorter: makeStringSorter('login'),
|
||||
}),
|
||||
makeColumn('Фамилия', 'surname', {
|
||||
editable: true,
|
||||
formItemRules: [{ required: true }, ...nameRules],
|
||||
sorter: makeStringSorter('surname'),
|
||||
}),
|
||||
makeColumn('Имя', 'name', {
|
||||
editable: true,
|
||||
formItemRules: nameRules,
|
||||
sorter: makeStringSorter('name'),
|
||||
}),
|
||||
makeColumn('Отчество', 'patronymic', {
|
||||
editable: true,
|
||||
formItemRules: nameRules,
|
||||
sorter: makeStringSorter('patronymic'),
|
||||
}),
|
||||
makeColumn('E-mail', 'email', {
|
||||
editable: true,
|
||||
formItemRules: [{ required: true }, ...emailRules],
|
||||
sorter: makeStringSorter('email'),
|
||||
}),
|
||||
makeColumn('Номер телефона', 'phone', {
|
||||
editable: true,
|
||||
formItemRules: phoneRules,
|
||||
sorter: makeStringSorter('phone'),
|
||||
}),
|
||||
makeColumn('Должность', 'position', {
|
||||
editable: true,
|
||||
sorter: makeStringSorter('position'),
|
||||
}),
|
||||
makeSelectColumn('Компания', 'idCompany', companies, '--', {
|
||||
editable: true,
|
||||
sorter: makeNumericSorter('idCompany'),
|
||||
}),
|
||||
])
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список компаний`
|
||||
), [])
|
||||
|
||||
useEffect(updateTable, [])
|
||||
|
||||
const handlerProps = {
|
||||
service: AdminUserService,
|
||||
setLoader: setShowLoader,
|
||||
@ -91,17 +104,27 @@ export default function UserController() {
|
||||
}
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={userColumns}
|
||||
dataSource={users}
|
||||
onRowAdd={makeActionHandler('insert', handlerProps)}
|
||||
onRowEdit={makeActionHandler('update', handlerProps)}
|
||||
onRowDelete={makeActionHandler('delete', handlerProps)}
|
||||
pagination={{ defaultPageSize: 14 }}
|
||||
<>
|
||||
<LoaderPortal show={showLoader}>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={columns}
|
||||
dataSource={users}
|
||||
onRowAdd={makeActionHandler('insert', handlerProps)}
|
||||
onRowEdit={makeActionHandler('update', handlerProps)}
|
||||
onRowDelete={makeActionHandler('delete', handlerProps)}
|
||||
additionalButtons={additionalButtons}
|
||||
buttonsWidth={120}
|
||||
pagination={{ defaultPageSize: 14 }}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
<ChangePassword
|
||||
userId={selectedId}
|
||||
visible={selectedId > 0}
|
||||
onCancel={() => setSelectedId(null)}
|
||||
onOk={() => setSelectedId(null)}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -1,22 +1,25 @@
|
||||
import { Select } from 'antd'
|
||||
import { memo, useEffect, useState } from 'react'
|
||||
import { TelemetryView } from '../../components/Views'
|
||||
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import { getTelemetryLabel } from '../../components/Views/TelemetryView'
|
||||
import { TelemetryView, getTelemetryLabel } from '../../components/Views'
|
||||
import {
|
||||
EditableTable,
|
||||
makeColumn,
|
||||
makeSelectColumn,
|
||||
makeActionHandler,
|
||||
makeStringSorter,
|
||||
makeNumericSorter
|
||||
makeNumericSorter,
|
||||
makeTagColumn
|
||||
} from '../../components/Table'
|
||||
import {
|
||||
AdminClusterService,
|
||||
AdminCompanyService,
|
||||
AdminTelemetryService,
|
||||
AdminWellService
|
||||
} from '../../services/api'
|
||||
import { arrayOrDefault } from '../../utils'
|
||||
|
||||
const wellTypes = [
|
||||
{ value: 1, label: 'Наклонно-направленная' },
|
||||
@ -29,55 +32,27 @@ const TelemetrySelect = memo(({ telemetry, value, onChange }) => {
|
||||
useEffect(() => {
|
||||
const options = telemetry.map((row) => ({
|
||||
value: row.id,
|
||||
label: getTelemetryLabel(row.info)
|
||||
label: getTelemetryLabel(row)
|
||||
}))
|
||||
setOptions(options)
|
||||
}, [telemetry])
|
||||
|
||||
const onSelectChange = (id) => {
|
||||
const value = telemetry.find((row) => row.id === id)
|
||||
onChange?.(value)
|
||||
onChange?.(telemetry.find((row) => row.id === id))
|
||||
}
|
||||
|
||||
return <Select options={options} value={value?.id} onChange={onSelectChange}/>
|
||||
})
|
||||
|
||||
export default function WellController() {
|
||||
const [clusters, setClusters] = useState([])
|
||||
const [columns, setColumns] = useState([])
|
||||
const [wells, setWells] = useState([])
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
const [telemetry, setTelemetry] = useState([])
|
||||
|
||||
const wellColumns = [
|
||||
makeSelectColumn('Куст', 'idCluster', clusters, '--', {
|
||||
width: 200,
|
||||
editable: true,
|
||||
sorter: makeNumericSorter('idCluster'),
|
||||
}),
|
||||
makeColumn('Название', 'caption', {
|
||||
width: 200,
|
||||
editable: true,
|
||||
sorter: makeStringSorter('caption'),
|
||||
}),
|
||||
makeSelectColumn('Тип', 'idWellType', wellTypes, '--', {
|
||||
width: 150,
|
||||
editable: true,
|
||||
sorter: makeNumericSorter('idWellType'),
|
||||
}),
|
||||
makeColumn('Широта', 'latitude', { width: 150, editable: true }),
|
||||
makeColumn('Долгота', 'longitude', { width: 150, editable: true }),
|
||||
makeColumn('Телеметрия', 'telemetry', {
|
||||
width: 150,
|
||||
editable: true,
|
||||
render: (telemetry) => <TelemetryView info={telemetry?.info} />,
|
||||
input: <TelemetrySelect telemetry={telemetry}/>,
|
||||
})
|
||||
]
|
||||
|
||||
const updateTable = () => invokeWebApiWrapperAsync(
|
||||
const updateTable = async () => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const wells = await AdminWellService.getAll()
|
||||
setWells(wells)
|
||||
setWells(arrayOrDefault(wells))
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список скважин`
|
||||
@ -85,13 +60,41 @@ export default function WellController() {
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
const telemetry = await AdminTelemetryService.getAll()
|
||||
setTelemetry(telemetry)
|
||||
const companies = arrayOrDefault(await AdminCompanyService.getAll())
|
||||
const telemetry = arrayOrDefault(await AdminTelemetryService.getAll())
|
||||
let clusters = arrayOrDefault(await AdminClusterService.getAll())
|
||||
clusters = clusters.map((cluster) => ({ value: cluster.id, label: cluster.caption }))
|
||||
|
||||
setColumns([
|
||||
makeSelectColumn('Куст', 'idCluster', clusters, '--', {
|
||||
width: 200,
|
||||
editable: true,
|
||||
sorter: makeNumericSorter('idCluster'),
|
||||
}),
|
||||
makeColumn('Название', 'caption', {
|
||||
width: 200,
|
||||
editable: true,
|
||||
sorter: makeStringSorter('caption'),
|
||||
}),
|
||||
makeSelectColumn('Тип', 'idWellType', wellTypes, '--', {
|
||||
width: 150,
|
||||
editable: true,
|
||||
sorter: makeNumericSorter('idWellType'),
|
||||
}),
|
||||
makeColumn('Широта', 'latitude', { width: 150, editable: true }),
|
||||
makeColumn('Долгота', 'longitude', { width: 150, editable: true }),
|
||||
makeColumn('Телеметрия', 'telemetry', {
|
||||
width: 150,
|
||||
editable: true,
|
||||
render: (telemetry) => <TelemetryView telemetry={telemetry} />,
|
||||
input: <TelemetrySelect telemetry={telemetry}/>,
|
||||
}),
|
||||
makeTagColumn('Компании', 'companies', companies, 'id', 'caption', {
|
||||
width: 400
|
||||
}),
|
||||
])
|
||||
|
||||
await updateTable()
|
||||
let clusters = await AdminClusterService.getAll()
|
||||
clusters = clusters?.map((cluster) => ({ value: cluster.id, label: cluster.caption }))
|
||||
setClusters(clusters ?? [])
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список кустов`
|
||||
@ -109,7 +112,7 @@ export default function WellController() {
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={wellColumns}
|
||||
columns={columns}
|
||||
dataSource={wells}
|
||||
onRowAdd={makeActionHandler('insert', handlerProps)}
|
||||
onRowEdit={makeActionHandler('update', handlerProps)}
|
||||
|
@ -39,7 +39,7 @@ export const AdminPanel = () => {
|
||||
<Link to={`${rootPath}/role`}>Роли</Link>
|
||||
</PrivateMenuItem>
|
||||
<PrivateMenuItem key={'permission'} permission={'permission_editor'}>
|
||||
<Link to={`${rootPath}/permission`}>Права</Link>
|
||||
<Link to={`${rootPath}/permission`}>Разрешения</Link>
|
||||
</PrivateMenuItem>
|
||||
<PrivateMenuItem key={'visit_log'}>
|
||||
<Link to={`${rootPath}/visit_log`}>Журнал посещений</Link>
|
||||
|
Loading…
Reference in New Issue
Block a user