forked from ddrilling/asb_cloud_front
Layout вынесен в отдельную директорию, добавлена админка, добавлена фабрика для колонок с Select
This commit is contained in:
parent
2c585ab227
commit
7a3d26e846
21
src/components/Layout/AdminLayoutPortal.tsx
Normal file
21
src/components/Layout/AdminLayoutPortal.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { Button, Layout } from 'antd'
|
||||
import { Link } from 'react-router-dom'
|
||||
import PageHeader from '../PageHeader'
|
||||
|
||||
type LayoutPortalProps = {
|
||||
title?: string
|
||||
[props: string]: any
|
||||
}
|
||||
|
||||
export const AdminLayoutPortal: React.FC<LayoutPortalProps> = ({title, ...props}) => (
|
||||
<Layout.Content>
|
||||
<PageHeader title={title}>
|
||||
<Button size={'large'}>
|
||||
<Link to={'/'}>Вернуться на основной сайт</Link>
|
||||
</Button>
|
||||
</PageHeader>
|
||||
<Layout>
|
||||
<Layout.Content className={'site-layout-background sheet'} {...props}/>
|
||||
</Layout>
|
||||
</Layout.Content>
|
||||
)
|
22
src/components/Layout/LayoutPortal.tsx
Normal file
22
src/components/Layout/LayoutPortal.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
import { Layout } from 'antd'
|
||||
import PageHeader from '../PageHeader'
|
||||
import WellTreeSelector from '../WellTreeSelector'
|
||||
|
||||
type LayoutPortalProps = {
|
||||
title?: string
|
||||
noSheet?: boolean
|
||||
[props: string]: any
|
||||
}
|
||||
|
||||
export const LayoutPortal: React.FC<LayoutPortalProps> = ({title, noSheet, ...props}) => (
|
||||
<Layout.Content>
|
||||
<PageHeader title={title}>
|
||||
<WellTreeSelector />
|
||||
</PageHeader>
|
||||
<Layout>
|
||||
{noSheet ? props.children : (
|
||||
<Layout.Content className={'site-layout-background sheet'} {...props}/>
|
||||
)}
|
||||
</Layout>
|
||||
</Layout.Content>
|
||||
)
|
2
src/components/Layout/index.ts
Normal file
2
src/components/Layout/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export { AdminLayoutPortal } from './AdminLayoutPortal'
|
||||
export { LayoutPortal } from './LayoutPortal'
|
@ -1,13 +0,0 @@
|
||||
import {Layout} from 'antd'
|
||||
import PageHeader from './PageHeader'
|
||||
|
||||
export const LayoutPortal = ({title, noSheet, ...props}) => (
|
||||
<Layout.Content>
|
||||
<PageHeader title={title}/>
|
||||
<Layout>
|
||||
{noSheet ? props.children : (
|
||||
<Layout.Content className={'site-layout-background sheet'} {...props}/>
|
||||
)}
|
||||
</Layout>
|
||||
</Layout.Content>
|
||||
)
|
@ -1,35 +1,24 @@
|
||||
import { Layout, Button } from 'antd'
|
||||
import { UserOutlined } from '@ant-design/icons'
|
||||
import logo from '../images/logo_32.png'
|
||||
import { Link } from 'react-router-dom'
|
||||
import WellTreeSelector from './WellTreeSelector'
|
||||
import { Layout } from 'antd'
|
||||
import { headerHeight } from '../utils'
|
||||
import { UserMenu } from './UserMenu'
|
||||
import logo from '../images/logo_32.png'
|
||||
|
||||
const { Header } = Layout
|
||||
|
||||
const logoStyle = { height: headerHeight }
|
||||
|
||||
const handleLogout = () => {
|
||||
localStorage.removeItem('login')
|
||||
localStorage.removeItem('token')
|
||||
type PageHeaderProps = {
|
||||
children?: React.ReactNode
|
||||
title?: string
|
||||
}
|
||||
|
||||
type PageHeaderProps = { title?: string }
|
||||
|
||||
export const PageHeader = ({ title = 'Мониторинг' }: PageHeaderProps) => (
|
||||
export const PageHeader: React.FC<PageHeaderProps> = ({ children, title = 'Мониторинг' }) => (
|
||||
<Layout>
|
||||
<Header className={'header'}>
|
||||
<Link to={'/'} style={logoStyle}>
|
||||
<Layout.Header className={'header'}>
|
||||
<Link to={'/'} style={{ height: headerHeight }}>
|
||||
<img src={logo} alt={'АСБ'} className={'logo'}/>
|
||||
</Link>
|
||||
<WellTreeSelector />
|
||||
{children}
|
||||
<h1 className={'title'}>{title}</h1>
|
||||
<Link to={'/login'} onClick={handleLogout}>
|
||||
<Button icon={<UserOutlined/>}>
|
||||
({localStorage['login']}) Выход
|
||||
</Button>
|
||||
</Link>
|
||||
</Header>
|
||||
<UserMenu />
|
||||
</Layout.Header>
|
||||
</Layout>
|
||||
)
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { ReactNode } from 'react'
|
||||
import { Table as RawTable } from 'antd'
|
||||
import { Select, Table as RawTable } from 'antd'
|
||||
import { OptionsType } from 'rc-select/lib/interface'
|
||||
import { tryAddKeys } from './EditableTable'
|
||||
import { makeNumericSorter, makeStringSorter} from './sorters'
|
||||
export { makeDateSorter, makeNumericSorter, makeStringSorter} from './sorters'
|
||||
@ -25,7 +26,7 @@ export const makeNumericRender = (fixed?: number) => (value: any, row: object):
|
||||
)
|
||||
}
|
||||
|
||||
export const makeNumericColumnOptions = (fixed?:number, sorterKey?:string ) => ({
|
||||
export const makeNumericColumnOptions = (fixed?: number, sorterKey?: string ) => ({
|
||||
editable: true,
|
||||
initialValue: 0,
|
||||
width:100,
|
||||
@ -57,30 +58,36 @@ interface columnPropsOther {
|
||||
formItemRules?: any[]
|
||||
// дефолтное значение при добавлении новой строки
|
||||
initialValue?: string|number
|
||||
|
||||
render?: (...attributes: any) => any
|
||||
}
|
||||
|
||||
export const makeColumn = (title:string | ReactNode, key:string, other?:columnPropsOther) => ({
|
||||
export const makeColumn = (title: string | ReactNode, key: string, other?: columnPropsOther) => ({
|
||||
title: title,
|
||||
key: key,
|
||||
dataIndex: key,
|
||||
...other,
|
||||
})
|
||||
|
||||
export const makeColumnsPlanFact = (title:string | ReactNode, key:string|string[], columsOther?:any|any[], gruopOther?:any) =>
|
||||
{
|
||||
export const makeColumnsPlanFact = (
|
||||
title:string | ReactNode,
|
||||
key:string | string[],
|
||||
columsOther?: any | any[],
|
||||
gruopOther?: any
|
||||
) => {
|
||||
let keyPlanLocal = key
|
||||
let keyFactLocal = key
|
||||
|
||||
if(key instanceof Array){
|
||||
if (key instanceof Array) {
|
||||
keyPlanLocal = key[0]
|
||||
keyFactLocal = key[1]
|
||||
}else{
|
||||
} else {
|
||||
keyPlanLocal = key + 'Plan'
|
||||
keyFactLocal = key + 'Fact'
|
||||
}
|
||||
|
||||
let columsOtherLoacl :any[2]
|
||||
if(columsOther instanceof Array)
|
||||
let columsOtherLoacl : any[2]
|
||||
if (columsOther instanceof Array)
|
||||
columsOtherLoacl = [columsOther[0], columsOther[1]]
|
||||
else
|
||||
columsOtherLoacl = [columsOther, columsOther]
|
||||
@ -109,7 +116,8 @@ export const makeTextColumn = (
|
||||
filters: object[],
|
||||
sorter?: (key: string) => any,
|
||||
render?: any,
|
||||
other?: any) => ({
|
||||
other?: any
|
||||
) => ({
|
||||
title: title,
|
||||
dataIndex: dataIndex,
|
||||
key: dataIndex,
|
||||
@ -120,9 +128,15 @@ export const makeTextColumn = (
|
||||
...other
|
||||
})
|
||||
|
||||
export const makeNumericColumn = (title: string, dataIndex: string,
|
||||
filters: object[], filterDelegate: (key: string | number) => any,
|
||||
renderDelegate: (_: any, row: object) => any, width: string, other?: columnPropsOther) => ({
|
||||
export const makeNumericColumn = (
|
||||
title: string,
|
||||
dataIndex: string,
|
||||
filters: object[],
|
||||
filterDelegate: (key: string | number) => any,
|
||||
renderDelegate: (_: any, row: object) => any,
|
||||
width: string,
|
||||
other?: columnPropsOther
|
||||
) => ({
|
||||
title: title,
|
||||
dataIndex: dataIndex,
|
||||
key: dataIndex,
|
||||
@ -135,12 +149,17 @@ export const makeNumericColumn = (title: string, dataIndex: string,
|
||||
...other
|
||||
})
|
||||
|
||||
export const makeNumericColumnPlanFact = (title: string, dataIndex: string, filters: object[],
|
||||
filterDelegate: (key: string | number) => any, renderDelegate: (_: any, row: object) => any, width: string) =>
|
||||
makeGroupColumn( title, [
|
||||
export const makeNumericColumnPlanFact = (
|
||||
title: string,
|
||||
dataIndex: string,
|
||||
filters: object[],
|
||||
filterDelegate: (key: string | number) => any,
|
||||
renderDelegate: (_: any, row: object) => any,
|
||||
width: string
|
||||
) => makeGroupColumn(title, [
|
||||
makeNumericColumn('п', dataIndex + 'Plan', filters, filterDelegate, renderDelegate, width),
|
||||
makeNumericColumn('ф', dataIndex + 'Fact', filters, filterDelegate, renderDelegate, width),
|
||||
])
|
||||
])
|
||||
|
||||
export const makeNumericStartEnd = (
|
||||
title: string,
|
||||
@ -150,7 +169,7 @@ export const makeNumericStartEnd = (
|
||||
filterDelegate: (key: string | number) => any,
|
||||
renderDelegate: (_: any, row: object) => any,
|
||||
width: string,
|
||||
) => makeGroupColumn( title, [
|
||||
) => makeGroupColumn(title, [
|
||||
makeNumericColumn('старт', dataIndex + 'Start', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Start')),
|
||||
makeNumericColumn('конец', dataIndex + 'End', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'End'))
|
||||
])
|
||||
@ -176,34 +195,43 @@ export const makeNumericAvgRange = (
|
||||
filterDelegate: (key: string | number) => any,
|
||||
renderDelegate: (_: any, row: object) => any,
|
||||
width: string
|
||||
) => makeGroupColumn( title, [
|
||||
) => makeGroupColumn(title, [
|
||||
makeNumericColumn('мин', dataIndex + 'Min', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Min')),
|
||||
makeNumericColumn('сред', dataIndex + 'Avg', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Avg')),
|
||||
makeNumericColumn('макс', dataIndex + 'Max', filters, filterDelegate, renderDelegate, width, makeNumericColumnOptions(fixed, dataIndex + 'Max'))
|
||||
])
|
||||
|
||||
export const makeSelectColumn = <T extends unknown = string>(
|
||||
title: string,
|
||||
dataIndex: string,
|
||||
options: OptionsType,
|
||||
defaultValue?: T,
|
||||
other?: columnPropsOther
|
||||
) => makeColumn(title, dataIndex, {
|
||||
input: <Select options={options}/>,
|
||||
render: (key) => options.find(option => option?.key === key)?.label ?? defaultValue ?? '--',
|
||||
...other
|
||||
})
|
||||
|
||||
type PaginationContainer = {
|
||||
skip?: number;
|
||||
take?: number;
|
||||
count?: number;
|
||||
items?: any[] | null;
|
||||
skip?: number
|
||||
take?: number
|
||||
count?: number
|
||||
items?: any[] | null
|
||||
}
|
||||
|
||||
export const makePaginationObject = (paginationContainer:PaginationContainer, ...other:any) => {
|
||||
let page = 1 + Math.floor((paginationContainer.skip??0) /(paginationContainer.take??1));
|
||||
return {
|
||||
export const makePaginationObject = (сontainer: PaginationContainer, ...other: any) => ({
|
||||
...other,
|
||||
pageSize: paginationContainer.take,
|
||||
total: paginationContainer.count ?? paginationContainer.items?.length ?? 0,
|
||||
current: page,
|
||||
}
|
||||
}
|
||||
pageSize: сontainer.take,
|
||||
total: сontainer.count ?? сontainer.items?.length ?? 0,
|
||||
current: 1 + Math.floor((сontainer.skip ?? 0) / (сontainer.take ?? 1))
|
||||
})
|
||||
|
||||
interface TableContainer {
|
||||
dataSource: any[];
|
||||
children?: any;
|
||||
dataSource: any[]
|
||||
children?: any
|
||||
}
|
||||
|
||||
export const Table = ({dataSource, children, ...other}: TableContainer) => {
|
||||
return <RawTable dataSource={tryAddKeys(dataSource)} {...other}>{children}</RawTable>
|
||||
}
|
||||
export const Table = ({dataSource, children, ...other}: TableContainer) => (
|
||||
<RawTable dataSource={tryAddKeys(dataSource)} {...other}>{children}</RawTable>
|
||||
)
|
||||
|
88
src/components/UserMenu.tsx
Normal file
88
src/components/UserMenu.tsx
Normal file
@ -0,0 +1,88 @@
|
||||
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 { 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'
|
||||
|
||||
const handleLogout = () => {
|
||||
localStorage.removeItem('login')
|
||||
localStorage.removeItem('token')
|
||||
}
|
||||
|
||||
const formLayout: FormProps = { labelCol: { span: 11 }, wrapperCol: { span: 16 } }
|
||||
|
||||
export const UserMenu: React.FC = () => {
|
||||
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()
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dropdown
|
||||
placement={'bottomRight'}
|
||||
overlay={(
|
||||
<Menu style={{ textAlign: 'right' }}>
|
||||
<PrivateMenuItem roles={['admin']}>
|
||||
<Link to={'/admin'} onClick={() => {}}>Панель администратора</Link>
|
||||
</PrivateMenuItem>
|
||||
<Menu.Item>
|
||||
<Link to={'/'} onClick={onChangePasswordClick}>Сменить пароль</Link>
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
<Link to={'/login'} onClick={handleLogout}>Выход</Link>
|
||||
</Menu.Item>
|
||||
</Menu>
|
||||
)}
|
||||
>
|
||||
<Button icon={<UserOutlined/>}>{localStorage['login']}</Button>
|
||||
</Dropdown>
|
||||
<Modal
|
||||
title={'Сменить пароль'}
|
||||
centered
|
||||
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>
|
||||
</>
|
||||
)
|
||||
}
|
89
src/pages/AdminPanel/ClusterController.jsx
Normal file
89
src/pages/AdminPanel/ClusterController.jsx
Normal file
@ -0,0 +1,89 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useHistory, useParams } from 'react-router'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { EditableTable, makeColumn, makeNumericColumn, makeSelectColumn } from '../../components/Table'
|
||||
import { AdminClusterService, DepositService } from '../../services/api'
|
||||
|
||||
export const ClusterController = () => {
|
||||
const { page } = useParams()
|
||||
const history = useHistory()
|
||||
|
||||
const [deposits, setDeposits] = useState([])
|
||||
const [clusters, setClusters] = useState([])
|
||||
const [pagination, setPagination] = useState({ current: page ?? 1, pageSize: 20 })
|
||||
const [pagintaionTotal, setPagintaionTotal] = useState(0)
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
|
||||
const clusterColumns = [
|
||||
makeSelectColumn('Месторождение', 'idDeposit', deposits, '--', { width: 200, editable: true }),
|
||||
makeColumn('Название', 'caption', { width: 200, editable: true }),
|
||||
makeColumn('Описание', 'description', { width: 500, editable: true }),
|
||||
makeNumericColumn('Широта', 'latitude', null, null, null, 150, { editable: true }),
|
||||
makeNumericColumn('Долгота', 'longitude', null, null, null, 150, { editable: true })
|
||||
]
|
||||
|
||||
const updateTable = () => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
history.push(`/admin/cluster/${pagination.current}`)
|
||||
const skip = ((pagination.current - 1) * pagination.pageSize) || 0
|
||||
const clusters = await AdminClusterService.getPage(skip, pagination.pageSize)
|
||||
if (!clusters?.items)
|
||||
throw Error(`Не удалось загрузить список кустов`)
|
||||
setClusters(clusters.items)
|
||||
setPagintaionTotal(clusters.count ?? clusters.items.length ?? 0)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список кустов`
|
||||
)
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
let deposits = await DepositService.getDeposits()
|
||||
deposits = deposits?.map((deposit) => ({ key: deposit.id, label: deposit.caption }))
|
||||
setDeposits(deposits ?? [])
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список месторождений`
|
||||
), [])
|
||||
|
||||
useEffect(updateTable, [pagination, history])
|
||||
|
||||
const onAdd = async (cluster) => {
|
||||
await AdminClusterService.insert(cluster)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
const onEdit = async (cluster) => {
|
||||
if (!cluster.id) return
|
||||
await AdminClusterService.put(cluster.id, cluster)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
const onDelete = async (cluster) => {
|
||||
if (!cluster.id) return
|
||||
await AdminClusterService.delete(cluster.id)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={clusterColumns}
|
||||
dataSource={clusters}
|
||||
onRowAdd={onAdd}
|
||||
onRowEdit={onEdit}
|
||||
onRowDelete={onDelete}
|
||||
pagination={{
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
total: pagintaionTotal,
|
||||
showSizeChanger: false,
|
||||
onChange: (current, pageSize) => setPagination({ current, pageSize })
|
||||
}}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
)
|
||||
}
|
75
src/pages/AdminPanel/CompanyController.jsx
Normal file
75
src/pages/AdminPanel/CompanyController.jsx
Normal file
@ -0,0 +1,75 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useHistory, useParams } from 'react-router'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { EditableTable, makeColumn } from '../../components/Table'
|
||||
import { AdminCompanyService } from '../../services/api'
|
||||
|
||||
const companyColumns = [
|
||||
makeColumn('Название', 'caption', { width: 200, editable: true }),
|
||||
makeColumn('Тип компании', 'companyTypeCaption', { width: 200, editable: true })
|
||||
]
|
||||
|
||||
export const CompanyController = () => {
|
||||
const { page } = useParams()
|
||||
const history = useHistory()
|
||||
|
||||
const [companies, setCompanies] = useState([])
|
||||
const [pagination, setPagination] = useState({current: page ?? 1, pageSize: 20})
|
||||
const [pagintaionTotal, setPagintaionTotal] = useState(0)
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
|
||||
const updateTable = () => invokeWebApiWrapperAsync(
|
||||
async() => {
|
||||
history.push(`/admin/company/${pagination.current}`)
|
||||
const skip = ((pagination.current - 1) * pagination.pageSize) || 0
|
||||
const companies = await AdminCompanyService.getPage(skip, pagination.pageSize)
|
||||
if (!companies?.items)
|
||||
throw Error(`Не удалось загрузить список кустов`)
|
||||
setCompanies(companies.items)
|
||||
setPagintaionTotal(companies.count ?? companies.items.length ?? 0)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список кустов`
|
||||
)
|
||||
|
||||
useEffect(updateTable, [pagination, history])
|
||||
|
||||
const onAdd = async (company) => {
|
||||
await AdminCompanyService.insert(company)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
const onEdit= async (company) => {
|
||||
if(!company.id) return
|
||||
await AdminCompanyService.put(company.id, company)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
const onDelete= async (company) => {
|
||||
if(!company.id) return
|
||||
await AdminCompanyService.delete(company.id)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={companyColumns}
|
||||
dataSource={companies}
|
||||
onRowAdd={onAdd}
|
||||
onRowEdit={onEdit}
|
||||
onRowDelete={onDelete}
|
||||
pagination={{
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
total: pagintaionTotal,
|
||||
showSizeChanger: false,
|
||||
onChange: (current, pageSize) => setPagination({ current, pageSize })
|
||||
}}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
)
|
||||
}
|
77
src/pages/AdminPanel/DepositController.jsx
Normal file
77
src/pages/AdminPanel/DepositController.jsx
Normal file
@ -0,0 +1,77 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useHistory, useParams } from 'react-router'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { EditableTable, makeColumn, makeNumericColumn } from '../../components/Table'
|
||||
import { AdminDepositService } from '../../services/api'
|
||||
|
||||
const depositColumns = [
|
||||
makeColumn('Название', 'caption', { width: 200, editable: true }),
|
||||
makeColumn('Описание', 'description', { width: 500, editable: true }),
|
||||
makeNumericColumn('Широта', 'latitude', null, null, null, 150, { editable: true }),
|
||||
makeNumericColumn('Долгота', 'longitude', null, null, null, 150, { editable: true })
|
||||
]
|
||||
|
||||
export const DepositController = () => {
|
||||
const { page } = useParams()
|
||||
const history = useHistory()
|
||||
|
||||
const [deposits, setDeposits] = useState([])
|
||||
const [pagination, setPagination] = useState({current: page ?? 1, pageSize: 20})
|
||||
const [pagintaionTotal, setPagintaionTotal] = useState(0)
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
|
||||
const updateTable = () => invokeWebApiWrapperAsync(
|
||||
async() => {
|
||||
history.push(`/admin/deposit/${pagination.current}`)
|
||||
const skip = ((pagination.current - 1) * pagination.pageSize) || 0
|
||||
const deposits = await AdminDepositService.getPage(skip, pagination.pageSize)
|
||||
if (!deposits?.items)
|
||||
throw Error(`Не удалось загрузить список месторождении`)
|
||||
setDeposits(deposits.items)
|
||||
setPagintaionTotal(deposits.count ?? deposits.items.length ?? 0)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список месторождении`
|
||||
)
|
||||
|
||||
useEffect(updateTable, [pagination, history])
|
||||
|
||||
const onAdd = async (deposit) => {
|
||||
await AdminDepositService.insert(deposit)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
const onEdit= async (deposit) => {
|
||||
if(!deposit.id) return
|
||||
await AdminDepositService.put(deposit.id, deposit)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
const onDelete= async (deposit) => {
|
||||
if(!deposit.id) return
|
||||
await AdminDepositService.delete(deposit.id)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={depositColumns}
|
||||
dataSource={deposits}
|
||||
onRowAdd={onAdd}
|
||||
onRowEdit={onEdit}
|
||||
onRowDelete={onDelete}
|
||||
pagination={{
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
total: pagintaionTotal,
|
||||
showSizeChanger: false,
|
||||
onChange: (current, pageSize) => setPagination({ current, pageSize })
|
||||
}}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
)
|
||||
}
|
104
src/pages/AdminPanel/UserController.jsx
Normal file
104
src/pages/AdminPanel/UserController.jsx
Normal file
@ -0,0 +1,104 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useHistory, useParams } from 'react-router'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { EditableTable, makeColumn, makeSelectColumn } from '../../components/Table'
|
||||
import { AdminCompanyService, AdminUserService } from '../../services/api'
|
||||
import { loginRules, nameRules, phoneRules, emailRules } from '../../utils/validationRules'
|
||||
|
||||
const maxCompaniesCount = 1500
|
||||
|
||||
export const UserController = () => {
|
||||
const { page } = useParams()
|
||||
const history = useHistory()
|
||||
|
||||
const [companies, setCompanies] = useState([])
|
||||
const [users, setUsers] = useState([])
|
||||
const [pagination, setPagination] = useState({current: page ?? 1, pageSize: 20})
|
||||
const [pagintaionTotal, setPagintaionTotal] = useState(0)
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
|
||||
const userColumns = [
|
||||
makeColumn('Логин', 'login', {
|
||||
editable: true,
|
||||
formItemRules: [{ required: true }, ...loginRules]
|
||||
}),
|
||||
makeColumn('Фамилия', 'surname', {
|
||||
editable: true,
|
||||
formItemRules: [{ required: true }, ...nameRules]
|
||||
}),
|
||||
makeColumn('Имя', 'name', { editable: true, formItemRules: nameRules }),
|
||||
makeColumn('Отчество', 'patronymic', { editable: true, formItemRules: nameRules }),
|
||||
makeColumn('E-mail', 'email', {
|
||||
editable: true,
|
||||
formItemRules: [{ required: true }, ...emailRules]
|
||||
}),
|
||||
makeColumn('Номер телефона', 'phone', { editable: true, formItemRules: phoneRules }),
|
||||
makeColumn('Должность', 'position', { editable: true }),
|
||||
makeSelectColumn('Компания', 'idCompany', companies, '--', { editable: true })
|
||||
]
|
||||
|
||||
const updateTable = () => invokeWebApiWrapperAsync(
|
||||
async() => {
|
||||
history.push(`/admin/user/${pagination.current}`)
|
||||
const skip = ((pagination.current - 1) * pagination.pageSize) || 0
|
||||
const users = await AdminUserService.getPage(skip, pagination.pageSize)
|
||||
if (!users?.items)
|
||||
throw Error(`Не удалось загрузить список пользователей`)
|
||||
setUsers(users.items)
|
||||
setPagintaionTotal(users.count ?? users.items.length ?? 0)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список пользователей`
|
||||
)
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
let companies = await AdminCompanyService.getPage(0, maxCompaniesCount)
|
||||
companies = companies?.map((company) => ({ key: company.id, label: company.caption }))
|
||||
setCompanies(companies?.items ?? [])
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список компаний`
|
||||
), [])
|
||||
|
||||
useEffect(updateTable, [pagination, history])
|
||||
|
||||
const onAdd = async (user) => {
|
||||
await AdminUserService.insert(user)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
const onEdit = async (user) => {
|
||||
if(!user.id) return
|
||||
await AdminUserService.put(user.id, user)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
const onDelete = async (user) => {
|
||||
if(!user.id) return
|
||||
await AdminUserService.delete(user.id)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={userColumns}
|
||||
dataSource={users}
|
||||
onRowAdd={onAdd}
|
||||
onRowEdit={onEdit}
|
||||
onRowDelete={onDelete}
|
||||
pagination={{
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
total: pagintaionTotal,
|
||||
showSizeChanger: false,
|
||||
onChange: (current, pageSize) => setPagination({ current, pageSize })
|
||||
}}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
)
|
||||
}
|
89
src/pages/AdminPanel/WellController.jsx
Normal file
89
src/pages/AdminPanel/WellController.jsx
Normal file
@ -0,0 +1,89 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useHistory, useParams } from 'react-router'
|
||||
import { invokeWebApiWrapperAsync } from '../../components/factory'
|
||||
import LoaderPortal from '../../components/LoaderPortal'
|
||||
import { EditableTable, makeColumn, makeNumericColumn, makeSelectColumn } from '../../components/Table'
|
||||
import { AdminWellService, ClusterService } from '../../services/api'
|
||||
|
||||
export const WellController = () => {
|
||||
const { page } = useParams()
|
||||
const history = useHistory()
|
||||
|
||||
const [clusters, setClusters] = useState([])
|
||||
const [wells, setWells] = useState([])
|
||||
const [pagination, setPagination] = useState({ current: page ?? 1, pageSize: 20 })
|
||||
const [pagintaionTotal, setPagintaionTotal] = useState(0)
|
||||
const [showLoader, setShowLoader] = useState(false)
|
||||
|
||||
const wellColumns = [
|
||||
makeSelectColumn('Куст', 'idCluster', clusters, '--', { width: 200 , editable: true }),
|
||||
makeColumn('Название', 'caption', { width: 200, editable: true }),
|
||||
makeColumn('Описание', 'description', { width: 500, editable: true }),
|
||||
makeNumericColumn('Широта', 'latitude', null, null, null, 150, { editable: true }),
|
||||
makeNumericColumn('Долгота', 'longitude', null, null, null, 150, { editable: true })
|
||||
]
|
||||
|
||||
const updateTable = () => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
history.push(`/admin/well/${pagination.current}`)
|
||||
const skip = ((pagination.current - 1) * pagination.pageSize) || 0
|
||||
const wells = await AdminWellService.getPage(skip, pagination.pageSize)
|
||||
if (!wells?.items)
|
||||
throw Error(`Не удалось загрузить список скважин`)
|
||||
setWells(wells.items)
|
||||
setPagintaionTotal(wells.count ?? wells.items.length ?? 0)
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список скважин`
|
||||
)
|
||||
|
||||
useEffect(() => invokeWebApiWrapperAsync(
|
||||
async () => {
|
||||
let clusters = await ClusterService.getClusters()
|
||||
clusters = clusters?.map((cluster) => ({ key: cluster.id, label: cluster.caption }))
|
||||
setClusters(clusters ?? [])
|
||||
},
|
||||
setShowLoader,
|
||||
`Не удалось загрузить список кустов`
|
||||
), [])
|
||||
|
||||
useEffect(updateTable, [pagination, history])
|
||||
|
||||
const onAdd = async (well) => {
|
||||
await AdminWellService.insert(well)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
const onEdit = async (well) => {
|
||||
if (!well.id) return
|
||||
await AdminWellService.put(well.id, well)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
const onDelete = async (well) => {
|
||||
if (!well.id) return
|
||||
await AdminWellService.delete(well.id)
|
||||
updateTable()
|
||||
}
|
||||
|
||||
return (
|
||||
<LoaderPortal show={showLoader}>
|
||||
<EditableTable
|
||||
size={'small'}
|
||||
bordered
|
||||
columns={wellColumns}
|
||||
dataSource={wells}
|
||||
onRowAdd={onAdd}
|
||||
onRowEdit={onEdit}
|
||||
onRowDelete={onDelete}
|
||||
pagination={{
|
||||
current: pagination.current,
|
||||
pageSize: pagination.pageSize,
|
||||
total: pagintaionTotal,
|
||||
showSizeChanger: false,
|
||||
onChange: (current, pageSize) => setPagination({ current, pageSize })
|
||||
}}
|
||||
/>
|
||||
</LoaderPortal>
|
||||
)
|
||||
}
|
66
src/pages/AdminPanel/index.jsx
Normal file
66
src/pages/AdminPanel/index.jsx
Normal file
@ -0,0 +1,66 @@
|
||||
import { Layout, Menu } from 'antd'
|
||||
import { Switch, Link, useParams, Redirect, Route } from 'react-router-dom'
|
||||
import { PrivateMenuItem, PrivateRoute } from '../../components/Private'
|
||||
import { ClusterController } from './ClusterController'
|
||||
import { CompanyController } from './CompanyController'
|
||||
import { DepositController } from './DepositController'
|
||||
import { UserController } from './UserController'
|
||||
import { WellController } from './WellController'
|
||||
|
||||
export const AdminPanel = () => {
|
||||
const { tab } = useParams()
|
||||
const rootPath = '/admin'
|
||||
|
||||
return (
|
||||
<Layout>
|
||||
<Menu
|
||||
mode={'horizontal'}
|
||||
selectable={true}
|
||||
selectedKeys={[tab]}
|
||||
>
|
||||
<PrivateMenuItem roles={['deposit_admin']} key={'deposit'}>
|
||||
<Link to={`${rootPath}/deposit`}>Управление месторождениями</Link>
|
||||
</PrivateMenuItem>
|
||||
<PrivateMenuItem roles={['cluster_admin']} key={'cluster'}>
|
||||
<Link to={`${rootPath}/cluster`}>Управление кустами</Link>
|
||||
</PrivateMenuItem>
|
||||
<PrivateMenuItem roles={['well_admin']} key={'well'}>
|
||||
<Link to={`${rootPath}/well`}>Управление скважинами</Link>
|
||||
</PrivateMenuItem>
|
||||
<PrivateMenuItem roles={['user_admin']} key={'user'}>
|
||||
<Link to={`${rootPath}/user`}>Управление пользователями</Link>
|
||||
</PrivateMenuItem>
|
||||
<PrivateMenuItem roles={['company_admin']} key={'company'}>
|
||||
<Link to={`${rootPath}/company`}>Управление компаниями</Link>
|
||||
</PrivateMenuItem>
|
||||
</Menu>
|
||||
|
||||
<Layout>
|
||||
<Layout.Content className={'site-layout-background'}>
|
||||
<Switch>
|
||||
<PrivateRoute roles={['deposit_admin']} path={`${rootPath}/deposit/:page?`}>
|
||||
<DepositController />
|
||||
</PrivateRoute>
|
||||
<PrivateRoute roles={['cluster_admin']} path={`${rootPath}/cluster/:page?`}>
|
||||
<ClusterController />
|
||||
</PrivateRoute>
|
||||
<PrivateRoute roles={['well_admin']} path={`${rootPath}/well/:page?`}>
|
||||
<WellController />
|
||||
</PrivateRoute>
|
||||
<PrivateRoute roles={['user_admin']} path={`${rootPath}/user/:page?`}>
|
||||
<UserController />
|
||||
</PrivateRoute>
|
||||
<PrivateRoute roles={['company_admin']} path={`${rootPath}/company/:page?`}>
|
||||
<CompanyController />
|
||||
</PrivateRoute>
|
||||
<Route path={'/'}>
|
||||
<Redirect to={`${rootPath}/deposit`}/>
|
||||
</Route>
|
||||
</Switch>
|
||||
</Layout.Content>
|
||||
</Layout>
|
||||
</Layout>
|
||||
)
|
||||
}
|
||||
|
||||
export default AdminPanel
|
@ -1,30 +1,37 @@
|
||||
import { Redirect, Route, Switch } from "react-router-dom";
|
||||
import Deposit from "./Deposit";
|
||||
import Cluster from "./Cluster";
|
||||
import Well from "./Well";
|
||||
import {LayoutPortal} from "../components/LayoutPortal";
|
||||
import { Redirect, Route, Switch } from 'react-router-dom'
|
||||
import { PrivateRoute } from '../components/Private'
|
||||
import { AdminLayoutPortal, LayoutPortal } from '../components/Layout'
|
||||
import Deposit from './Deposit'
|
||||
import Cluster from './Cluster'
|
||||
import Well from './Well'
|
||||
import AdminPanel from './AdminPanel'
|
||||
|
||||
export default function Main() {
|
||||
return (
|
||||
export const Main = () => (
|
||||
<Switch>
|
||||
<Route path="/deposit">
|
||||
<PrivateRoute path={'/admin/:tab?'} roles={['admin']}>
|
||||
<AdminLayoutPortal title={'Администраторская панель'}>
|
||||
<AdminPanel />
|
||||
</AdminLayoutPortal>
|
||||
</PrivateRoute>
|
||||
<Route path={'/deposit'}>
|
||||
<LayoutPortal noSheet title='Месторождение'>
|
||||
<Deposit />
|
||||
</LayoutPortal>
|
||||
</Route>
|
||||
<Route path="/cluster/:idClaster/:tab?">
|
||||
<LayoutPortal title="Анализ скважин куста">
|
||||
<Route path={'/cluster/:idClaster/:tab?'}>
|
||||
<LayoutPortal title={'Анализ скважин куста'}>
|
||||
<Cluster />
|
||||
</LayoutPortal>
|
||||
</Route>
|
||||
<Route path="/well/:idWell/:tab?">
|
||||
<Route path={'/well/:idWell/:tab?'}>
|
||||
<LayoutPortal>
|
||||
<Well />
|
||||
</LayoutPortal>
|
||||
</Route>
|
||||
<Route path="/">
|
||||
<Route path={'/'}>
|
||||
<Redirect to={{ pathname: `/deposit` }} />
|
||||
</Route>
|
||||
</Switch>
|
||||
);
|
||||
}
|
||||
)
|
||||
|
||||
export default Main
|
||||
|
Loading…
Reference in New Issue
Block a user