expand sub tasks

This commit is contained in:
chamiakJ
2025-07-03 01:31:05 +05:30
parent 3bef18901a
commit ecd4d29a38
435 changed files with 13150 additions and 11087 deletions

View File

@@ -85,7 +85,7 @@ const createFilters = (items: { id: string; name: string }[]) =>
const ProjectList: React.FC = () => {
const [filteredInfo, setFilteredInfo] = useState<Record<string, FilterValue | null>>({});
const [isLoading, setIsLoading] = useState(false);
const { t } = useTranslation('all-project-list');
const dispatch = useAppDispatch();
const navigate = useNavigate();
@@ -94,14 +94,14 @@ const ProjectList: React.FC = () => {
const { trackMixpanelEvent } = useMixpanelTracking();
// Get view state from Redux
const { mode: viewMode, groupBy } = useAppSelector((state) => state.projectViewReducer);
const { requestParams, groupedRequestParams, groupedProjects } = useAppSelector(state => state.projectsReducer);
const { mode: viewMode, groupBy } = useAppSelector(state => state.projectViewReducer);
const { requestParams, groupedRequestParams, groupedProjects } = useAppSelector(
state => state.projectsReducer
);
const { projectStatuses } = useAppSelector(state => state.projectStatusesReducer);
const { projectHealths } = useAppSelector(state => state.projectHealthReducer);
const { projectCategories } = useAppSelector(state => state.projectCategoriesReducer);
const { filteredCategories, filteredStatuses } = useAppSelector(
state => state.projectsReducer
);
const { filteredCategories, filteredStatuses } = useAppSelector(state => state.projectsReducer);
const {
data: projectsData,
@@ -175,18 +175,20 @@ const ProjectList: React.FC = () => {
);
// Memoize category filters to prevent unnecessary recalculations
const categoryFilters = useMemo(() =>
createFilters(
projectCategories.map(category => ({ id: category.id || '', name: category.name || '' }))
),
const categoryFilters = useMemo(
() =>
createFilters(
projectCategories.map(category => ({ id: category.id || '', name: category.name || '' }))
),
[projectCategories]
);
// Memoize status filters to prevent unnecessary recalculations
const statusFilters = useMemo(() =>
createFilters(
projectStatuses.map(status => ({ id: status.id || '', name: status.name || '' }))
),
const statusFilters = useMemo(
() =>
createFilters(
projectStatuses.map(status => ({ id: status.id || '', name: status.name || '' }))
),
[projectStatuses]
);
@@ -221,39 +223,36 @@ const ProjectList: React.FC = () => {
if (viewMode === ProjectViewType.LIST) {
return projectsData?.body?.total || 0;
} else {
return groupedProjects.data?.data?.reduce((total, group) => total + group.project_count, 0) || 0;
return (
groupedProjects.data?.data?.reduce((total, group) => total + group.project_count, 0) || 0
);
}
}, [viewMode, projectsData?.body?.total, groupedProjects.data?.data]);
// Memoize the grouped projects data transformation
const transformedGroupedProjects = useMemo(() => {
return groupedProjects.data?.data?.map(group => ({
groupKey: group.group_key,
groupName: group.group_name,
groupColor: group.group_color,
projects: group.projects,
count: group.project_count,
totalProgress: 0,
totalTasks: 0
})) || [];
return (
groupedProjects.data?.data?.map(group => ({
groupKey: group.group_key,
groupName: group.group_name,
groupColor: group.group_color,
projects: group.projects,
count: group.project_count,
totalProgress: 0,
totalTasks: 0,
})) || []
);
}, [groupedProjects.data?.data]);
// Memoize the table data source
const tableDataSource = useMemo(() =>
projectsData?.body?.data || [],
[projectsData?.body?.data]
);
const tableDataSource = useMemo(() => projectsData?.body?.data || [], [projectsData?.body?.data]);
// Memoize the empty text component
const emptyText = useMemo(() =>
<Empty description={t('noProjects')} />,
[t]
);
const emptyText = useMemo(() => <Empty description={t('noProjects')} />, [t]);
// Memoize the pagination show total function
const paginationShowTotal = useMemo(() =>
(total: number, range: [number, number]) =>
`${range[0]}-${range[1]} of ${total} groups`,
const paginationShowTotal = useMemo(
() => (total: number, range: [number, number]) => `${range[0]}-${range[1]} of ${total} groups`,
[]
);
@@ -291,18 +290,20 @@ const ProjectList: React.FC = () => {
newParams.size = newPagination.pageSize || DEFAULT_PAGE_SIZE;
dispatch(setRequestParams(newParams));
// Also update grouped request params to keep them in sync
dispatch(setGroupedRequestParams({
...groupedRequestParams,
statuses: newParams.statuses,
categories: newParams.categories,
order: newParams.order,
field: newParams.field,
index: newParams.index,
size: newParams.size,
}));
dispatch(
setGroupedRequestParams({
...groupedRequestParams,
statuses: newParams.statuses,
categories: newParams.categories,
order: newParams.order,
field: newParams.field,
index: newParams.index,
size: newParams.size,
})
);
setFilteredInfo(filters);
},
[dispatch, setSortingValues, groupedRequestParams]
@@ -332,24 +333,28 @@ const ProjectList: React.FC = () => {
(value: IProjectFilter) => {
const newFilterIndex = filters.indexOf(value);
setFilterIndex(newFilterIndex);
// Update both request params for consistency
dispatch(setRequestParams({ filter: newFilterIndex }));
dispatch(setGroupedRequestParams({
...groupedRequestParams,
filter: newFilterIndex,
index: 1 // Reset to first page when changing filter
}));
dispatch(
setGroupedRequestParams({
...groupedRequestParams,
filter: newFilterIndex,
index: 1, // Reset to first page when changing filter
})
);
// Refresh data based on current view mode
if (viewMode === ProjectViewType.LIST) {
refetchProjects();
} else if (viewMode === ProjectViewType.GROUP && groupBy) {
dispatch(fetchGroupedProjects({
...groupedRequestParams,
filter: newFilterIndex,
index: 1
}));
dispatch(
fetchGroupedProjects({
...groupedRequestParams,
filter: newFilterIndex,
index: 1,
})
);
}
},
[filters, setFilterIndex, dispatch, refetchProjects, viewMode, groupBy, groupedRequestParams]
@@ -369,18 +374,18 @@ const ProjectList: React.FC = () => {
(e: React.ChangeEvent<HTMLInputElement>) => {
const searchValue = e.target.value;
trackMixpanelEvent(evt_projects_search);
// Update both request params for consistency
dispatch(setRequestParams({ search: searchValue, index: 1 }));
if (viewMode === ProjectViewType.GROUP) {
const newGroupedParams = {
...groupedRequestParams,
search: searchValue,
index: 1
search: searchValue,
index: 1,
};
dispatch(setGroupedRequestParams(newGroupedParams));
// Trigger debounced search in group mode
debouncedGroupedSearch(newGroupedParams);
}
@@ -429,13 +434,16 @@ const ProjectList: React.FC = () => {
dispatch(setProjectId(null));
}, [dispatch]);
const navigateToProject = useCallback((project_id: string | undefined, default_view: string | undefined) => {
if (project_id) {
navigate(
`/worklenz/projects/${project_id}?tab=${default_view === 'BOARD' ? 'board' : 'tasks-list'}&pinned_tab=${default_view === 'BOARD' ? 'board' : 'tasks-list'}`
);
}
}, [navigate]);
const navigateToProject = useCallback(
(project_id: string | undefined, default_view: string | undefined) => {
if (project_id) {
navigate(
`/worklenz/projects/${project_id}?tab=${default_view === 'BOARD' ? 'board' : 'tasks-list'}&pinned_tab=${default_view === 'BOARD' ? 'board' : 'tasks-list'}`
);
}
},
[navigate]
);
// Preload project view components on hover for smoother navigation
const handleProjectHover = useCallback((project_id: string | undefined) => {
@@ -444,7 +452,7 @@ const ProjectList: React.FC = () => {
import('@/pages/projects/projectView/project-view').catch(() => {
// Silently fail if preload doesn't work
});
// Also preload critical task management components
import('@/components/task-management/task-list-board').catch(() => {
// Silently fail if preload doesn't work
@@ -536,7 +544,17 @@ const ProjectList: React.FC = () => {
),
},
],
[t, categoryFilters, statusFilters, filteredInfo, filteredCategories, filteredStatuses, navigate, dispatch, isOwnerOrAdmin]
[
t,
categoryFilters,
statusFilters,
filteredInfo,
filteredCategories,
filteredStatuses,
navigate,
dispatch,
isOwnerOrAdmin,
]
);
useEffect(() => {
@@ -551,17 +569,19 @@ const ProjectList: React.FC = () => {
const filterIndex = getFilterIndex();
dispatch(setRequestParams({ filter: filterIndex }));
// Also sync with grouped request params on initial load
dispatch(setGroupedRequestParams({
filter: filterIndex,
index: 1,
size: DEFAULT_PAGE_SIZE,
field: 'name',
order: 'ascend',
search: '',
groupBy: '',
statuses: null,
categories: null,
}));
dispatch(
setGroupedRequestParams({
filter: filterIndex,
index: 1,
size: DEFAULT_PAGE_SIZE,
field: 'name',
order: 'ascend',
search: '',
groupBy: '',
statuses: null,
categories: null,
})
);
}, [dispatch, getFilterIndex]);
useEffect(() => {
@@ -605,11 +625,7 @@ const ProjectList: React.FC = () => {
defaultValue={filters[getFilterIndex()] ?? filters[0]}
onChange={handleSegmentChange}
/>
<Segmented
options={viewToggleOptions}
value={viewMode}
onChange={handleViewToggle}
/>
<Segmented options={viewToggleOptions} value={viewMode} onChange={handleViewToggle} />
{viewMode === ProjectViewType.GROUP && (
<Select
value={groupBy}
@@ -658,15 +674,19 @@ const ProjectList: React.FC = () => {
loading={groupedProjects.loading}
t={t}
/>
{!groupedProjects.loading && groupedProjects.data?.data && groupedProjects.data.data.length > 0 && (
<div style={{ marginTop: '24px', textAlign: 'center' }}>
<Pagination
{...groupedPaginationConfig}
onChange={(page, pageSize) => handleGroupedTableChange({ current: page, pageSize })}
showTotal={paginationShowTotal}
/>
</div>
)}
{!groupedProjects.loading &&
groupedProjects.data?.data &&
groupedProjects.data.data.length > 0 && (
<div style={{ marginTop: '24px', textAlign: 'center' }}>
<Pagination
{...groupedPaginationConfig}
onChange={(page, pageSize) =>
handleGroupedTableChange({ current: page, pageSize })
}
showTotal={paginationShowTotal}
/>
</div>
)}
</div>
)}
</Skeleton>
@@ -677,4 +697,4 @@ const ProjectList: React.FC = () => {
);
};
export default ProjectList;
export default ProjectList;