Enhance FilterSidebar and ObjectTable Components with View Filter and User Overrides
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
All checks were successful
farmcontrol/farmcontrol-ui/pipeline/head This commit looks good
- Added support for viewFilter and isEditing props in FilterSidebar to manage filter states more effectively. - Updated ObjectTable to handle user overrides for filter and sort states when switching views, ensuring accurate data representation. - Integrated userOverridesRef to maintain filter and sort states during editing, improving user experience when managing views. - Adjusted loading logic in ObjectTableNavigationButtons to prioritize effective filters based on active views, enhancing data consistency.
This commit is contained in:
parent
3d1926aba0
commit
789e66b0f3
@ -45,7 +45,9 @@ const FilterSidebar = ({
|
||||
type,
|
||||
filter = {},
|
||||
onFilterChange,
|
||||
masterFilter = {}
|
||||
masterFilter = {},
|
||||
viewFilter = {},
|
||||
isEditing = false
|
||||
}) => {
|
||||
const model = getModelByName(type)
|
||||
const modelProps = getModelProperties(type)
|
||||
@ -113,6 +115,11 @@ const FilterSidebar = ({
|
||||
[filterableProperties]
|
||||
)
|
||||
|
||||
const viewFilterKeys = useMemo(
|
||||
() => new Set(Object.keys(viewFilter).filter((k) => viewFilter[k] !== '')),
|
||||
[viewFilter]
|
||||
)
|
||||
|
||||
const activeFields = Object.keys(localFilter)
|
||||
|
||||
const usedFields = new Set(activeFields)
|
||||
@ -192,39 +199,46 @@ const FilterSidebar = ({
|
||||
/>
|
||||
</Flex>
|
||||
<Flex vertical gap={8}>
|
||||
{rows.map((row) => (
|
||||
<Space.Compact key={row.field} style={{ width: '100%' }}>
|
||||
<Select
|
||||
placeholder='Field'
|
||||
value={row.field || undefined}
|
||||
onChange={(v) => changeField(row.field, v)}
|
||||
options={availableOptions(row.field)}
|
||||
allowClear={false}
|
||||
labelRender={({ label }) => (
|
||||
<FieldSelectLabel label={label} options={fieldOptions} />
|
||||
)}
|
||||
style={{ flexShrink: 0 }}
|
||||
classNames={{ root: 'filter-sidebar-field-select' }}
|
||||
/>
|
||||
<FilterInput
|
||||
placeholder='Value'
|
||||
value={row.value}
|
||||
onChange={(value) => changeValue(row.field, value)}
|
||||
style={{ flex: 1 }}
|
||||
propertyFilter={{
|
||||
modelType: type,
|
||||
propertyName: row.field
|
||||
}}
|
||||
filter={localFilter}
|
||||
masterFilter={masterFilter}
|
||||
/>
|
||||
<Button
|
||||
icon={<CloseOutlined />}
|
||||
onClick={() => removeRow(row.field)}
|
||||
title='Remove filter'
|
||||
/>
|
||||
</Space.Compact>
|
||||
))}
|
||||
{rows.map((row) => {
|
||||
const isViewRow =
|
||||
viewFilterKeys.has(row.field) && !isEditing
|
||||
return (
|
||||
<Space.Compact key={row.field} style={{ width: '100%' }}>
|
||||
<Select
|
||||
placeholder='Field'
|
||||
value={row.field || undefined}
|
||||
onChange={(v) => changeField(row.field, v)}
|
||||
options={availableOptions(row.field)}
|
||||
allowClear={false}
|
||||
disabled={isViewRow}
|
||||
labelRender={({ label }) => (
|
||||
<FieldSelectLabel label={label} options={fieldOptions} />
|
||||
)}
|
||||
style={{ flexShrink: 0 }}
|
||||
classNames={{ root: 'filter-sidebar-field-select' }}
|
||||
/>
|
||||
<FilterInput
|
||||
placeholder='Value'
|
||||
value={row.value}
|
||||
onChange={(value) => changeValue(row.field, value)}
|
||||
style={{ flex: 1 }}
|
||||
disabled={isViewRow}
|
||||
propertyFilter={{
|
||||
modelType: type,
|
||||
propertyName: row.field
|
||||
}}
|
||||
filter={localFilter}
|
||||
masterFilter={masterFilter}
|
||||
/>
|
||||
<Button
|
||||
icon={<CloseOutlined />}
|
||||
onClick={() => removeRow(row.field)}
|
||||
title='Remove filter'
|
||||
disabled={isViewRow}
|
||||
/>
|
||||
</Space.Compact>
|
||||
)
|
||||
})}
|
||||
{rows.length === 0 && (
|
||||
<MissingPlaceholder message='No filters applied.' />
|
||||
)}
|
||||
@ -238,7 +252,9 @@ FilterSidebar.propTypes = {
|
||||
type: PropTypes.string.isRequired,
|
||||
filter: PropTypes.object,
|
||||
onFilterChange: PropTypes.func.isRequired,
|
||||
masterFilter: PropTypes.object
|
||||
masterFilter: PropTypes.object,
|
||||
viewFilter: PropTypes.object,
|
||||
isEditing: PropTypes.bool
|
||||
}
|
||||
|
||||
export default FilterSidebar
|
||||
|
||||
@ -329,6 +329,7 @@ const ObjectTable = forwardRef(
|
||||
const listViewFilter = objectListView?.listViewFilter ?? null
|
||||
const listViewSort = objectListView?.listViewSort ?? null
|
||||
const activeObjectView = objectListView?.activeObjectView ?? null
|
||||
const userOverridesRef = objectListView?.userOverridesRef ?? null
|
||||
const onObjectViewFilterSortChange = objectListView?.isEditing
|
||||
? objectListView.handleFilterSortChange
|
||||
: null
|
||||
@ -1369,12 +1370,6 @@ const ObjectTable = forwardRef(
|
||||
|
||||
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
|
||||
|
||||
assignSidebarToView(listViewId)
|
||||
@ -1394,6 +1389,9 @@ const ObjectTable = forwardRef(
|
||||
|
||||
const applyViewState = useCallback(
|
||||
(filterState, sorterState, options = {}) => {
|
||||
// Switching views clears user overrides
|
||||
if (userOverridesRef) userOverridesRef.current = null
|
||||
|
||||
let filter
|
||||
let sorter
|
||||
|
||||
@ -1630,7 +1628,16 @@ const ObjectTable = forwardRef(
|
||||
listViewFilterRef.current = next
|
||||
// Editing a view: keep filter/sort on the draft view only
|
||||
onObjectViewFilterSortChangeRef.current?.(next, nextSorter)
|
||||
} else if (!listViewIdRef.current) {
|
||||
} else if (listViewIdRef.current) {
|
||||
// On a view but not editing: track as user override for startEditing
|
||||
if (userOverridesRef) {
|
||||
userOverridesRef.current = {
|
||||
...(userOverridesRef.current || {}),
|
||||
filter: next,
|
||||
sort: nextSorter
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// All tab: persist personal filter/sort to URL/session
|
||||
persistTableState(next, nextSorter)
|
||||
}
|
||||
@ -1651,7 +1658,20 @@ const ObjectTable = forwardRef(
|
||||
newSidebarFilter,
|
||||
tableSorter
|
||||
)
|
||||
} else if (!listViewIdRef.current) {
|
||||
} else if (listViewIdRef.current) {
|
||||
// On a view but not editing: track as user override for startEditing
|
||||
if (userOverridesRef) {
|
||||
userOverridesRef.current = {
|
||||
...(userOverridesRef.current || {}),
|
||||
filter: newSidebarFilter,
|
||||
sort:
|
||||
userOverridesRef.current?.sort ??
|
||||
(tableSorterRef.current?.field
|
||||
? tableSorterRef.current
|
||||
: undefined)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
persistFilter(newSidebarFilter)
|
||||
}
|
||||
const effective = buildEffectiveFilter(newSidebarFilter)
|
||||
@ -1667,7 +1687,8 @@ const ObjectTable = forwardRef(
|
||||
loadPage,
|
||||
persistFilter,
|
||||
resolveSorter,
|
||||
tableSorter
|
||||
tableSorter,
|
||||
userOverridesRef
|
||||
]
|
||||
)
|
||||
|
||||
@ -1681,7 +1702,16 @@ const ObjectTable = forwardRef(
|
||||
tableSorterRef.current = nextSorter
|
||||
if (activeObjectViewRef.current) {
|
||||
onObjectViewFilterSortChangeRef.current?.(sidebarFilter, nextSorter)
|
||||
} else if (!listViewIdRef.current) {
|
||||
} else if (listViewIdRef.current) {
|
||||
// On a view but not editing: track as user override for startEditing
|
||||
if (userOverridesRef) {
|
||||
userOverridesRef.current = {
|
||||
...(userOverridesRef.current || {}),
|
||||
sort: nextSorter,
|
||||
filter: userOverridesRef.current?.filter ?? sidebarFilter
|
||||
}
|
||||
}
|
||||
} else {
|
||||
persistSort(nextSorter)
|
||||
}
|
||||
const effective = buildEffectiveFilter(sidebarFilter)
|
||||
@ -1696,7 +1726,8 @@ const ObjectTable = forwardRef(
|
||||
persistSort,
|
||||
resolveSorter,
|
||||
sidebarFilter,
|
||||
suppressTableChange
|
||||
suppressTableChange,
|
||||
userOverridesRef
|
||||
]
|
||||
)
|
||||
|
||||
@ -2157,6 +2188,8 @@ const ObjectTable = forwardRef(
|
||||
}
|
||||
onFilterChange={handleSidebarFilterChange}
|
||||
masterFilter={resolvedMasterFilter}
|
||||
viewFilter={listViewFilter}
|
||||
isEditing={!!activeObjectView}
|
||||
/>
|
||||
</Flex>
|
||||
)}
|
||||
|
||||
@ -26,7 +26,8 @@ const ObjectTableNavigationButtons = ({
|
||||
const [neighbors, setNeighbors] = useState({ next: null, previous: null })
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
const { getObjectListFilter, getObjectListSorter } = useTableState()
|
||||
const { getObjectListFilter, getObjectListSorter, getObjectListView } =
|
||||
useTableState()
|
||||
const { getPersistedFilter, getPersistedSorter } = useTableStatePersistence({
|
||||
scope: objectType,
|
||||
useFilterInSession: true,
|
||||
@ -42,6 +43,9 @@ const ObjectTableNavigationButtons = ({
|
||||
const getObjectListSorterRef = useRef(getObjectListSorter)
|
||||
getObjectListSorterRef.current = getObjectListSorter
|
||||
|
||||
const getObjectListViewRef = useRef(getObjectListView)
|
||||
getObjectListViewRef.current = getObjectListView
|
||||
|
||||
const getPersistedFilterRef = useRef(getPersistedFilter)
|
||||
getPersistedFilterRef.current = getPersistedFilter
|
||||
|
||||
@ -59,18 +63,23 @@ const ObjectTableNavigationButtons = ({
|
||||
|
||||
const loadNeighbors = async () => {
|
||||
setLoading(true)
|
||||
// Prefer the effective list filter (view + user filters) when available
|
||||
// When a view is active, use its effective filter exclusively — never
|
||||
// fall back to the All-tab persisted filter which belongs to a different
|
||||
// context and would produce wrong neighbors.
|
||||
const hasActiveView = !!getObjectListViewRef.current(objectType)
|
||||
const listFilter = getActiveFilterValues(
|
||||
getObjectListFilterRef.current(objectType)
|
||||
)
|
||||
const persistedFilter = getActiveFilterValues(
|
||||
getPersistedFilterRef.current()
|
||||
)
|
||||
const persistedFilter = hasActiveView
|
||||
? {}
|
||||
: getActiveFilterValues(getPersistedFilterRef.current())
|
||||
const filter =
|
||||
Object.keys(listFilter).length > 0 ? listFilter : persistedFilter
|
||||
|
||||
const listSorter = getObjectListSorterRef.current(objectType)
|
||||
const persistedSorter = getPersistedSorterRef.current()
|
||||
const persistedSorter = hasActiveView
|
||||
? {}
|
||||
: getPersistedSorterRef.current()
|
||||
const sorter =
|
||||
listSorter?.field && listSorter?.order ? listSorter : persistedSorter
|
||||
|
||||
|
||||
@ -13,10 +13,7 @@ import { ApiServerContext } from './ApiServerContext'
|
||||
import { AuthContext } from './AuthContext'
|
||||
import { useTableState } from './TableStateContext'
|
||||
import useViewMode from '../hooks/useViewMode'
|
||||
import {
|
||||
DEFAULT_VIEW_MODE,
|
||||
normalizeViewMode
|
||||
} from '../common/viewModeUtils'
|
||||
import { DEFAULT_VIEW_MODE, normalizeViewMode } from '../common/viewModeUtils'
|
||||
|
||||
const ObjectListViewContext = createContext()
|
||||
|
||||
@ -25,8 +22,7 @@ const ALL_TAB_KEY = 'all'
|
||||
const getLastViewStorageKey = (objectType) =>
|
||||
`objectListView:${objectType}:lastViewId`
|
||||
|
||||
const getViewsCacheKey = (objectType) =>
|
||||
`objectListView:${objectType}:views`
|
||||
const getViewsCacheKey = (objectType) => `objectListView:${objectType}:views`
|
||||
|
||||
const readLastViewId = (objectType) => {
|
||||
if (!objectType) return null
|
||||
@ -99,9 +95,7 @@ export const getObjectViewKey = (view) =>
|
||||
|
||||
const resolveViewFromId = (views, viewId) => {
|
||||
if (!viewId) return null
|
||||
return (
|
||||
views.find((view) => String(view._id) === String(viewId)) || null
|
||||
)
|
||||
return views.find((view) => String(view._id) === String(viewId)) || null
|
||||
}
|
||||
|
||||
const cloneViews = (views) =>
|
||||
@ -121,11 +115,7 @@ const viewStateEqual = (left, right) =>
|
||||
const viewModeEqual = (left, right) =>
|
||||
JSON.stringify(left ?? null) === JSON.stringify(right ?? null)
|
||||
|
||||
export const ObjectListViewProvider = ({
|
||||
children,
|
||||
objectType,
|
||||
tableRef
|
||||
}) => {
|
||||
export const ObjectListViewProvider = ({ children, objectType, tableRef }) => {
|
||||
const {
|
||||
fetchObjectViews,
|
||||
createObjectView,
|
||||
@ -138,9 +128,7 @@ export const ObjectListViewProvider = ({
|
||||
const { token, authInitialized } = useContext(AuthContext)
|
||||
const { getViewFromUrl, persistView, setObjectListView } = useTableState()
|
||||
|
||||
const [views, setViews] = useState(
|
||||
() => readCachedViews(objectType) || []
|
||||
)
|
||||
const [views, setViews] = useState(() => readCachedViews(objectType) || [])
|
||||
const [draftViews, setDraftViews] = useState(() => {
|
||||
const cached = readCachedViews(objectType)
|
||||
return cached ? cloneViews(cached) : []
|
||||
@ -155,10 +143,14 @@ export const ObjectListViewProvider = ({
|
||||
activeTabKeyRef.current = activeTabKey
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
const [viewSyncReady, setViewSyncReady] = useState(false)
|
||||
const [userViewModeOverride, setUserViewModeOverride] = useState(null)
|
||||
|
||||
const editSnapshotRef = useRef([])
|
||||
const wasEditingRef = useRef(false)
|
||||
const isEditingRef = useRef(isEditing)
|
||||
// ObjectTable writes its current user overrides (filter/sort) here so
|
||||
// startEditing can merge them into the draft view without prop drilling.
|
||||
const userOverridesRef = useRef(null)
|
||||
const activeViewRef = useRef(null)
|
||||
const viewsStateRef = useRef({ views, draftViews, isEditing })
|
||||
const urlSyncedRef = useRef(false)
|
||||
@ -188,8 +180,9 @@ export const ObjectListViewProvider = ({
|
||||
const activeView = useMemo(() => {
|
||||
if (activeTabKey === ALL_TAB_KEY) return null
|
||||
const view =
|
||||
displayedViews.find((item) => String(item._id) === String(activeTabKey)) ||
|
||||
null
|
||||
displayedViews.find(
|
||||
(item) => String(item._id) === String(activeTabKey)
|
||||
) || null
|
||||
if (view?._isDraft) {
|
||||
return { ...view, filter: view.filter || {}, sort: view.sort || {} }
|
||||
}
|
||||
@ -213,8 +206,11 @@ export const ObjectListViewProvider = ({
|
||||
(tabKey) => {
|
||||
if (!tableRef?.current?.applyViewState) return
|
||||
|
||||
const { views: savedViews, draftViews: drafts, isEditing: editing } =
|
||||
viewsStateRef.current
|
||||
const {
|
||||
views: savedViews,
|
||||
draftViews: drafts,
|
||||
isEditing: editing
|
||||
} = viewsStateRef.current
|
||||
const list = editing ? drafts : savedViews
|
||||
const view =
|
||||
tabKey === ALL_TAB_KEY
|
||||
@ -289,10 +285,7 @@ export const ObjectListViewProvider = ({
|
||||
|
||||
persistView(key)
|
||||
writeLastViewId(objectType, key)
|
||||
setObjectListView(
|
||||
objectType,
|
||||
key ? { id: key, name } : null
|
||||
)
|
||||
setObjectListView(objectType, key ? { id: key, name } : null)
|
||||
},
|
||||
[objectType, persistView, setObjectListView]
|
||||
)
|
||||
@ -402,6 +395,8 @@ export const ObjectListViewProvider = ({
|
||||
(tabKey) => {
|
||||
if (String(tabKey) === String(activeTabKey)) return
|
||||
|
||||
setUserViewModeOverride(null)
|
||||
userOverridesRef.current = null
|
||||
setActiveTabKey(tabKey)
|
||||
|
||||
if (tabKey === ALL_TAB_KEY) {
|
||||
@ -431,7 +426,24 @@ export const ObjectListViewProvider = ({
|
||||
const startEditing = useCallback(() => {
|
||||
const snapshot = cloneViews(views)
|
||||
editSnapshotRef.current = snapshot
|
||||
|
||||
// If the user had filter/sort/viewMode overrides while browsing a view,
|
||||
// merge them into the active draft so the save picks them up.
|
||||
const overrides = userOverridesRef.current
|
||||
if (overrides && activeTabKeyRef.current !== ALL_TAB_KEY) {
|
||||
const activeId = activeTabKeyRef.current
|
||||
for (const draft of snapshot) {
|
||||
if (String(draft._id) !== String(activeId)) continue
|
||||
if (overrides.filter) draft.filter = overrides.filter
|
||||
if (overrides.sort) draft.sort = overrides.sort
|
||||
if (overrides.viewMode !== undefined)
|
||||
draft.viewMode = overrides.viewMode
|
||||
}
|
||||
}
|
||||
|
||||
setDraftViews(snapshot)
|
||||
setUserViewModeOverride(null)
|
||||
userOverridesRef.current = null
|
||||
setIsEditing(true)
|
||||
}, [views])
|
||||
|
||||
@ -440,6 +452,7 @@ export const ObjectListViewProvider = ({
|
||||
setDraftViews(snapshot)
|
||||
setViews(snapshot)
|
||||
setIsEditing(false)
|
||||
userOverridesRef.current = null
|
||||
|
||||
if (activeTabKey !== ALL_TAB_KEY) {
|
||||
const stillExists = snapshot.some(
|
||||
@ -458,8 +471,12 @@ export const ObjectListViewProvider = ({
|
||||
const saveEdits = useCallback(async () => {
|
||||
setSaving(true)
|
||||
try {
|
||||
const originalById = new Map(views.map((view) => [String(view._id), view]))
|
||||
const draftById = new Map(draftViews.map((view) => [String(view._id), view]))
|
||||
const originalById = new Map(
|
||||
views.map((view) => [String(view._id), view])
|
||||
)
|
||||
const draftById = new Map(
|
||||
draftViews.map((view) => [String(view._id), view])
|
||||
)
|
||||
|
||||
let newActiveId = activeTabKey
|
||||
|
||||
@ -542,13 +559,7 @@ export const ObjectListViewProvider = ({
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}, [
|
||||
activeTabKey,
|
||||
draftViews,
|
||||
objectType,
|
||||
rememberActiveView,
|
||||
views
|
||||
])
|
||||
}, [activeTabKey, draftViews, objectType, rememberActiveView, views])
|
||||
|
||||
const handleTabEdit = useCallback(
|
||||
async (targetKeyOrEvent, action) => {
|
||||
@ -642,40 +653,45 @@ export const ObjectListViewProvider = ({
|
||||
)
|
||||
}, [])
|
||||
|
||||
const reorderDraftViews = useCallback((fromValue, toValue, insertBefore) => {
|
||||
if (!isEditing) return
|
||||
const reorderDraftViews = useCallback(
|
||||
(fromValue, toValue, insertBefore) => {
|
||||
if (!isEditing) return
|
||||
|
||||
setDraftViews((prev) => {
|
||||
const fromIndex = prev.findIndex(
|
||||
(view) => String(view._id) === String(fromValue)
|
||||
)
|
||||
const toIndex = prev.findIndex(
|
||||
(view) => String(view._id) === String(toValue)
|
||||
)
|
||||
if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) {
|
||||
return prev
|
||||
}
|
||||
setDraftViews((prev) => {
|
||||
const fromIndex = prev.findIndex(
|
||||
(view) => String(view._id) === String(fromValue)
|
||||
)
|
||||
const toIndex = prev.findIndex(
|
||||
(view) => String(view._id) === String(toValue)
|
||||
)
|
||||
if (fromIndex === -1 || toIndex === -1 || fromIndex === toIndex) {
|
||||
return prev
|
||||
}
|
||||
|
||||
let targetIndex = insertBefore ? toIndex : toIndex + 1
|
||||
if (fromIndex < targetIndex) {
|
||||
targetIndex -= 1
|
||||
}
|
||||
if (fromIndex === targetIndex) {
|
||||
return prev
|
||||
}
|
||||
let targetIndex = insertBefore ? toIndex : toIndex + 1
|
||||
if (fromIndex < targetIndex) {
|
||||
targetIndex -= 1
|
||||
}
|
||||
if (fromIndex === targetIndex) {
|
||||
return prev
|
||||
}
|
||||
|
||||
const next = [...prev]
|
||||
const [moved] = next.splice(fromIndex, 1)
|
||||
next.splice(targetIndex, 0, moved)
|
||||
return next
|
||||
})
|
||||
}, [isEditing])
|
||||
const next = [...prev]
|
||||
const [moved] = next.splice(fromIndex, 1)
|
||||
next.splice(targetIndex, 0, moved)
|
||||
return next
|
||||
})
|
||||
},
|
||||
[isEditing]
|
||||
)
|
||||
|
||||
const mergeViewUpdate = useCallback(
|
||||
(viewId, updatedData) => {
|
||||
const mergeOne = (prev) =>
|
||||
prev.map((view) =>
|
||||
String(view._id) === String(viewId) ? { ...view, ...updatedData } : view
|
||||
String(view._id) === String(viewId)
|
||||
? { ...view, ...updatedData }
|
||||
: view
|
||||
)
|
||||
|
||||
setViews(mergeOne)
|
||||
@ -694,7 +710,9 @@ export const ObjectListViewProvider = ({
|
||||
? getPreviousTabKey(list, viewId)
|
||||
: null
|
||||
|
||||
setViews((prev) => prev.filter((view) => String(view._id) !== String(viewId)))
|
||||
setViews((prev) =>
|
||||
prev.filter((view) => String(view._id) !== String(viewId))
|
||||
)
|
||||
if (!isEditing) {
|
||||
setDraftViews((prev) =>
|
||||
prev.filter((view) => String(view._id) !== String(viewId))
|
||||
@ -716,7 +734,14 @@ export const ObjectListViewProvider = ({
|
||||
applyViewToTable(previousKey)
|
||||
}
|
||||
},
|
||||
[activeTabKey, applyViewToTable, draftViews, isEditing, rememberActiveView, views]
|
||||
[
|
||||
activeTabKey,
|
||||
applyViewToTable,
|
||||
draftViews,
|
||||
isEditing,
|
||||
rememberActiveView,
|
||||
views
|
||||
]
|
||||
)
|
||||
|
||||
const handleFilterSortChange = useCallback((filter, sorter) => {
|
||||
@ -767,10 +792,7 @@ export const ObjectListViewProvider = ({
|
||||
|
||||
updateEventHandlerRef.current = updateEventHandler
|
||||
|
||||
const subscriptionFilter = useMemo(
|
||||
() => ({ objectType }),
|
||||
[objectType]
|
||||
)
|
||||
const subscriptionFilter = useMemo(() => ({ objectType }), [objectType])
|
||||
|
||||
useEffect(() => {
|
||||
if (connected !== true || !objectType || !authReady) return
|
||||
@ -860,9 +882,7 @@ export const ObjectListViewProvider = ({
|
||||
? String(activeTabKey)
|
||||
: null
|
||||
const listViewFilter = activeView?.filter || EMPTY_FILTER
|
||||
const listViewSort = activeView?.sort?.field
|
||||
? activeView.sort
|
||||
: EMPTY_SORT
|
||||
const listViewSort = activeView?.sort?.field ? activeView.sort : EMPTY_SORT
|
||||
const listViewViewMode = activeView?.viewMode ?? null
|
||||
// Only expose the editable view while editing — ObjectTable uses this to
|
||||
// route filter/sort changes into the draft instead of URL/session persistence.
|
||||
@ -876,9 +896,7 @@ export const ObjectListViewProvider = ({
|
||||
if (isEditingRef.current) {
|
||||
const updateInList = (list) =>
|
||||
list.map((item) =>
|
||||
String(item._id) === String(view._id)
|
||||
? { ...item, viewMode }
|
||||
: item
|
||||
String(item._id) === String(view._id) ? { ...item, viewMode } : item
|
||||
)
|
||||
|
||||
activeViewRef.current = { ...view, viewMode }
|
||||
@ -891,20 +909,22 @@ export const ObjectListViewProvider = ({
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await apiRef.current.updateObjectView(view._id, { viewMode })
|
||||
mergeViewUpdate(view._id, { viewMode })
|
||||
} catch (error) {
|
||||
console.error('Error updating view mode:', error)
|
||||
message.error('Failed to save view mode')
|
||||
// Not editing: store as a temporary user override instead of saving
|
||||
setUserViewModeOverride(viewMode)
|
||||
userOverridesRef.current = {
|
||||
...(userOverridesRef.current || {}),
|
||||
viewMode
|
||||
}
|
||||
},
|
||||
[mergeViewUpdate]
|
||||
[]
|
||||
)
|
||||
|
||||
const isCustomView = activeTabKey !== ALL_TAB_KEY
|
||||
|
||||
const viewMode = useMemo(() => {
|
||||
if (isCustomView && userViewModeOverride) {
|
||||
return normalizeViewMode(userViewModeOverride)
|
||||
}
|
||||
if (isCustomView && listViewViewMode) {
|
||||
return normalizeViewMode(listViewViewMode)
|
||||
}
|
||||
@ -912,7 +932,7 @@ export const ObjectListViewProvider = ({
|
||||
return DEFAULT_VIEW_MODE
|
||||
}
|
||||
return normalizeViewMode(userViewMode)
|
||||
}, [isCustomView, listViewViewMode, userViewMode])
|
||||
}, [isCustomView, listViewViewMode, userViewMode, userViewModeOverride])
|
||||
|
||||
const setViewMode = useCallback(
|
||||
(nextMode) => {
|
||||
@ -963,6 +983,7 @@ export const ObjectListViewProvider = ({
|
||||
removeView,
|
||||
handleFilterSortChange,
|
||||
handleViewModeChange,
|
||||
userOverridesRef,
|
||||
reloadViews: () => loadViewsRef.current(true),
|
||||
viewTabSignatures
|
||||
}),
|
||||
|
||||
@ -481,10 +481,16 @@ export const TableStateProvider = ({ children }) => {
|
||||
|
||||
const hasStoredFilter = useCallback((scope) => {
|
||||
if (!scope) return false
|
||||
// When a view is active, only report the effective list filter (which
|
||||
// already accounts for the view). Don't fall back to the All-tab session
|
||||
// filter — that belongs to a different context.
|
||||
if (objectListViews[scope]) {
|
||||
return Object.keys(objectListFilters[scope] || {}).length > 0
|
||||
}
|
||||
if (hasStoredSessionFilter(scope)) return true
|
||||
if (Object.keys(objectListFilters[scope] || {}).length > 0) return true
|
||||
return Object.keys(getActiveFilterValues(readListFilterFromSession(scope) || {})).length > 0
|
||||
}, [objectListFilters])
|
||||
}, [objectListFilters, objectListViews])
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user