Refactor Dashboard Components to Remove Unused State Management

- Removed unnecessary useState hooks for view state management in multiple dashboard components (Invoices, PaymentPolicies, Payments, TaxRecords, FilamentStocks, OrderItems, PartStocks, ProductStocks, PurchaseOrders, Shipments, StockAudits, StockEvents, StockLocations, StockTransfers, AppPasswords, AuditLogs, Couriers, CourierServices, DocumentJobs, DocumentPrinters, DocumentSizes, DocumentTemplates, Filaments, FilamentSkus).
- Updated ObjectListViewProvider integration to streamline component functionality and improve maintainability.
- Adjusted CSS variables in App.css for enhanced styling consistency across components.
This commit is contained in:
Tom Butcher 2026-09-02 22:41:49 +01:00
parent 444e694593
commit 9d1a032e10
59 changed files with 1383 additions and 1271 deletions

View File

@ -47,6 +47,12 @@
font-family: 'DM Sans';
}
:root {
--color-list-view-tabs-border: #8282822e;
--color-button-border: #8282822e;
--color-button-background: #8282822e;
}
.ant-dropdown {
z-index: 999;
}
@ -2754,7 +2760,7 @@ span.ant-skeleton-input.ant-skeleton-input-sm.text-skeleton {
}
.list-view-tabs-options-dropdown-privacy {
margin: 3px 12px 8px;
margin: 3px 12px 14px 16px;
}
.segmented-nav-drag-handle {

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Invoices = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('invoices')
@ -36,13 +31,11 @@ const Invoices = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='invoice'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='invoice'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const Invoices = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const Invoices = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,10 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const PaymentPolicies = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('paymentPolicy')
const [columnVisibility, setColumnVisibility] =
@ -31,12 +27,11 @@ const PaymentPolicies = () => {
useSortSidebarVisibility('PaymentPolicies')
return (
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='paymentPolicy'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='paymentPolicy'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -69,7 +64,7 @@ const PaymentPolicies = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -86,10 +81,9 @@ const PaymentPolicies = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Payments = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('payments')
@ -36,13 +31,11 @@ const Payments = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='payment'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='payment'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const Payments = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const Payments = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const TaxRecords = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('taxRecord')
@ -36,13 +31,11 @@ const TaxRecords = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='taxRecord'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='taxRecord'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const TaxRecords = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const TaxRecords = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,6 +1,6 @@
// src/filamentStocks.js
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -21,12 +21,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const FilamentStocks = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('filamentStocks')
@ -41,13 +35,11 @@ const FilamentStocks = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='filamentStock'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='filamentStock'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -80,8 +72,6 @@ const FilamentStocks = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -98,11 +88,10 @@ const FilamentStocks = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const OrderItems = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('orderItems')
@ -36,13 +31,11 @@ const OrderItems = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='orderItem'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='orderItem'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const OrderItems = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const OrderItems = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,6 +1,6 @@
// src/partStocks.js
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -21,12 +21,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const PartStocks = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('partStocks')
@ -41,13 +35,11 @@ const PartStocks = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='partStock'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='partStock'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -80,8 +72,6 @@ const PartStocks = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -98,11 +88,10 @@ const PartStocks = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,7 +1,7 @@
// src/components/Dashboard/Inventory/ProductStocks.jsx
// ProductStocks - tracks assembled products consisting of part stocks
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -22,12 +22,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const ProductStocks = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('productStocks')
@ -42,13 +36,11 @@ const ProductStocks = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='productStock'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='productStock'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -81,8 +73,6 @@ const ProductStocks = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -99,11 +89,10 @@ const ProductStocks = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const PurchaseOrders = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('purchaseOrders')
@ -36,13 +31,11 @@ const PurchaseOrders = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='purchaseOrder'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='purchaseOrder'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const PurchaseOrders = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const PurchaseOrders = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Shipments = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('shipments')
@ -36,13 +31,11 @@ const Shipments = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='shipment'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='shipment'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const Shipments = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const Shipments = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,6 +1,6 @@
// src/stockAudits.js
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -21,12 +21,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const StockAudits = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('stockAudits')
@ -41,13 +35,11 @@ const StockAudits = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='stockAudit'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='stockAudit'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -80,8 +72,6 @@ const StockAudits = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -98,11 +88,10 @@ const StockAudits = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTable from '../common/ObjectTable'
@ -17,10 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const StockEvents = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('stockEvents')
@ -35,13 +31,11 @@ const StockEvents = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='stockEvent'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='stockEvent'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -74,8 +68,6 @@ const StockEvents = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +84,10 @@ const StockEvents = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -19,12 +19,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const StockLocations = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('stockLocations')
@ -39,13 +33,11 @@ const StockLocations = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='stockLocation'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='stockLocation'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -78,8 +70,6 @@ const StockLocations = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -96,11 +86,10 @@ const StockLocations = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -19,12 +19,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const StockTransfers = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('stockTransfers')
@ -39,13 +33,11 @@ const StockTransfers = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='stockTransfer'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='stockTransfer'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -78,8 +70,6 @@ const StockTransfers = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -96,11 +86,10 @@ const StockTransfers = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ColumnViewButton from '../common/ColumnViewButton'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const AppPasswords = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('appPassword')
const [columnVisibility, setColumnVisibility] =
@ -35,13 +30,11 @@ const AppPasswords = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='appPassword'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='appPassword'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
@ -74,8 +67,6 @@ const AppPasswords = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type='appPassword'
@ -92,12 +83,10 @@ const AppPasswords = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
/>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import useColumnVisibility from '../hooks/useColumnVisibility'
@ -16,11 +16,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const AuditLogs = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [columnVisibility, updateColumnVisibility] =
useColumnVisibility('auditLog')
@ -33,13 +28,11 @@ const AuditLogs = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='auditLog'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='auditLog'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -68,8 +61,6 @@ const AuditLogs = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -85,11 +76,10 @@ const AuditLogs = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const CourierServices = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('courierService')
@ -36,13 +31,11 @@ const CourierServices = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='courierService'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='courierService'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const CourierServices = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const CourierServices = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Couriers = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('courier')
@ -35,13 +30,11 @@ const Couriers = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='courier'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='courier'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -74,7 +67,7 @@ const Couriers = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -91,11 +84,9 @@ const Couriers = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const DocumentJobs = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('documentJob')
@ -36,13 +31,11 @@ const DocumentJobs = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='documentJob'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='documentJob'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const DocumentJobs = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const DocumentJobs = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,10 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const DocumentPrinters = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('documentPrinter')
@ -35,13 +31,11 @@ const DocumentPrinters = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='documentPrinter'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='documentPrinter'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -74,7 +68,7 @@ const DocumentPrinters = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -91,11 +85,9 @@ const DocumentPrinters = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,10 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const DocumentSizes = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('documentSize')
const [columnVisibility, setColumnVisibility] =
@ -34,13 +30,11 @@ const DocumentSizes = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='documentSize'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='documentSize'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -73,7 +67,7 @@ const DocumentSizes = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -90,11 +84,9 @@ const DocumentSizes = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const DocumentTemplates = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('documentTemplate')
@ -36,13 +31,11 @@ const DocumentTemplates = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='documentTemplate'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='documentTemplate'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const DocumentTemplates = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const DocumentTemplates = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -19,12 +19,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const FilamentSkus = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('filamentSkus')
@ -39,13 +33,11 @@ const FilamentSkus = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='filamentSku'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='filamentSku'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -78,8 +70,6 @@ const FilamentSkus = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -96,11 +86,10 @@ const FilamentSkus = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,6 +1,6 @@
// src/filaments.js
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -21,11 +21,6 @@ import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
const Filaments = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
// View mode state (cards/list), persisted in sessionStorage via custom hook
const [viewMode, setViewMode] = useViewMode('filament')
@ -41,13 +36,11 @@ const Filaments = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='filament'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='filament'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
@ -80,8 +73,6 @@ const Filaments = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type='filament'
@ -98,12 +89,10 @@ const Filaments = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
/>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Files = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('file')
@ -33,17 +28,11 @@ const Files = () => {
const [showSortSidebar, setShowSortSidebar] =
useSortSidebarVisibility('Files')
return (
<>
<ObjectListViewProvider objectType='file' tableRef={tableRef}>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='file'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='file'
pageName='list'
@ -57,8 +46,8 @@ const Files = () => {
/>
<ExportListButton objectType='file' />
</Space>
<ListViewTabs />
<Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
@ -73,8 +62,7 @@ const Files = () => {
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -91,11 +79,9 @@ const Files = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</ObjectListViewProvider>
)
}

View File

@ -1,6 +1,6 @@
// src/hosts.js
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -21,11 +21,6 @@ import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
const Hosts = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
// View mode state (cards/list), persisted in sessionStorage via custom hook
const [viewMode, setViewMode] = useViewMode('host')
@ -40,13 +35,11 @@ const Hosts = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='host'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='host'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
@ -79,8 +72,6 @@ const Hosts = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type='host'
@ -97,12 +88,10 @@ const Hosts = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
/>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -19,11 +19,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Materials = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('material')
@ -38,13 +33,11 @@ const Materials = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='material'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='material'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
@ -77,8 +70,6 @@ const Materials = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type='material'
@ -95,12 +86,10 @@ const Materials = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
/>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const NoteTypes = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('noteType')
@ -36,13 +31,11 @@ const NoteTypes = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='noteType'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='noteType'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const NoteTypes = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const NoteTypes = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -19,12 +19,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const PartSkus = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('partSkus')
@ -38,13 +32,11 @@ const PartSkus = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='partSku'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='partSku'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -77,8 +69,6 @@ const PartSkus = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -95,11 +85,10 @@ const PartSkus = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,6 +1,6 @@
// src/gcodefiles.js
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
@ -24,10 +24,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Parts = (filter) => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('part')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('part')
@ -40,13 +36,11 @@ const Parts = (filter) => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='part'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='part'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -79,7 +73,7 @@ const Parts = (filter) => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -97,11 +91,9 @@ const Parts = (filter) => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const PermissionSettingsList = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('permissionSetting')
@ -36,13 +31,11 @@ const PermissionSettingsList = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='permissionSetting'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='permissionSetting'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const PermissionSettingsList = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const PermissionSettingsList = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -19,11 +19,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const ProductCategories = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('productCategory')
@ -38,13 +33,11 @@ const ProductCategories = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='productCategory'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='productCategory'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
@ -77,8 +70,6 @@ const ProductCategories = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type='productCategory'
@ -95,12 +86,10 @@ const ProductCategories = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
/>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
@ -19,12 +19,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const ProductSkus = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('productSkus')
@ -39,13 +33,11 @@ const ProductSkus = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='productSku'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='productSku'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -78,8 +70,6 @@ const ProductSkus = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -96,11 +86,10 @@ const ProductSkus = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,6 +1,6 @@
// src/gcodefiles.js
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { useNavigate } from 'react-router-dom'
import {
Button,
@ -36,11 +36,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Products = () => {
const navigate = useNavigate()
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('Products')
@ -310,13 +305,11 @@ const Products = () => {
}
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='product'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='product'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -350,7 +343,7 @@ const Products = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type={'product'}
@ -366,11 +359,9 @@ const Products = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const StockAuditLevels = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('stockAuditLevel')
@ -35,12 +30,11 @@ const StockAuditLevels = () => {
useSortSidebarVisibility('StockAuditLevels')
return (
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='stockAuditLevel'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='stockAuditLevel'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -73,7 +67,7 @@ const StockAuditLevels = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -90,10 +84,9 @@ const StockAuditLevels = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const TaxRates = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('taxRate')
@ -35,13 +30,11 @@ const TaxRates = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='taxRate'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='taxRate'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -74,7 +67,7 @@ const TaxRates = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -91,11 +84,9 @@ const TaxRates = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const UserGroups = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('userGroup')
@ -35,13 +30,11 @@ const UserGroups = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='userGroup'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='userGroup'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -74,7 +67,7 @@ const UserGroups = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -91,11 +84,9 @@ const UserGroups = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Users = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('user')
@ -35,12 +30,11 @@ const Users = () => {
return (
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='user'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='user'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -73,7 +67,7 @@ const Users = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type={'user'}
@ -90,10 +84,9 @@ const Users = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,10 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Vendors = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('vendor')
@ -33,46 +29,40 @@ const Vendors = () => {
useSortSidebarVisibility('Vendors')
return (
<>
<ObjectListViewProvider objectType='vendor' tableRef={tableRef}>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='vendor'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='vendor'
pageName='list'
onReload={() => tableRef.current?.reload()}
/>
<ColumnViewButton
type='vendor'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='vendor' />
</Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
/>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
type='vendor'
pageName='list'
onReload={() => tableRef.current?.reload()}
/>
<ColumnViewButton
type='vendor'
loading={false}
visibleState={columnVisibility}
updateVisibleState={setColumnVisibility}
/>
<ExportListButton objectType='vendor' />
</Space>
<ListViewTabs />
<Space>
<SortSidebarButton
active={showSortSidebar}
onClick={() => setShowSortSidebar(!showSortSidebar)}
/>
<FilterSidebarButton
active={showFilterSidebar}
onClick={() => setShowFilterSidebar(!showFilterSidebar)}
/>
<ObjectTableViewButton
viewMode={viewMode}
setViewMode={setViewMode}
/>
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -89,11 +79,9 @@ const Vendors = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
@ -17,10 +17,6 @@ import useViewMode from '../hooks/useViewMode'
const FilamentProfiles = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('FilamentProfiles')
const [columnVisibility, setColumnVisibility] =
@ -33,13 +29,11 @@ const FilamentProfiles = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='filamentProfile'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='filamentProfile'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
@ -71,8 +65,6 @@ const FilamentProfiles = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type='filamentProfile'
@ -89,11 +81,10 @@ const FilamentProfiles = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,6 +1,6 @@
// src/gcodefiles.js
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import useColumnVisibility from '../hooks/useColumnVisibility'
import ObjectTable from '../common/ObjectTable'
@ -20,10 +20,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const GCodeFiles = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('gcodeFile')
@ -38,13 +34,11 @@ const GCodeFiles = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='gcodeFile'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='gcodeFile'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
@ -77,7 +71,7 @@ const GCodeFiles = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type='gcodeFile'
@ -94,11 +88,9 @@ const GCodeFiles = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,6 +1,6 @@
// src/Jobs.js
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import useColumnVisibility from '../hooks/useColumnVisibility.jsx'
import ObjectTable from '../common/ObjectTable.jsx'
@ -19,10 +19,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Jobs = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('job')
@ -36,13 +32,11 @@ const Jobs = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='job'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='job'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,8 +69,6 @@ const Jobs = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type={'job'}
@ -93,11 +85,10 @@ const Jobs = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ColumnViewButton from '../common/ColumnViewButton'
import ExportListButton from '../common/ExportListButton'
@ -16,10 +16,6 @@ import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
import useViewMode from '../hooks/useViewMode'
const PrinterProfiles = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('PrinterProfiles')
const [columnVisibility, setColumnVisibility] =
@ -32,13 +28,11 @@ const PrinterProfiles = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='printerProfile'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='printerProfile'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
@ -70,8 +64,6 @@ const PrinterProfiles = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type='printerProfile'
@ -88,11 +80,10 @@ const PrinterProfiles = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,6 +1,6 @@
// src/Printers.js
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Space, Flex } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -20,11 +20,6 @@ import useSortSidebarVisibility from '../hooks/useSortSidebarVisibility'
const Printers = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
// View mode state (cards/list), persisted in sessionStorage via custom hook
const [viewMode, setViewMode] = useViewMode('Printers')
@ -41,13 +36,11 @@ const Printers = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='printer'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='printer'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space>
<ObjectActions
@ -80,8 +73,6 @@ const Printers = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type='printer'
@ -98,12 +89,10 @@ const Printers = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
/>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,6 +1,6 @@
// src/SubJobs.js
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import useColumnVisibility from '../hooks/useColumnVisibility.jsx'
import ObjectTable from '../common/ObjectTable.jsx'
@ -19,10 +19,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const SubJobs = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('subJob')
@ -36,13 +32,11 @@ const SubJobs = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='subJob'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='subJob'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,8 +69,6 @@ const SubJobs = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
type={'subJob'}
@ -93,11 +85,10 @@ const SubJobs = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Clients = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('client')
@ -35,13 +30,11 @@ const Clients = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='client'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='client'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -74,7 +67,7 @@ const Clients = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -91,11 +84,9 @@ const Clients = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,10 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const FulfillmentPolicies = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('fulfillmentPolicy')
const [columnVisibility, setColumnVisibility] =
@ -31,12 +27,11 @@ const FulfillmentPolicies = () => {
useSortSidebarVisibility('FulfillmentPolicies')
return (
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='fulfillmentPolicy'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='fulfillmentPolicy'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -69,7 +64,7 @@ const FulfillmentPolicies = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -86,10 +81,9 @@ const FulfillmentPolicies = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const ListingVarients = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('listingVarients')
@ -36,13 +31,11 @@ const ListingVarients = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='listingVarient'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='listingVarient'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const ListingVarients = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const ListingVarients = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Listings = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('listings')
@ -36,13 +31,11 @@ const Listings = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='listing'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='listing'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const Listings = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const Listings = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const Marketplaces = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('marketplaces')
@ -36,13 +31,11 @@ const Marketplaces = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='marketplace'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='marketplace'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const Marketplaces = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const Marketplaces = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,10 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const ReturnPolicies = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('returnPolicy')
const [columnVisibility, setColumnVisibility] = useColumnVisibility('returnPolicy')
@ -30,12 +26,11 @@ const ReturnPolicies = () => {
useSortSidebarVisibility('ReturnPolicies')
return (
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='returnPolicy'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='returnPolicy'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -68,7 +63,7 @@ const ReturnPolicies = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -85,10 +80,9 @@ const ReturnPolicies = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,4 +1,4 @@
import { useRef, useState } from 'react'
import { useRef } from 'react'
import { Flex, Space } from 'antd'
import ObjectTable from '../common/ObjectTable'
import ObjectActions from '../common/ObjectActions'
@ -17,11 +17,6 @@ import { ObjectListViewProvider } from '../context/ObjectListViewContext'
const SalesOrders = () => {
const tableRef = useRef()
const [viewState, setViewState] = useState({
activeView: null,
handleFilterSortChange: null
})
const [viewMode, setViewMode] = useViewMode('salesOrders')
@ -36,13 +31,11 @@ const SalesOrders = () => {
return (
<>
<Flex vertical={'true'} gap='large' className='h-100'>
<ObjectListViewProvider
objectType='salesOrder'
tableRef={tableRef}
onViewStateChange={setViewState}
>
<ObjectListViewProvider
objectType='salesOrder'
tableRef={tableRef}
>
<Flex vertical={'true'} gap='large' className='h-100'>
<Flex justify='space-between' style={{ minHeight: 0 }} gap='small'>
<Space size='small'>
<ObjectActions
@ -75,7 +68,7 @@ const SalesOrders = () => {
<ListViewEditButtons showStartingDivider={true} />
</Space>
</Flex>
</ObjectListViewProvider>
<ObjectTable
ref={tableRef}
visibleColumns={columnVisibility}
@ -92,11 +85,9 @@ const SalesOrders = () => {
saveSortInUrl={true}
useSortInSession={true}
useSortInUrl={true}
activeObjectView={viewState.activeView}
onObjectViewFilterSortChange={viewState.handleFilterSortChange}
/>
</Flex>
</>
</Flex>
</ObjectListViewProvider>
)
}

View File

@ -1,5 +1,5 @@
import PropTypes from 'prop-types'
import React, { useMemo } from 'react'
import React, { useEffect, useMemo, useState } from 'react'
import {
Input,
Dropdown,
@ -46,7 +46,8 @@ const ViewOptionsDropdown = ({ view, isEditing, onChange, onDelete }) => {
domEvent.stopPropagation()
onDelete?.()
}
}
},
{ type: 'divider' }
]
}
@ -203,6 +204,18 @@ const ListViewTabs = () => {
viewTabSignatures
} = useObjectListView()
const [animated, setAnimated] = useState(false)
useEffect(() => {
if (initialLoading) {
setAnimated(false)
return undefined
}
const timeoutId = setTimeout(() => setAnimated(true), 10)
return () => clearTimeout(timeoutId)
}, [initialLoading])
const model = getModelByName(objectType)
const options = useMemo(() => {
@ -286,6 +299,7 @@ const ListViewTabs = () => {
options={options}
onChange={selectTab}
onReorder={isEditing ? reorderDraftViews : undefined}
animated={animated}
/>
{isEditing && (
<Button

View File

@ -52,11 +52,13 @@ import QuickPropertyFilters from './QuickPropertyFilters'
import FilterInput from './FilterInput'
import {
getActiveFilterValues,
useTableState,
useTableStatePersistence
} from '../context/TableStateContext'
import { hasActionPermission } from '../../../database/permissions'
import Tooltip from './Tooltip'
import { ObjectTableFilterContext } from './ObjectTableFilterContext'
import ObjectListViewContext from '../context/ObjectListViewContext'
const logger = loglevel.getLogger('DasboardTable')
logger.setLevel(config.logLevel)
@ -91,6 +93,37 @@ const fromFilterExpression = (expr) => {
return [expr]
}
const areValuesEqual = (v1, v2) => {
const id1 = v1 && typeof v1 === 'object' && v1._id ? v1._id : v1
const id2 = v2 && typeof v2 === 'object' && v2._id ? v2._id : v2
return String(id1) === String(id2)
}
const getChangedKeys = (existing, updated) => {
if (!updated || typeof updated !== 'object') return []
return Object.keys(updated).filter((key) => {
if (key === '_id' || key === 'objectType') return false
if (!existing) return true
return !areValuesEqual(existing[key], updated[key])
})
}
const updateAffectsFilterOrSort = (
changedKeys,
filter,
masterFilter,
sorter
) => {
if (!changedKeys.length) return false
const filterKeys = new Set([
...Object.keys(filter || {}),
...Object.keys(masterFilter || {})
])
if (changedKeys.some((key) => filterKeys.has(key))) return true
if (sorter?.field && changedKeys.includes(sorter.field)) return true
return false
}
const ColumnFilterDropdown = ({
setSelectedKeys,
selectedKeys,
@ -255,8 +288,6 @@ const ObjectTable = forwardRef(
saveSortInUrl = false,
useSortInSession = false,
useSortInUrl = false,
activeObjectView = null,
onObjectViewFilterSortChange,
onRowAction
},
ref
@ -264,6 +295,14 @@ const ObjectTable = forwardRef(
const { token, userProfile } = useContext(AuthContext)
const { isElectron } = useContext(ElectronContext)
const { callAction } = useActions()
const objectListView = useContext(ObjectListViewContext)
const listViewId = objectListView?.listViewId ?? null
const listViewFilter = objectListView?.listViewFilter ?? null
const listViewSort = objectListView?.listViewSort ?? null
const activeObjectView = objectListView?.activeObjectView ?? null
const onObjectViewFilterSortChange = objectListView?.isEditing
? objectListView.handleFilterSortChange
: null
const onStateChangeRef = useRef(onStateChange)
useEffect(() => {
onStateChangeRef.current = onStateChange
@ -271,7 +310,7 @@ const ObjectTable = forwardRef(
const {
fetchObjects,
connected,
subscribeToObjectUpdates,
subscribeToAllObjectUpdates,
subscribeToObjectTypeUpdates,
updateMultipleObjects,
setObjectActivity,
@ -282,13 +321,19 @@ const ObjectTable = forwardRef(
const {
getPersistedFilter,
getPersistedSorter,
getAllTabPersistedFilter,
getAllTabPersistedSorter,
persistFilter,
persistSort,
persistTableState,
persistAllTabTableState,
registerPageFilter,
registerPageSorter
registerPageSorter,
registerObjectListFilter,
registerObjectListSorter
} = useTableStatePersistence({
scope: type,
viewId: listViewId,
pagePath: location.pathname,
saveFilterInSession,
saveFilterInUrl,
@ -299,6 +344,7 @@ const ObjectTable = forwardRef(
useSortInSession,
useSortInUrl
})
const { getViewFromUrl } = useTableState()
var adjustedScrollHeight = scrollHeight
if (isMobile) {
adjustedScrollHeight = 'calc(var(--unit-100vh) - 298px)'
@ -319,6 +365,8 @@ const ObjectTable = forwardRef(
const model = getModelByName(type)
const activeFilterRef = useRef({})
const tableSorterRef = useRef({})
const listViewIdRef = useRef(listViewId)
const listViewFilterRef = useRef(listViewFilter)
const activeObjectViewRef = useRef(activeObjectView)
activeObjectViewRef.current = activeObjectView
const onObjectViewFilterSortChangeRef = useRef(onObjectViewFilterSortChange)
@ -327,6 +375,25 @@ const ObjectTable = forwardRef(
const [tableSorter, setTableSorter] = useState({})
const [initialized, setInitialized] = useState(false)
// Keep refs in sync with props. applyViewState may update them earlier in
// the same tick; props remain the source of truth after commit.
useEffect(() => {
listViewIdRef.current = listViewId
listViewFilterRef.current = listViewFilter || {}
}, [listViewId, listViewFilter])
const lastAppliedViewKeyRef = useRef(null)
const buildEffectiveFilter = useCallback((userFilter, viewFilter = listViewFilterRef.current) => {
const active = getActiveFilterValues(userFilter)
const viewId = listViewIdRef.current
if (!viewId) return active
return {
...getActiveFilterValues(viewFilter || {}),
...active
}
}, [])
// Table state
const [pages, setPages] = useState([])
const pagesRef = useRef(pages)
@ -362,14 +429,15 @@ const ObjectTable = forwardRef(
onStateChangeRef.current?.({ isEditing, editLoading })
}, [isEditing, editLoading])
const subscribedIdsRef = useRef([])
// const [typeSubscribed, setTypeSubscribed] = useState(false)
const unsubscribesRef = useRef([])
const updateEventHandlerRef = useRef()
const subscribeToObjectTypeUpdatesRef = useRef(null)
const subscribedTypeRef = useRef(null)
const subscribeToAllObjectUpdatesRef = useRef(null)
const newEventHandlerRef = useRef()
const subscriptionFilterRef = useRef()
const effectiveSorterRef = useRef({})
const reloadRef = useRef(null)
const silentReloadRef = useRef(null)
const subscribeToObjectTypeUpdatesFnRef = useRef(
subscribeToObjectTypeUpdates
)
@ -412,16 +480,36 @@ const ObjectTable = forwardRef(
const pendingScrollAnchorRef = useRef(null)
const dataLoadGenerationRef = useRef(0)
const [tableListKey, setTableListKey] = useState(0)
const isInternalSortUpdateRef = useRef(false)
// Suppress Ant Design Table onChange while we remount / set filter+sort
// programmatically. Remounting with controlled filteredValue often fires
// onChange with all-null filters (sorter intact) and would wipe the view.
const isInternalTableChangeRef = useRef(false)
const internalTableChangeClearRafRef = useRef(null)
const suppressTableChange = useCallback(() => {
isInternalTableChangeRef.current = true
if (internalTableChangeClearRafRef.current != null) {
cancelAnimationFrame(internalTableChangeClearRafRef.current)
}
// Double rAF: wait until after the remounted Table commits and any
// synchronous/mount onChange from Ant Design has run.
internalTableChangeClearRafRef.current = requestAnimationFrame(() => {
internalTableChangeClearRafRef.current = requestAnimationFrame(() => {
internalTableChangeClearRafRef.current = null
isInternalTableChangeRef.current = false
})
})
}, [])
const beginDataReload = useCallback(() => {
dataLoadGenerationRef.current += 1
loadingPagesRef.current.clear()
pendingScrollAnchorRef.current = null
pagesRef.current = []
suppressTableChange()
setTableListKey((key) => key + 1)
return dataLoadGenerationRef.current
}, [])
}, [suppressTableChange])
const isStaleDataLoad = useCallback((generation) => {
return generation !== dataLoadGenerationRef.current
@ -799,19 +887,35 @@ const ObjectTable = forwardRef(
[loadNextPage, loadPreviousPage, skeletonsHeight]
)
const reload = useCallback(async () => {
setLazyLoading(true)
try {
const loadedPages = pagesRef.current.filter(
(page) => !page.isSkeletonPage
)
for (let i = 0; i < loadedPages.length; i++) {
await fetchData(loadedPages[i].pageNum)
const reloadLoadedPages = useCallback(
async ({ silent = false } = {}) => {
if (!silent) {
setLazyLoading(true)
}
} finally {
setLazyLoading(false)
}
}, [fetchData])
try {
const loadedPages = pagesRef.current.filter(
(page) => !page.isSkeletonPage
)
for (let i = 0; i < loadedPages.length; i++) {
await fetchData(loadedPages[i].pageNum)
}
} finally {
if (!silent) {
setLazyLoading(false)
}
}
},
[fetchData]
)
const reload = useCallback(() => reloadLoadedPages(), [reloadLoadedPages])
const silentReload = useCallback(
() => reloadLoadedPages({ silent: true }),
[reloadLoadedPages]
)
reloadRef.current = reload
silentReloadRef.current = silentReload
const startEditing = useCallback(() => {
setIsEditing(true)
@ -855,138 +959,139 @@ const ObjectTable = forwardRef(
}
}, [type, updateMultipleObjects, reload, tableData, clearObjectActivity])
// Update event handler for real-time updates
const updateEventHandler = useCallback((id, updatedData) => {
setPages((prevPages) =>
prevPages.map((page) => {
const updatedItems = page.items.map((item) => {
if (item._id === id) {
return { ...item, ...updatedData }
}
return item
})
return {
...page,
items: updatedItems
}
})
)
if (rowFormsRef.current[id]) {
rowFormsRef.current[id].setFieldsValue(updatedData)
const findTableItem = useCallback((id) => {
for (const page of pagesRef.current) {
const item = page.items?.find(
(entry) => String(entry._id) === String(id) && !entry.isSkeleton
)
if (item) return item
}
return null
}, [])
// Update event handler for real-time updates
const updateEventHandler = useCallback(
(id, updatedData) => {
const existingItem = findTableItem(id)
const changedKeys = getChangedKeys(existingItem, updatedData)
if (
updateAffectsFilterOrSort(
changedKeys,
subscriptionFilterRef.current,
{},
effectiveSorterRef.current
)
) {
silentReloadRef.current?.()
return
}
if (!existingItem) return
setPages((prevPages) =>
prevPages.map((page) => {
const updatedItems = page.items.map((item) => {
if (String(item._id) === String(id)) {
return { ...item, ...updatedData }
}
return item
})
return {
...page,
items: updatedItems
}
})
)
if (rowFormsRef.current[id]) {
rowFormsRef.current[id].setFieldsValue(updatedData)
}
},
[findTableItem]
)
// Store the latest updateEventHandler in a ref
updateEventHandlerRef.current = updateEventHandler
const newEventHandler = useCallback(
(params) => {
logger.debug('New event handler:', params)
reload()
},
[reload]
)
const newEventHandler = useCallback((params) => {
logger.debug('New event handler:', params)
silentReloadRef.current?.()
}, [])
const subscriptionFilter = useMemo(() => {
const active = {}
Object.entries(sidebarFilter).forEach(([k, v]) => {
if (v !== '' && v !== undefined) active[k] = v
})
return { ...masterFilter, ...active }
}, [sidebarFilter, masterFilter])
// Effective filter for ObjectTableFilterContext, breadcrumb, and neighbors:
// view definition + any user-entered sidebar filters.
const effectiveFilter = useMemo(() => {
const userFilter = getActiveFilterValues(sidebarFilter)
if (!listViewId) return userFilter
return {
...getActiveFilterValues(listViewFilter || {}),
...userFilter
}
}, [listViewId, listViewFilter, sidebarFilter])
const effectiveSorter = useMemo(() => {
if (tableSorter?.field && tableSorter?.order) {
return { field: tableSorter.field, order: tableSorter.order }
}
if (listViewId && listViewSort?.field && listViewSort?.order) {
return { field: listViewSort.field, order: listViewSort.order }
}
return {}
}, [listViewId, listViewSort, tableSorter])
const subscriptionFilter = useMemo(
() => ({ ...masterFilter, ...effectiveFilter }),
[effectiveFilter, masterFilter]
)
const tableFilterContextValue = useMemo(
() => ({
filter: getActiveFilterValues(sidebarFilter),
filter: effectiveFilter,
masterFilter
}),
[sidebarFilter, masterFilter]
[effectiveFilter, masterFilter]
)
newEventHandlerRef.current = newEventHandler
subscriptionFilterRef.current = subscriptionFilter
effectiveSorterRef.current = effectiveSorter
subscribeToObjectTypeUpdatesFnRef.current = subscribeToObjectTypeUpdates
// Subscribe to real-time updates for all items
useEffect(() => {
if (pages.length > 0 && connected == true) {
// Get all non-skeleton item IDs from all pages
const allItemIds = pages
.flatMap((page) => page.items || [])
.filter((item) => !item.isSkeleton)
.map((item) => item._id)
.filter(Boolean)
// Find new items that need subscription
const newItemIds = allItemIds.filter(
(id) => !subscribedIdsRef.current.includes(id)
)
// Subscribe to new items only
newItemIds.forEach((itemId) => {
const unsubscribe = subscribeToObjectUpdates(
itemId?.toLowerCase(),
type,
(updateData) => {
updateEventHandlerRef.current(itemId, updateData)
}
)
subscribedIdsRef.current.push(itemId)
if (unsubscribe) {
unsubscribesRef.current.push(unsubscribe)
}
})
// Clean up subscriptions for items that are no longer in the pages
const currentSubscribedIds = subscribedIdsRef.current
const itemsToUnsubscribe = currentSubscribedIds.filter(
(id) => !allItemIds.includes(id)
)
itemsToUnsubscribe.forEach((itemId) => {
const index = subscribedIdsRef.current.indexOf(itemId)
if (index > -1) {
subscribedIdsRef.current.splice(index, 1)
const unsubscribe = unsubscribesRef.current[index]
if (unsubscribe) {
unsubscribe()
}
unsubscribesRef.current.splice(index, 1)
}
})
}
}, [pages, type, subscribeToObjectUpdates, connected])
// Cleanup effect for component unmount
// Cleanup subscriptions on unmount
useEffect(() => {
return () => {
if (connected == true && unsubscribesRef.current) {
// Clean up all subscriptions when component unmounts
unsubscribesRef.current.forEach((unsubscribe) => {
if (unsubscribe) unsubscribe()
})
unsubscribesRef.current = []
subscribedIdsRef.current = []
// Clean up type subscription
}
if (connected == true && subscribeToObjectTypeUpdatesRef.current) {
subscribeToObjectTypeUpdatesRef.current()
subscribeToObjectTypeUpdatesRef.current = null
}
if (connected == true && subscribeToAllObjectUpdatesRef.current) {
subscribeToAllObjectUpdatesRef.current()
subscribeToAllObjectUpdatesRef.current = null
}
}
}, [connected])
// Subscribe to all object updates for this type
useEffect(() => {
if (connected !== true || !type) return
const unsubscribe = subscribeToAllObjectUpdates(type, (id, updateData) => {
updateEventHandlerRef.current(id, updateData)
})
subscribeToAllObjectUpdatesRef.current = unsubscribe
return () => {
if (unsubscribe) unsubscribe()
if (subscribeToAllObjectUpdatesRef.current === unsubscribe) {
subscribeToAllObjectUpdatesRef.current = null
}
}
}, [type, connected, subscribeToAllObjectUpdates])
useEffect(() => {
if (connected !== true) return
if (subscribedTypeRef.current === type) return
console.log(
'subscribing to object type updates',
type,
subscriptionFilterRef.current
)
const unsubscribe = subscribeToObjectTypeUpdatesFnRef.current(
type,
subscriptionFilterRef.current,
@ -1047,6 +1152,8 @@ const ObjectTable = forwardRef(
} finally {
if (!isStaleDataLoad(generation)) {
setLoading(false)
// Data/column swap can also trigger a spurious Table onChange.
suppressTableChange()
}
}
},
@ -1057,11 +1164,18 @@ const ObjectTable = forwardRef(
createSkeletonPage,
fetchPage,
isStaleDataLoad,
setTablePages
setTablePages,
suppressTableChange
]
)
const loadInitialPage = useCallback(async () => {
// A selected view applies its own filter/sort via applyViewState.
// Do not load (or clear) filters here — that races and wipes the view.
if (getViewFromUrl() || listViewIdRef.current) {
return
}
const filter = getPersistedFilter()
const sorter = getPersistedSorter()
const activeFilter = getActiveFilterValues(filter)
@ -1083,22 +1197,85 @@ const ObjectTable = forwardRef(
}, [
getPersistedFilter,
getPersistedSorter,
getViewFromUrl,
initialPage,
loadPage,
resolveSorter
])
// When a view becomes available, apply its definition once. Skip empty
// prop-lag updates so we never clear an already-applied view filter.
useEffect(() => {
if (!listViewId || activeObjectView) {
if (!listViewId) lastAppliedViewKeyRef.current = null
return
}
const viewFilter = getActiveFilterValues(listViewFilter || {})
const viewSort =
listViewSort?.field && listViewSort?.order
? { field: listViewSort.field, order: listViewSort.order }
: {}
const viewKey = `${listViewId}:${JSON.stringify(viewFilter)}:${JSON.stringify(viewSort)}`
if (lastAppliedViewKeyRef.current === viewKey) return
// Prop lag: listViewId is set but filter/sort props are still empty.
// Never overwrite a non-empty sidebar/query with {}.
const hasViewState =
Object.keys(viewFilter).length > 0 || Boolean(viewSort.field)
if (!hasViewState) return
lastAppliedViewKeyRef.current = viewKey
setSidebarFilter(viewFilter)
setTableSorter(viewSort)
tableSorterRef.current = viewSort
activeFilterRef.current = viewFilter
loadPage(
initialPage,
viewFilter,
viewSort.field ? viewSort : null
)
}, [
activeObjectView,
initialPage,
listViewFilter,
listViewId,
listViewSort,
loadPage
])
const applyViewState = useCallback(
(filterState, sorterState) => {
(filterState, sorterState, options = {}) => {
let filter
let sorter
if (filterState == null && sorterState == null) {
const filter = getPersistedFilter()
const sorter = getPersistedSorter()
const activeFilter = getActiveFilterValues(filter)
// Ignore accidental All restores while a view is still selected
// (e.g. load race). Explicit All tab switches pass forceAll.
if (
!options.forceAll &&
(listViewIdRef.current || getViewFromUrl())
) {
return
}
listViewIdRef.current = null
listViewFilterRef.current = {}
lastAppliedViewKeyRef.current = null
filter = getAllTabPersistedFilter()
sorter = getAllTabPersistedSorter()
const resolvedSorter = resolveSorter(sorter)
const nextSorter = sorter?.field ? sorter : {}
const activeFilter = getActiveFilterValues(filter)
setSidebarFilter(filter)
tableSorterRef.current = sorter?.field ? sorter : {}
setTableSorter(sorter?.field ? sorter : {})
tableSorterRef.current = nextSorter
setTableSorter(nextSorter)
activeFilterRef.current = activeFilter
persistAllTabTableState(filter, nextSorter)
loadPage(
initialPage,
activeFilter,
@ -1107,14 +1284,39 @@ const ObjectTable = forwardRef(
return
}
const filter = filterState || {}
const sorter = sorterState?.field ? sorterState : {}
const activeFilter = getActiveFilterValues(filter)
// View tab: apply the view definition. Sidebar starts as the view
// filter so the UI matches; All-tab user settings must not leak in.
const viewId =
options.viewId !== undefined
? options.viewId
: listViewIdRef.current
const viewFilter =
options.viewFilter !== undefined
? options.viewFilter || {}
: filterState || {}
listViewIdRef.current = viewId
listViewFilterRef.current = viewFilter
filter = viewFilter
sorter =
options.viewSort?.field
? options.viewSort
: sorterState?.field
? sorterState
: {}
const resolvedSorter = resolveSorter(sorter)
const nextSorter = sorter?.field ? sorter : {}
const activeFilter = getActiveFilterValues(filter)
lastAppliedViewKeyRef.current = `${viewId || ''}:${JSON.stringify(activeFilter)}:${JSON.stringify(nextSorter)}`
setSidebarFilter(filter)
tableSorterRef.current = sorter
setTableSorter(sorter)
tableSorterRef.current = nextSorter
setTableSorter(nextSorter)
activeFilterRef.current = activeFilter
loadPage(
initialPage,
activeFilter,
@ -1122,10 +1324,12 @@ const ObjectTable = forwardRef(
)
},
[
getPersistedFilter,
getPersistedSorter,
getAllTabPersistedFilter,
getAllTabPersistedSorter,
getViewFromUrl,
initialPage,
loadPage,
persistAllTabTableState,
resolveSorter
]
)
@ -1180,14 +1384,16 @@ const ObjectTable = forwardRef(
}, [type, masterFilter, beginDataReload])
useEffect(() => {
registerPageFilter(sidebarFilter)
registerPageFilter(effectiveFilter)
registerObjectListFilter(effectiveFilter)
return () => registerPageFilter({})
}, [sidebarFilter, registerPageFilter])
}, [effectiveFilter, registerPageFilter, registerObjectListFilter])
useEffect(() => {
registerPageSorter(tableSorter)
registerPageSorter(effectiveSorter)
registerObjectListSorter(effectiveSorter)
return () => registerPageSorter({})
}, [tableSorter, registerPageSorter])
}, [effectiveSorter, registerPageSorter, registerObjectListSorter])
const getFilterDropdown = ({
setSelectedKeys,
@ -1207,13 +1413,13 @@ const ObjectTable = forwardRef(
propertyName={propertyName}
propertyLabel={propertyLabel}
modelType={type}
filter={sidebarFilter}
filter={effectiveFilter}
masterFilter={masterFilter}
/>
)
const handleTableChange = (pagination, filters, sorter) => {
if (isInternalSortUpdateRef.current) return
if (isInternalTableChangeRef.current) return
const next = { ...sidebarFilter }
@ -1233,29 +1439,42 @@ const ObjectTable = forwardRef(
setSidebarFilter(next)
setTableSorter(nextSorter)
const effective = buildEffectiveFilter(next)
activeFilterRef.current = effective
if (activeObjectViewRef.current) {
// Editing a view: keep filter/sort on the draft view only
onObjectViewFilterSortChangeRef.current?.(next, nextSorter)
} else {
} else if (!listViewIdRef.current) {
// All tab: persist personal filter/sort to URL/session
persistTableState(next, nextSorter)
}
loadPage(initialPage, getActiveFilterValues(next), resolveSorter(nextSorter))
loadPage(initialPage, effective, resolveSorter(nextSorter))
}
const handleSidebarFilterChange = useCallback(
(newSidebarFilter) => {
setSidebarFilter(newSidebarFilter)
const effective = buildEffectiveFilter(newSidebarFilter)
activeFilterRef.current = effective
if (activeObjectViewRef.current) {
onObjectViewFilterSortChangeRef.current?.(newSidebarFilter, tableSorter)
} else {
} else if (!listViewIdRef.current) {
persistFilter(newSidebarFilter)
}
loadPage(
initialPage,
getActiveFilterValues(newSidebarFilter),
effective,
resolveSorter(tableSorter)
)
},
[initialPage, loadPage, persistFilter, resolveSorter, tableSorter]
[
buildEffectiveFilter,
initialPage,
loadPage,
persistFilter,
resolveSorter,
tableSorter
]
)
const handleSidebarSortChange = useCallback(
@ -1263,24 +1482,29 @@ const ObjectTable = forwardRef(
const nextSorter = newSorter?.field
? { field: newSorter.field, order: newSorter.order }
: {}
isInternalSortUpdateRef.current = true
suppressTableChange()
setTableSorter(nextSorter)
tableSorterRef.current = nextSorter
if (activeObjectViewRef.current) {
onObjectViewFilterSortChangeRef.current?.(sidebarFilter, nextSorter)
} else {
} else if (!listViewIdRef.current) {
persistSort(nextSorter)
}
loadPage(
initialPage,
getActiveFilterValues(sidebarFilter),
buildEffectiveFilter(sidebarFilter),
resolveSorter(nextSorter)
)
requestAnimationFrame(() => {
isInternalSortUpdateRef.current = false
})
},
[initialPage, loadPage, persistSort, resolveSorter, sidebarFilter]
[
buildEffectiveFilter,
initialPage,
loadPage,
persistSort,
resolveSorter,
sidebarFilter,
suppressTableChange
]
)
const modelProperties = getModelProperties(type)
@ -1420,7 +1644,7 @@ const ObjectTable = forwardRef(
propertyLabel: prop.label
})
columnConfig.filteredValue = fromFilterExpression(
sidebarFilter[prop.name]
effectiveFilter[prop.name]
)
}
@ -1670,7 +1894,7 @@ const ObjectTable = forwardRef(
{showSortSidebar && (
<SortSidebar
type={type}
sorter={tableSorter}
sorter={effectiveSorter}
onSortChange={handleSidebarSortChange}
stretchHeight={showSortSidebar && !showFilterSidebar}
/>
@ -1679,7 +1903,7 @@ const ObjectTable = forwardRef(
<Flex style={{ flex: 1, minHeight: 0 }}>
<FilterSidebar
type={type}
filter={sidebarFilter}
filter={effectiveFilter}
onFilterChange={handleSidebarFilterChange}
masterFilter={masterFilter}
/>
@ -1726,8 +1950,6 @@ ObjectTable.propTypes = {
saveSortInUrl: PropTypes.bool,
useSortInSession: PropTypes.bool,
useSortInUrl: PropTypes.bool,
activeObjectView: PropTypes.object,
onObjectViewFilterSortChange: PropTypes.func,
onRowAction: PropTypes.func
}

View File

@ -7,6 +7,7 @@ import { ApiServerContext } from '../context/ApiServerContext'
import { AuthContext } from '../context/AuthContext'
import {
getActiveFilterValues,
useTableState,
useTableStatePersistence
} from '../context/TableStateContext'
import KeyboardShortcut from './KeyboardShortcut.jsx'
@ -25,6 +26,7 @@ const ObjectTableNavigationButtons = ({
const [neighbors, setNeighbors] = useState({ next: null, previous: null })
const [loading, setLoading] = useState(false)
const { getObjectListFilter, getObjectListSorter } = useTableState()
const { getPersistedFilter, getPersistedSorter } = useTableStatePersistence({
scope: objectType,
useFilterInSession: true,
@ -34,6 +36,12 @@ const ObjectTableNavigationButtons = ({
const getObjectNeighborsRef = useRef(getObjectNeighbors)
getObjectNeighborsRef.current = getObjectNeighbors
const getObjectListFilterRef = useRef(getObjectListFilter)
getObjectListFilterRef.current = getObjectListFilter
const getObjectListSorterRef = useRef(getObjectListSorter)
getObjectListSorterRef.current = getObjectListSorter
const getPersistedFilterRef = useRef(getPersistedFilter)
getPersistedFilterRef.current = getPersistedFilter
@ -51,8 +59,21 @@ const ObjectTableNavigationButtons = ({
const loadNeighbors = async () => {
setLoading(true)
const filter = getActiveFilterValues(getPersistedFilterRef.current())
const sorter = getPersistedSorterRef.current()
// Prefer the effective list filter (view + user filters) when available
const listFilter = getActiveFilterValues(
getObjectListFilterRef.current(objectType)
)
const persistedFilter = getActiveFilterValues(
getPersistedFilterRef.current()
)
const filter =
Object.keys(listFilter).length > 0 ? listFilter : persistedFilter
const listSorter = getObjectListSorterRef.current(objectType)
const persistedSorter = getPersistedSorterRef.current()
const sorter =
listSorter?.field && listSorter?.order ? listSorter : persistedSorter
const result = await getObjectNeighborsRef.current(objectType, {
id: _id,
filter,

View File

@ -60,7 +60,8 @@ const SegmentedNav = ({
onChange,
onReorder,
className,
disabled = false
disabled = false,
animated = true
}) => {
const groupRef = useRef(null)
const thumbRef = useRef(null)
@ -95,6 +96,12 @@ const SegmentedNav = ({
const toMetrics = getItemMetrics(itemRefs.current.get(value))
prevValueRef.current = value
if (!animated) {
setIsAnimating(false)
setThumbRect(null)
return undefined
}
if (!fromMetrics || !toMetrics) {
return undefined
}
@ -119,7 +126,7 @@ const SegmentedNav = ({
cancelAnimationFrame(startFrame)
cancelAnimationFrame(endFrame)
}
}, [value])
}, [value, animated])
const finishAnimation = useCallback(() => {
setIsAnimating(false)
@ -425,7 +432,8 @@ SegmentedNav.propTypes = {
onChange: PropTypes.func,
onReorder: PropTypes.func,
className: PropTypes.string,
disabled: PropTypes.bool
disabled: PropTypes.bool,
animated: PropTypes.bool
}
export default SegmentedNav

View File

@ -17,6 +17,56 @@ const ObjectListViewContext = createContext()
const ALL_TAB_KEY = 'all'
const getLastViewStorageKey = (objectType) =>
`objectListView:${objectType}:lastViewId`
const getViewsCacheKey = (objectType) =>
`objectListView:${objectType}:views`
const readLastViewId = (objectType) => {
if (!objectType) return null
try {
return sessionStorage.getItem(getLastViewStorageKey(objectType)) || null
} catch {
return null
}
}
const writeLastViewId = (objectType, viewId) => {
if (!objectType) return
try {
const key = getLastViewStorageKey(objectType)
if (viewId) {
sessionStorage.setItem(key, String(viewId))
} else {
sessionStorage.removeItem(key)
}
} catch {
// Ignore sessionStorage failures (private mode, quota, etc.)
}
}
const readCachedViews = (objectType) => {
if (!objectType) return null
try {
const raw = sessionStorage.getItem(getViewsCacheKey(objectType))
if (raw == null) return null
const parsed = JSON.parse(raw)
return Array.isArray(parsed) ? parsed : null
} catch {
return null
}
}
const writeCachedViews = (objectType, views) => {
if (!objectType) return
try {
sessionStorage.setItem(getViewsCacheKey(objectType), JSON.stringify(views))
} catch {
// Ignore sessionStorage failures (private mode, quota, etc.)
}
}
const getPreviousTabKey = (viewList, deletedKey) => {
const index = viewList.findIndex(
(view) => String(view._id) === String(deletedKey)
@ -35,6 +85,9 @@ const DEFAULT_VIEW = {
sort: {}
}
const EMPTY_FILTER = {}
const EMPTY_SORT = {}
// eslint-disable-next-line react-refresh/only-export-components
export const getObjectViewKey = (view) =>
view?._id != null ? String(view._id) : null
@ -62,8 +115,7 @@ const viewStateEqual = (left, right) =>
export const ObjectListViewProvider = ({
children,
objectType,
tableRef,
onViewStateChange
tableRef
}) => {
const {
fetchObjectViews,
@ -77,12 +129,21 @@ export const ObjectListViewProvider = ({
const { token, authInitialized } = useContext(AuthContext)
const { getViewFromUrl, persistView } = useTableState()
const [views, setViews] = useState([])
const [draftViews, setDraftViews] = useState([])
const [initialLoading, setInitialLoading] = useState(true)
const [views, setViews] = useState(
() => readCachedViews(objectType) || []
)
const [draftViews, setDraftViews] = useState(() => {
const cached = readCachedViews(objectType)
return cached ? cloneViews(cached) : []
})
const [initialLoading, setInitialLoading] = useState(
() => readCachedViews(objectType) == null
)
const [viewsFetched, setViewsFetched] = useState(false)
const [saving, setSaving] = useState(false)
const [activeTabKey, setActiveTabKey] = useState(ALL_TAB_KEY)
const [isEditing, setIsEditing] = useState(false)
const [viewSyncReady, setViewSyncReady] = useState(false)
const editSnapshotRef = useRef([])
const wasEditingRef = useRef(false)
@ -140,15 +201,22 @@ export const ObjectListViewProvider = ({
if (tabKey !== ALL_TAB_KEY && !view) return
// "All" outside edit mode restores the user's persisted filter/sort
// "All" restores the user's personal filter/sort from URL/session
if (tabKey === ALL_TAB_KEY && !editing) {
tableRef.current.applyViewState(null, null)
tableRef.current.applyViewState(null, null, { forceAll: true })
return
}
// Views apply their own filter/sort into the table (and filter context).
// They are never written to the filter/sort URL params.
tableRef.current.applyViewState(
view?.filter || {},
view?.sort?.field ? view.sort : {}
view?.sort?.field ? view.sort : {},
{
viewId: view?._id != null ? String(view._id) : null,
viewFilter: view?.filter || {},
viewSort: view?.sort?.field ? view.sort : {}
}
)
},
[tableRef]
@ -157,18 +225,23 @@ export const ObjectListViewProvider = ({
const loadViews = useCallback(
async (silent = false) => {
if (!authReady || !objectType) return
if (!silent) setInitialLoading(true)
const hasCache = readCachedViews(objectType) != null
// Keep showing cached tabs while refreshing; only spin when nothing to show
if (!silent && !hasCache) setInitialLoading(true)
try {
const { data } = await apiRef.current.fetchObjectViews(objectType)
const nextViews = data || []
setViews(nextViews)
writeCachedViews(objectType, nextViews)
if (!isEditingRef.current) {
setDraftViews(cloneViews(nextViews))
}
setViewsFetched(true)
return nextViews
} catch (error) {
console.error('Error fetching object views:', error)
return []
setViewsFetched(true)
return hasCache ? readCachedViews(objectType) || [] : []
} finally {
if (!silent) setInitialLoading(false)
}
@ -179,54 +252,109 @@ export const ObjectListViewProvider = ({
const loadViewsRef = useRef(loadViews)
loadViewsRef.current = loadViews
const rememberActiveView = useCallback(
(viewId) => {
const key = viewId ? String(viewId) : null
persistView(key)
writeLastViewId(objectType, key)
},
[objectType, persistView]
)
useEffect(() => {
urlSyncedRef.current = false
setViewSyncReady(false)
setViewsFetched(false)
setActiveTabKey(ALL_TAB_KEY)
const cached = readCachedViews(objectType)
if (cached) {
setViews(cached)
if (!isEditingRef.current) {
setDraftViews(cloneViews(cached))
}
setInitialLoading(false)
} else {
setViews([])
setDraftViews([])
setInitialLoading(true)
}
}, [objectType])
useEffect(() => {
loadViews()
}, [loadViews])
// Keep the local cache in sync with authoritative (non-draft) views
useEffect(() => {
if (initialLoading) return
if (!objectType || initialLoading) return
writeCachedViews(objectType, views)
}, [objectType, views, initialLoading])
useEffect(() => {
if (initialLoading || !viewSyncReady) return
applyViewToTable(activeTabKey)
}, [activeTabKey, applyViewToTable, initialLoading, isEditing])
}, [
activeTabKey,
applyViewToTable,
initialLoading,
isEditing,
viewSyncReady,
viewsFetched
])
useEffect(() => {
if (initialLoading || urlSyncedRef.current) return
if (urlSyncedRef.current) return
const viewId = getViewFromUrl()
if (!viewId) {
// Allow early restore from cache; only block when we have nothing to match against yet
if (initialLoading && views.length === 0) return
const urlViewId = getViewFromUrl()
const rememberedViewId = readLastViewId(objectType)
const candidateViewId = urlViewId || rememberedViewId
if (!candidateViewId) {
urlSyncedRef.current = true
setViewSyncReady(true)
return
}
const matched =
resolveViewFromId(views, viewId) ||
resolveViewFromId(draftViews, viewId)
resolveViewFromId(views, candidateViewId) ||
resolveViewFromId(draftViews, candidateViewId)
if (matched) {
setActiveTabKey(String(matched._id))
const matchedId = String(matched._id)
setActiveTabKey(matchedId)
writeLastViewId(objectType, matchedId)
if (!matched._isDraft) {
// Keep viewId in the URL and strip any leftover filter/sort params
persistView(matchedId)
}
urlSyncedRef.current = true
setViewSyncReady(true)
return
}
// Wait until views have loaded before deciding the URL viewId is invalid
if (views.length === 0) return
// Wait until views have loaded from the network before deciding the candidate is invalid
if (!viewsFetched) return
if (!isEditing) {
persistView(null)
rememberActiveView(null)
}
urlSyncedRef.current = true
setViewSyncReady(true)
}, [
initialLoading,
viewsFetched,
views,
draftViews,
isEditing,
objectType,
getViewFromUrl,
persistView
persistView,
rememberActiveView
])
const selectTab = useCallback(
@ -236,7 +364,7 @@ export const ObjectListViewProvider = ({
setActiveTabKey(tabKey)
if (tabKey === ALL_TAB_KEY) {
persistView(null)
rememberActiveView(null)
return
}
@ -245,9 +373,9 @@ export const ObjectListViewProvider = ({
)
if (!view || view._isDraft) return
persistView(getObjectViewKey(view))
rememberActiveView(getObjectViewKey(view))
},
[activeTabKey, draftViews, isEditing, persistView, views]
[activeTabKey, draftViews, isEditing, rememberActiveView, views]
)
useEffect(() => {
@ -278,13 +406,13 @@ export const ObjectListViewProvider = ({
)
if (!stillExists) {
setActiveTabKey(ALL_TAB_KEY)
persistView(null)
rememberActiveView(null)
applyViewToTable(ALL_TAB_KEY)
}
}
await loadViewsRef.current(true)
}, [activeTabKey, applyViewToTable, persistView])
}, [activeTabKey, applyViewToTable, rememberActiveView])
const saveEdits = useCallback(async () => {
setSaving(true)
@ -355,11 +483,11 @@ export const ObjectListViewProvider = ({
if (resolvedTabKey !== ALL_TAB_KEY && savedView && !savedView._isDraft) {
setActiveTabKey(resolvedTabKey)
persistView(getObjectViewKey(savedView))
rememberActiveView(getObjectViewKey(savedView))
} else if (resolvedTabKey !== ALL_TAB_KEY && !savedView) {
resolvedTabKey = ALL_TAB_KEY
setActiveTabKey(ALL_TAB_KEY)
persistView(null)
rememberActiveView(null)
}
message.success('Views saved')
@ -373,7 +501,7 @@ export const ObjectListViewProvider = ({
activeTabKey,
draftViews,
objectType,
persistView,
rememberActiveView,
views
])
@ -407,7 +535,7 @@ export const ObjectListViewProvider = ({
const newKey = String(created._id)
setActiveTabKey(newKey)
applyViewToTable(newKey)
persistView(getObjectViewKey(created))
rememberActiveView(getObjectViewKey(created))
} else {
message.error('Failed to create view')
}
@ -437,13 +565,13 @@ export const ObjectListViewProvider = ({
if (isActiveDeleted) {
setActiveTabKey(previousKey)
if (previousKey === ALL_TAB_KEY) {
persistView(null)
rememberActiveView(null)
} else {
const view = draftViews.find(
(item) => String(item._id) === String(previousKey)
)
if (view && !view._isDraft) {
persistView(getObjectViewKey(view))
rememberActiveView(getObjectViewKey(view))
}
}
applyViewToTable(previousKey)
@ -456,7 +584,7 @@ export const ObjectListViewProvider = ({
draftViews,
isEditing,
objectType,
persistView,
rememberActiveView,
views
]
)
@ -531,19 +659,19 @@ export const ObjectListViewProvider = ({
if (isActiveDeleted) {
setActiveTabKey(previousKey)
if (previousKey === ALL_TAB_KEY) {
persistView(null)
rememberActiveView(null)
} else {
const view = list.find(
(item) => String(item._id) === String(previousKey)
)
if (view && !view._isDraft) {
persistView(getObjectViewKey(view))
rememberActiveView(getObjectViewKey(view))
}
}
applyViewToTable(previousKey)
}
},
[activeTabKey, applyViewToTable, draftViews, isEditing, persistView, views]
[activeTabKey, applyViewToTable, draftViews, isEditing, rememberActiveView, views]
)
const handleFilterSortChange = useCallback((filter, sorter) => {
@ -682,40 +810,17 @@ export const ObjectListViewProvider = ({
[]
)
const handleFilterSortChangeRef = useRef(handleFilterSortChange)
handleFilterSortChangeRef.current = handleFilterSortChange
const stableFilterSortHandlerRef = useRef((filter, sorter) => {
handleFilterSortChangeRef.current?.(filter, sorter)
})
const lastSyncedViewKeyRef = useRef(null)
useEffect(() => {
const viewKey = isEditing
? `edit:${activeView?._id ?? activeTabKey}`
: activeView?._id != null
? String(activeView._id)
: activeTabKey === ALL_TAB_KEY
? 'all'
: null
if (viewKey === lastSyncedViewKeyRef.current) return
lastSyncedViewKeyRef.current = viewKey
onViewStateChange?.({
activeView: isEditing ? activeViewRef.current : null,
isEditing,
handleFilterSortChange: isEditing
? stableFilterSortHandlerRef.current
: null
})
}, [
activeTabKey,
activeView?._id,
isEditing,
onViewStateChange
])
const listViewId =
activeTabKey !== ALL_TAB_KEY && activeTabKey != null
? String(activeTabKey)
: null
const listViewFilter = activeView?.filter || EMPTY_FILTER
const listViewSort = activeView?.sort?.field
? activeView.sort
: EMPTY_SORT
// Only expose the editable view while editing — ObjectTable uses this to
// route filter/sort changes into the draft instead of URL/session persistence.
const activeObjectView = isEditing ? activeView : null
const viewTabSignatures = useMemo(
() => displayedViews.map(getViewTabSignature).join('|'),
@ -728,7 +833,11 @@ export const ObjectListViewProvider = ({
ALL_TAB_KEY,
views: displayedViews,
activeView,
activeObjectView,
activeTabKey,
listViewId,
listViewFilter,
listViewSort,
initialLoading,
saving,
isEditing,
@ -749,7 +858,11 @@ export const ObjectListViewProvider = ({
objectType,
displayedViews,
activeView,
activeObjectView,
activeTabKey,
listViewId,
listViewFilter,
listViewSort,
initialLoading,
saving,
isEditing,
@ -788,8 +901,7 @@ export const useObjectListView = () => {
ObjectListViewProvider.propTypes = {
children: PropTypes.node.isRequired,
objectType: PropTypes.string.isRequired,
tableRef: PropTypes.object,
onViewStateChange: PropTypes.func
tableRef: PropTypes.object
}
// eslint-disable-next-line react-refresh/only-export-components

View File

@ -3,6 +3,7 @@ import {
useCallback,
useContext,
useMemo,
useRef,
useState
} from 'react'
import { useSearchParams } from 'react-router-dom'
@ -16,6 +17,8 @@ export const VIEW_URL_PARAM = 'viewId'
const getSessionFilterKey = (scope) => `tableState:${scope}:filter`
const getSessionSortKey = (scope) => `tableState:${scope}:sort`
const getSessionListFilterKey = (scope) => `tableState:${scope}:listFilter`
const getSessionListSortKey = (scope) => `tableState:${scope}:listSort`
// eslint-disable-next-line react-refresh/only-export-components
export const getActiveFilterValues = (filterState) => {
@ -89,6 +92,42 @@ const writeSortToSession = (scope, sorter) => {
}
}
const readListFilterFromSession = (scope) => {
try {
return parseJsonParam(sessionStorage.getItem(getSessionListFilterKey(scope)))
} catch {
return null
}
}
const readListSortFromSession = (scope) => {
try {
const parsed = parseJsonParam(sessionStorage.getItem(getSessionListSortKey(scope)))
if (!parsed?.field || !parsed?.order) return null
return { field: parsed.field, order: parsed.order }
} catch {
return null
}
}
const writeListFilterToSession = (scope, filter) => {
const key = getSessionListFilterKey(scope)
if (Object.keys(filter).length > 0) {
sessionStorage.setItem(key, JSON.stringify(filter))
} else {
sessionStorage.removeItem(key)
}
}
const writeListSortToSession = (scope, sorter) => {
const key = getSessionListSortKey(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 }
@ -100,6 +139,24 @@ export const TableStateProvider = ({ children }) => {
const [searchParams, setSearchParams] = useSearchParams()
const [pageFilters, setPageFilters] = useState({})
const [pageSorters, setPageSorters] = useState({})
// Effective list filters (view filter + user filters). Survives navigating
// from the list page to an info page so breadcrumb/neighbors keep working.
const [objectListFilters, setObjectListFilters] = useState({})
const [objectListSorters, setObjectListSorters] = useState({})
const searchParamsRef = useRef(searchParams)
searchParamsRef.current = searchParams
// Keep sequential URL writes in the same tick from clobbering each other
// (e.g. persistView then persistTableState both calling setSearchParams).
const updateSearchParams = useCallback(
(mutator) => {
const next = new URLSearchParams(searchParamsRef.current)
mutator(next)
searchParamsRef.current = next
setSearchParams(next, { replace: true })
},
[setSearchParams]
)
const getPersistedFilter = useCallback(
(scope, { useFilterInUrl = false, useFilterInSession = false } = {}) => {
@ -137,18 +194,23 @@ export const TableStateProvider = ({ children }) => {
)
const persistView = useCallback(
(viewId) => {
const next = new URLSearchParams(searchParams)
// Drop legacy `view` param that previously stored `_reference`
next.delete('view')
if (viewId) {
next.set(VIEW_URL_PARAM, String(viewId))
} else {
next.delete(VIEW_URL_PARAM)
}
setSearchParams(next, { replace: true })
(viewId, { clearFilterSort = Boolean(viewId) } = {}) => {
updateSearchParams((next) => {
// Drop legacy `view` param that previously stored `_reference`
next.delete('view')
if (viewId) {
next.set(VIEW_URL_PARAM, String(viewId))
} else {
next.delete(VIEW_URL_PARAM)
}
// View filter/sort live on the view object — never in the URL
if (clearFilterSort) {
next.delete(FILTER_URL_PARAM)
next.delete(SORT_URL_PARAM)
}
})
},
[searchParams, setSearchParams]
[updateSearchParams]
)
const persistFilter = useCallback(
@ -162,16 +224,16 @@ export const TableStateProvider = ({ children }) => {
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 })
updateSearchParams((next) => {
if (Object.keys(active).length > 0) {
next.set(FILTER_URL_PARAM, JSON.stringify(active))
} else {
next.delete(FILTER_URL_PARAM)
}
})
}
},
[searchParams, setSearchParams]
[updateSearchParams]
)
const persistSort = useCallback(
@ -185,16 +247,16 @@ export const TableStateProvider = ({ children }) => {
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 })
updateSearchParams((next) => {
if (nextSorter.field && nextSorter.order) {
next.set(SORT_URL_PARAM, JSON.stringify(nextSorter))
} else {
next.delete(SORT_URL_PARAM)
}
})
}
},
[searchParams, setSearchParams]
[updateSearchParams]
)
const persistTableState = useCallback(
@ -220,25 +282,25 @@ export const TableStateProvider = ({ children }) => {
}
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)
updateSearchParams((next) => {
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)
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]
[updateSearchParams]
)
const setPageFilter = useCallback((path, filter) => {
@ -268,12 +330,71 @@ export const TableStateProvider = ({ children }) => {
})
}, [])
const setObjectListFilter = useCallback((objectType, filter) => {
if (!objectType) return
const active = getActiveFilterValues(filter)
writeListFilterToSession(objectType, active)
setObjectListFilters((prev) => {
const hasActive = Object.keys(active).length > 0
if (!hasActive) {
if (!(objectType in prev)) return prev
const next = { ...prev }
delete next[objectType]
return next
}
return { ...prev, [objectType]: active }
})
}, [])
const setObjectListSorter = useCallback((objectType, sorter) => {
if (!objectType) return
const nextSorter = normalizeSorter(sorter)
writeListSortToSession(objectType, nextSorter)
setObjectListSorters((prev) => {
const hasSort = nextSorter?.field && nextSorter?.order
if (!hasSort) {
if (!(objectType in prev)) return prev
const next = { ...prev }
delete next[objectType]
return next
}
return { ...prev, [objectType]: nextSorter }
})
}, [])
const getObjectListFilter = useCallback(
(objectType) => {
if (!objectType) return {}
if (objectListFilters[objectType]) {
return objectListFilters[objectType]
}
return getActiveFilterValues(readListFilterFromSession(objectType) || {})
},
[objectListFilters]
)
const getObjectListSorter = useCallback(
(objectType) => {
if (!objectType) return {}
if (objectListSorters[objectType]) {
return objectListSorters[objectType]
}
return readListSortFromSession(objectType) || {}
},
[objectListSorters]
)
const hasPageFilter = useCallback(
(path) => Object.keys(pageFilters[path] || {}).length > 0,
[pageFilters]
)
const hasStoredFilter = useCallback((scope) => hasStoredSessionFilter(scope), [])
const hasStoredFilter = useCallback((scope) => {
if (!scope) return false
if (hasStoredSessionFilter(scope)) return true
if (Object.keys(objectListFilters[scope] || {}).length > 0) return true
return Object.keys(getActiveFilterValues(readListFilterFromSession(scope) || {})).length > 0
}, [objectListFilters])
const value = useMemo(
() => ({
@ -286,10 +407,16 @@ export const TableStateProvider = ({ children }) => {
persistTableState,
setPageFilter,
setPageSorter,
setObjectListFilter,
setObjectListSorter,
getObjectListFilter,
getObjectListSorter,
hasPageFilter,
hasStoredFilter,
pageFilters,
pageSorters
pageSorters,
objectListFilters,
objectListSorters
}),
[
getPersistedFilter,
@ -301,10 +428,16 @@ export const TableStateProvider = ({ children }) => {
persistTableState,
setPageFilter,
setPageSorter,
setObjectListFilter,
setObjectListSorter,
getObjectListFilter,
getObjectListSorter,
hasPageFilter,
hasStoredFilter,
pageFilters,
pageSorters
pageSorters,
objectListFilters,
objectListSorters
]
)
@ -327,6 +460,7 @@ export const useTableState = () => {
// eslint-disable-next-line react-refresh/only-export-components
export const useTableStatePersistence = ({
scope,
viewId = null,
pagePath,
saveFilterInSession = false,
saveFilterInUrl = false,
@ -344,15 +478,58 @@ export const useTableStatePersistence = ({
persistSort: persistSortCtx,
persistTableState: persistTableStateCtx,
setPageFilter,
setPageSorter
setPageSorter,
setObjectListFilter,
setObjectListSorter
} = useTableState()
// View filter/sort belong to the view object — never read/write them via
// the shared filter/sort URL or session keys used by the "All" tab.
const isViewScope = Boolean(viewId)
const readOptions = useMemo(
() => ({ useFilterInUrl, useFilterInSession, useSortInUrl, useSortInSession }),
[useFilterInUrl, useFilterInSession, useSortInUrl, useSortInSession]
() => ({
useFilterInUrl: isViewScope ? false : useFilterInUrl,
useFilterInSession: isViewScope ? false : useFilterInSession,
useSortInUrl: isViewScope ? false : useSortInUrl,
useSortInSession: isViewScope ? false : useSortInSession
}),
[
isViewScope,
useFilterInUrl,
useFilterInSession,
useSortInUrl,
useSortInSession
]
)
const saveOptions = useMemo(
() => ({
saveFilterInSession: isViewScope ? false : saveFilterInSession,
saveFilterInUrl: isViewScope ? false : saveFilterInUrl,
saveSortInSession: isViewScope ? false : saveSortInSession,
saveSortInUrl: isViewScope ? false : saveSortInUrl
}),
[
isViewScope,
saveFilterInSession,
saveFilterInUrl,
saveSortInSession,
saveSortInUrl
]
)
const allTabReadOptions = useMemo(
() => ({
useFilterInUrl,
useFilterInSession,
useSortInUrl,
useSortInSession
}),
[useFilterInUrl, useFilterInSession, useSortInUrl, useSortInSession]
)
const allTabSaveOptions = useMemo(
() => ({
saveFilterInSession,
saveFilterInUrl,
@ -372,6 +549,18 @@ export const useTableStatePersistence = ({
[getPersistedSorterCtx, scope, readOptions]
)
// Always reads the All-tab filter/sort, even while a view is still selected
// (used when switching back to All before listViewId has cleared).
const getAllTabPersistedFilter = useCallback(
() => getPersistedFilterCtx(scope, allTabReadOptions),
[getPersistedFilterCtx, scope, allTabReadOptions]
)
const getAllTabPersistedSorter = useCallback(
() => getPersistedSorterCtx(scope, allTabReadOptions),
[getPersistedSorterCtx, scope, allTabReadOptions]
)
const persistFilter = useCallback(
(filterState) => persistFilterCtx(scope, filterState, saveOptions),
[persistFilterCtx, scope, saveOptions]
@ -388,6 +577,12 @@ export const useTableStatePersistence = ({
[persistTableStateCtx, scope, saveOptions]
)
const persistAllTabTableState = useCallback(
(filterState, sorter) =>
persistTableStateCtx(scope, filterState, sorter, allTabSaveOptions),
[persistTableStateCtx, scope, allTabSaveOptions]
)
const registerPageFilter = useCallback(
(filterState) => {
if (pagePath) {
@ -406,14 +601,38 @@ export const useTableStatePersistence = ({
[pagePath, setPageSorter]
)
const registerObjectListFilter = useCallback(
(filterState) => {
if (scope) {
setObjectListFilter(scope, filterState)
}
},
[scope, setObjectListFilter]
)
const registerObjectListSorter = useCallback(
(sorter) => {
if (scope) {
setObjectListSorter(scope, sorter)
}
},
[scope, setObjectListSorter]
)
return {
persistenceScope: scope,
getPersistedFilter,
getPersistedSorter,
getAllTabPersistedFilter,
getAllTabPersistedSorter,
persistFilter,
persistSort,
persistTableState,
persistAllTabTableState,
registerPageFilter,
registerPageSorter,
registerObjectListFilter,
registerObjectListSorter,
getActiveFilterValues
}
}