Merge pull request #114 from chamikaJ/fix/reporting-member-pagination
Fix/reporting member pagination
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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}
|
||||||
|
|||||||
@@ -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>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -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 => {
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user