Добавлено View для роли

Добавлен столбец роли на страницу admin/user
Проверка уникальности логина отключена до исправления
Добавлена кнопка дублирования скважины
This commit is contained in:
Александр Сироткин 2021-12-28 14:34:12 +05:00
parent e084727c72
commit f228b64a6f
9 changed files with 173 additions and 82 deletions

View File

@ -209,34 +209,42 @@ export const makeSelectColumn = <T extends unknown = string>(
other?: columnPropsOther, other?: columnPropsOther,
selectOther?: SelectProps<SelectValue> selectOther?: SelectProps<SelectValue>
) => makeColumn(title, dataIndex, { ) => makeColumn(title, dataIndex, {
...other,
input: <Select options={options} {...selectOther}/>, input: <Select options={options} {...selectOther}/>,
render: (value) => options?.find(option => option?.value === value)?.label ?? defaultValue ?? value ?? '--', render: (value) => {
...other const item = options?.find(option => option?.value === value)
return other?.render?.(item?.value) ?? item?.label ?? defaultValue ?? value ?? '--'
}
}) })
const makeTagInput = <T extends Record<string, any>>(id_key: string, value_key: string) => memo<{ const makeTagInput = <T extends Record<string, any>>(value_key: string, label_key: string) => memo<{
options: T[], options: T[],
value?: T[], value?: T[],
onChange?: (values: T[]) => void onChange?: (values: T[]) => void
}>(({ options, value, onChange }) => { }>(({ options, value, onChange }) => {
const [selectOptions, setSelectOptions] = useState<OptionsType>([]) const [selectOptions, setSelectOptions] = useState<OptionsType>([])
const [selectedValue, setSelectedValue] = useState<SelectValue>([])
useEffect(() => { useEffect(() => {
setSelectOptions(options.map((elm) => ({ setSelectOptions(options.map((elm) => ({
value: elm[id_key], value: String(elm[value_key]),
label: elm[value_key] label: elm[label_key],
}))) })))
}, [options]) }, [options])
useEffect(() => {
setSelectedValue(value?.map((elm) => String(elm[value_key])) ?? [])
}, [value])
const onSelectChange = (rawValues?: SelectValue) => { const onSelectChange = (rawValues?: SelectValue) => {
let values: string[] = [] let values: any[] = []
if (typeof rawValues === 'string') if (typeof rawValues === 'string')
values = rawValues.split(',') values = rawValues.split(',')
else if (typeof rawValues === 'number') else if (Array.isArray(rawValues))
values = [`${rawValues}`] values = rawValues
const objectValues: T[] = values.reduce((out: T[], id: string) => { const objectValues: T[] = values.reduce((out: T[], value: string) => {
const res = options.find((option) => `${option[id_key]}` === id) const res = options.find((option) => String(option[value_key]) === String(value))
if (res) out.push(res) if (res) out.push(res)
return out return out
}, []) }, [])
@ -248,7 +256,7 @@ const makeTagInput = <T extends Record<string, any>>(id_key: string, value_key:
<Select <Select
mode={'tags'} mode={'tags'}
options={selectOptions} options={selectOptions}
value={value?.join(',')} value={selectedValue}
onChange={onSelectChange} onChange={onSelectChange}
/> />
) )
@ -266,7 +274,7 @@ export const makeTagColumn = <T extends Record<string, any>>(
return makeColumn(title, dataIndex, { return makeColumn(title, dataIndex, {
...other, ...other,
render: (item?: T[]) => item?.map((elm: T) => <Tag key={elm[label_key]} color='blue'>{elm[label_key]}</Tag>) ?? '-', render: (item?: T[]) => item?.map((elm: T) => <Tag key={elm[label_key]} color={'blue'}>{other?.render?.(elm) ?? elm[label_key]}</Tag>) ?? '-',
input: <InputComponent options={options} />, input: <InputComponent options={options} />,
}) })
} }

View File

@ -1,6 +1,7 @@
import { memo } from 'react' import { memo } from 'react'
import { Tooltip } from 'antd' import { Tooltip } from 'antd'
import { BankOutlined } from '@ant-design/icons' import { BankOutlined } from '@ant-design/icons'
import { CompanyDto } from '../../services/api' import { CompanyDto } from '../../services/api'
import { Grid, GridItem } from '../Grid' import { Grid, GridItem } from '../Grid'
@ -11,7 +12,7 @@ export type CompanyViewProps = {
export const CompanyView = memo<CompanyViewProps>(({ company }) => company ? ( export const CompanyView = memo<CompanyViewProps>(({ company }) => company ? (
<Tooltip title={ <Tooltip title={
<Grid style={{ columnGap: '8px' }}> <Grid style={{ columnGap: '8px' }}>
<GridItem row={1} col={1}>тип:</GridItem> <GridItem row={1} col={1}>Тип:</GridItem>
<GridItem row={1} col={2}>{company?.companyTypeCaption}</GridItem> <GridItem row={1} col={2}>{company?.companyTypeCaption}</GridItem>
</Grid> </Grid>
}> }>

View File

@ -0,0 +1,42 @@
import { memo } from 'react'
import { Tag, Tooltip } from 'antd'
import { UserRoleDto } from '../../services/api'
import { Grid, GridItem } from '../Grid'
import PermissionView from './PermissionView'
export type RoleViewProps = {
role?: UserRoleDto
parentRole?: UserRoleDto
}
export const RoleView = memo<RoleViewProps>(({ role, parentRole }) => role ? (
<Tooltip
overlayInnerStyle={{ width: '400px' }}
title={
<Grid>
<GridItem row={1} col={1}>Название:</GridItem>
<GridItem row={1} col={2}>{role.caption}</GridItem>
<GridItem row={2} col={1}>Роль-родитель:</GridItem>
<GridItem row={2} col={2}>{parentRole?.caption ?? 'Отсутствует'}</GridItem>
<GridItem row={3} col={1}>Тип:</GridItem>
<GridItem row={3} col={2}>{role.idType}</GridItem>
<GridItem row={4} col={1}>Разрешения:</GridItem>
<GridItem row={5} col={1} colSpan={3}>
{role.permissions?.map((permission, i) => (
<Tag key={i} color={'blue'}>
<PermissionView info={permission} />
</Tag>
)) ?? '-'}
</GridItem>
</Grid>
}
>
{role.caption}
</Tooltip>
) : (
<Tooltip title={'нет данных'}>-</Tooltip>
))

View File

@ -3,7 +3,7 @@ import { Tooltip } from 'antd'
import { TelemetryDto, TelemetryInfoDto } from '../../services/api' import { TelemetryDto, TelemetryInfoDto } from '../../services/api'
import { Grid, GridItem } from '../Grid' import { Grid, GridItem } from '../Grid'
const lables: { [labelKey: string]: string } = { const lables: Record<string, string> = {
timeZoneId: 'Временная зона', timeZoneId: 'Временная зона',
timeZoneOffsetTotalHours: 'Сдвиг временной зоны', timeZoneOffsetTotalHours: 'Сдвиг временной зоны',
drillingStartDate: 'Начало бурения', drillingStartDate: 'Начало бурения',

View File

@ -3,6 +3,7 @@ import { Tooltip } from 'antd'
import { UserOutlined } from '@ant-design/icons' import { UserOutlined } from '@ant-design/icons'
import { UserDto } from '../../services/api' import { UserDto } from '../../services/api'
import { Grid, GridItem } from '../Grid' import { Grid, GridItem } from '../Grid'
import { CompanyView } from './CompanyView'
export type UserViewProps = { export type UserViewProps = {
user?: UserDto user?: UserDto
@ -21,7 +22,9 @@ export const UserView = memo<UserViewProps>(({ user }) => user ? (
<GridItem row={3} col={2}>{user?.patronymic}</GridItem> <GridItem row={3} col={2}>{user?.patronymic}</GridItem>
<GridItem row={4} col={1}>Компания:</GridItem> <GridItem row={4} col={1}>Компания:</GridItem>
<GridItem row={4} col={2}>{user?.company?.caption}</GridItem> <GridItem row={4} col={2}>
<CompanyView company={user?.company}/>
</GridItem>
</Grid> </Grid>
)}> )}>
<UserOutlined style={{ marginRight: 8 }}/> <UserOutlined style={{ marginRight: 8 }}/>

View File

@ -1,11 +1,13 @@
export type { CompanyViewProps } from './CompanyView'
export type { MarkViewProps } from './MarkView'
export type { PermissionViewProps } from './PermissionView' export type { PermissionViewProps } from './PermissionView'
export type { TelemetryViewProps } from './TelemetryView' export type { TelemetryViewProps } from './TelemetryView'
export type { CompanyViewProps } from './CompanyView'
export type { MarkViewProps } from './MarkView'
export type { RoleViewProps } from './RoleView'
export type { UserViewProps } from './UserView' export type { UserViewProps } from './UserView'
export { CompanyView } from './CompanyView'
export { MarkView } from './MarkView'
export { PermissionView } from './PermissionView' export { PermissionView } from './PermissionView'
export { TelemetryView, getTelemetryLabel } from './TelemetryView' export { TelemetryView, getTelemetryLabel } from './TelemetryView'
export { UserView } from './UserView' export { CompanyView } from './CompanyView'
export { MarkView } from './MarkView'
export { RoleView } from './RoleView'
export { UserView } from './UserView'

View File

@ -1,41 +1,13 @@
import { Select, Tag } from 'antd'
import { memo, useEffect, useState } from 'react' import { memo, useEffect, useState } from 'react'
import LoaderPortal from '../../components/LoaderPortal' import LoaderPortal from '../../components/LoaderPortal'
import { PermissionView } from '../../components/Views' import { PermissionView } from '../../components/Views'
import { invokeWebApiWrapperAsync } from '../../components/factory' import { invokeWebApiWrapperAsync } from '../../components/factory'
import { EditableTable, makeActionHandler, makeColumn, makeSelectColumn } from '../../components/Table' import { EditableTable, makeActionHandler, makeColumn, makeSelectColumn, makeTagColumn } from '../../components/Table'
import { AdminPermissionService, AdminUserRoleService } from '../../services/api' import { AdminPermissionService, AdminUserRoleService } from '../../services/api'
import { arrayOrDefault } from '../../utils' import { arrayOrDefault } from '../../utils'
const PermissionTag = memo(({ permissions, value, onChange }) => { export const RoleController = memo(() => {
const [options, setOptions] = useState([])
useEffect(() => {
setOptions(permissions.map((elm) => ({ key: Date.now(), value: `${elm.id}`, label: elm.name })))
}, [permissions])
console.log({ permissions, value })
const onSelectChange = (values) => {
const arr = values.map((id) => permissions.find((elm) => `${elm.id}` === id))
onChange?.(arr)
}
const selectValue = value?.map((val) => `${val.id}`)
return (
<Select
showSearch
mode={'tags'}
options={options}
value={selectValue}
onChange={onSelectChange}
/>
)
})
export const RoleController = () => {
const [permissions, setPermissions] = useState([]) const [permissions, setPermissions] = useState([])
const [roles, setRoles] = useState([]) const [roles, setRoles] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
@ -54,15 +26,10 @@ export const RoleController = () => {
width: 200, width: 200,
editable: true editable: true
}, { allowClear: true }), }, { allowClear: true }),
makeColumn('Права доступа', 'permissions', { makeTagColumn('Права доступа', 'permissions', permissions, 'id', 'name', {
width: 200, width: 200,
editable: true, editable: true,
input: <PermissionTag permissions={permissions} />, render: (permission) => <PermissionView info={permission} />,
render: (item) => item?.map((elm) => (
<Tag key={elm.name} color={'blue'}>
<PermissionView info={elm} />
</Tag>
)) ?? '-',
}), }),
]) ])
}, [roles, permissions]) }, [roles, permissions])
@ -101,6 +68,6 @@ export const RoleController = () => {
/> />
</LoaderPortal> </LoaderPortal>
) )
} })
export default RoleController export default RoleController

View File

@ -1,13 +1,47 @@
import { useEffect, useState } from 'react' import { Button, Select, Tag } from 'antd'
import { UserSwitchOutlined } from '@ant-design/icons' import { UserSwitchOutlined } from '@ant-design/icons'
import { invokeWebApiWrapperAsync } from '../../components/factory' import { memo, useEffect, useState } from 'react'
import { RoleView } from '../../components/Views'
import LoaderPortal from '../../components/LoaderPortal' import LoaderPortal from '../../components/LoaderPortal'
import { EditableTable, makeColumn, makeSelectColumn, makeActionHandler, makeStringSorter, makeNumericSorter } from '../../components/Table' import { ChangePassword } from '../../components/ChangePassword'
import { AdminCompanyService, AdminUserService } from '../../services/api' import { invokeWebApiWrapperAsync } from '../../components/factory'
import {
EditableTable,
makeColumn,
makeSelectColumn,
makeActionHandler,
makeStringSorter,
makeNumericSorter
} from '../../components/Table'
import { AdminCompanyService, AdminUserRoleService, AdminUserService } from '../../services/api'
import { createLoginRules, nameRules, phoneRules, emailRules } from '../../utils/validationRules' import { createLoginRules, nameRules, phoneRules, emailRules } from '../../utils/validationRules'
import { arrayOrDefault } from '../../utils' import { arrayOrDefault } from '../../utils'
import { Button } from 'antd'
import { ChangePassword } from '../../components/ChangePassword' const RoleTag = memo(({ roles, value, onChange }) => {
const [options, setOptions] = useState([])
useEffect(() => {
setOptions(roles.map((elm) => ({
key: Date.now(),
value: `${elm.caption}`,
label: elm.caption
})))
}, [roles])
console.log(value)
return (
<Select
allowClear
showSearch
mode={'tags'}
options={options}
value={value ?? []}
onChange={onChange}
/>
)
})
export default function UserController() { export default function UserController() {
const [users, setUsers] = useState([]) const [users, setUsers] = useState([])
@ -35,25 +69,30 @@ export default function UserController() {
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
let companies = arrayOrDefault(await AdminCompanyService.getAll()) const roles = arrayOrDefault(await AdminUserRoleService.getAll())
companies = companies?.map((company) => ({ value: company.id, label: company.caption })) const companies = arrayOrDefault(await AdminCompanyService.getAll()).map((company) => ({
value: company.id,
label: company.caption
}))
const users = arrayOrDefault(await AdminUserService.getAll()) const users = arrayOrDefault(await AdminUserService.getAll())
setUsers(users) setUsers(users)
setColumns([ setColumns([
makeColumn('Логин', 'login', { makeColumn('Логин', 'login', {
editable: true, editable: true,
formItemRules: [ formItemRules: [
{ required: true }, { required: true },
...createLoginRules, ...createLoginRules,
() => ({ // () => ({
validator(_, value) { // validator(_, value) {
if (!value || users.findIndex((user) => user.login === value) < 0) // if (!value || users.findIndex((user) => user.login === value) < 0)
return Promise.resolve() // return Promise.resolve()
return Promise.reject(new Error('Логин уже занят!')) // return Promise.reject(new Error('Логин уже занят!'))
} // }
}) // })
// TODO: Для проверки уникальности логина необходимо исключить из выборки логин выбранного пользователя
], ],
sorter: makeStringSorter('login'), sorter: makeStringSorter('login'),
}), }),
@ -86,10 +125,19 @@ export default function UserController() {
editable: true, editable: true,
sorter: makeStringSorter('position'), sorter: makeStringSorter('position'),
}), }),
makeColumn('Роли', 'roleNames', {
editable: true,
input: <RoleTag roles={roles} />,
render: (item) => item?.map((elm) => (
<Tag key={elm} color={'blue'}>
<RoleView role={roles.find((role) => role.caption === elm)} />
</Tag>
)) ?? '-'
}),
makeSelectColumn('Компания', 'idCompany', companies, '--', { makeSelectColumn('Компания', 'idCompany', companies, '--', {
editable: true, editable: true,
sorter: makeNumericSorter('idCompany'), sorter: makeNumericSorter('idCompany'),
}), })
]) ])
}, },
setShowLoader, setShowLoader,
@ -114,7 +162,7 @@ export default function UserController() {
onRowAdd={makeActionHandler('insert', handlerProps)} onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('update', handlerProps)} onRowEdit={makeActionHandler('update', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)} onRowDelete={makeActionHandler('delete', handlerProps)}
additionalButtons={additionalButtons} // additionalButtons={additionalButtons}
buttonsWidth={120} buttonsWidth={120}
pagination={{ defaultPageSize: 14 }} pagination={{ defaultPageSize: 14 }}
/> />

View File

@ -1,9 +1,10 @@
import { Select } from 'antd' import { Button, Select } from 'antd'
import { ForkOutlined } from '@ant-design/icons'
import { memo, useEffect, useState } from 'react' import { memo, useEffect, useState } from 'react'
import LoaderPortal from '../../components/LoaderPortal' import LoaderPortal from '../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../components/factory' import { invokeWebApiWrapperAsync } from '../../components/factory'
import { TelemetryView, getTelemetryLabel } from '../../components/Views' import { TelemetryView, getTelemetryLabel, CompanyView } from '../../components/Views'
import { import {
EditableTable, EditableTable,
makeColumn, makeColumn,
@ -11,13 +12,13 @@ import {
makeActionHandler, makeActionHandler,
makeStringSorter, makeStringSorter,
makeNumericSorter, makeNumericSorter,
makeTagColumn makeTagColumn,
} from '../../components/Table' } from '../../components/Table'
import { import {
AdminClusterService, AdminClusterService,
AdminCompanyService, AdminCompanyService,
AdminTelemetryService, AdminTelemetryService,
AdminWellService AdminWellService,
} from '../../services/api' } from '../../services/api'
import { arrayOrDefault } from '../../utils' import { arrayOrDefault } from '../../utils'
@ -58,12 +59,27 @@ export default function WellController() {
`Не удалось загрузить список скважин` `Не удалось загрузить список скважин`
) )
const duplicateWell = (well) => {
// TODO: Метод дубликации скважины
}
const addititonalButtons = (record, editingKey) => (
<Button
icon={<ForkOutlined />}
title={'Дублировать скважину'}
disabled={(editingKey ?? '') !== ''}
onClick={() => duplicateWell(record)}
/>
)
useEffect(() => invokeWebApiWrapperAsync( useEffect(() => invokeWebApiWrapperAsync(
async () => { async () => {
const companies = arrayOrDefault(await AdminCompanyService.getAll()) const companies = arrayOrDefault(await AdminCompanyService.getAll())
const telemetry = arrayOrDefault(await AdminTelemetryService.getAll()) const telemetry = arrayOrDefault(await AdminTelemetryService.getAll())
let clusters = arrayOrDefault(await AdminClusterService.getAll()) const clusters = arrayOrDefault(await AdminClusterService.getAll()).map((cluster) => ({
clusters = clusters.map((cluster) => ({ value: cluster.id, label: cluster.caption })) value: cluster.id,
label: cluster.caption
}))
setColumns([ setColumns([
makeSelectColumn('Куст', 'idCluster', clusters, '--', { makeSelectColumn('Куст', 'idCluster', clusters, '--', {
@ -90,7 +106,9 @@ export default function WellController() {
input: <TelemetrySelect telemetry={telemetry}/>, input: <TelemetrySelect telemetry={telemetry}/>,
}), }),
makeTagColumn('Компании', 'companies', companies, 'id', 'caption', { makeTagColumn('Компании', 'companies', companies, 'id', 'caption', {
width: 400 width: 400,
editable: true,
render: (company) => <CompanyView company={company} />,
}), }),
]) ])
@ -117,6 +135,8 @@ export default function WellController() {
onRowAdd={makeActionHandler('insert', handlerProps)} onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('update', handlerProps)} onRowEdit={makeActionHandler('update', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)} onRowDelete={makeActionHandler('delete', handlerProps)}
additionalButtons={addititonalButtons}
buttonsWidth={95}
/> />
</LoaderPortal> </LoaderPortal>
) )