forked from ddrilling/asb_cloud_front
* Добавлено управление столбцами таблиц
* Сменён цвет фона страницы входа * Лого вынесено вне формы входа
This commit is contained in:
parent
79ca654d45
commit
0c092aa138
@ -1,9 +1,10 @@
|
|||||||
import { Form, Table, Button, Popconfirm } from 'antd'
|
import { memo, useCallback, useState, useEffect } from 'react'
|
||||||
|
import { Form, Button, Popconfirm } from 'antd'
|
||||||
import { EditOutlined, SaveOutlined, PlusOutlined, CloseCircleOutlined, DeleteOutlined } from '@ant-design/icons'
|
import { EditOutlined, SaveOutlined, PlusOutlined, CloseCircleOutlined, DeleteOutlined } from '@ant-design/icons'
|
||||||
import { useState, useEffect, memo, useCallback } from 'react'
|
|
||||||
|
|
||||||
import { invokeWebApiWrapperAsync } from '@components/factory'
|
import { invokeWebApiWrapperAsync } from '@components/factory'
|
||||||
|
|
||||||
|
import { Table } from '.'
|
||||||
import { EditableCell } from './EditableCell'
|
import { EditableCell } from './EditableCell'
|
||||||
|
|
||||||
const newRowKeyValue = 'newRow'
|
const newRowKeyValue = 'newRow'
|
||||||
|
57
src/components/Table/Table.tsx
Normal file
57
src/components/Table/Table.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { memo, useCallback, useEffect, useState, ReactNode } from 'react'
|
||||||
|
import { Table as RawTable, TableProps } from 'antd'
|
||||||
|
import { ColumnGroupType, ColumnType } from 'antd/lib/table'
|
||||||
|
|
||||||
|
import { OmitExtends } from '@utils'
|
||||||
|
import { getTableSettings, setTableSettings } from '@utils/storage'
|
||||||
|
import { applySettings, ColumnSettings, TableSettings } from '@utils/table_settings'
|
||||||
|
|
||||||
|
import TableSettingsChanger from './TableSettingsChanger'
|
||||||
|
import { tryAddKeys } from './EditableTable'
|
||||||
|
|
||||||
|
export type BaseTableColumn<T = any> = ColumnGroupType<T> | ColumnType<T>
|
||||||
|
export type TableColumns<T = any> = OmitExtends<BaseTableColumn<T>, ColumnSettings>[]
|
||||||
|
|
||||||
|
export type TableContainer = TableProps<any> & {
|
||||||
|
columns: TableColumns
|
||||||
|
dataSource: any[]
|
||||||
|
children?: ReactNode
|
||||||
|
tableName?: string
|
||||||
|
showSettingsChanger?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Table = memo<TableContainer>(({ columns, dataSource, children, tableName, showSettingsChanger, ...other }) => {
|
||||||
|
const [newColumns, setNewColumns] = useState<TableColumns>([])
|
||||||
|
const [settings, setSettings] = useState<TableSettings>({})
|
||||||
|
|
||||||
|
const onSettingsChanged = useCallback((settings?: TableSettings | null) => {
|
||||||
|
if (tableName)
|
||||||
|
setTableSettings(tableName, settings)
|
||||||
|
setSettings(settings ?? {})
|
||||||
|
}, [tableName])
|
||||||
|
|
||||||
|
useEffect(() => setSettings(tableName ? getTableSettings(tableName) : {}), [tableName])
|
||||||
|
useEffect(() => setNewColumns(() => {
|
||||||
|
const newColumns = applySettings(columns, settings)
|
||||||
|
if (tableName && showSettingsChanger) {
|
||||||
|
const oldTitle = newColumns[0].title
|
||||||
|
newColumns[0].title = (props) => (
|
||||||
|
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'stretch', justifyContent: 'space-between', position: 'relative', padding: '16px 0' }}>
|
||||||
|
<TableSettingsChanger columns={columns} settings={settings} onChange={onSettingsChanged}/>
|
||||||
|
<div>
|
||||||
|
{typeof oldTitle === 'function' ? oldTitle(props) : oldTitle}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return newColumns
|
||||||
|
}), [settings, columns, onSettingsChanged, showSettingsChanger, tableName])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<RawTable columns={newColumns} dataSource={tryAddKeys(dataSource)} {...other}>{children}</RawTable>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default Table
|
96
src/components/Table/TableSettingsChanger.tsx
Normal file
96
src/components/Table/TableSettingsChanger.tsx
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import { memo, useCallback, useEffect, useState } from 'react'
|
||||||
|
import { ColumnsType } from 'antd/lib/table'
|
||||||
|
import { Button, Modal, Switch, Table } from 'antd'
|
||||||
|
import { SettingOutlined } from '@ant-design/icons'
|
||||||
|
|
||||||
|
import { ColumnSettings, makeSettings, mergeSettings, TableSettings } from '@utils/table_settings'
|
||||||
|
import { TableColumns } from './Table'
|
||||||
|
import { makeColumn } from '.'
|
||||||
|
|
||||||
|
const parseSettings = (columns?: TableColumns, settings?: TableSettings | null): ColumnSettings[] => {
|
||||||
|
const newSettings = mergeSettings(makeSettings(columns ?? []), settings ?? {})
|
||||||
|
return Object.values(newSettings).map((set, i) => ({ ...set, key: i }))
|
||||||
|
}
|
||||||
|
|
||||||
|
const unparseSettings = (columns: ColumnSettings[]): TableSettings =>
|
||||||
|
Object.fromEntries(columns.map((column) => [column.columnName, column]))
|
||||||
|
|
||||||
|
export type TableSettingsChangerProps = {
|
||||||
|
title?: string
|
||||||
|
columns?: TableColumns
|
||||||
|
settings?: TableSettings | null
|
||||||
|
onChange: (settings: TableSettings | null) => void
|
||||||
|
}
|
||||||
|
|
||||||
|
export const TableSettingsChanger = memo<TableSettingsChangerProps>(({ title, columns, settings, onChange }) => {
|
||||||
|
const [visible, setVisible] = useState<boolean>(false)
|
||||||
|
const [newSettings, setNewSettings] = useState<ColumnSettings[]>(parseSettings(columns, settings))
|
||||||
|
const [tableColumns, setTableColumns] = useState<ColumnsType<ColumnSettings>>([])
|
||||||
|
|
||||||
|
const onVisibilityChange = useCallback((index: number, visible: boolean) => {
|
||||||
|
setNewSettings((oldSettings) => {
|
||||||
|
const newSettings = [...oldSettings]
|
||||||
|
newSettings[index].visible = visible
|
||||||
|
return newSettings
|
||||||
|
})
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
const toogleAll = useCallback((show: boolean) => {
|
||||||
|
setNewSettings((oldSettings) => oldSettings.map((column) => {
|
||||||
|
column.visible = show
|
||||||
|
return column
|
||||||
|
}))
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTableColumns([
|
||||||
|
makeColumn('Название', 'title'),
|
||||||
|
makeColumn(null, 'visible', {
|
||||||
|
title: () => (
|
||||||
|
<>
|
||||||
|
Показать
|
||||||
|
<Button type={'link'} onClick={() => toogleAll(true)}>Показать все</Button>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
render: (visible: boolean, _?: ColumnSettings, index: number = NaN) => (
|
||||||
|
<Switch
|
||||||
|
checked={visible}
|
||||||
|
checkedChildren={'Отображён'}
|
||||||
|
unCheckedChildren={'Скрыт'}
|
||||||
|
onChange={(visible) => onVisibilityChange(index, visible)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
}, [toogleAll, onVisibilityChange])
|
||||||
|
|
||||||
|
useEffect(() => setNewSettings(parseSettings(columns, settings)), [columns, settings])
|
||||||
|
|
||||||
|
const onModalOk = useCallback(() => {
|
||||||
|
onChange(unparseSettings(newSettings))
|
||||||
|
setVisible(false)
|
||||||
|
}, [newSettings, onChange])
|
||||||
|
|
||||||
|
const onModalCancel = useCallback(() => {
|
||||||
|
setNewSettings(parseSettings(columns, settings))
|
||||||
|
setVisible(false)
|
||||||
|
}, [columns, settings])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Modal
|
||||||
|
centered
|
||||||
|
visible={visible}
|
||||||
|
onCancel={onModalCancel}
|
||||||
|
onOk={onModalOk}
|
||||||
|
title={title ?? 'Настройка отображения таблицы'}
|
||||||
|
width={1000}
|
||||||
|
>
|
||||||
|
<Table columns={tableColumns} dataSource={newSettings} />
|
||||||
|
</Modal>
|
||||||
|
<Button size={'small'} style={{ position: 'absolute', left: 0, top: 0, opacity: .5 }} type={'link'} onClick={() => setVisible(true)} icon={<SettingOutlined />}/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
export default TableSettingsChanger
|
@ -1,15 +1,18 @@
|
|||||||
import { memo, useEffect, useState, ReactNode } from 'react'
|
import { memo, useEffect, useState, ReactNode } from 'react'
|
||||||
import { InputNumber, Select, Table as RawTable, Tag, SelectProps, TableProps } from 'antd'
|
import { InputNumber, Select, Tag, SelectProps } from 'antd'
|
||||||
import { DefaultOptionType, SelectValue } from 'antd/lib/select'
|
|
||||||
import { ColumnProps } from 'antd/lib/table'
|
import { ColumnProps } from 'antd/lib/table'
|
||||||
|
import { DefaultOptionType, SelectValue } from 'antd/lib/select'
|
||||||
import { Rule } from 'rc-field-form/lib/interface'
|
import { Rule } from 'rc-field-form/lib/interface'
|
||||||
|
|
||||||
import { tryAddKeys } from './EditableTable'
|
import { SimpleTimezoneDto } from '@api'
|
||||||
import { makeNumericSorter, makeStringSorter } from './sorters'
|
import { makeNumericSorter, makeStringSorter } from './sorters'
|
||||||
|
|
||||||
export { makeDateSorter, makeNumericSorter, makeStringSorter } from './sorters'
|
export { makeDateSorter, makeNumericSorter, makeStringSorter } from './sorters'
|
||||||
export { EditableTable, makeActionHandler } from './EditableTable'
|
export { EditableTable, makeActionHandler } from './EditableTable'
|
||||||
export { DatePickerWrapper } from './DatePickerWrapper'
|
export { DatePickerWrapper } from './DatePickerWrapper'
|
||||||
|
export { Table } from './Table'
|
||||||
|
|
||||||
|
export type { BaseTableColumn, TableColumns, TableContainer } from './Table'
|
||||||
|
|
||||||
export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/
|
export const RegExpIsFloat = /^[-+]?\d+\.?\d*$/
|
||||||
|
|
||||||
@ -302,15 +305,6 @@ export const makePaginationObject = (сontainer: PaginationContainer, ...other:
|
|||||||
current: 1 + Math.floor((сontainer.skip ?? 0) / (сontainer.take ?? 1))
|
current: 1 + Math.floor((сontainer.skip ?? 0) / (сontainer.take ?? 1))
|
||||||
})
|
})
|
||||||
|
|
||||||
export type TableContainer = TableProps<any> & {
|
|
||||||
dataSource: any[]
|
|
||||||
children?: ReactNode
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Table = memo<TableContainer>(({dataSource, children, ...other}) => (
|
|
||||||
<RawTable dataSource={tryAddKeys(dataSource)} {...other}>{children}</RawTable>
|
|
||||||
))
|
|
||||||
|
|
||||||
const rawTimezones = {
|
const rawTimezones = {
|
||||||
'Калининград': 2,
|
'Калининград': 2,
|
||||||
'Москва': 3,
|
'Москва': 3,
|
||||||
@ -337,11 +331,7 @@ const timezoneOptions = Object
|
|||||||
value: id,
|
value: id,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
export type TimezoneSelectProps = SelectProps & {
|
export const TimezoneSelect = memo<SelectProps>(({ onChange, ...other }) => {
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TimezoneSelect = memo<TimezoneSelectProps>(({ onChange, ...other }) => {
|
|
||||||
const [id, setId] = useState<keyof typeof rawTimezones | null>(null)
|
const [id, setId] = useState<keyof typeof rawTimezones | null>(null)
|
||||||
|
|
||||||
useEffect(() => onChange?.({
|
useEffect(() => onChange?.({
|
||||||
@ -353,6 +343,12 @@ export const TimezoneSelect = memo<TimezoneSelectProps>(({ onChange, ...other })
|
|||||||
return (<Select {...other} onChange={setId} value={id} />)
|
return (<Select {...other} onChange={setId} value={id} />)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const makeTimezoneRenderer = () => (timezone?: SimpleTimezoneDto) => {
|
||||||
|
if (!timezone) return 'UTC~?? :: Неизвестно'
|
||||||
|
const { hours, timezoneId } = timezone
|
||||||
|
return `UTC${hours && hours > 0 ? '+':''}${hours ? ('0' + hours).slice(-2) : '~??'} :: ${timezoneId ?? 'Неизвестно'}`
|
||||||
|
}
|
||||||
|
|
||||||
export const makeTimezoneColumn = (
|
export const makeTimezoneColumn = (
|
||||||
title: string = 'Зона',
|
title: string = 'Зона',
|
||||||
key: string = 'timezone',
|
key: string = 'timezone',
|
||||||
@ -362,11 +358,7 @@ export const makeTimezoneColumn = (
|
|||||||
) => makeColumn(title, key, {
|
) => makeColumn(title, key, {
|
||||||
width: 100,
|
width: 100,
|
||||||
editable: true,
|
editable: true,
|
||||||
render: (timezone) => {
|
render: makeTimezoneRenderer(),
|
||||||
if (!timezone) return 'UTC~?? :: Неизвестно'
|
|
||||||
const { hours, timezoneId } = timezone
|
|
||||||
return `UTC${hours > 0 ? '+':''}${hours ? ('0' + hours).slice(-2) : '~??'} :: ${timezoneId ?? 'Неизвестно'}`
|
|
||||||
},
|
|
||||||
input: (
|
input: (
|
||||||
<TimezoneSelect
|
<TimezoneSelect
|
||||||
allowClear={allowClear}
|
allowClear={allowClear}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { Button, Layout } from 'antd'
|
import { Button, Layout } from 'antd'
|
||||||
import {
|
import {
|
||||||
|
AuditOutlined,
|
||||||
CheckOutlined,
|
CheckOutlined,
|
||||||
CloseOutlined,
|
CloseOutlined,
|
||||||
FileWordOutlined,
|
FileWordOutlined,
|
||||||
LoadingOutlined,
|
LoadingOutlined,
|
||||||
ReloadOutlined,
|
ReloadOutlined,
|
||||||
WarningOutlined
|
WarningOutlined,
|
||||||
} from '@ant-design/icons'
|
} from '@ant-design/icons'
|
||||||
import { memo, useCallback, useEffect, useState } from 'react'
|
import { memo, useCallback, useEffect, useState } from 'react'
|
||||||
|
|
||||||
@ -30,7 +31,7 @@ const idStateUnknown = -1
|
|||||||
|
|
||||||
const stateString = {
|
const stateString = {
|
||||||
[idStateNotInitialized]: { icon: CloseOutlined, text: 'Не настроена' },
|
[idStateNotInitialized]: { icon: CloseOutlined, text: 'Не настроена' },
|
||||||
[idStateApproving]: { icon: LoadingOutlined, text: 'Согласовывается' },
|
[idStateApproving]: { icon: AuditOutlined, text: 'Согласовывается' },
|
||||||
[idStateCreating]: { icon: LoadingOutlined, text: 'Формируется' },
|
[idStateCreating]: { icon: LoadingOutlined, text: 'Формируется' },
|
||||||
[idStateReady]: { icon: CheckOutlined, text: 'Сформирована' },
|
[idStateReady]: { icon: CheckOutlined, text: 'Сформирована' },
|
||||||
[idStateError]: { icon: WarningOutlined, text: 'Ошибка формирования' },
|
[idStateError]: { icon: WarningOutlined, text: 'Ошибка формирования' },
|
||||||
|
@ -12,8 +12,6 @@ import { AuthService } from '@api'
|
|||||||
import '@styles/index.css'
|
import '@styles/index.css'
|
||||||
import Logo from '@images/Logo'
|
import Logo from '@images/Logo'
|
||||||
|
|
||||||
const logoIcon = <Logo width={130} />
|
|
||||||
|
|
||||||
export const Login = memo(() => {
|
export const Login = memo(() => {
|
||||||
const history = useHistory()
|
const history = useHistory()
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
@ -34,24 +32,27 @@ export const Login = memo(() => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<LoaderPortal show={showLoader} className={'loader-container login_page shadow'}>
|
<LoaderPortal show={showLoader} className={'loader-container login_page shadow'}>
|
||||||
<Card title={'Система мониторинга'} className={'shadow'} bordered={true} style={{ width: 350 }} extra={logoIcon}>
|
<div style={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
<Form onFinish={handleLogin}>
|
<Logo style={{ marginBottom: '10px' }}/>
|
||||||
<Form.Item name={'login'} rules={loginRules}>
|
<Card title={'Система мониторинга'} className={'shadow'} bordered={true} style={{ width: 350 }}>
|
||||||
<Input placeholder={'Пользователь'} prefix={<UserOutlined />} />
|
<Form onFinish={handleLogin}>
|
||||||
</Form.Item>
|
<Form.Item name={'login'} rules={loginRules}>
|
||||||
<Form.Item name={'password'} rules={passwordRules}>
|
<Input placeholder={'Пользователь'} prefix={<UserOutlined />} />
|
||||||
<Input.Password placeholder={'Пароль'} prefix={<LockOutlined />} />
|
</Form.Item>
|
||||||
</Form.Item>
|
<Form.Item name={'password'} rules={passwordRules}>
|
||||||
<Form.Item>
|
<Input.Password placeholder={'Пароль'} prefix={<LockOutlined />} />
|
||||||
<div className={'login-button'}>
|
</Form.Item>
|
||||||
<Button type={'primary'} htmlType={'submit'}>Вход</Button>
|
<Form.Item>
|
||||||
|
<div className={'login-button'}>
|
||||||
|
<Button type={'primary'} htmlType={'submit'}>Вход</Button>
|
||||||
|
</div>
|
||||||
|
</Form.Item>
|
||||||
|
<div className={'text-align-center'}>
|
||||||
|
<Link to={`/register`}>Отправить заявку на регистрацию</Link>
|
||||||
</div>
|
</div>
|
||||||
</Form.Item>
|
</Form>
|
||||||
<div className={'text-align-center'}>
|
</Card>
|
||||||
<Link to={`/register`}>Отправить заявку на регистрацию</Link>
|
</div>
|
||||||
</div>
|
|
||||||
</Form>
|
|
||||||
</Card>
|
|
||||||
</LoaderPortal>
|
</LoaderPortal>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { memo, useCallback } from 'react'
|
import { memo, useCallback } from 'react'
|
||||||
import { Layout, Menu, Popover } from 'antd'
|
import { Layout, Menu } from 'antd'
|
||||||
import { Switch, useParams, useHistory, useLocation } from 'react-router-dom'
|
import { Switch, useParams, useHistory, useLocation } from 'react-router-dom'
|
||||||
import {
|
import {
|
||||||
BarChartOutlined,
|
BarChartOutlined,
|
||||||
|
@ -29,14 +29,15 @@ html {
|
|||||||
margin-left: 30px;
|
margin-left: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.login_page{
|
.login_page {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
height:100%;
|
height: 100%;
|
||||||
width:100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 24px;
|
padding: 24px;
|
||||||
|
background-color: #9d9d9d;
|
||||||
}
|
}
|
||||||
|
|
||||||
.shadow{
|
.shadow{
|
||||||
@ -107,7 +108,7 @@ html {
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.border_small{
|
.border_small{
|
||||||
border: 1px solid rgba(0, 0, 0, 0.05);
|
border: 1px solid rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,3 +9,10 @@ export const mainFrameSize = () => ({
|
|||||||
})
|
})
|
||||||
|
|
||||||
export const arrayOrDefault = <T extends unknown>(arr?: unknown, def: T[] = []): T[] => Array.isArray(arr) ? arr : def
|
export const arrayOrDefault = <T extends unknown>(arr?: unknown, def: T[] = []): T[] => Array.isArray(arr) ? arr : def
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Объединить типы, исключив совпадающие поля справа
|
||||||
|
* @param T Тип, передаваемый полностью
|
||||||
|
* @param R Аддитивный тип
|
||||||
|
*/
|
||||||
|
export type OmitExtends<T, R> = T & Omit<R, keyof T>
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { OpenAPI, UserTokenDto } from '@api'
|
import { OpenAPI, UserTokenDto } from '@api'
|
||||||
import { Role, Permission } from './permissions'
|
import { Role, Permission } from './permissions'
|
||||||
|
import { normalizeColumn, optimizeColumn, TableSettings, TableSettingsStore } from './table_settings'
|
||||||
|
|
||||||
export enum StorageNames {
|
export enum StorageNames {
|
||||||
userId = 'userId',
|
userId = 'userId',
|
||||||
@ -7,6 +8,7 @@ export enum StorageNames {
|
|||||||
login = 'login',
|
login = 'login',
|
||||||
permissions = 'permissions',
|
permissions = 'permissions',
|
||||||
roles = 'roles',
|
roles = 'roles',
|
||||||
|
tableSettings = 'tableSettings',
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getArrayFromLocalStorage = <T extends string = string>(name: string, sep: string | RegExp = ','): T[] | null => {
|
export const getArrayFromLocalStorage = <T extends string = string>(name: string, sep: string | RegExp = ','): T[] | null => {
|
||||||
@ -39,3 +41,33 @@ export const removeUser = () => {
|
|||||||
localStorage.removeItem(StorageNames.permissions)
|
localStorage.removeItem(StorageNames.permissions)
|
||||||
localStorage.removeItem(StorageNames.roles)
|
localStorage.removeItem(StorageNames.roles)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getTableSettings = (tableName: string): TableSettings => {
|
||||||
|
const tablesJSON = localStorage.getItem(StorageNames.tableSettings)
|
||||||
|
if (!tablesJSON) return {}
|
||||||
|
try {
|
||||||
|
const tables: TableSettingsStore = JSON.parse(tablesJSON)
|
||||||
|
if (tableName in tables) {
|
||||||
|
const columns = tables[tableName] ?? {}
|
||||||
|
for (const [name, column] of Object.entries(columns))
|
||||||
|
columns[name] = normalizeColumn(column, name)
|
||||||
|
return columns
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
return {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const setTableSettings = (tableName: string, settings?: TableSettings | null): boolean => {
|
||||||
|
const currentJSON = localStorage.getItem(StorageNames.tableSettings)
|
||||||
|
try {
|
||||||
|
const currentStore: TableSettingsStore = currentJSON ? JSON.parse(currentJSON) : {}
|
||||||
|
const newSettings = settings ?? null
|
||||||
|
if (newSettings)
|
||||||
|
for (const [name, column] of Object.entries(newSettings))
|
||||||
|
newSettings[name] = optimizeColumn(column)
|
||||||
|
currentStore[tableName] = newSettings
|
||||||
|
localStorage.setItem(StorageNames.tableSettings, JSON.stringify(currentStore))
|
||||||
|
return true
|
||||||
|
} catch {}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
57
src/utils/table_settings.ts
Normal file
57
src/utils/table_settings.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { TableColumns } from '@components/Table'
|
||||||
|
|
||||||
|
export type ColumnSettings = {
|
||||||
|
columnName?: string
|
||||||
|
title?: string
|
||||||
|
visible?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TableSettings = Record<string, ColumnSettings>
|
||||||
|
export type TableSettingsStore = Record<string, TableSettings | null>
|
||||||
|
|
||||||
|
export const makeSettings = (columns: TableColumns): TableSettings => {
|
||||||
|
const settings: TableSettings = {}
|
||||||
|
columns.forEach((column) => {
|
||||||
|
if (!column.key) return
|
||||||
|
const key = String(column.key)
|
||||||
|
settings[key] = {
|
||||||
|
columnName: key,
|
||||||
|
title: typeof column.title === 'string' ? column.title : key,
|
||||||
|
visible: column.visible ?? true,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return settings
|
||||||
|
}
|
||||||
|
|
||||||
|
export const mergeSettings = (...settings: TableSettings[]): TableSettings => {
|
||||||
|
const newSettings: TableSettings = {}
|
||||||
|
for (const setting of settings) {
|
||||||
|
for (const [name, column] of Object.entries(setting)) {
|
||||||
|
newSettings[name] = {
|
||||||
|
...newSettings[name],
|
||||||
|
...column,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newSettings
|
||||||
|
}
|
||||||
|
|
||||||
|
export const normalizeColumn = (column: ColumnSettings, name?: string): ColumnSettings => ({
|
||||||
|
...column,
|
||||||
|
columnName: column.columnName ?? name,
|
||||||
|
visible: column.visible ?? true,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const optimizeColumn = (column: ColumnSettings): ColumnSettings => ({
|
||||||
|
...column,
|
||||||
|
visible: column.visible ?? true,
|
||||||
|
})
|
||||||
|
|
||||||
|
export const applySettings = (columns: TableColumns, settings: TableSettings): TableColumns => {
|
||||||
|
let newColumns: TableColumns = columns.map((column) => ({ ...column }))
|
||||||
|
newColumns = newColumns.filter((column) => {
|
||||||
|
const name = String(column.key)
|
||||||
|
return !(name in settings) || settings[name]?.visible
|
||||||
|
})
|
||||||
|
return newColumns
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user