* Фабрики фильтров вынесены в файл 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 defaultPagination = {
defaultPageSize: 14,
showSizeChanger: true,
}
export const makeNumericRender = (fixed?: number) => (value: any, row: object): ReactNode => {
let val = '-'
if ((value ?? null) !== null && Number.isFinite(+value)) {

View File

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

View File

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

View File

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

View File

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

View File

@ -16,7 +16,7 @@ export const RoleController = memo(() => {
const loadRoles = async () => {
const roles = await AdminUserRoleService.getAll()
setRoles(Array.isArray(roles) ? roles : [])
setRoles(arrayOrDefault(roles))
}
useEffect(() => {
@ -28,7 +28,6 @@ export const RoleController = memo(() => {
editable: true
}, { allowClear: true }),
makeTagColumn('Разрешения', 'permissions', permissions, 'id', 'name', {
width: 200,
editable: true,
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 { memo, useEffect, useState } from 'react'
import { useEffect, useState } from 'react'
import { RoleView } from '../../components/Views'
import LoaderPortal from '../../components/LoaderPortal'
import { ChangePassword } from '../../components/ChangePassword'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import { RoleView } from '../../../components/Views'
import LoaderPortal from '../../../components/LoaderPortal'
import { ChangePassword } from '../../../components/ChangePassword'
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 { arrayOrDefault } from '../../utils'
makeNumericSorter,
defaultPagination
} from '../../../components/Table'
import { AdminCompanyService, AdminUserRoleService, AdminUserService } from '../../../services/api'
import { createLoginRules, nameRules, phoneRules, emailRules } from '../../../utils/validationRules'
import { arrayOrDefault } from '../../../utils'
const RoleTag = memo(({ roles, value, onChange }) => {
const [options, setOptions] = useState([])
import RoleTag from './RoleTag'
useEffect(() => {
setOptions(roles.map((elm) => ({
key: Date.now(),
value: `${elm.caption}`,
label: elm.caption
})))
}, [roles])
import { makeTextOnFilter, makeTextFilters } from '../../../utils/table'
return (
<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() {
export const UserController = () => {
const [users, setUsers] = useState([])
const [showLoader, setShowLoader] = useState(false)
const [columns, setColumns] = useState([])
@ -104,7 +58,7 @@ export default function UserController() {
const users = arrayOrDefault(await AdminUserService.getAll())
setUsers(users)
const filters = makeDataFilters(users, ['surname', 'name', 'patronymic', 'email'])
const filters = makeTextFilters(users, ['surname', 'name', 'patronymic', 'email'])
setColumns([
@ -130,7 +84,7 @@ export default function UserController() {
sorter: makeStringSorter('surname'),
filters: filters.surname,
filterSearch: true,
onFilter: makeOnFilter('surname'),
onFilter: makeTextOnFilter('surname'),
}),
makeColumn('Имя', 'name', {
editable: true,
@ -138,7 +92,7 @@ export default function UserController() {
sorter: makeStringSorter('name'),
filters: filters.name,
filterSearch: true,
onFilter: makeOnFilter('name'),
onFilter: makeTextOnFilter('name'),
}),
makeColumn('Отчество', 'patronymic', {
editable: true,
@ -146,7 +100,7 @@ export default function UserController() {
sorter: makeStringSorter('patronymic'),
filters: filters.patronymic,
filterSearch: true,
onFilter: makeOnFilter('patronymic'),
onFilter: makeTextOnFilter('patronymic'),
}),
makeColumn('E-mail', 'email', {
editable: true,
@ -154,7 +108,7 @@ export default function UserController() {
sorter: makeStringSorter('email'),
filters: filters.email,
filterSearch: true,
onFilter: makeOnFilter('email'),
onFilter: makeTextOnFilter('email'),
}),
makeColumn('Номер телефона', 'phone', {
editable: true,
@ -204,7 +158,7 @@ export default function UserController() {
onRowDelete={makeActionHandler('delete', handlerProps)}
additionalButtons={additionalButtons}
buttonsWidth={120}
pagination={{ defaultPageSize: 14 }}
pagination={defaultPagination}
/>
</LoaderPortal>
<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 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 { arrayOrDefault, formatDate } from '../../utils'
@ -17,11 +17,6 @@ const columns = [
}),
]
const pagination = {
defaultPageSize: 16,
showSizeChanger: true,
}
export const VisitLog = memo(() => {
const [logData, setLogData] = useState([])
const [isLoading, setIsLoading] = useState(false)
@ -44,7 +39,7 @@ export const VisitLog = memo(() => {
bordered
columns={columns}
dataSource={logData}
pagination={pagination}
pagination={defaultPagination}
/>
</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 { memo, useEffect, useState } from 'react'
import LoaderPortal from '../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../components/factory'
import { TelemetryView, getTelemetryLabel, CompanyView } from '../../components/Views'
import LoaderPortal from '../../../components/LoaderPortal'
import { invokeWebApiWrapperAsync } from '../../../components/factory'
import { TelemetryView, CompanyView } from '../../../components/Views'
import {
EditableTable,
makeColumn,
@ -13,46 +13,27 @@ import {
makeStringSorter,
makeNumericSorter,
makeTagColumn,
} from '../../components/Table'
defaultPagination,
} from '../../../components/Table'
import {
AdminClusterService,
AdminCompanyService,
AdminTelemetryService,
AdminWellService,
} from '../../services/api'
import { arrayOrDefault } from '../../utils'
import { coordsFixed } from './DepositController'
} from '../../../services/api'
import { arrayOrDefault } from '../../../utils'
import '../../styles/admin.css'
import { coordsFixed } from '../DepositController'
import TelemetrySelect from './TelemetrySelect'
import '../../../styles/admin.css'
const wellTypes = [
{ value: 1, label: 'Наклонно-направленная' },
{ value: 2, label: 'Горизонтальная' },
]
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 function WellController() {
export const WellController = () => {
const [columns, setColumns] = useState([])
const [wells, setWells] = useState([])
const [showLoader, setShowLoader] = useState(false)
@ -142,6 +123,7 @@ export default function WellController() {
bordered
columns={columns}
dataSource={wells}
pagination={defaultPagination}
onRowAdd={makeActionHandler('insert', handlerProps, recordParser)}
onRowEdit={makeActionHandler('put', handlerProps, recordParser)}
onRowDelete={makeActionHandler('delete', handlerProps)}
@ -151,3 +133,5 @@ export default function WellController() {
</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
}