diff --git a/libs/designer/src/lib/ui/common/LoopsPager/LoopsPager.tsx b/libs/designer/src/lib/ui/common/LoopsPager/LoopsPager.tsx index b0ab0225cae..9a16bb08f27 100644 --- a/libs/designer/src/lib/ui/common/LoopsPager/LoopsPager.tsx +++ b/libs/designer/src/lib/ui/common/LoopsPager/LoopsPager.tsx @@ -6,8 +6,8 @@ import { getForeachItemsCount } from './helper'; import { RunService } from '@microsoft/designer-client-services-logic-apps'; import type { PageChangeEventArgs, PageChangeEventHandler } from '@microsoft/designer-ui'; import { Pager } from '@microsoft/designer-ui'; -import { isNullOrUndefined, type LogicAppsV2 } from '@microsoft/utils-logic-apps'; -import { useEffect, useState } from 'react'; +import { FindPreviousAndNextPage, isNullOrUndefined, type LogicAppsV2 } from '@microsoft/utils-logic-apps'; +import { useCallback, useEffect, useState } from 'react'; import { useQuery } from 'react-query'; import { useDispatch } from 'react-redux'; @@ -41,7 +41,7 @@ export const LoopsPager = ({ metadata, scopeId, collapsed }: LoopsPagerProps) => }, []) .sort(); - setFailedRepetitions(sortedFailedRepetitions); + setFailedRepetitions(sortedFailedRepetitions.sort((a, b) => a - b)); }; const onRunRepetitionsError = async () => { @@ -67,6 +67,8 @@ export const LoopsPager = ({ metadata, scopeId, collapsed }: LoopsPagerProps) => } }, [runInstance?.id, refetch, scopeId, normalizedType]); + const findPreviousAndNextFailed = useCallback((page: number) => FindPreviousAndNextPage(page, failedRepetitions), [failedRepetitions]); + if (!forEachItemsCount || isError || collapsed) { return null; } @@ -77,23 +79,13 @@ export const LoopsPager = ({ metadata, scopeId, collapsed }: LoopsPagerProps) => }; const onClickNextFailed: PageChangeEventHandler = (page: PageChangeEventArgs) => { - let nextFailedRepetition = -1; - if (failedRepetitions.includes(page.value - 1)) { - nextFailedRepetition = page.value - 1; - } else if (page.value - 1 < failedRepetitions[0]) { - nextFailedRepetition = failedRepetitions[0]; - } + const { nextFailedRepetition } = findPreviousAndNextFailed(page.value - 1); dispatch(setRunIndex({ page: nextFailedRepetition, nodeId: scopeId })); setCurrentPage(nextFailedRepetition + 1); }; const onClickPreviousFailed: PageChangeEventHandler = (page: PageChangeEventArgs) => { - let prevFailedRepetition = -1; - if (failedRepetitions.includes(page.value - 1)) { - prevFailedRepetition = page.value - 1; - } else if (page.value - 1 > failedRepetitions[failedRepetitions.length - 1]) { - prevFailedRepetition = failedRepetitions[failedRepetitions.length - 1]; - } + const { prevFailedRepetition } = findPreviousAndNextFailed(page.value - 1); dispatch(setRunIndex({ page: prevFailedRepetition, nodeId: scopeId })); setCurrentPage(prevFailedRepetition + 1); }; @@ -105,10 +97,7 @@ export const LoopsPager = ({ metadata, scopeId, collapsed }: LoopsPagerProps) => const failedIterationProps = failedRepetitions.length > 0 ? { - max: - failedRepetitions[failedRepetitions.length - 1] + 1 <= forEachItemsCount - ? failedRepetitions[failedRepetitions.length - 1] + 1 - : forEachItemsCount, + max: failedRepetitions.length > 1 ? failedRepetitions[failedRepetitions.length - 1] + 1 : 0, min: failedRepetitions[0] + 1 >= 1 ? failedRepetitions[0] + 1 : 1, onClickNext: onClickNextFailed, onClickPrevious: onClickPreviousFailed, diff --git a/libs/utils/src/lib/helpers/__test__/functions.spec.ts b/libs/utils/src/lib/helpers/__test__/functions.spec.ts index 163a1b4be56..504eadad3ca 100644 --- a/libs/utils/src/lib/helpers/__test__/functions.spec.ts +++ b/libs/utils/src/lib/helpers/__test__/functions.spec.ts @@ -44,6 +44,7 @@ import { trim, uniqueArray, unmap, + FindPreviousAndNextPage, } from './../functions'; describe('lib/helpers/functions', () => { @@ -1013,6 +1014,43 @@ describe('lib/helpers/functions', () => { }); }); + describe('FindPreviousAndNextPage', () => { + it('should return correct previous and next page', () => { + const bookmarks = [1, 2, 3, 4, 5]; + const page = 3; + const result = FindPreviousAndNextPage(page, bookmarks); + expect(result).toEqual({ nextFailedRepetition: 3, prevFailedRepetition: 3 }); + }); + + it('should return correct previous and next page when page is not in bookmarks', () => { + const bookmarks = [1, 2, 4, 5]; + const page = 3; + const result = FindPreviousAndNextPage(page, bookmarks); + expect(result).toEqual({ nextFailedRepetition: 4, prevFailedRepetition: 2 }); + }); + + it('should return -1 for both previous and next page when bookmarks is empty', () => { + const bookmarks: number[] = []; + const page = 3; + const result = FindPreviousAndNextPage(page, bookmarks); + expect(result).toEqual({ nextFailedRepetition: -1, prevFailedRepetition: -1 }); + }); + + it('should return correct previous and next page when page is less than all bookmarks', () => { + const bookmarks = [2, 3, 4, 5]; + const page = 1; + const result = FindPreviousAndNextPage(page, bookmarks); + expect(result).toEqual({ nextFailedRepetition: 2, prevFailedRepetition: -1 }); + }); + + it('should return correct previous and next page when page is greater than all bookmarks', () => { + const bookmarks = [1, 2, 3, 4]; + const page = 5; + const result = FindPreviousAndNextPage(page, bookmarks); + expect(result).toEqual({ nextFailedRepetition: -1, prevFailedRepetition: 4 }); + }); + }); + describe('isValidJSON', () => { it('returns true is input is valid JSON string', () => { const json = { diff --git a/libs/utils/src/lib/helpers/functions.ts b/libs/utils/src/lib/helpers/functions.ts index d1d39695776..c3dab7b7192 100644 --- a/libs/utils/src/lib/helpers/functions.ts +++ b/libs/utils/src/lib/helpers/functions.ts @@ -1046,3 +1046,25 @@ export const getRecordEntry = (record: Record | undefined, key: st if (!record || !key || !Object.hasOwn(record, key)) return undefined; return record[key]; }; + +export const FindPreviousAndNextPage = (page: number, bookmarks: number[]) => { + let left = 0; + let right = bookmarks.length - 1; + let nextFailedRepetition = -1; + let prevFailedRepetition = -1; + while (left <= right) { + const mid = Math.floor((left + right) / 2); + if (bookmarks[mid] < page) { + prevFailedRepetition = bookmarks[mid]; + left = mid + 1; + } else if (bookmarks[mid] > page) { + nextFailedRepetition = bookmarks[mid]; + right = mid - 1; + } else { + prevFailedRepetition = bookmarks[mid]; + nextFailedRepetition = bookmarks[mid]; + break; + } + } + return { nextFailedRepetition, prevFailedRepetition }; +};