[WEB-5379] fix: optimize filter handling in work item components (#8079)

This commit is contained in:
Prateek Shourya 2025-11-07 20:28:10 +05:30 committed by GitHub
parent 1e5f583a24
commit dfc49eea27
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 112 additions and 63 deletions

View file

@ -62,17 +62,15 @@ const WorkItemFilterRoot = observer((props: TWorkItemFilterProps) => {
[isTemporary, entityId] [isTemporary, entityId]
); );
// memoize initial values to prevent re-computations when reference changes // memoize initial values to prevent re-computations when reference changes
const initialUserFilters = useMemo( const initialUserFilters = useMemo(() => initialWorkItemFilters.richFilters, [initialWorkItemFilters]);
() => initialWorkItemFilters.richFilters,
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
); // Empty dependency array to capture only the initial value
const workItemFiltersConfig = useWorkItemFiltersConfig({ const workItemFiltersConfig = useWorkItemFiltersConfig({
allowedFilters: filtersToShowByLayout ? filtersToShowByLayout : [], allowedFilters: filtersToShowByLayout ? filtersToShowByLayout : [],
...entityConfigProps, ...entityConfigProps,
}); });
// get or create filter instance // get or create filter instance
const workItemLayoutFilter = getOrCreateFilter({ const workItemLayoutFilter = useMemo(
() =>
getOrCreateFilter({
entityType, entityType,
entityId: workItemEntityID, entityId: workItemEntityID,
initialExpression: initialUserFilters, initialExpression: initialUserFilters,
@ -82,7 +80,10 @@ const WorkItemFilterRoot = observer((props: TWorkItemFilterProps) => {
updateViewOptions, updateViewOptions,
}, },
showOnMount, showOnMount,
}); }),
// eslint-disable-next-line react-hooks/exhaustive-deps
[entityType, workItemEntityID, saveViewOptions, updateViewOptions, updateFilters]
);
// delete filter instance when component unmounts // delete filter instance when component unmounts
useEffect( useEffect(
@ -92,8 +93,14 @@ const WorkItemFilterRoot = observer((props: TWorkItemFilterProps) => {
[deleteFilter, entityType, workItemEntityID] [deleteFilter, entityType, workItemEntityID]
); );
useEffect(() => {
workItemLayoutFilter.configManager.setAreConfigsReady(workItemFiltersConfig.areAllConfigsInitialized); workItemLayoutFilter.configManager.setAreConfigsReady(workItemFiltersConfig.areAllConfigsInitialized);
workItemLayoutFilter.configManager.registerAll(workItemFiltersConfig.configs); workItemLayoutFilter.configManager.registerAll(workItemFiltersConfig.configs);
}, [
workItemFiltersConfig.areAllConfigsInitialized,
workItemFiltersConfig.configs,
workItemLayoutFilter.configManager,
]);
return <>{typeof children === "function" ? children({ filter: workItemLayoutFilter }) : children}</>; return <>{typeof children === "function" ? children({ filter: workItemLayoutFilter }) : children}</>;
}); });

View file

@ -88,6 +88,17 @@ export const ProjectLevelWorkItemFiltersHOC = observer((props: TProjectLevelWork
isCurrentUserOwner, isCurrentUserOwner,
] ]
); );
const createViewLabel = useMemo(() => props.saveViewOptions?.label, [props.saveViewOptions?.label]);
const updateViewLabel = useMemo(() => props.updateViewOptions?.label, [props.updateViewOptions?.label]);
const hasAdditionalChanges = useMemo(
() =>
!isEqual(initialWorkItemFilters?.displayFilters, viewDetails?.display_filters) ||
!isEqual(
removeNillKeys(initialWorkItemFilters?.displayProperties),
removeNillKeys(viewDetails?.display_properties)
),
[initialWorkItemFilters, viewDetails]
);
const getDefaultViewDetailPayload: () => Partial<IProjectView> = useCallback( const getDefaultViewDetailPayload: () => Partial<IProjectView> = useCallback(
() => ({ () => ({
@ -108,6 +119,17 @@ export const ProjectLevelWorkItemFiltersHOC = observer((props: TProjectLevelWork
[initialWorkItemFilters] [initialWorkItemFilters]
); );
const handleViewSave = useCallback(
(expression: TWorkItemFilterExpression) => {
setCreateViewPayload({
...getDefaultViewDetailPayload(),
...getViewFilterPayload(expression),
});
setIsCreateViewModalOpen(true);
},
[getDefaultViewDetailPayload, getViewFilterPayload]
);
const handleViewUpdate = useCallback( const handleViewUpdate = useCallback(
(filterExpression: TWorkItemFilterExpression) => { (filterExpression: TWorkItemFilterExpression) => {
if (!viewDetails) { if (!viewDetails) {
@ -153,6 +175,25 @@ export const ProjectLevelWorkItemFiltersHOC = observer((props: TProjectLevelWork
[viewDetails, updateView, workspaceSlug, projectId, getViewFilterPayload] [viewDetails, updateView, workspaceSlug, projectId, getViewFilterPayload]
); );
const saveViewOptions = useMemo(
() => ({
label: createViewLabel,
isDisabled: !canCreateView,
onViewSave: handleViewSave,
}),
[createViewLabel, canCreateView, handleViewSave]
);
const updateViewOptions = useMemo(
() => ({
label: updateViewLabel,
isDisabled: !canUpdateView,
hasAdditionalChanges,
onViewUpdate: handleViewUpdate,
}),
[updateViewLabel, canUpdateView, hasAdditionalChanges, handleViewUpdate]
);
return ( return (
<> <>
<CreateUpdateProjectViewModal <CreateUpdateProjectViewModal
@ -177,28 +218,8 @@ export const ProjectLevelWorkItemFiltersHOC = observer((props: TProjectLevelWork
memberIds={getProjectMemberIds(projectId, false) ?? undefined} memberIds={getProjectMemberIds(projectId, false) ?? undefined}
moduleIds={getProjectModuleIds(projectId) ?? undefined} moduleIds={getProjectModuleIds(projectId) ?? undefined}
stateIds={getProjectStateIds(projectId)} stateIds={getProjectStateIds(projectId)}
saveViewOptions={{ saveViewOptions={saveViewOptions}
label: props.saveViewOptions?.label, updateViewOptions={updateViewOptions}
isDisabled: !canCreateView,
onViewSave: (expression) => {
setCreateViewPayload({
...getDefaultViewDetailPayload(),
...getViewFilterPayload(expression),
});
setIsCreateViewModalOpen(true);
},
}}
updateViewOptions={{
label: props.updateViewOptions?.label,
isDisabled: !canUpdateView,
hasAdditionalChanges:
!isEqual(initialWorkItemFilters?.displayFilters, viewDetails?.display_filters) ||
!isEqual(
removeNillKeys(initialWorkItemFilters?.displayProperties),
removeNillKeys(viewDetails?.display_properties)
),
onViewUpdate: handleViewUpdate,
}}
> >
{children} {children}
</WorkItemFiltersHOC> </WorkItemFiltersHOC>

View file

@ -70,6 +70,17 @@ export const WorkspaceLevelWorkItemFiltersHOC = observer((props: TWorkspaceLevel
isCurrentUserOwner, isCurrentUserOwner,
] ]
); );
const createViewLabel = useMemo(() => props.saveViewOptions?.label, [props.saveViewOptions?.label]);
const updateViewLabel = useMemo(() => props.updateViewOptions?.label, [props.updateViewOptions?.label]);
const hasAdditionalChanges = useMemo(
() =>
!isEqual(initialWorkItemFilters?.displayFilters, viewDetails?.display_filters) ||
!isEqual(
removeNillKeys(initialWorkItemFilters?.displayProperties),
removeNillKeys(viewDetails?.display_properties)
),
[initialWorkItemFilters, viewDetails]
);
const getDefaultViewDetailPayload: () => Partial<IWorkspaceView> = useCallback( const getDefaultViewDetailPayload: () => Partial<IWorkspaceView> = useCallback(
() => ({ () => ({
@ -89,6 +100,17 @@ export const WorkspaceLevelWorkItemFiltersHOC = observer((props: TWorkspaceLevel
[initialWorkItemFilters] [initialWorkItemFilters]
); );
const handleViewSave = useCallback(
(expression: TWorkItemFilterExpression) => {
setCreateViewPayload({
...getDefaultViewDetailPayload(),
...getViewFilterPayload(expression),
});
setIsCreateViewModalOpen(true);
},
[getDefaultViewDetailPayload, getViewFilterPayload]
);
const handleViewUpdate = useCallback( const handleViewUpdate = useCallback(
(filterExpression: TWorkItemFilterExpression) => { (filterExpression: TWorkItemFilterExpression) => {
if (!viewDetails) { if (!viewDetails) {
@ -140,6 +162,25 @@ export const WorkspaceLevelWorkItemFiltersHOC = observer((props: TWorkspaceLevel
[viewDetails, updateGlobalView, workspaceSlug, getViewFilterPayload] [viewDetails, updateGlobalView, workspaceSlug, getViewFilterPayload]
); );
const saveViewOptions = useMemo(
() => ({
label: createViewLabel,
isDisabled: !canCreateView,
onViewSave: handleViewSave,
}),
[createViewLabel, canCreateView, handleViewSave]
);
const updateViewOptions = useMemo(
() => ({
label: updateViewLabel,
isDisabled: !canUpdateView,
hasAdditionalChanges,
onViewUpdate: handleViewUpdate,
}),
[updateViewLabel, canUpdateView, hasAdditionalChanges, handleViewUpdate]
);
return ( return (
<> <>
<CreateUpdateWorkspaceViewModal <CreateUpdateWorkspaceViewModal
@ -155,28 +196,8 @@ export const WorkspaceLevelWorkItemFiltersHOC = observer((props: TWorkspaceLevel
memberIds={getWorkspaceMemberIds(workspaceSlug)} memberIds={getWorkspaceMemberIds(workspaceSlug)}
labelIds={getWorkspaceLabelIds(workspaceSlug)} labelIds={getWorkspaceLabelIds(workspaceSlug)}
projectIds={joinedProjectIds} projectIds={joinedProjectIds}
saveViewOptions={{ saveViewOptions={saveViewOptions}
label: props.saveViewOptions?.label, updateViewOptions={updateViewOptions}
isDisabled: !canCreateView,
onViewSave: (expression) => {
setCreateViewPayload({
...getDefaultViewDetailPayload(),
...getViewFilterPayload(expression),
});
setIsCreateViewModalOpen(true);
},
}}
updateViewOptions={{
label: props.updateViewOptions?.label,
isDisabled: !canUpdateView,
hasAdditionalChanges:
!isEqual(initialWorkItemFilters?.displayFilters, viewDetails?.display_filters) ||
!isEqual(
removeNillKeys(initialWorkItemFilters?.displayProperties),
removeNillKeys(viewDetails?.display_properties)
),
onViewUpdate: handleViewUpdate,
}}
> >
{children} {children}
</WorkItemFiltersHOC> </WorkItemFiltersHOC>

View file

@ -160,7 +160,7 @@ export class FilterInstance<P extends TFilterProperty, E extends TExternalFilter
id: observable, id: observable,
initialFilterExpression: observable, initialFilterExpression: observable,
expression: observable, expression: observable,
expressionOptions: observable, expressionOptions: observable.struct,
adapter: observable, adapter: observable,
configManager: observable, configManager: observable,
// computed // computed