* Фабрики фильтров вынесены в файл utils/table

* Добавлена константа defaultPagination
* UserController и WellController перемещены в одноимённые директории и разделены по компонентам
* Ширина колонки прав ролей освобождена
* Назначение типа компании изменено на Select по id
This commit is contained in:
Александр Сироткин 2022-01-13 20:32:46 +05:00
parent 4e9a541cdb
commit 1b42f77fef
12 changed files with 195 additions and 148 deletions

View File

@ -12,6 +12,11 @@ export { SelectFromDictionary } from './SelectFromDictionary'
export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/ export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/
export const defaultPagination = {
defaultPageSize: 14,
showSizeChanger: true,
}
export const makeNumericRender = (fixed?: number) => (value: any, row: object): ReactNode => { export const makeNumericRender = (fixed?: number) => (value: any, row: object): ReactNode => {
let val = '-' let val = '-'
if ((value ?? null) !== null && Number.isFinite(+value)) { if ((value ?? null) !== null && Number.isFinite(+value)) {

View File

@ -7,7 +7,8 @@ import {
makeColumn, makeColumn,
makeSelectColumn, makeSelectColumn,
makeActionHandler, makeActionHandler,
makeStringSorter makeStringSorter,
defaultPagination
} from '../../components/Table' } from '../../components/Table'
import { AdminClusterService, AdminDepositService } from '../../services/api' import { AdminClusterService, AdminDepositService } from '../../services/api'
import { arrayOrDefault } from '../../utils' import { arrayOrDefault } from '../../utils'
@ -69,8 +70,9 @@ export const ClusterController = () => {
<EditableTable <EditableTable
size={'small'} size={'small'}
bordered bordered
columns={clusterColumns}
dataSource={clusters} dataSource={clusters}
columns={clusterColumns}
pagination={defaultPagination}
onRowAdd={makeActionHandler('insert', handlerProps)} onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('put', handlerProps)} onRowEdit={makeActionHandler('put', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)} onRowDelete={makeActionHandler('delete', handlerProps)}

View File

@ -2,39 +2,52 @@ import { useEffect, useState } from 'react'
import { invokeWebApiWrapperAsync } from '../../components/factory' import { invokeWebApiWrapperAsync } from '../../components/factory'
import LoaderPortal from '../../components/LoaderPortal' import LoaderPortal from '../../components/LoaderPortal'
import { EditableTable, makeColumn, makeActionHandler, makeStringSorter } from '../../components/Table' import {
import { AdminCompanyService } from '../../services/api' EditableTable,
makeColumn,
makeActionHandler,
makeStringSorter,
makeSelectColumn,
defaultPagination
} from '../../components/Table'
import { AdminCompanyService, AdminCompanyTypeService } from '../../services/api'
import { arrayOrDefault } from '../../utils' import { arrayOrDefault } from '../../utils'
import { min1 } from '../../utils/validationRules' import { min1 } from '../../utils/validationRules'
const companyColumns = [
export const CompanyController = () => {
const [columns, setColumns] = useState([])
const [companies, setCompanies] = useState([])
const [showLoader, setShowLoader] = useState(false)
const updateTable = async () => {
const companies = await AdminCompanyService.getAll()
setCompanies(arrayOrDefault(companies))
}
useEffect(() => invokeWebApiWrapperAsync(
async() => {
const companyTypes = arrayOrDefault(await AdminCompanyTypeService.getAll()).map((companyType) => ({
value: companyType.id,
label: companyType.caption,
}))
setColumns([
makeColumn('Название', 'caption', { makeColumn('Название', 'caption', {
width: 200, width: 200,
editable: true, editable: true,
sorter: makeStringSorter('caption'), sorter: makeStringSorter('caption'),
formItemRules: min1, formItemRules: min1,
}), }),
makeColumn('Тип компании', 'companyTypeCaption', { makeSelectColumn('Тип компании', 'idCompanyType', companyTypes, null, {
width: 200, width: 200,
editable: true, editable: true
sorter: makeStringSorter('companyTypeCaption')
}), }),
] ])
export default function CompanyController() { await updateTable()
const [companies, setCompanies] = useState([]) }
const [showLoader, setShowLoader] = useState(false) ), [])
const updateTable = () => invokeWebApiWrapperAsync(
async() => {
const companies = await AdminCompanyService.getAll()
setCompanies(arrayOrDefault(companies))
},
setShowLoader,
`Не удалось загрузить список кустов`
)
useEffect(updateTable, [])
const handlerProps = { const handlerProps = {
service: AdminCompanyService, service: AdminCompanyService,
@ -48,8 +61,9 @@ export default function CompanyController() {
<EditableTable <EditableTable
size={'small'} size={'small'}
bordered bordered
columns={companyColumns} columns={columns}
dataSource={companies} dataSource={companies}
pagination={defaultPagination}
onRowAdd={makeActionHandler('insert', handlerProps)} onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('put', handlerProps)} onRowEdit={makeActionHandler('put', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)} onRowDelete={makeActionHandler('delete', handlerProps)}
@ -57,3 +71,5 @@ export default function CompanyController() {
</LoaderPortal> </LoaderPortal>
) )
} }
export default CompanyController

View File

@ -2,7 +2,13 @@ import { useEffect, useState } from 'react'
import { invokeWebApiWrapperAsync } from '../../components/factory' import { invokeWebApiWrapperAsync } from '../../components/factory'
import LoaderPortal from '../../components/LoaderPortal' import LoaderPortal from '../../components/LoaderPortal'
import { EditableTable, makeColumn, makeActionHandler, makeStringSorter } from '../../components/Table' import {
EditableTable,
makeColumn,
makeActionHandler,
makeStringSorter,
defaultPagination
} from '../../components/Table'
import { AdminCompanyTypeService } from '../../services/api' import { AdminCompanyTypeService } from '../../services/api'
import { arrayOrDefault } from '../../utils' import { arrayOrDefault } from '../../utils'
import { min1 } from '../../utils/validationRules' import { min1 } from '../../utils/validationRules'
@ -16,11 +22,6 @@ const columns = [
}), }),
] ]
const pagination = {
defaultPageSize: 16,
showSizeChanger: true,
}
export const CompanyTypeController = () => { export const CompanyTypeController = () => {
const [companyTypes, setCompanyTypes] = useState([]) const [companyTypes, setCompanyTypes] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
@ -50,7 +51,7 @@ export const CompanyTypeController = () => {
size={'small'} size={'small'}
columns={columns} columns={columns}
dataSource={companyTypes} dataSource={companyTypes}
pagination={pagination} pagination={defaultPagination}
onRowAdd={makeActionHandler('insert', handlerProps)} onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('put', handlerProps)} onRowEdit={makeActionHandler('put', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)} onRowDelete={makeActionHandler('delete', handlerProps)}

View File

@ -1,7 +1,8 @@
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import LoaderPortal from '../../components/LoaderPortal' import LoaderPortal from '../../components/LoaderPortal'
import { EditableTable, makeColumn, makeActionHandler } from '../../components/Table' import { invokeWebApiWrapperAsync } from '../../components/factory'
import { EditableTable, makeColumn, makeActionHandler, defaultPagination } from '../../components/Table'
import { AdminDepositService } from '../../services/api' import { AdminDepositService } from '../../services/api'
import { arrayOrDefault } from '../../utils' import { arrayOrDefault } from '../../utils'
import { min1 } from '../../utils/validationRules' import { min1 } from '../../utils/validationRules'
@ -14,7 +15,7 @@ const depositColumns = [
makeColumn('Долгота', 'longitude', { width: 150, editable: true, render: coordsFixed }) makeColumn('Долгота', 'longitude', { width: 150, editable: true, render: coordsFixed })
] ]
export default function DepositController() { export const DepositController = () => {
const [deposits, setDeposits] = useState([]) const [deposits, setDeposits] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
@ -41,8 +42,9 @@ export default function DepositController() {
<EditableTable <EditableTable
size={'small'} size={'small'}
bordered bordered
columns={depositColumns}
dataSource={deposits} dataSource={deposits}
columns={depositColumns}
pagination={defaultPagination}
onRowAdd={makeActionHandler('insert', handlerProps)} onRowAdd={makeActionHandler('insert', handlerProps)}
onRowEdit={makeActionHandler('put', handlerProps)} onRowEdit={makeActionHandler('put', handlerProps)}
onRowDelete={makeActionHandler('delete', handlerProps)} onRowDelete={makeActionHandler('delete', handlerProps)}
@ -50,3 +52,5 @@ export default function DepositController() {
</LoaderPortal> </LoaderPortal>
) )
} }
export default DepositController

View File

@ -16,7 +16,7 @@ export const RoleController = memo(() => {
const loadRoles = async () => { const loadRoles = async () => {
const roles = await AdminUserRoleService.getAll() const roles = await AdminUserRoleService.getAll()
setRoles(Array.isArray(roles) ? roles : []) setRoles(arrayOrDefault(roles))
} }
useEffect(() => { useEffect(() => {
@ -28,7 +28,6 @@ export const RoleController = memo(() => {
editable: true editable: true
}, { allowClear: true }), }, { allowClear: true }),
makeTagColumn('Разрешения', 'permissions', permissions, 'id', 'name', { makeTagColumn('Разрешения', 'permissions', permissions, 'id', 'name', {
width: 200,
editable: true, editable: true,
render: (permission) => <PermissionView info={permission} />, render: (permission) => <PermissionView info={permission} />,
}), }),

View File

@ -0,0 +1,27 @@
import { Select } from 'antd'
import { memo, useEffect, useState } from 'react'
export 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])
return (
<Select
allowClear
showSearch
mode={'tags'}
options={options}
value={value ?? []}
onChange={onChange}
/>
)
})
export default RoleTag

View File

@ -1,75 +1,29 @@
import { Button, Select, Tag } from 'antd' import { Button, Tag } from 'antd'
import { UserSwitchOutlined } from '@ant-design/icons' import { UserSwitchOutlined } from '@ant-design/icons'
import { memo, useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { RoleView } from '../../components/Views' import { RoleView } from '../../../components/Views'
import LoaderPortal from '../../components/LoaderPortal' import LoaderPortal from '../../../components/LoaderPortal'
import { ChangePassword } from '../../components/ChangePassword' import { ChangePassword } from '../../../components/ChangePassword'
import { invokeWebApiWrapperAsync } from '../../components/factory' import { invokeWebApiWrapperAsync } from '../../../components/factory'
import { import {
EditableTable, EditableTable,
makeColumn, makeColumn,
makeSelectColumn, makeSelectColumn,
makeActionHandler, makeActionHandler,
makeStringSorter, makeStringSorter,
makeNumericSorter makeNumericSorter,
} from '../../components/Table' defaultPagination
import { AdminCompanyService, AdminUserRoleService, AdminUserService } from '../../services/api' } from '../../../components/Table'
import { createLoginRules, nameRules, phoneRules, emailRules } from '../../utils/validationRules' import { AdminCompanyService, AdminUserRoleService, AdminUserService } from '../../../services/api'
import { arrayOrDefault } from '../../utils' import { createLoginRules, nameRules, phoneRules, emailRules } from '../../../utils/validationRules'
import { arrayOrDefault } from '../../../utils'
const RoleTag = memo(({ roles, value, onChange }) => { import RoleTag from './RoleTag'
const [options, setOptions] = useState([])
useEffect(() => { import { makeTextOnFilter, makeTextFilters } from '../../../utils/table'
setOptions(roles.map((elm) => ({
key: Date.now(),
value: `${elm.caption}`,
label: elm.caption
})))
}, [roles])
return ( export const UserController = () => {
<Select
allowClear
showSearch
mode={'tags'}
options={options}
value={value ?? []}
onChange={onChange}
/>
)
})
const makeOnFilter = (key) => (value, record) => record?.[key]?.startsWith(value)
const makeDataFilters = (array, keys) => {
const filters = Array(keys.length)
for (let i = 0; i < keys.length; i++)
filters[i] = []
array.forEach((row) => {
if (!row) return
keys.forEach((key, idx) => {
if (row[key] && filters[idx].indexOf(row[key]) < 0)
filters[idx].push(row[key])
})
})
const out = {}
keys.forEach((key, idx) => {
filters[idx].sort()
out[key] = filters[idx].map((filter) => ({
value: filter,
text: filter,
}))
})
return out
}
export default function UserController() {
const [users, setUsers] = useState([]) const [users, setUsers] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
const [columns, setColumns] = useState([]) const [columns, setColumns] = useState([])
@ -104,7 +58,7 @@ export default function UserController() {
const users = arrayOrDefault(await AdminUserService.getAll()) const users = arrayOrDefault(await AdminUserService.getAll())
setUsers(users) setUsers(users)
const filters = makeDataFilters(users, ['surname', 'name', 'patronymic', 'email']) const filters = makeTextFilters(users, ['surname', 'name', 'patronymic', 'email'])
setColumns([ setColumns([
@ -130,7 +84,7 @@ export default function UserController() {
sorter: makeStringSorter('surname'), sorter: makeStringSorter('surname'),
filters: filters.surname, filters: filters.surname,
filterSearch: true, filterSearch: true,
onFilter: makeOnFilter('surname'), onFilter: makeTextOnFilter('surname'),
}), }),
makeColumn('Имя', 'name', { makeColumn('Имя', 'name', {
editable: true, editable: true,
@ -138,7 +92,7 @@ export default function UserController() {
sorter: makeStringSorter('name'), sorter: makeStringSorter('name'),
filters: filters.name, filters: filters.name,
filterSearch: true, filterSearch: true,
onFilter: makeOnFilter('name'), onFilter: makeTextOnFilter('name'),
}), }),
makeColumn('Отчество', 'patronymic', { makeColumn('Отчество', 'patronymic', {
editable: true, editable: true,
@ -146,7 +100,7 @@ export default function UserController() {
sorter: makeStringSorter('patronymic'), sorter: makeStringSorter('patronymic'),
filters: filters.patronymic, filters: filters.patronymic,
filterSearch: true, filterSearch: true,
onFilter: makeOnFilter('patronymic'), onFilter: makeTextOnFilter('patronymic'),
}), }),
makeColumn('E-mail', 'email', { makeColumn('E-mail', 'email', {
editable: true, editable: true,
@ -154,7 +108,7 @@ export default function UserController() {
sorter: makeStringSorter('email'), sorter: makeStringSorter('email'),
filters: filters.email, filters: filters.email,
filterSearch: true, filterSearch: true,
onFilter: makeOnFilter('email'), onFilter: makeTextOnFilter('email'),
}), }),
makeColumn('Номер телефона', 'phone', { makeColumn('Номер телефона', 'phone', {
editable: true, editable: true,
@ -204,7 +158,7 @@ export default function UserController() {
onRowDelete={makeActionHandler('delete', handlerProps)} onRowDelete={makeActionHandler('delete', handlerProps)}
additionalButtons={additionalButtons} additionalButtons={additionalButtons}
buttonsWidth={120} buttonsWidth={120}
pagination={{ defaultPageSize: 14 }} pagination={defaultPagination}
/> />
</LoaderPortal> </LoaderPortal>
<ChangePassword <ChangePassword
@ -216,3 +170,5 @@ export default function UserController() {
</> </>
) )
} }
export default UserController

View File

@ -2,7 +2,7 @@ import { memo, useEffect, useState } from 'react'
import { invokeWebApiWrapperAsync } from '../../components/factory' import { invokeWebApiWrapperAsync } from '../../components/factory'
import LoaderPortal from '../../components/LoaderPortal' import LoaderPortal from '../../components/LoaderPortal'
import { makeColumn, makeDateSorter, makeStringSorter, Table } from '../../components/Table' import { defaultPagination, makeColumn, makeDateSorter, makeStringSorter, Table } from '../../components/Table'
import { RequerstTrackerService } from '../../services/api' import { RequerstTrackerService } from '../../services/api'
import { arrayOrDefault, formatDate } from '../../utils' import { arrayOrDefault, formatDate } from '../../utils'
@ -17,11 +17,6 @@ const columns = [
}), }),
] ]
const pagination = {
defaultPageSize: 16,
showSizeChanger: true,
}
export const VisitLog = memo(() => { export const VisitLog = memo(() => {
const [logData, setLogData] = useState([]) const [logData, setLogData] = useState([])
const [isLoading, setIsLoading] = useState(false) const [isLoading, setIsLoading] = useState(false)
@ -44,7 +39,7 @@ export const VisitLog = memo(() => {
bordered bordered
columns={columns} columns={columns}
dataSource={logData} dataSource={logData}
pagination={pagination} pagination={defaultPagination}
/> />
</LoaderPortal> </LoaderPortal>
) )

View File

@ -0,0 +1,28 @@
import { memo } from 'react'
import { Select } from 'antd'
import { getTelemetryLabel } from '../../../components/Views'
export const TelemetrySelect = memo(({ telemetry, value, onChange }) => {
const onSelectChange = (id) => {
onChange?.(telemetry.find((row) => row.id === id))
}
return (
<Select
value={value?.id}
onChange={onSelectChange}
dropdownClassName={'telemetry_select'}
>
{telemetry.map((row, i) => (
<Select.Option key={i} value={row.id}>
<span className={row?.info?.well ? 'telemetry_used' : 'telemetry_unused'}>
{getTelemetryLabel(row)}
</span>
</Select.Option>
))}
</Select>
)
})
export default TelemetrySelect

View File

@ -1,10 +1,10 @@
import { Button, Select } from 'antd' import { useEffect, useState } from 'react'
import { Button } from 'antd'
import { CopyOutlined } from '@ant-design/icons' import { CopyOutlined } from '@ant-design/icons'
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, CompanyView } from '../../components/Views' import { TelemetryView, CompanyView } from '../../../components/Views'
import { import {
EditableTable, EditableTable,
makeColumn, makeColumn,
@ -13,46 +13,27 @@ import {
makeStringSorter, makeStringSorter,
makeNumericSorter, makeNumericSorter,
makeTagColumn, makeTagColumn,
} from '../../components/Table' defaultPagination,
} 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'
import { coordsFixed } from './DepositController'
import '../../styles/admin.css' import { coordsFixed } from '../DepositController'
import TelemetrySelect from './TelemetrySelect'
import '../../../styles/admin.css'
const wellTypes = [ const wellTypes = [
{ value: 1, label: 'Наклонно-направленная' }, { value: 1, label: 'Наклонно-направленная' },
{ value: 2, label: 'Горизонтальная' }, { value: 2, label: 'Горизонтальная' },
] ]
const TelemetrySelect = memo(({ telemetry, value, onChange }) => { export const WellController = () => {
const onSelectChange = (id) => {
onChange?.(telemetry.find((row) => row.id === id))
}
return (
<Select
value={value?.id}
onChange={onSelectChange}
dropdownClassName={'telemetry_select'}
>
{telemetry.map((row, i) => (
<Select.Option key={i} value={row.id}>
<span className={row?.info?.well ? 'telemetry_used' : 'telemetry_unused'}>
{getTelemetryLabel(row)}
</span>
</Select.Option>
))}
</Select>
)
})
export default function WellController() {
const [columns, setColumns] = useState([]) const [columns, setColumns] = useState([])
const [wells, setWells] = useState([]) const [wells, setWells] = useState([])
const [showLoader, setShowLoader] = useState(false) const [showLoader, setShowLoader] = useState(false)
@ -142,6 +123,7 @@ export default function WellController() {
bordered bordered
columns={columns} columns={columns}
dataSource={wells} dataSource={wells}
pagination={defaultPagination}
onRowAdd={makeActionHandler('insert', handlerProps, recordParser)} onRowAdd={makeActionHandler('insert', handlerProps, recordParser)}
onRowEdit={makeActionHandler('put', handlerProps, recordParser)} onRowEdit={makeActionHandler('put', handlerProps, recordParser)}
onRowDelete={makeActionHandler('delete', handlerProps)} onRowDelete={makeActionHandler('delete', handlerProps)}
@ -151,3 +133,5 @@ export default function WellController() {
</LoaderPortal> </LoaderPortal>
) )
} }
export default WellController

30
src/utils/table.ts Normal file
View File

@ -0,0 +1,30 @@
export const makeTextOnFilter = (key: string) =>
(value: string, record?: Record<string, string>) => record?.[key]?.startsWith(value)
export const makeTextFilters = (array: Record<string, unknown>[], keys: string[]) => {
const filters: string[][] = Array(keys.length)
filters.forEach((_, idx) => filters[idx] = [])
array.forEach((row) => {
if (!row) return
keys.forEach((key, idx) => {
if (!row[key]) return
const value = String(row[key])
if (filters[idx].indexOf(value) < 0)
filters[idx].push(value)
})
})
const out: Record<string, { value: string, text: string }[]> = {}
keys.forEach((key, idx) => {
filters[idx].sort()
out[key] = filters[idx].map((filter) => ({
value: filter,
text: filter,
}))
})
return out
}