Добавлена view для разрешений

К отображению телеметрии добавлено id
Актуализирован редактор ролей
Добавлена кнопка смены пароля
Окно смены пароля вынесено в компонент
This commit is contained in:
Александр Сироткин 2021-12-27 18:06:26 +05:00
parent 1b17ee2cfd
commit e084727c72
11 changed files with 380 additions and 262 deletions

View 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>
)
})

View File

@ -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>
),
}

View File

@ -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

View File

@ -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)}
/>
</>
)
}

View 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

View File

@ -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>
))

View File

@ -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'

View File

@ -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 (

View File

@ -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>
</>
)
}

View File

@ -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)}

View File

@ -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>