Merge pull request #114 from chamikaJ/fix/reporting-member-pagination

Fix/reporting member pagination
This commit is contained in:
Chamika J
2025-05-09 09:50:51 +05:30
committed by GitHub
7 changed files with 59 additions and 23 deletions

View File

@@ -322,7 +322,7 @@ export default class ProjectInsightsController extends WorklenzControllerBase {
(SELECT get_task_assignees(tasks.id)) AS assignees (SELECT get_task_assignees(tasks.id)) AS assignees
FROM tasks FROM tasks
JOIN work_log ON work_log.task_id = tasks.id JOIN work_log ON work_log.task_id = tasks.id
WHERE project_id = $1 WHERE project_id = $1 AND total_minutes <> 0 AND (total_minutes * 60) <> work_log.total_time_spent
AND CASE AND CASE
WHEN ($2 IS TRUE) THEN project_id IS NOT NULL WHEN ($2 IS TRUE) THEN project_id IS NOT NULL
ELSE archived IS FALSE END ELSE archived IS FALSE END

View File

@@ -107,6 +107,10 @@ const membersReportsSlice = createSlice({
setDateRange: (state, action) => { setDateRange: (state, action) => {
state.dateRange = action.payload; state.dateRange = action.payload;
}, },
setPagination: (state, action) => {
state.index = action.payload.index;
state.pageSize = action.payload.pageSize;
},
}, },
extraReducers: builder => { extraReducers: builder => {
builder builder
@@ -139,5 +143,6 @@ export const {
setOrder, setOrder,
setDuration, setDuration,
setDateRange, setDateRange,
setPagination,
} = membersReportsSlice.actions; } = membersReportsSlice.actions;
export default membersReportsSlice.reducer; export default membersReportsSlice.reducer;

View File

@@ -1,4 +1,4 @@
import { useEffect, useState, useRef } from 'react'; import { useEffect, useState, useRef, useMemo } from 'react';
import { useAppSelector } from '@/hooks/useAppSelector'; import { useAppSelector } from '@/hooks/useAppSelector';
import TaskListFilters from '../taskList/task-list-filters/task-list-filters'; import TaskListFilters from '../taskList/task-list-filters/task-list-filters';
import { Flex, Skeleton } from 'antd'; 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 { ITaskStatusCreateRequest } from '@/types/tasks/task-status-create-request';
import { statusApiService } from '@/api/taskAttributes/status/status.api.service'; import { statusApiService } from '@/api/taskAttributes/status/status.api.service';
import logger from '@/utils/errorLogger'; import logger from '@/utils/errorLogger';
import { tasksApiService } from '@/api/tasks/tasks.api.service';
import { checkTaskDependencyStatus } from '@/utils/check-task-dependency-status'; import { checkTaskDependencyStatus } from '@/utils/check-task-dependency-status';
const ProjectViewBoard = () => { const ProjectViewBoard = () => {
@@ -45,7 +44,10 @@ const ProjectViewBoard = () => {
const authService = useAuthService(); const authService = useAuthService();
const currentSession = authService.getCurrentSession(); const currentSession = authService.getCurrentSession();
const { trackMixpanelEvent } = useMixpanelTracking(); 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 { projectId } = useAppSelector(state => state.projectReducer);
const { taskGroups, groupBy, loadingGroups, search, archived } = useAppSelector(state => state.boardReducer); const { taskGroups, groupBy, loadingGroups, search, archived } = useAppSelector(state => state.boardReducer);
const { statusCategories, loading: loadingStatusCategories } = useAppSelector( const { statusCategories, loading: loadingStatusCategories } = useAppSelector(
@@ -56,14 +58,34 @@ const ProjectViewBoard = () => {
// Store the original source group ID when drag starts // Store the original source group ID when drag starts
const originalSourceGroupIdRef = useRef<string | null>(null); const originalSourceGroupIdRef = useRef<string | null>(null);
// Update loading state based on all loading conditions
useEffect(() => { useEffect(() => {
if (projectId && groupBy && projectView === 'kanban') { setIsLoading(loadingGroups || loadingStatusCategories);
if (!loadingGroups) { }, [loadingGroups, loadingStatusCategories]);
dispatch(fetchBoardTaskGroups(projectId));
// 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]); }, [dispatch, projectId, groupBy, projectView, search, archived]);
// Create sensors with memoization to prevent unnecessary re-renders
const sensors = useSensors( const sensors = useSensors(
useSensor(MouseSensor, { useSensor(MouseSensor, {
// Require the mouse to move by 10 pixels before activating // Require the mouse to move by 10 pixels before activating
@@ -394,18 +416,16 @@ const ProjectViewBoard = () => {
}; };
}, [socket]); }, [socket]);
// Track analytics event on component mount
useEffect(() => { useEffect(() => {
trackMixpanelEvent(evt_project_board_visit); trackMixpanelEvent(evt_project_board_visit);
if (!statusCategories.length && projectId) { }, []);
dispatch(fetchStatusesCategories());
}
}, [dispatch, projectId]);
return ( return (
<Flex vertical gap={16}> <Flex vertical gap={16}>
<TaskListFilters position={'board'} /> <TaskListFilters position={'board'} />
<Skeleton active loading={loadingGroups} className='mt-4 p-4'> <Skeleton active loading={isLoading} className='mt-4 p-4'>
<DndContext <DndContext
sensors={sensors} sensors={sensors}
collisionDetection={closestCorners} collisionDetection={closestCorners}

View File

@@ -105,8 +105,8 @@ const OverLoggedTasksTable = () => {
{ {
key: 'overLoggedTime', key: 'overLoggedTime',
title: 'Over Logged Time', title: 'Over Logged Time',
render: (record: IInsightTasks) => ( render: (_, record: IInsightTasks) => (
<Typography.Text>{record.overlogged_time}</Typography.Text> <Typography.Text>{record.overlogged_time_string}</Typography.Text>
), ),
}, },
]; ];

View File

@@ -6,9 +6,14 @@ import { useAppDispatch } from '@/hooks/useAppDispatch';
import CustomTableTitle from '@/components/CustomTableTitle'; import CustomTableTitle from '@/components/CustomTableTitle';
import TasksProgressCell from './tablesCells/tasksProgressCell/TasksProgressCell'; import TasksProgressCell from './tablesCells/tasksProgressCell/TasksProgressCell';
import MemberCell from './tablesCells/memberCell/MemberCell'; 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 { useAppSelector } from '@/hooks/useAppSelector';
import MembersReportsDrawer from '@/features/reporting/membersReports/membersReportsDrawer/members-reports-drawer'; import MembersReportsDrawer from '@/features/reporting/membersReports/membersReportsDrawer/members-reports-drawer';
import { PaginationConfig } from 'antd/es/pagination';
const MembersReportsTable = () => { const MembersReportsTable = () => {
const { t } = useTranslation('reporting-members'); const { t } = useTranslation('reporting-members');
@@ -16,7 +21,9 @@ const MembersReportsTable = () => {
const [selectedId, setSelectedId] = useState<string | null>(null); const [selectedId, setSelectedId] = useState<string | null>(null);
const { duration, dateRange } = useAppSelector(state => state.reportingReducer); 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 // function to handle drawer toggle
const handleDrawerOpen = (id: string) => { const handleDrawerOpen = (id: string) => {
@@ -24,6 +31,10 @@ const MembersReportsTable = () => {
dispatch(toggleMembersReportsDrawer()); dispatch(toggleMembersReportsDrawer());
}; };
const handleOnChange = (pagination: any, filters: any, sorter: any, extra: any) => {
dispatch(setPagination({ index: pagination.current, pageSize: pagination.pageSize }));
};
const columns: TableColumnsType = [ const columns: TableColumnsType = [
{ {
key: 'member', key: 'member',
@@ -40,7 +51,7 @@ const MembersReportsTable = () => {
title: <CustomTableTitle title={t('tasksProgressColumn')} />, title: <CustomTableTitle title={t('tasksProgressColumn')} />,
render: record => { render: record => {
const { todo, doing, done } = record.tasks_stat; const { todo, doing, done } = record.tasks_stat;
return (todo || doing || done) ? <TasksProgressCell tasksStat={record.tasks_stat} /> : '-'; return todo || doing || done ? <TasksProgressCell tasksStat={record.tasks_stat} /> : '-';
}, },
}, },
{ {
@@ -95,7 +106,7 @@ const MembersReportsTable = () => {
useEffect(() => { useEffect(() => {
if (!isLoading) dispatch(fetchMembersData({ duration, dateRange })); if (!isLoading) dispatch(fetchMembersData({ duration, dateRange }));
}, [dispatch, archived, searchQuery, dateRange]); }, [dispatch, archived, searchQuery, dateRange, index, pageSize]);
return ( return (
<ConfigProvider <ConfigProvider
@@ -113,6 +124,7 @@ const MembersReportsTable = () => {
dataSource={membersList} dataSource={membersList}
rowKey={record => record.id} rowKey={record => record.id}
pagination={{ showSizeChanger: true, defaultPageSize: 10, total: total }} pagination={{ showSizeChanger: true, defaultPageSize: 10, total: total }}
onChange={(pagination, filters, sorter, extra) => handleOnChange(pagination, filters, sorter, extra)}
scroll={{ x: 'max-content' }} scroll={{ x: 'max-content' }}
loading={isLoading} loading={isLoading}
onRow={record => { onRow={record => {

View File

@@ -25,9 +25,7 @@ const MembersReports = () => {
useDocumentTitle('Reporting - Members'); useDocumentTitle('Reporting - Members');
const currentSession = useAuthService().getCurrentSession(); const currentSession = useAuthService().getCurrentSession();
const { archived, searchQuery } = useAppSelector( const { archived, searchQuery, total } = useAppSelector(state => state.membersReportsReducer);
state => state.membersReportsReducer,
);
const { duration, dateRange } = useAppSelector(state => state.reportingReducer); const { duration, dateRange } = useAppSelector(state => state.reportingReducer);
@@ -44,7 +42,7 @@ const MembersReports = () => {
return ( return (
<Flex vertical> <Flex vertical>
<CustomPageHeader <CustomPageHeader
title={`Members`} title={`Members (${total})`}
children={ children={
<Space> <Space>
<Button> <Button>

View File

@@ -41,6 +41,7 @@ export interface IInsightTasks {
updated_at?: string; updated_at?: string;
total_minutes?: string; total_minutes?: string;
overlogged_time?: string; overlogged_time?: string;
overlogged_time_string?: string;
days_overdue?: number; days_overdue?: number;
is_overdue?: boolean; is_overdue?: boolean;
parent_task_id?: string; parent_task_id?: string;