Compare commits
No commits in common. "55a2ad2c5f4ba2e16587eb3831ef0d80184545c0" and "84b7197bb97bfbb028b8c6577de03483dae4c6c3" have entirely different histories.
55a2ad2c5f
...
84b7197bb9
@ -12,7 +12,6 @@ import DashboardBreadcrumb from './common/DashboardBreadcrumb'
|
|||||||
import DeveloperSidebar from './Developer/DeveloperSidebar'
|
import DeveloperSidebar from './Developer/DeveloperSidebar'
|
||||||
import { useThemeContext } from './context/ThemeContext'
|
import { useThemeContext } from './context/ThemeContext'
|
||||||
import { MessageProvider } from './context/MessageContext'
|
import { MessageProvider } from './context/MessageContext'
|
||||||
import { TableStateProvider } from './context/TableStateContext'
|
|
||||||
|
|
||||||
const { Content } = Layout
|
const { Content } = Layout
|
||||||
|
|
||||||
@ -29,42 +28,40 @@ const DashboardLayout = ({ children }) => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<MessageProvider>
|
<MessageProvider>
|
||||||
<TableStateProvider>
|
<Layout
|
||||||
<Layout
|
style={{ height: 'var(--unit-100vh)' }}
|
||||||
style={{ height: 'var(--unit-100vh)' }}
|
className={isDarkMode ? 'dark-mode' : 'light-mode'}
|
||||||
className={isDarkMode ? 'dark-mode' : 'light-mode'}
|
>
|
||||||
>
|
<DashboardNavigation />
|
||||||
<DashboardNavigation />
|
<Layout>
|
||||||
<Layout>
|
{isProduction ? (
|
||||||
{isProduction ? (
|
<ProductionSidebar />
|
||||||
<ProductionSidebar />
|
) : isInventory ? (
|
||||||
) : isInventory ? (
|
<InventorySidebar />
|
||||||
<InventorySidebar />
|
) : isFinance ? (
|
||||||
) : isFinance ? (
|
<FinanceSidebar />
|
||||||
<FinanceSidebar />
|
) : isSales ? (
|
||||||
) : isSales ? (
|
<SalesSidebar />
|
||||||
<SalesSidebar />
|
) : isManagement ? (
|
||||||
) : isManagement ? (
|
<ManagementSidebar />
|
||||||
<ManagementSidebar />
|
) : isDeveloper ? (
|
||||||
) : isDeveloper ? (
|
<DeveloperSidebar />
|
||||||
<DeveloperSidebar />
|
) : (
|
||||||
) : (
|
<ProductionSidebar /> // Default to production sidebar
|
||||||
<ProductionSidebar /> // Default to production sidebar
|
)}
|
||||||
)}
|
<Layout style={{ padding: '24px' }}>
|
||||||
<Layout style={{ padding: '24px' }}>
|
<Content>
|
||||||
<Content>
|
<Flex vertical style={{ height: '100%' }} gap='20px'>
|
||||||
<Flex vertical style={{ height: '100%' }} gap='20px'>
|
<Flex justify='space-between'>
|
||||||
<Flex justify='space-between'>
|
<DashboardBreadcrumb style={{ margin: '16px 0' }} />
|
||||||
<DashboardBreadcrumb style={{ margin: '16px 0' }} />
|
|
||||||
</Flex>
|
|
||||||
|
|
||||||
{children}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Content>
|
|
||||||
</Layout>
|
{children}
|
||||||
|
</Flex>
|
||||||
|
</Content>
|
||||||
</Layout>
|
</Layout>
|
||||||
</Layout>
|
</Layout>
|
||||||
</TableStateProvider>
|
</Layout>
|
||||||
</MessageProvider>
|
</MessageProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -87,14 +87,6 @@ const Printers = () => {
|
|||||||
cards={viewMode === 'cards'}
|
cards={viewMode === 'cards'}
|
||||||
visibleColumns={columnVisibility}
|
visibleColumns={columnVisibility}
|
||||||
expandHeight={true}
|
expandHeight={true}
|
||||||
saveFilterInSession={true}
|
|
||||||
saveFilterInUrl={true}
|
|
||||||
useFilterInSession={true}
|
|
||||||
useFilterInUrl={true}
|
|
||||||
saveSortInSession={true}
|
|
||||||
saveSortInUrl={true}
|
|
||||||
useSortInSession={true}
|
|
||||||
useSortInUrl={true}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Modal
|
<Modal
|
||||||
|
|||||||
@ -3,9 +3,7 @@ import { Breadcrumb, Button, Flex, Space } from 'antd'
|
|||||||
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
import { Link, useLocation, useNavigate } from 'react-router-dom'
|
||||||
import ArrowLeftIcon from '../../Icons/ArrowLeftIcon'
|
import ArrowLeftIcon from '../../Icons/ArrowLeftIcon'
|
||||||
import ArrowRightIcon from '../../Icons/ArrowRightIcon'
|
import ArrowRightIcon from '../../Icons/ArrowRightIcon'
|
||||||
import FilterIcon from '../../Icons/FilterIcon'
|
|
||||||
import { getModelByName } from '../../../database/ObjectModels'
|
import { getModelByName } from '../../../database/ObjectModels'
|
||||||
import { useTableState } from '../context/TableStateContext'
|
|
||||||
|
|
||||||
const breadcrumbNameMap = {
|
const breadcrumbNameMap = {
|
||||||
production: 'Production',
|
production: 'Production',
|
||||||
@ -28,7 +26,6 @@ const mainSections = ['production', 'inventory', 'management', 'developer']
|
|||||||
const DashboardBreadcrumb = () => {
|
const DashboardBreadcrumb = () => {
|
||||||
const location = useLocation()
|
const location = useLocation()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const { hasPageFilter, hasStoredFilter } = useTableState()
|
|
||||||
const pathSnippets = location.pathname.split('/').filter((i) => i)
|
const pathSnippets = location.pathname.split('/').filter((i) => i)
|
||||||
|
|
||||||
function segmentToModel(segment) {
|
function segmentToModel(segment) {
|
||||||
@ -51,9 +48,6 @@ const DashboardBreadcrumb = () => {
|
|||||||
const model = segmentToModel(segment)
|
const model = segmentToModel(segment)
|
||||||
const modelLabelPlural = model?.label ? `${model.label}s` : null
|
const modelLabelPlural = model?.label ? `${model.label}s` : null
|
||||||
const name = breadcrumbNameMap[segment] || modelLabelPlural || segment
|
const name = breadcrumbNameMap[segment] || modelLabelPlural || segment
|
||||||
const showFilterIcon =
|
|
||||||
hasPageFilter(url) ||
|
|
||||||
(model?.name && model.name !== 'unknown' && hasStoredFilter(model.name))
|
|
||||||
if (isMainSection) {
|
if (isMainSection) {
|
||||||
return {
|
return {
|
||||||
title: (
|
title: (
|
||||||
@ -67,19 +61,8 @@ const DashboardBreadcrumb = () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
title: (
|
title: (
|
||||||
<Link
|
<Link to={url} style={{ padding: '0 12px' }}>
|
||||||
to={url}
|
|
||||||
style={{
|
|
||||||
padding: '0 12px',
|
|
||||||
display: 'inline-flex',
|
|
||||||
alignItems: 'center',
|
|
||||||
gap: 6
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{name}
|
{name}
|
||||||
{showFilterIcon && (
|
|
||||||
<FilterIcon style={{ fontSize: 12, opacity: 0.65 }} />
|
|
||||||
)}
|
|
||||||
</Link>
|
</Link>
|
||||||
),
|
),
|
||||||
key: url
|
key: url
|
||||||
|
|||||||
@ -39,14 +39,13 @@ import ObjectCard from './ObjectCard'
|
|||||||
import FilterSidebar from './FilterSidebar'
|
import FilterSidebar from './FilterSidebar'
|
||||||
import XMarkIcon from '../../Icons/XMarkIcon'
|
import XMarkIcon from '../../Icons/XMarkIcon'
|
||||||
import CheckIcon from '../../Icons/CheckIcon'
|
import CheckIcon from '../../Icons/CheckIcon'
|
||||||
import { useNavigate, useLocation } from 'react-router-dom'
|
import { useNavigate } from 'react-router-dom'
|
||||||
import QuestionCircleIcon from '../../Icons/QuestionCircleIcon'
|
import QuestionCircleIcon from '../../Icons/QuestionCircleIcon'
|
||||||
import { AuthContext } from '../context/AuthContext'
|
import { AuthContext } from '../context/AuthContext'
|
||||||
import { ElectronContext } from '../context/ElectronContext'
|
import { ElectronContext } from '../context/ElectronContext'
|
||||||
import ActionsIcon from '../../Icons/ActionsIcon'
|
import ActionsIcon from '../../Icons/ActionsIcon'
|
||||||
import FilterIcon from '../../Icons/FilterIcon'
|
import FilterIcon from '../../Icons/FilterIcon'
|
||||||
import ScrollBox from './ScrollBox'
|
import ScrollBox from './ScrollBox'
|
||||||
import { useTableStatePersistence } from '../context/TableStateContext'
|
|
||||||
|
|
||||||
const logger = loglevel.getLogger('DasboardTable')
|
const logger = loglevel.getLogger('DasboardTable')
|
||||||
logger.setLevel(config.logLevel)
|
logger.setLevel(config.logLevel)
|
||||||
@ -118,14 +117,6 @@ const ObjectTable = forwardRef(
|
|||||||
showFilterSidebar = false,
|
showFilterSidebar = false,
|
||||||
expandHeight = false,
|
expandHeight = false,
|
||||||
showActions = true,
|
showActions = true,
|
||||||
saveFilterInSession = false,
|
|
||||||
saveFilterInUrl = false,
|
|
||||||
useFilterInSession = false,
|
|
||||||
useFilterInUrl = false,
|
|
||||||
saveSortInSession = false,
|
|
||||||
saveSortInUrl = false,
|
|
||||||
useSortInSession = false,
|
|
||||||
useSortInUrl = false,
|
|
||||||
onRowAction
|
onRowAction
|
||||||
},
|
},
|
||||||
ref
|
ref
|
||||||
@ -148,26 +139,6 @@ const ObjectTable = forwardRef(
|
|||||||
} = useContext(ApiServerContext)
|
} = useContext(ApiServerContext)
|
||||||
const isMobile = useMediaQuery({ maxWidth: 768 })
|
const isMobile = useMediaQuery({ maxWidth: 768 })
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
const location = useLocation()
|
|
||||||
const {
|
|
||||||
getPersistedFilter,
|
|
||||||
getPersistedSorter,
|
|
||||||
persistFilter,
|
|
||||||
persistTableState,
|
|
||||||
registerPageFilter,
|
|
||||||
getActiveFilterValues
|
|
||||||
} = useTableStatePersistence({
|
|
||||||
scope: type,
|
|
||||||
pagePath: location.pathname,
|
|
||||||
saveFilterInSession,
|
|
||||||
saveFilterInUrl,
|
|
||||||
useFilterInSession,
|
|
||||||
useFilterInUrl,
|
|
||||||
saveSortInSession,
|
|
||||||
saveSortInUrl,
|
|
||||||
useSortInSession,
|
|
||||||
useSortInUrl
|
|
||||||
})
|
|
||||||
var adjustedScrollHeight = scrollHeight
|
var adjustedScrollHeight = scrollHeight
|
||||||
if (isMobile) {
|
if (isMobile) {
|
||||||
adjustedScrollHeight = 'calc(var(--unit-100vh) - 298px)'
|
adjustedScrollHeight = 'calc(var(--unit-100vh) - 298px)'
|
||||||
@ -189,7 +160,6 @@ const ObjectTable = forwardRef(
|
|||||||
const activeFilterRef = useRef({})
|
const activeFilterRef = useRef({})
|
||||||
const tableSorterRef = useRef({})
|
const tableSorterRef = useRef({})
|
||||||
const [sidebarFilter, setSidebarFilter] = useState({})
|
const [sidebarFilter, setSidebarFilter] = useState({})
|
||||||
const [tableSorter, setTableSorter] = useState({})
|
|
||||||
const [initialized, setInitialized] = useState(false)
|
const [initialized, setInitialized] = useState(false)
|
||||||
|
|
||||||
// Table state
|
// Table state
|
||||||
@ -811,24 +781,8 @@ const ObjectTable = forwardRef(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const loadInitialPage = useCallback(async () => {
|
const loadInitialPage = useCallback(async () => {
|
||||||
const filter = getPersistedFilter()
|
loadPage(initialPage)
|
||||||
const sorter = getPersistedSorter()
|
}, [initialPage, loadPage])
|
||||||
const activeFilter = getActiveFilterValues(filter)
|
|
||||||
|
|
||||||
if (Object.keys(filter).length > 0) {
|
|
||||||
setSidebarFilter(filter)
|
|
||||||
}
|
|
||||||
if (sorter.field) {
|
|
||||||
tableSorterRef.current = sorter
|
|
||||||
setTableSorter(sorter)
|
|
||||||
}
|
|
||||||
|
|
||||||
loadPage(
|
|
||||||
initialPage,
|
|
||||||
activeFilter,
|
|
||||||
sorter.field ? sorter : null
|
|
||||||
)
|
|
||||||
}, [getPersistedFilter, getPersistedSorter, initialPage, loadPage])
|
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
reload,
|
reload,
|
||||||
@ -869,7 +823,6 @@ const ObjectTable = forwardRef(
|
|||||||
activeFilterRef.current = {}
|
activeFilterRef.current = {}
|
||||||
tableSorterRef.current = {}
|
tableSorterRef.current = {}
|
||||||
setSidebarFilter({})
|
setSidebarFilter({})
|
||||||
setTableSorter({})
|
|
||||||
setInitialized(false)
|
setInitialized(false)
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
setLazyLoading(false)
|
setLazyLoading(false)
|
||||||
@ -877,11 +830,6 @@ const ObjectTable = forwardRef(
|
|||||||
}
|
}
|
||||||
}, [type, masterFilter])
|
}, [type, masterFilter])
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
registerPageFilter(sidebarFilter)
|
|
||||||
return () => registerPageFilter({})
|
|
||||||
}, [sidebarFilter, registerPageFilter])
|
|
||||||
|
|
||||||
const getFilterDropdown = ({
|
const getFilterDropdown = ({
|
||||||
setSelectedKeys,
|
setSelectedKeys,
|
||||||
selectedKeys,
|
selectedKeys,
|
||||||
@ -918,6 +866,14 @@ const ObjectTable = forwardRef(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getActiveFilter = useCallback((filterState) => {
|
||||||
|
const active = {}
|
||||||
|
Object.entries(filterState).forEach(([k, v]) => {
|
||||||
|
if (v !== '' && v !== undefined) active[k] = v
|
||||||
|
})
|
||||||
|
return active
|
||||||
|
}, [])
|
||||||
|
|
||||||
const handleTableChange = (pagination, filters, sorter) => {
|
const handleTableChange = (pagination, filters, sorter) => {
|
||||||
const next = { ...sidebarFilter }
|
const next = { ...sidebarFilter }
|
||||||
|
|
||||||
@ -929,28 +885,23 @@ const ObjectTable = forwardRef(
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const nextSorter = {
|
|
||||||
field: sorter.columnKey,
|
|
||||||
order: sorter.order
|
|
||||||
}
|
|
||||||
|
|
||||||
setSidebarFilter(next)
|
setSidebarFilter(next)
|
||||||
setTableSorter(nextSorter)
|
|
||||||
persistTableState(next, nextSorter)
|
|
||||||
setPages([])
|
setPages([])
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
loadPage(initialPage, getActiveFilterValues(next), nextSorter)
|
loadPage(initialPage, getActiveFilter(next), {
|
||||||
|
field: sorter.columnKey,
|
||||||
|
order: sorter.order
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSidebarFilterChange = useCallback(
|
const handleSidebarFilterChange = useCallback(
|
||||||
(newSidebarFilter) => {
|
(newSidebarFilter) => {
|
||||||
setSidebarFilter(newSidebarFilter)
|
setSidebarFilter(newSidebarFilter)
|
||||||
persistFilter(newSidebarFilter)
|
|
||||||
setPages([])
|
setPages([])
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
loadPage(initialPage, getActiveFilterValues(newSidebarFilter))
|
loadPage(initialPage, getActiveFilter(newSidebarFilter))
|
||||||
},
|
},
|
||||||
[initialPage, loadPage, persistFilter]
|
[initialPage, loadPage, getActiveFilter]
|
||||||
)
|
)
|
||||||
|
|
||||||
const modelProperties = getModelProperties(type)
|
const modelProperties = getModelProperties(type)
|
||||||
@ -1037,8 +988,6 @@ const ObjectTable = forwardRef(
|
|||||||
|
|
||||||
const columnConfig = {
|
const columnConfig = {
|
||||||
sorter: isSortable ? { multiple: 1 } : undefined,
|
sorter: isSortable ? { multiple: 1 } : undefined,
|
||||||
sortOrder:
|
|
||||||
tableSorter?.field === prop.name ? tableSorter.order : null,
|
|
||||||
title: prop.label,
|
title: prop.label,
|
||||||
width: prop.columnWidth || width,
|
width: prop.columnWidth || width,
|
||||||
fixed: isMobile ? undefined : fixed,
|
fixed: isMobile ? undefined : fixed,
|
||||||
@ -1357,14 +1306,6 @@ ObjectTable.propTypes = {
|
|||||||
showFilterSidebar: PropTypes.bool,
|
showFilterSidebar: PropTypes.bool,
|
||||||
expandHeight: PropTypes.bool,
|
expandHeight: PropTypes.bool,
|
||||||
showActions: PropTypes.bool,
|
showActions: PropTypes.bool,
|
||||||
saveFilterInSession: PropTypes.bool,
|
|
||||||
saveFilterInUrl: PropTypes.bool,
|
|
||||||
useFilterInSession: PropTypes.bool,
|
|
||||||
useFilterInUrl: PropTypes.bool,
|
|
||||||
saveSortInSession: PropTypes.bool,
|
|
||||||
saveSortInUrl: PropTypes.bool,
|
|
||||||
useSortInSession: PropTypes.bool,
|
|
||||||
useSortInUrl: PropTypes.bool,
|
|
||||||
onRowAction: PropTypes.func
|
onRowAction: PropTypes.func
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,365 +0,0 @@
|
|||||||
import {
|
|
||||||
createContext,
|
|
||||||
useCallback,
|
|
||||||
useContext,
|
|
||||||
useMemo,
|
|
||||||
useState
|
|
||||||
} from 'react'
|
|
||||||
import { useSearchParams } from 'react-router-dom'
|
|
||||||
import PropTypes from 'prop-types'
|
|
||||||
|
|
||||||
const TableStateContext = createContext()
|
|
||||||
|
|
||||||
export const FILTER_URL_PARAM = 'filter'
|
|
||||||
export const SORT_URL_PARAM = 'sort'
|
|
||||||
|
|
||||||
const getSessionFilterKey = (scope) => `tableState:${scope}:filter`
|
|
||||||
const getSessionSortKey = (scope) => `tableState:${scope}:sort`
|
|
||||||
|
|
||||||
export const getActiveFilterValues = (filterState) => {
|
|
||||||
const active = {}
|
|
||||||
Object.entries(filterState || {}).forEach(([k, v]) => {
|
|
||||||
if (v !== '' && v !== undefined) active[k] = v
|
|
||||||
})
|
|
||||||
return active
|
|
||||||
}
|
|
||||||
|
|
||||||
const parseJsonParam = (raw) => {
|
|
||||||
if (!raw) return null
|
|
||||||
try {
|
|
||||||
const parsed = JSON.parse(raw)
|
|
||||||
return typeof parsed === 'object' && parsed !== null ? parsed : null
|
|
||||||
} catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const readFilterFromUrl = (searchParams) =>
|
|
||||||
parseJsonParam(searchParams.get(FILTER_URL_PARAM))
|
|
||||||
|
|
||||||
const readSortFromUrl = (searchParams) => {
|
|
||||||
const parsed = parseJsonParam(searchParams.get(SORT_URL_PARAM))
|
|
||||||
if (!parsed?.field || !parsed?.order) return null
|
|
||||||
return { field: parsed.field, order: parsed.order }
|
|
||||||
}
|
|
||||||
|
|
||||||
const readFilterFromSession = (scope) => {
|
|
||||||
try {
|
|
||||||
return parseJsonParam(sessionStorage.getItem(getSessionFilterKey(scope)))
|
|
||||||
} catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const hasStoredSessionFilter = (scope) => {
|
|
||||||
if (!scope) return false
|
|
||||||
return Object.keys(getActiveFilterValues(readFilterFromSession(scope) || {})).length > 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const readSortFromSession = (scope) => {
|
|
||||||
try {
|
|
||||||
const parsed = parseJsonParam(sessionStorage.getItem(getSessionSortKey(scope)))
|
|
||||||
if (!parsed?.field || !parsed?.order) return null
|
|
||||||
return { field: parsed.field, order: parsed.order }
|
|
||||||
} catch {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const writeFilterToSession = (scope, filter) => {
|
|
||||||
const key = getSessionFilterKey(scope)
|
|
||||||
if (Object.keys(filter).length > 0) {
|
|
||||||
sessionStorage.setItem(key, JSON.stringify(filter))
|
|
||||||
} else {
|
|
||||||
sessionStorage.removeItem(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const writeSortToSession = (scope, sorter) => {
|
|
||||||
const key = getSessionSortKey(scope)
|
|
||||||
if (sorter?.field && sorter?.order) {
|
|
||||||
sessionStorage.setItem(key, JSON.stringify(sorter))
|
|
||||||
} else {
|
|
||||||
sessionStorage.removeItem(key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const normalizeSorter = (sorter) => {
|
|
||||||
if (sorter?.field && sorter?.order) {
|
|
||||||
return { field: sorter.field, order: sorter.order }
|
|
||||||
}
|
|
||||||
return {}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TableStateProvider = ({ children }) => {
|
|
||||||
const [searchParams, setSearchParams] = useSearchParams()
|
|
||||||
const [pageFilters, setPageFilters] = useState({})
|
|
||||||
|
|
||||||
const getPersistedFilter = useCallback(
|
|
||||||
(scope, { useFilterInUrl = false, useFilterInSession = false } = {}) => {
|
|
||||||
if (useFilterInUrl) {
|
|
||||||
const fromUrl = readFilterFromUrl(searchParams)
|
|
||||||
if (fromUrl) return fromUrl
|
|
||||||
}
|
|
||||||
if (useFilterInSession) {
|
|
||||||
const fromSession = readFilterFromSession(scope)
|
|
||||||
if (fromSession) return fromSession
|
|
||||||
}
|
|
||||||
return {}
|
|
||||||
},
|
|
||||||
[searchParams]
|
|
||||||
)
|
|
||||||
|
|
||||||
const getPersistedSorter = useCallback(
|
|
||||||
(scope, { useSortInUrl = false, useSortInSession = false } = {}) => {
|
|
||||||
if (useSortInUrl) {
|
|
||||||
const fromUrl = readSortFromUrl(searchParams)
|
|
||||||
if (fromUrl) return fromUrl
|
|
||||||
}
|
|
||||||
if (useSortInSession) {
|
|
||||||
const fromSession = readSortFromSession(scope)
|
|
||||||
if (fromSession) return fromSession
|
|
||||||
}
|
|
||||||
return {}
|
|
||||||
},
|
|
||||||
[searchParams]
|
|
||||||
)
|
|
||||||
|
|
||||||
const persistFilter = useCallback(
|
|
||||||
(
|
|
||||||
scope,
|
|
||||||
filterState,
|
|
||||||
{ saveFilterInSession = false, saveFilterInUrl = false } = {}
|
|
||||||
) => {
|
|
||||||
const active = getActiveFilterValues(filterState)
|
|
||||||
if (saveFilterInSession) {
|
|
||||||
writeFilterToSession(scope, active)
|
|
||||||
}
|
|
||||||
if (saveFilterInUrl) {
|
|
||||||
const next = new URLSearchParams(searchParams)
|
|
||||||
if (Object.keys(active).length > 0) {
|
|
||||||
next.set(FILTER_URL_PARAM, JSON.stringify(active))
|
|
||||||
} else {
|
|
||||||
next.delete(FILTER_URL_PARAM)
|
|
||||||
}
|
|
||||||
setSearchParams(next, { replace: true })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[searchParams, setSearchParams]
|
|
||||||
)
|
|
||||||
|
|
||||||
const persistSort = useCallback(
|
|
||||||
(
|
|
||||||
scope,
|
|
||||||
sorter,
|
|
||||||
{ saveSortInSession = false, saveSortInUrl = false } = {}
|
|
||||||
) => {
|
|
||||||
const nextSorter = normalizeSorter(sorter)
|
|
||||||
if (saveSortInSession) {
|
|
||||||
writeSortToSession(scope, nextSorter)
|
|
||||||
}
|
|
||||||
if (saveSortInUrl) {
|
|
||||||
const next = new URLSearchParams(searchParams)
|
|
||||||
if (nextSorter.field && nextSorter.order) {
|
|
||||||
next.set(SORT_URL_PARAM, JSON.stringify(nextSorter))
|
|
||||||
} else {
|
|
||||||
next.delete(SORT_URL_PARAM)
|
|
||||||
}
|
|
||||||
setSearchParams(next, { replace: true })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[searchParams, setSearchParams]
|
|
||||||
)
|
|
||||||
|
|
||||||
const persistTableState = useCallback(
|
|
||||||
(
|
|
||||||
scope,
|
|
||||||
filterState,
|
|
||||||
sorter,
|
|
||||||
{
|
|
||||||
saveFilterInSession = false,
|
|
||||||
saveFilterInUrl = false,
|
|
||||||
saveSortInSession = false,
|
|
||||||
saveSortInUrl = false
|
|
||||||
} = {}
|
|
||||||
) => {
|
|
||||||
const active = getActiveFilterValues(filterState)
|
|
||||||
const nextSorter = normalizeSorter(sorter)
|
|
||||||
|
|
||||||
if (saveFilterInSession) {
|
|
||||||
writeFilterToSession(scope, active)
|
|
||||||
}
|
|
||||||
if (saveSortInSession) {
|
|
||||||
writeSortToSession(scope, nextSorter)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (saveFilterInUrl || saveSortInUrl) {
|
|
||||||
const next = new URLSearchParams(searchParams)
|
|
||||||
if (saveFilterInUrl) {
|
|
||||||
if (Object.keys(active).length > 0) {
|
|
||||||
next.set(FILTER_URL_PARAM, JSON.stringify(active))
|
|
||||||
} else {
|
|
||||||
next.delete(FILTER_URL_PARAM)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (saveSortInUrl) {
|
|
||||||
if (nextSorter.field && nextSorter.order) {
|
|
||||||
next.set(SORT_URL_PARAM, JSON.stringify(nextSorter))
|
|
||||||
} else {
|
|
||||||
next.delete(SORT_URL_PARAM)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setSearchParams(next, { replace: true })
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[searchParams, setSearchParams]
|
|
||||||
)
|
|
||||||
|
|
||||||
const setPageFilter = useCallback((path, filter) => {
|
|
||||||
const active = getActiveFilterValues(filter)
|
|
||||||
setPageFilters((prev) => {
|
|
||||||
const hasActive = Object.keys(active).length > 0
|
|
||||||
if (!hasActive) {
|
|
||||||
if (!(path in prev)) return prev
|
|
||||||
const next = { ...prev }
|
|
||||||
delete next[path]
|
|
||||||
return next
|
|
||||||
}
|
|
||||||
return { ...prev, [path]: active }
|
|
||||||
})
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const hasPageFilter = useCallback(
|
|
||||||
(path) => Object.keys(pageFilters[path] || {}).length > 0,
|
|
||||||
[pageFilters]
|
|
||||||
)
|
|
||||||
|
|
||||||
const hasStoredFilter = useCallback((scope) => hasStoredSessionFilter(scope), [])
|
|
||||||
|
|
||||||
const value = useMemo(
|
|
||||||
() => ({
|
|
||||||
getPersistedFilter,
|
|
||||||
getPersistedSorter,
|
|
||||||
persistFilter,
|
|
||||||
persistSort,
|
|
||||||
persistTableState,
|
|
||||||
setPageFilter,
|
|
||||||
hasPageFilter,
|
|
||||||
hasStoredFilter,
|
|
||||||
pageFilters
|
|
||||||
}),
|
|
||||||
[
|
|
||||||
getPersistedFilter,
|
|
||||||
getPersistedSorter,
|
|
||||||
persistFilter,
|
|
||||||
persistSort,
|
|
||||||
persistTableState,
|
|
||||||
setPageFilter,
|
|
||||||
hasPageFilter,
|
|
||||||
hasStoredFilter,
|
|
||||||
pageFilters
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<TableStateContext.Provider value={value}>
|
|
||||||
{children}
|
|
||||||
</TableStateContext.Provider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useTableState = () => {
|
|
||||||
const context = useContext(TableStateContext)
|
|
||||||
if (!context) {
|
|
||||||
throw new Error('useTableState must be used within a TableStateProvider')
|
|
||||||
}
|
|
||||||
return context
|
|
||||||
}
|
|
||||||
|
|
||||||
export const useTableStatePersistence = ({
|
|
||||||
scope,
|
|
||||||
pagePath,
|
|
||||||
saveFilterInSession = false,
|
|
||||||
saveFilterInUrl = false,
|
|
||||||
useFilterInSession = false,
|
|
||||||
useFilterInUrl = false,
|
|
||||||
saveSortInSession = false,
|
|
||||||
saveSortInUrl = false,
|
|
||||||
useSortInSession = false,
|
|
||||||
useSortInUrl = false
|
|
||||||
}) => {
|
|
||||||
const {
|
|
||||||
getPersistedFilter: getPersistedFilterCtx,
|
|
||||||
getPersistedSorter: getPersistedSorterCtx,
|
|
||||||
persistFilter: persistFilterCtx,
|
|
||||||
persistSort: persistSortCtx,
|
|
||||||
persistTableState: persistTableStateCtx,
|
|
||||||
setPageFilter
|
|
||||||
} = useTableState()
|
|
||||||
|
|
||||||
const readOptions = useMemo(
|
|
||||||
() => ({ useFilterInUrl, useFilterInSession, useSortInUrl, useSortInSession }),
|
|
||||||
[useFilterInUrl, useFilterInSession, useSortInUrl, useSortInSession]
|
|
||||||
)
|
|
||||||
|
|
||||||
const saveOptions = useMemo(
|
|
||||||
() => ({
|
|
||||||
saveFilterInSession,
|
|
||||||
saveFilterInUrl,
|
|
||||||
saveSortInSession,
|
|
||||||
saveSortInUrl
|
|
||||||
}),
|
|
||||||
[saveFilterInSession, saveFilterInUrl, saveSortInSession, saveSortInUrl]
|
|
||||||
)
|
|
||||||
|
|
||||||
const getPersistedFilter = useCallback(
|
|
||||||
() => getPersistedFilterCtx(scope, readOptions),
|
|
||||||
[getPersistedFilterCtx, scope, readOptions]
|
|
||||||
)
|
|
||||||
|
|
||||||
const getPersistedSorter = useCallback(
|
|
||||||
() => getPersistedSorterCtx(scope, readOptions),
|
|
||||||
[getPersistedSorterCtx, scope, readOptions]
|
|
||||||
)
|
|
||||||
|
|
||||||
const persistFilter = useCallback(
|
|
||||||
(filterState) => persistFilterCtx(scope, filterState, saveOptions),
|
|
||||||
[persistFilterCtx, scope, saveOptions]
|
|
||||||
)
|
|
||||||
|
|
||||||
const persistSort = useCallback(
|
|
||||||
(sorter) => persistSortCtx(scope, sorter, saveOptions),
|
|
||||||
[persistSortCtx, scope, saveOptions]
|
|
||||||
)
|
|
||||||
|
|
||||||
const persistTableState = useCallback(
|
|
||||||
(filterState, sorter) =>
|
|
||||||
persistTableStateCtx(scope, filterState, sorter, saveOptions),
|
|
||||||
[persistTableStateCtx, scope, saveOptions]
|
|
||||||
)
|
|
||||||
|
|
||||||
const registerPageFilter = useCallback(
|
|
||||||
(filterState) => {
|
|
||||||
if (pagePath) {
|
|
||||||
setPageFilter(pagePath, filterState)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[pagePath, setPageFilter]
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
getPersistedFilter,
|
|
||||||
getPersistedSorter,
|
|
||||||
persistFilter,
|
|
||||||
persistSort,
|
|
||||||
persistTableState,
|
|
||||||
registerPageFilter,
|
|
||||||
getActiveFilterValues
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TableStateProvider.propTypes = {
|
|
||||||
children: PropTypes.node.isRequired
|
|
||||||
}
|
|
||||||
|
|
||||||
export default TableStateContext
|
|
||||||
@ -226,7 +226,7 @@ export const Printer = {
|
|||||||
'connectedAt',
|
'connectedAt',
|
||||||
'updatedAt'
|
'updatedAt'
|
||||||
],
|
],
|
||||||
filters: ['name', '_reference', 'state', 'tags'],
|
filters: ['name', '_id', 'state', 'tags'],
|
||||||
sorters: ['name', 'state', 'connectedAt'],
|
sorters: ['name', 'state', 'connectedAt'],
|
||||||
group: ['tags'],
|
group: ['tags'],
|
||||||
properties: [
|
properties: [
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user