From 4a2393881b0fc79974508b6691e5c0aa23b747c0 Mon Sep 17 00:00:00 2001 From: chamikaJ Date: Wed, 7 May 2025 16:32:13 +0530 Subject: [PATCH 1/3] Enhance project view board loading state and data fetching - Introduced a local loading state to improve user experience by displaying a skeleton loader while data is being fetched. - Refactored data loading logic to utilize async/await and Promise.all for concurrent dispatching of task-related data, ensuring efficient data retrieval. - Updated rendering logic to conditionally display the skeleton loader based on the new loading state, enhancing UI responsiveness. --- .../projectView/board/project-view-board.tsx | 44 ++++++++++++++----- 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/worklenz-frontend/src/pages/projects/projectView/board/project-view-board.tsx b/worklenz-frontend/src/pages/projects/projectView/board/project-view-board.tsx index 0ec238a8..2b1a7604 100644 --- a/worklenz-frontend/src/pages/projects/projectView/board/project-view-board.tsx +++ b/worklenz-frontend/src/pages/projects/projectView/board/project-view-board.tsx @@ -1,4 +1,4 @@ -import { useEffect, useState, useRef } from 'react'; +import { useEffect, useState, useRef, useMemo } from 'react'; import { useAppSelector } from '@/hooks/useAppSelector'; import TaskListFilters from '../taskList/task-list-filters/task-list-filters'; import { Flex, Skeleton } from 'antd'; @@ -35,7 +35,6 @@ import { evt_project_board_visit, evt_project_task_list_drag_and_move } from '@/ import { ITaskStatusCreateRequest } from '@/types/tasks/task-status-create-request'; import { statusApiService } from '@/api/taskAttributes/status/status.api.service'; import logger from '@/utils/errorLogger'; -import { tasksApiService } from '@/api/tasks/tasks.api.service'; import { checkTaskDependencyStatus } from '@/utils/check-task-dependency-status'; const ProjectViewBoard = () => { @@ -45,7 +44,10 @@ const ProjectViewBoard = () => { const authService = useAuthService(); const currentSession = authService.getCurrentSession(); const { trackMixpanelEvent } = useMixpanelTracking(); - const [ currentTaskIndex, setCurrentTaskIndex] = useState(-1); + const [currentTaskIndex, setCurrentTaskIndex] = useState(-1); + // Add local loading state to immediately show skeleton + const [isLoading, setIsLoading] = useState(true); + const { projectId } = useAppSelector(state => state.projectReducer); const { taskGroups, groupBy, loadingGroups, search, archived } = useAppSelector(state => state.boardReducer); const { statusCategories, loading: loadingStatusCategories } = useAppSelector( @@ -56,14 +58,34 @@ const ProjectViewBoard = () => { // Store the original source group ID when drag starts const originalSourceGroupIdRef = useRef(null); + // Update loading state based on all loading conditions useEffect(() => { - if (projectId && groupBy && projectView === 'kanban') { - if (!loadingGroups) { - dispatch(fetchBoardTaskGroups(projectId)); + setIsLoading(loadingGroups || loadingStatusCategories); + }, [loadingGroups, loadingStatusCategories]); + + // Load data efficiently with async/await and Promise.all + useEffect(() => { + const loadData = async () => { + if (projectId && groupBy && projectView === 'kanban') { + const promises = []; + + if (!loadingGroups) { + promises.push(dispatch(fetchBoardTaskGroups(projectId))); + } + + if (!statusCategories.length) { + promises.push(dispatch(fetchStatusesCategories())); + } + + // Wait for all data to load + await Promise.all(promises); } - } + }; + + loadData(); }, [dispatch, projectId, groupBy, projectView, search, archived]); + // Create sensors with memoization to prevent unnecessary re-renders const sensors = useSensors( useSensor(MouseSensor, { // Require the mouse to move by 10 pixels before activating @@ -394,18 +416,16 @@ const ProjectViewBoard = () => { }; }, [socket]); + // Track analytics event on component mount useEffect(() => { trackMixpanelEvent(evt_project_board_visit); - if (!statusCategories.length && projectId) { - dispatch(fetchStatusesCategories()); - } - }, [dispatch, projectId]); + }, []); return ( - + Date: Thu, 8 May 2025 13:59:31 +0530 Subject: [PATCH 2/3] Implement pagination for members reports and update UI components - Added a new `setPagination` action to manage pagination state in the members reports slice. - Updated the members reports page to display the total number of members in the header. - Enhanced the members reports table to handle pagination changes, ensuring data is fetched correctly based on the current page and page size. --- .../membersReports/membersReportsSlice.ts | 5 +++++ .../members-reports-table.tsx | 20 +++++++++++++++---- .../members-reports/members-reports.tsx | 6 ++---- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/worklenz-frontend/src/features/reporting/membersReports/membersReportsSlice.ts b/worklenz-frontend/src/features/reporting/membersReports/membersReportsSlice.ts index 157f8d7e..2dcd9fe1 100644 --- a/worklenz-frontend/src/features/reporting/membersReports/membersReportsSlice.ts +++ b/worklenz-frontend/src/features/reporting/membersReports/membersReportsSlice.ts @@ -107,6 +107,10 @@ const membersReportsSlice = createSlice({ setDateRange: (state, action) => { state.dateRange = action.payload; }, + setPagination: (state, action) => { + state.index = action.payload.index; + state.pageSize = action.payload.pageSize; + }, }, extraReducers: builder => { builder @@ -139,5 +143,6 @@ export const { setOrder, setDuration, setDateRange, + setPagination, } = membersReportsSlice.actions; export default membersReportsSlice.reducer; diff --git a/worklenz-frontend/src/pages/reporting/members-reports/members-reports-table/members-reports-table.tsx b/worklenz-frontend/src/pages/reporting/members-reports/members-reports-table/members-reports-table.tsx index e94e9818..26b26112 100644 --- a/worklenz-frontend/src/pages/reporting/members-reports/members-reports-table/members-reports-table.tsx +++ b/worklenz-frontend/src/pages/reporting/members-reports/members-reports-table/members-reports-table.tsx @@ -6,9 +6,14 @@ import { useAppDispatch } from '@/hooks/useAppDispatch'; import CustomTableTitle from '@/components/CustomTableTitle'; import TasksProgressCell from './tablesCells/tasksProgressCell/TasksProgressCell'; import MemberCell from './tablesCells/memberCell/MemberCell'; -import { fetchMembersData, toggleMembersReportsDrawer } from '@/features/reporting/membersReports/membersReportsSlice'; +import { + fetchMembersData, + setPagination, + toggleMembersReportsDrawer, +} from '@/features/reporting/membersReports/membersReportsSlice'; import { useAppSelector } from '@/hooks/useAppSelector'; import MembersReportsDrawer from '@/features/reporting/membersReports/membersReportsDrawer/members-reports-drawer'; +import { PaginationConfig } from 'antd/es/pagination'; const MembersReportsTable = () => { const { t } = useTranslation('reporting-members'); @@ -16,7 +21,9 @@ const MembersReportsTable = () => { const [selectedId, setSelectedId] = useState(null); const { duration, dateRange } = useAppSelector(state => state.reportingReducer); - const { membersList, isLoading, total, archived, searchQuery } = useAppSelector(state => state.membersReportsReducer); + const { membersList, isLoading, total, archived, searchQuery, index, pageSize } = useAppSelector( + state => state.membersReportsReducer + ); // function to handle drawer toggle const handleDrawerOpen = (id: string) => { @@ -24,6 +31,10 @@ const MembersReportsTable = () => { dispatch(toggleMembersReportsDrawer()); }; + const handleOnChange = (pagination: any, filters: any, sorter: any, extra: any) => { + dispatch(setPagination({ index: pagination.current, pageSize: pagination.pageSize })); + }; + const columns: TableColumnsType = [ { key: 'member', @@ -40,7 +51,7 @@ const MembersReportsTable = () => { title: , render: record => { const { todo, doing, done } = record.tasks_stat; - return (todo || doing || done) ? : '-'; + return todo || doing || done ? : '-'; }, }, { @@ -95,7 +106,7 @@ const MembersReportsTable = () => { useEffect(() => { if (!isLoading) dispatch(fetchMembersData({ duration, dateRange })); - }, [dispatch, archived, searchQuery, dateRange]); + }, [dispatch, archived, searchQuery, dateRange, index, pageSize]); return ( { dataSource={membersList} rowKey={record => record.id} pagination={{ showSizeChanger: true, defaultPageSize: 10, total: total }} + onChange={(pagination, filters, sorter, extra) => handleOnChange(pagination, filters, sorter, extra)} scroll={{ x: 'max-content' }} loading={isLoading} onRow={record => { diff --git a/worklenz-frontend/src/pages/reporting/members-reports/members-reports.tsx b/worklenz-frontend/src/pages/reporting/members-reports/members-reports.tsx index 3465b08d..9986fd59 100644 --- a/worklenz-frontend/src/pages/reporting/members-reports/members-reports.tsx +++ b/worklenz-frontend/src/pages/reporting/members-reports/members-reports.tsx @@ -25,9 +25,7 @@ const MembersReports = () => { useDocumentTitle('Reporting - Members'); const currentSession = useAuthService().getCurrentSession(); - const { archived, searchQuery } = useAppSelector( - state => state.membersReportsReducer, - ); + const { archived, searchQuery, total } = useAppSelector(state => state.membersReportsReducer); const { duration, dateRange } = useAppSelector(state => state.reportingReducer); @@ -44,7 +42,7 @@ const MembersReports = () => { return (