diff --git a/__tests__/Unit/Components/Tasks/ProgressCard.test.tsx b/__tests__/Unit/Components/Tasks/ProgressCard.test.tsx index ebb98b4e9..6d7c21c79 100644 --- a/__tests__/Unit/Components/Tasks/ProgressCard.test.tsx +++ b/__tests__/Unit/Components/Tasks/ProgressCard.test.tsx @@ -7,7 +7,7 @@ import { setupServer } from 'msw/node'; import handlers from '../../../../__mocks__/handlers'; import { mockGetTaskProgress } from '../../../../__mocks__/db/progresses'; import ProgressCard from '@/components/ProgressCard'; - +import { useRouter } from 'next/router'; const server = setupServer(...handlers); const dateMay29 = moment(mockGetTaskProgress.data[0].createdAt).fromNow(); @@ -28,17 +28,72 @@ beforeAll(() => { afterEach(() => server.resetHandlers()); afterAll(() => server.close()); +jest.mock('@/hooks/useIntersection', () => { + return jest.fn().mockImplementation(() => ({ + loadingRef: { current: {} }, + onLoadMore: jest.fn(), + })); +}); + +jest.mock('next/router', () => ({ + useRouter: jest.fn(), +})); + describe('ProgressCard Component', () => { + const mockFetchMoreProgresses = jest.fn(); + it('should render the ProgressCard', async () => { + const mockRouter = { + query: { dev: 'false' }, + }; + (useRouter as jest.Mock).mockReturnValue(mockRouter); renderWithRouter( - + ); const progress = screen.getByTestId('progressCard'); expect(progress).toBeInTheDocument(); expect(progress).toHaveTextContent('Progress Updates'); }); + + it('should show loading ref only when devFlag is true', () => { + const mockRouter = { + query: { dev: 'true' }, + }; + (useRouter as jest.Mock).mockReturnValue(mockRouter); + const { rerender } = renderWithRouter( + + + + ); + + expect(screen.queryByText('Loading...')).not.toBeInTheDocument(); + + rerender( + + + + ); + + expect(screen.getByText('Loading...')).toBeInTheDocument(); + }); + it('should render Asc/Dsc button ', async () => { const { getByRole } = renderWithRouter( diff --git a/__tests__/Unit/Components/Tasks/TaskDetails.test.tsx b/__tests__/Unit/Components/Tasks/TaskDetails.test.tsx index 261d9994f..d6ac674ed 100644 --- a/__tests__/Unit/Components/Tasks/TaskDetails.test.tsx +++ b/__tests__/Unit/Components/Tasks/TaskDetails.test.tsx @@ -52,6 +52,13 @@ jest.mock('@/hooks/useUserData', () => { }); }); +jest.mock('@/hooks/useIntersection', () => { + return jest.fn().mockImplementation(() => ({ + loadingRef: { current: {} }, + onLoadMore: jest.fn(), + })); +}); + const mockNavigateToUpdateProgressPage = jest.fn(); const mockHandleEditedTaskDetails = jest.fn(); @@ -464,8 +471,10 @@ test('Should render No task progress', async () => { progressUpdatesSection = screen.getByText('Progress Updates'); }); expect(progressUpdatesSection).toBeInTheDocument(); - const noProgressText = screen.getByText('No Progress found'); - expect(noProgressText).toBeInTheDocument(); + await waitFor(() => { + const noProgressText = screen.getByText('No Progress found'); + expect(noProgressText).toBeInTheDocument(); + }); }); test('should call progress details query', async () => { diff --git a/src/app/services/progressesApi.ts b/src/app/services/progressesApi.ts index 1e8b7eab9..19271dba5 100644 --- a/src/app/services/progressesApi.ts +++ b/src/app/services/progressesApi.ts @@ -5,6 +5,8 @@ type queryParamsType = { userId?: string; taskId?: string; dev?: boolean; + size?: number; + page?: number; }; export const progressesApi = api.injectEndpoints({ @@ -21,10 +23,10 @@ export const progressesApi = api.injectEndpoints({ }), }), getProgressDetails: builder.query({ - query: ({ userId, taskId, dev }: queryParamsType) => { + query: ({ userId, taskId, dev, size, page }: queryParamsType) => { return { url: '/progresses', - params: { userId, taskId, dev }, + params: { userId, taskId, dev, size, page }, }; }, providesTags: ['Progress_Details'], diff --git a/src/components/ProgressCard/ProgressCard.module.scss b/src/components/ProgressCard/ProgressCard.module.scss index c51f8258b..146da0486 100755 --- a/src/components/ProgressCard/ProgressCard.module.scss +++ b/src/components/ProgressCard/ProgressCard.module.scss @@ -2,3 +2,10 @@ margin-left: 0.25rem; cursor: pointer; } + +.loadingContainer { + display: flex; + justify-content: center; + align-items: center; + width: 100%; +} diff --git a/src/components/ProgressCard/index.tsx b/src/components/ProgressCard/index.tsx index d2397b6d0..e936274bb 100755 --- a/src/components/ProgressCard/index.tsx +++ b/src/components/ProgressCard/index.tsx @@ -1,18 +1,38 @@ -import React, { useEffect, useState } from 'react'; +import React, { ElementRef, useEffect, useRef, useState } from 'react'; import TaskContainer from '../taskDetails/TaskContainer'; import { ProgressDetailsData } from '@/types/standup.type'; import styles from '@/components/ProgressCard/ProgressCard.module.scss'; import LatestProgressUpdateCard from '../taskDetails/ProgressUpdateCard/LatestProgressUpdateCard'; import ProgressUpdateCard from '../taskDetails/ProgressUpdateCard/ProgressUpdateCard'; - +import useIntersection from '@/hooks/useIntersection'; type SortedProgressType = { data: ProgressDetailsData[]; order: number }; -type Props = { taskProgress: ProgressDetailsData[] }; +type Props = { + taskProgress: ProgressDetailsData[]; + fetchMoreProgresses: () => void; + isFetchingProgress: boolean; + devFlag: boolean; +}; -export default function ProgressCard({ taskProgress }: Props) { +export default function ProgressCard({ + taskProgress, + fetchMoreProgresses, + isFetchingProgress, + devFlag, +}: Props) { const [sortedProgress, setSortedProgress] = useState(); const sortedProgressLength = sortedProgress?.data?.length; - + const loadingRef = useRef>(null); + const [hasFetched, setHasFetched] = useState(false); + useIntersection({ + loadingRef, + onLoadMore: () => { + if (hasFetched) { + fetchMoreProgresses(); + } + }, + earlyReturn: sortedProgressLength === 0, + }); const reverseSortingOrder = () => { if (sortedProgress && sortedProgressLength) { const newSortedArr: ProgressDetailsData[] = []; @@ -38,6 +58,7 @@ export default function ProgressCard({ taskProgress }: Props) { : 1; }); setSortedProgress({ data: sorted, order: 1 }); + setHasFetched(true); } }, [taskProgress]); @@ -82,9 +103,16 @@ export default function ProgressCard({ taskProgress }: Props) { > {sortedProgress && sortedProgressLength ? (
{cardsToShow}
+ ) : isFetchingProgress ? ( + '' ) : ( 'No Progress found' )} + {devFlag && ( +
+ {isFetchingProgress && 'Loading...'} +
+ )} ); } diff --git a/src/components/taskDetails/index.tsx b/src/components/taskDetails/index.tsx index 54f118891..50766a0fb 100755 --- a/src/components/taskDetails/index.tsx +++ b/src/components/taskDetails/index.tsx @@ -69,16 +69,48 @@ const TaskDetails: FC = ({ taskID }) => { const { isUserAuthorized } = useUserData(); const [isEditing, setIsEditing] = useState(false); const [isOpen, setIsOpen] = useState(false); + const [page, setPage] = useState(0); const [loading, setLoading] = useState(false); + const [progressesDataPaginated, setProgressesDataPaginated] = useState< + ProgressDetailsData[] + >([]); const { data, isError, isLoading, isFetching, refetch } = useGetTaskDetailsQuery(taskID); const { data: extensionRequests } = useGetExtensionRequestDetailsQuery(taskID); - const { data: progressData, refetch: refetchProgress } = - useGetProgressDetailsQuery({ - taskId: taskID, - dev: isDev, + const { + data: progressData, + refetch: refetchProgress, + isFetching: isFetchingProgress, + } = useGetProgressDetailsQuery({ + taskId: taskID, + dev: isDev, + size: 10, + page: page, + }); + + useEffect(() => { + setProgressesDataPaginated((prevProgressesDataPaginated) => { + if (progressData?.data) { + return [...prevProgressesDataPaginated, ...progressData.data]; + } + return prevProgressesDataPaginated; }); + }, [progressData]); + + const fetchMoreProgresses = () => { + if (progressData?.links?.next) { + const nextPageUrl = progressData.links.next; + const urlParams = new URLSearchParams(nextPageUrl.split('?')[1]); + const nextPage = parseInt(urlParams.get('page') || '0', 10); + setPage(nextPage); + } + }; + useEffect(() => { + if (page > 0) { + refetchProgress(); + } + }, [page, refetchProgress]); const isExtensionRequestPending = Boolean( extensionRequests?.allExtensionRequests.length @@ -224,8 +256,10 @@ const TaskDetails: FC = ({ taskID }) => { } const shouldRenderParentContainer = () => !isLoading && !isError && data; - - const taskProgress: ProgressDetailsData[] = progressData?.data || []; + const progressDataToSend = isDev + ? progressesDataPaginated + : progressData?.data || []; + const taskProgress: ProgressDetailsData[] = progressDataToSend || []; return ( @@ -271,7 +305,12 @@ const TaskDetails: FC = ({ taskID }) => { taskDetailsData={taskDetailsData} /> - + ; message: string; count: number; + links?: Links; }; type Section = {