From d10482c5be9b64272dc306c1701cd67f6dd89cbf Mon Sep 17 00:00:00 2001 From: yiping-wu <141200259+doujiao-001@users.noreply.github.com> Date: Thu, 14 Mar 2024 15:06:45 +0800 Subject: [PATCH] Adm 793 [frontend] Reduce the time of timeout to 10 minutes (#1141) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * AMD-793 [frontend] feature: add reverify logic and timeout alert * AMD-793 [frontend] refactor: abstract config button component for config page * AMD-793 [frontend] refactor: abstract timeout alert component for config page * AMD-793 [frontend] feat: add timeout alert component to config page for three settings * AMD-793 [frontend] test: add test for timeout alert component * AMD-793 [frontend] test: add test for config button group component * [ADM-793] style: fix css check * AMD-793 [frontend] fix: set HB timeout and timeout alert false in SourceControlClient.ts and PipelineToolClient.ts * AMD-793 [frontend] fix: fix test for PipelineToolClient.test.ts and SourceControlClient.test.ts * [ADM-793] style: fix border css * [ADM-793] test: fix test for useVerifyBoardEffect * AMD-793 [frontend] test: add test of isHBTimeout for useVerifySourceControlTokenEffect * [ADM-793] test: add test for useVerifyPipelineToolEffect * [ADM-793] test: add test for getFieldsWithNoVerifiedError * AMD-793 [frontend] test: remove not use mock in test file and change test case name * ADM-856:[backend]feat: update all feign clients decoder error message (#1133) * ADM-856:[backend]feat: update buildkite feign client decoder error message * ADM-856:[backend]feat: update github feign client decoder error message * ADM-856:[backend]feat: update jira feign client decoder error message --------- Co-authored-by: guzhongren * ADM-697:[docx] docx: add docx spike the logic of calculating card rework and api design (#1137) * ADM-697:[docx] docx: add docx spike the logic of calculating card rework and api design * ADM-697:[docx] docx: add spike export rework content when generate board csv * ADM-697:[docx] docx: add spike calculate rework times * ADM-697:[docx] docx: rewrite the doc of calculate rework times when don't check "Consider the 'Flag' as 'Block' * ADM-697:[docx] docx: add logic for consider flag is block * ADM-697:[docx] docx: fix some words * ADM-697:[docx] docx: add logic of judge is our need rework --------- Co-authored-by: yulongcai * ADM-691: [frontend] feat: add feat about rework settings (#1138) * ADM-691: [frontend] feat: add new metrics field * ADM-691: [frontend] feat: add rework setting title * ADM-691: [frontend] feat: complete basic function about rework settings * ADM-691: [frontend] feat: use redux to store fields * ADM-691: [frontend] feat: replace undefind to null * ADM-691: [frontend] feat: import config with rework times settings * ADM-691: [frontend] fix: fix unit test * ADM-691: [frontend] fix: fix unit test * ADM-691: [frontend] fix: fix unit test * ADM-691: [frontend] chore: refactor code after cr * ADM-691: [frontend] fix: fix unit test * ADM-691: [frontend] fix: fix unit test * ADM-691: [frontend] test: improve test coverage * ADM-691: [frontend] test: remove unused code * ADM-691: [frontend] test: add test for rework setting * ADM-691: [frontend] fix: fix e2e * ADM-691: [frontend] fix: fix sonar --------- Co-authored-by: Leiqiuhong * ADM-856:[backend]feat: add decode default case error message (#1139) * ADM-837:[docs] docs: spike about optimizing generate report backend logic (#1140) * ADM-837:[docs]feat: add half completed spike doc * ADM-837: [docs] completed spike solution for export-allMetrics button polling api. * ADM-837: [docs] completed spike design for export-allMetrics button polling api. * ADM-837: [docs] add note to notice allMetricCompleted as true --------- Co-authored-by: Yunlong Gan * add test case * AMD-793 [frontend] style: fix color for reverify button * [ADM-793] fix: revert httpTimeout * Build(deps): bump softprops/action-gh-release from 1 to 2 (#1136) Bumps [softprops/action-gh-release](https://github.com/softprops/action-gh-release) from 1 to 2. - [Release notes](https://github.com/softprops/action-gh-release/releases) - [Changelog](https://github.com/softprops/action-gh-release/blob/master/CHANGELOG.md) - [Commits](https://github.com/softprops/action-gh-release/compare/v1...v2) --- updated-dependencies: - dependency-name: softprops/action-gh-release dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: guzhongren * AMD-793 [frontend] fix: remove annotation in test file * AMD-793 [frontend] fix: update test name for clear * AMD-793 [frontend] fix: update test name use given when then * chore: extend the overall timeout of e2e 1 more minute. (#1144) * chore: extend the overall timeout of e2e 1 more minute. * distinguish local and CI for overall timeout. * ADM-691: [frontend] fix: add request fields (#1143) * ADM-691: [frontend] fix: add request fields * ADM-691: [frontend] fix: fix sonar cloud * ADM-691: [frontend] fix: fix unit test * ADM-691: [frontend] chore: modify sytle, rename field * ADM-691: [frontend] fix: fix sonar issue * ADM-691: [frontend] fix: remove unused code --------- Co-authored-by: Leiqiuhong * ADM-691: [frontend] fix: fix sonar issue (#1145) * [ADM-793] refactor: rename AXIOS_REQUEST_ERROR_CODE * AMD-793 [frontend] fix: update alert position to keep element still * [ADM-793] feat: enhance style for alert * [ADM-793] feat: change moduleType text * [ADM-793] style: enhance style --------- Signed-off-by: dependabot[bot] Co-authored-by: yp.wu Co-authored-by: xuebing Co-authored-by: yp.wu Co-authored-by: Steveay <907221539@qq.com> Co-authored-by: guzhongren Co-authored-by: Genhao Liu <103744663+Liughgood@users.noreply.github.com> Co-authored-by: yulongcai Co-authored-by: neomgb <123063936+neomgb@users.noreply.github.com> Co-authored-by: Leiqiuhong Co-authored-by: sqsq5566 <154306546+sqsq5566@users.noreply.github.com> Co-authored-by: Yunlong Gan Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: 李雪冰 <33832990+lxuebing@users.noreply.github.com> Co-authored-by: Chao <89126516+mrcuriosity-tw@users.noreply.github.com> --- .../client/PipelineToolClient.test.ts | 6 +- .../client/SourceControlClient.test.ts | 12 +- .../ConfigStep/ConfigButtonGroup.test.tsx | 30 +++++ .../ConfigStep/TimeoutAlet.test.tsx | 53 ++++++++ .../PresentationForErrorCases.test.tsx | 4 +- .../MetricsStep/MetricsStep.test.tsx | 4 +- .../containers/ReportStep/ReportStep.test.tsx | 2 +- frontend/__tests__/fixtures.ts | 23 +++- .../hooks/useVerifyBoardEffect.test.tsx | 114 ++++++++++-------- .../useVerifyPipelineToolEffect.test.tsx | 49 +++++--- ...useVerifySourceControlTokenEffect.test.tsx | 15 +++ frontend/src/clients/HttpClient.ts | 4 +- .../clients/pipeline/PipelineToolClient.ts | 8 +- frontend/src/clients/report/ReportClient.ts | 13 ++ frontend/src/clients/report/dto/request.ts | 29 ++--- frontend/src/clients/report/dto/response.ts | 15 +++ .../sourceControl/SourceControlClient.ts | 8 +- frontend/src/components/Common/Buttons.ts | 4 +- .../PresentationForErrorCases/index.tsx | 4 +- frontend/src/constants/resources.ts | 14 ++- .../src/containers/ConfigStep/Board/index.tsx | 42 ++++--- .../ConfigStep/ConfigButton/index.tsx | 35 ++++++ .../ConfigStep/PipelineTool/index.tsx | 51 +++++--- .../ConfigStep/SourceControl/index.tsx | 46 +++---- .../ConfigStep/TimeoutAlert/index.tsx | 29 +++++ .../ConfigStep/TimeoutAlert/style.tsx | 23 ++++ frontend/src/containers/ConfigStep/style.tsx | 9 ++ .../containers/MetricsStep/Advance/style.tsx | 1 + .../MetricsStep/CycleTime/index.tsx | 2 +- .../MetricsStep/CycleTime/style.tsx | 1 - .../BranchSelection/BranchChip/index.tsx | 4 +- .../MetricsStep/ReworkSettings/index.tsx | 2 +- frontend/src/containers/MetricsStep/index.tsx | 4 +- .../ReportStep/BoardMetrics/index.tsx | 10 +- frontend/src/hooks/useGenerateReportEffect.ts | 4 +- frontend/src/hooks/useGetBoardInfo.ts | 6 +- frontend/src/hooks/useVerifyBoardEffect.ts | 15 ++- .../src/hooks/useVerifyPipelineToolEffect.ts | 12 +- .../useVerifySourceControlTokenEffect.ts | 12 +- 39 files changed, 530 insertions(+), 189 deletions(-) create mode 100644 frontend/__tests__/containers/ConfigStep/ConfigButtonGroup.test.tsx create mode 100644 frontend/__tests__/containers/ConfigStep/TimeoutAlet.test.tsx create mode 100644 frontend/src/containers/ConfigStep/ConfigButton/index.tsx create mode 100644 frontend/src/containers/ConfigStep/TimeoutAlert/index.tsx create mode 100644 frontend/src/containers/ConfigStep/TimeoutAlert/style.tsx diff --git a/frontend/__tests__/client/PipelineToolClient.test.ts b/frontend/__tests__/client/PipelineToolClient.test.ts index 1b7b098e04..a36c47e16e 100644 --- a/frontend/__tests__/client/PipelineToolClient.test.ts +++ b/frontend/__tests__/client/PipelineToolClient.test.ts @@ -21,7 +21,7 @@ afterAll(() => server.close()); describe('PipelineToolClient', () => { describe('verify pipelineTool request', () => { it('should isPipelineVerified is true when pipelineTool verify response status 204', async () => { - const result = await pipelineToolClient.verify(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS); + const result = await pipelineToolClient.verify(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS, jest.fn(), jest.fn()); expect(result.code).toEqual(HttpStatusCode.NoContent); }); @@ -45,7 +45,7 @@ describe('PipelineToolClient', () => { it.each(errorCases)('should return error code when verify endponint returns error', async ({ code }) => { server.use(rest.post(MOCK_PIPELINE_VERIFY_URL, (req, res, ctx) => res(ctx.status(code)))); - const result = await pipelineToolClient.verify(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS); + const result = await pipelineToolClient.verify(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS, jest.fn(), jest.fn()); expect(result.code).toEqual(code); }); @@ -122,7 +122,7 @@ describe('PipelineToolClient', () => { const result = await pipelineToolClient.getInfo(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS); - expect(result.code).toEqual('HB_TIMEOUT'); + expect(result.code).toEqual('NETWORK_TIMEOUT'); expect(result.data).toBeUndefined(); }); diff --git a/frontend/__tests__/client/SourceControlClient.test.ts b/frontend/__tests__/client/SourceControlClient.test.ts index f486c3a050..c1f9f286dd 100644 --- a/frontend/__tests__/client/SourceControlClient.test.ts +++ b/frontend/__tests__/client/SourceControlClient.test.ts @@ -15,7 +15,11 @@ describe('verify sourceControl request', () => { afterAll(() => server.close()); it('should return isSourceControlVerify true when sourceControl verify response status is 204', async () => { - const result = await sourceControlClient.verifyToken(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS); + const result = await sourceControlClient.verifyToken( + MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS, + jest.fn(), + jest.fn(), + ); expect(result.code).toEqual(204); }); @@ -27,7 +31,7 @@ describe('verify sourceControl request', () => { ), ); - sourceControlClient.verifyToken(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS).catch((e) => { + sourceControlClient.verifyToken(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS, jest.fn(), jest.fn()).catch((e) => { expect(e).toBeInstanceOf(Error); expect((e as Error).message).toMatch(VERIFY_ERROR_MESSAGE.BAD_REQUEST); }); @@ -40,7 +44,7 @@ describe('verify sourceControl request', () => { ), ); - sourceControlClient.verifyToken(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS).catch((e) => { + sourceControlClient.verifyToken(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS, jest.fn(), jest.fn()).catch((e) => { expect(e).toBeInstanceOf(Error); expect((e as Error).message).toMatch(VERIFY_ERROR_MESSAGE.NOT_FOUND); }); @@ -58,7 +62,7 @@ describe('verify sourceControl request', () => { ), ); - sourceControlClient.verifyToken(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS).catch((e) => { + sourceControlClient.verifyToken(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS, jest.fn(), jest.fn()).catch((e) => { expect(e).toBeInstanceOf(Error); expect((e as Error).message).toMatch(VERIFY_ERROR_MESSAGE.INTERNAL_SERVER_ERROR); }); diff --git a/frontend/__tests__/containers/ConfigStep/ConfigButtonGroup.test.tsx b/frontend/__tests__/containers/ConfigStep/ConfigButtonGroup.test.tsx new file mode 100644 index 0000000000..bb21821265 --- /dev/null +++ b/frontend/__tests__/containers/ConfigStep/ConfigButtonGroup.test.tsx @@ -0,0 +1,30 @@ +import { ConfigButtonGrop } from '@src/containers/ConfigStep/ConfigButton'; +import { render, screen } from '@testing-library/react'; +import React from 'react'; + +describe('ConfigButtonGroup', () => { + const setup = (isVerified: boolean, isLoading: boolean, isVerifyTimeOut: boolean, isDisableVerifyButton: boolean) => { + return render( + , + ); + }; + + it('should render a verified and rest button given isVerified is true and isLoading is false', () => { + setup(true, false, false, false); + + expect(screen.getByText('Verified')).toBeInTheDocument(); + expect(screen.getByText('Reset')).toBeInTheDocument(); + expect(screen.getByText('Verified')).toBeDisabled(); + }); + it('should render a Reverify button given isVerifyTimeOut is true', () => { + setup(false, false, true, false); + + expect(screen.getByText('Reverify')).toBeInTheDocument(); + expect(screen.getByText('Reverify')).toHaveAttribute('type', 'submit'); + }); +}); diff --git a/frontend/__tests__/containers/ConfigStep/TimeoutAlet.test.tsx b/frontend/__tests__/containers/ConfigStep/TimeoutAlet.test.tsx new file mode 100644 index 0000000000..986fdd3461 --- /dev/null +++ b/frontend/__tests__/containers/ConfigStep/TimeoutAlet.test.tsx @@ -0,0 +1,53 @@ +import { TimeoutAlert } from '@src/containers/ConfigStep/TimeoutAlert'; +import { act, render, screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import React from 'react'; + +describe('TimeoutAlert', () => { + const setIsShowAlert = jest.fn(); + const setup = ( + setIsShowAlert: (value: boolean) => void, + isShowAlert: boolean, + isVerifyTimeOut: boolean, + moduleType: string, + ) => { + return render( + , + ); + }; + + it('should render board message given moduleType is board', () => { + setup(setIsShowAlert, true, true, 'Board'); + const message = screen.getByText('Board'); + + expect(message).toBeInTheDocument(); + }); + it('should not render the alert given isVerifyTimeOut or isShowAlert is false', () => { + setup(setIsShowAlert, false, true, 'Board'); + + expect(screen.queryByText('Board')).not.toBeInTheDocument(); + + setup(setIsShowAlert, true, false, 'Board'); + + expect(screen.queryByText('Board')).not.toBeInTheDocument(); + }); + + it('should call setIsShowAlert with false when click the close icon given init value', async () => { + setup(setIsShowAlert, true, true, 'any'); + const closeIcon = screen.getByTestId('CloseIcon'); + + act(() => { + userEvent.click(closeIcon); + }); + + await waitFor(() => { + expect(setIsShowAlert).toHaveBeenCalledTimes(1); + expect(setIsShowAlert).toHaveBeenCalledWith(false); + }); + }); +}); diff --git a/frontend/__tests__/containers/MetricsStep/DeploymentFrequencySettings/PresentationForErrorCases.test.tsx b/frontend/__tests__/containers/MetricsStep/DeploymentFrequencySettings/PresentationForErrorCases.test.tsx index e20d498b24..7e155a3847 100644 --- a/frontend/__tests__/containers/MetricsStep/DeploymentFrequencySettings/PresentationForErrorCases.test.tsx +++ b/frontend/__tests__/containers/MetricsStep/DeploymentFrequencySettings/PresentationForErrorCases.test.tsx @@ -41,7 +41,7 @@ describe('', () => { it('should display "try again" when error code is axios predefined error: $code', async () => { const retrySpy = jest.fn(); const mockTimeoutError = { - code: 'HB_TIMEOUT', + code: 'NETWORK_TIMEOUT', errorTitle: 'Service Unavailable!', errorMessage: 'Data loading failed, please try again', isLoading: false, @@ -63,7 +63,7 @@ describe('', () => { it('should not fire duplicated retry behavior when retry func is loading', async () => { const retrySpy = jest.fn(); const mockTimeoutErrorProps = { - code: 'HB_TIMEOUT', + code: 'NETWORK_TIMEOUT', errorTitle: 'Service Unavailable!', errorMessage: 'Data loading failed, please try again', isLoading: true, diff --git a/frontend/__tests__/containers/MetricsStep/MetricsStep.test.tsx b/frontend/__tests__/containers/MetricsStep/MetricsStep.test.tsx index ffdb8ede62..49346b1642 100644 --- a/frontend/__tests__/containers/MetricsStep/MetricsStep.test.tsx +++ b/frontend/__tests__/containers/MetricsStep/MetricsStep.test.tsx @@ -324,13 +324,13 @@ describe('MetricsStep', () => { await waitFor(() => { expect(screen.getByText(/crew settings/i)).toBeInTheDocument(); }); - expect(screen.getByText(/board mapping/i)).toBeInTheDocument(); + expect(screen.getByText(/board mappings/i)).toBeInTheDocument(); }); it('should show retry button when call get info timeout', async () => { server.use( rest.post(MOCK_BOARD_INFO_URL, (_, res) => { - return res.networkError('HB_TIMEOUT'); + return res.networkError('NETWORK_TIMEOUT'); }), ); setup(); diff --git a/frontend/__tests__/containers/ReportStep/ReportStep.test.tsx b/frontend/__tests__/containers/ReportStep/ReportStep.test.tsx index f3c4af1e1b..2a7266bbc3 100644 --- a/frontend/__tests__/containers/ReportStep/ReportStep.test.tsx +++ b/frontend/__tests__/containers/ReportStep/ReportStep.test.tsx @@ -348,7 +348,7 @@ describe('Report Step', () => { describe('export board data', () => { it('should not show export board button when not selecting board metrics', () => { - const { queryByText } = setup([REQUIRED_DATA_LIST[4]]); + const { queryByText } = setup([REQUIRED_DATA_LIST[5]]); const exportPipelineButton = queryByText(EXPORT_BOARD_DATA); diff --git a/frontend/__tests__/fixtures.ts b/frontend/__tests__/fixtures.ts index 5426bf7f9d..6a416dc38b 100644 --- a/frontend/__tests__/fixtures.ts +++ b/frontend/__tests__/fixtures.ts @@ -207,6 +207,10 @@ export const MOCK_GENERATE_REPORT_REQUEST_PARAMS: ReportRequestDTO = { targetFields: [{ key: 'parent', name: 'Parent', flag: false }], doneColumn: ['Done'], overrideFields: [{ key: '123', name: 'Story Point', flag: true }], + reworkTimesSetting: { + reworkState: 'Done', + excludedStates: [], + }, }, }; @@ -339,7 +343,7 @@ export const MOCK_GITHUB_VERIFY_RESPONSE = { }; export const CREWS_SETTING = 'Crew settings'; -export const BOARD_MAPPING = 'Board mapping'; +export const BOARD_MAPPING = 'Board mappings'; export const CLASSIFICATION_SETTING = 'Classification setting'; export const REAL_DONE = 'Real done setting'; export const DEPLOYMENT_FREQUENCY_SETTINGS = 'Pipeline settings'; @@ -404,6 +408,19 @@ export const MOCK_REPORT_RESPONSE: ReportResponseDTO = { }, ], }, + rework: { + totalReworkTimes: 111, + reworkState: 'Done', + fromToDo: 111, + fromInDev: 111, + fromBlock: 111, + fromWaitingForTesting: 111, + fromTesting: 111, + fromReview: 111, + fromDone: 111, + totalReworkCards: 111, + reworkCardsRatio: 111, + }, deploymentFrequency: { avgDeploymentFrequency: { name: 'Average', @@ -651,6 +668,7 @@ export const EMPTY_REPORT_VALUES: ReportResponseDTO = { velocity: null, classificationList: null, cycleTime: null, + rework: null, deploymentFrequency: null, changeFailureRate: null, meanTimeToRecovery: null, @@ -747,6 +765,9 @@ export const CYCLE_TIME_SETTINGS_SECTION = 'Cycle time settings section'; export const REAL_DONE_SETTING_SECTION = 'Real done setting section'; export const SELECT_CONSIDER_AS_DONE_MESSAGE = 'Must select which you want to consider as Done'; export const MOCK_SOURCE_CONTROL_VERIFY_ERROR_CASE_TEXT = 'Token is incorrect!'; +export const MOCK_PIPELINE_VERIFY_UNAUTHORIZED_TEXT = 'Token is incorrect!'; +export const MOCK_PIPELINE_VERIFY_FORBIDDEN_ERROR_TEXT = + 'Forbidden request, please change your token with correct access permission.'; export const FAKE_TOKEN = 'fake-token'; diff --git a/frontend/__tests__/hooks/useVerifyBoardEffect.test.tsx b/frontend/__tests__/hooks/useVerifyBoardEffect.test.tsx index 52f24de57c..18d63bc007 100644 --- a/frontend/__tests__/hooks/useVerifyBoardEffect.test.tsx +++ b/frontend/__tests__/hooks/useVerifyBoardEffect.test.tsx @@ -1,11 +1,15 @@ import { useVerifyBoardEffect, useVerifyBoardStateInterface } from '@src/hooks/useVerifyBoardEffect'; -import { MOCK_BOARD_URL_FOR_JIRA, FAKE_TOKEN } from '@test/fixtures'; import { act, renderHook, waitFor } from '@testing-library/react'; -import { setupServer } from 'msw/node'; +import { FAKE_TOKEN } from '@test/fixtures'; import { HttpStatusCode } from 'axios'; +import { InternalServerException } from '@src/exceptions/InternalServerException'; +import { UnauthorizedException } from '@src/exceptions/UnauthorizedException'; +import { NotFoundException } from '@src/exceptions/NotFoundException'; +import { TimeoutException } from '@src/exceptions/TimeoutException'; +import { AXIOS_REQUEST_ERROR_CODE } from '@src/constants/resources'; +import { boardClient } from '@src/clients/board/BoardClient'; import { BOARD_TYPES } from '@test/fixtures'; -import { rest } from 'msw'; const mockDispatch = jest.fn(); jest.mock('react-redux', () => ({ @@ -18,8 +22,6 @@ jest.mock('@src/hooks/useAppDispatch', () => ({ useAppDispatch: jest.fn(() => jest.fn()), })); -const server = setupServer(); - const updateFields = (result: { current: useVerifyBoardStateInterface }) => { result.current.updateField('Board Id', '1'); result.current.updateField('Email', 'fake@qq.com'); @@ -28,10 +30,8 @@ const updateFields = (result: { current: useVerifyBoardStateInterface }) => { }; describe('use verify board state', () => { - beforeAll(() => server.listen()); afterAll(() => { jest.clearAllMocks(); - server.close(); }); it('should got initial data state when hook render given none input', async () => { const { result } = renderHook(() => useVerifyBoardEffect()); @@ -41,16 +41,13 @@ describe('use verify board state', () => { }); it('should got email and token fields error message when call verify function given a invalid token', async () => { - server.use( - rest.post(MOCK_BOARD_URL_FOR_JIRA, (_, res, ctx) => { - return res(ctx.status(HttpStatusCode.Unauthorized)); - }), - ); + const mockedError = new UnauthorizedException('', HttpStatusCode.Unauthorized, ''); + boardClient.getVerifyBoard = jest.fn().mockImplementation(() => Promise.reject(mockedError)); const { result } = renderHook(() => useVerifyBoardEffect()); - await act(() => { - updateFields(result); - result.current.verifyJira(); + await act(async () => { + await updateFields(result); + await result.current.verifyJira(); }); const emailFiled = result.current.fields.find((field) => field.key === 'Email'); @@ -61,22 +58,34 @@ describe('use verify board state', () => { ); }); - it('when call verify function given a invalid site then should got site field error message', async () => { - server.use( - rest.post(MOCK_BOARD_URL_FOR_JIRA, (_, res, ctx) => { - return res( - ctx.status(HttpStatusCode.NotFound), - ctx.json({ - message: 'site is incorrect', - }), - ); - }), - ); + it('should clear email validatedError when updateField by Email given fetch error ', async () => { + const mockedError = new UnauthorizedException('', HttpStatusCode.Unauthorized, ''); + boardClient.getVerifyBoard = jest.fn().mockImplementation(() => Promise.reject(mockedError)); const { result } = renderHook(() => useVerifyBoardEffect()); - await act(() => { - updateFields(result); - result.current.verifyJira(); + await act(async () => { + await updateFields(result); + await result.current.verifyJira(); + }); + + const emailFiled = result.current.fields.find((field) => field.key === 'Email'); + expect(emailFiled?.verifiedError).toBe('Email is incorrect!'); + + await act(async () => { + await result.current.updateField('Email', 'fake@qq.com'); + }); + const emailText = result.current.fields.find((field) => field.key === 'Email'); + expect(emailText?.verifiedError).toBe(''); + }); + + it('should got site field error message when call verify function given a invalid site', async () => { + const mockedError = new NotFoundException('site is incorrect', HttpStatusCode.NotFound, 'site is incorrect'); + boardClient.getVerifyBoard = jest.fn().mockImplementation(() => Promise.reject(mockedError)); + + const { result } = renderHook(() => useVerifyBoardEffect()); + await act(async () => { + await updateFields(result); + await result.current.verifyJira(); }); await waitFor(() => { @@ -87,16 +96,8 @@ describe('use verify board state', () => { }); it('should got board id field error message when call verify function given a invalid board id', async () => { - server.use( - rest.post(MOCK_BOARD_URL_FOR_JIRA, (_, res, ctx) => { - return res( - ctx.status(HttpStatusCode.NotFound), - ctx.json({ - message: 'boardId is incorrect', - }), - ); - }), - ); + const mockedError = new NotFoundException('boardId is incorrect', HttpStatusCode.NotFound, 'boardId is incorrect'); + boardClient.getVerifyBoard = jest.fn().mockImplementation(() => Promise.reject(mockedError)); const { result } = renderHook(() => useVerifyBoardEffect()); await act(() => { @@ -111,16 +112,13 @@ describe('use verify board state', () => { }); it('should got token fields error message when call verify function given a unknown error', async () => { - server.use( - rest.post(MOCK_BOARD_URL_FOR_JIRA, (_, res, ctx) => { - return res(ctx.status(HttpStatusCode.ServiceUnavailable)); - }), - ); + const mockedError = new InternalServerException('', HttpStatusCode.ServiceUnavailable, ''); + boardClient.getVerifyBoard = jest.fn().mockImplementation(() => Promise.reject(mockedError)); const { result } = renderHook(() => useVerifyBoardEffect()); - await act(() => { - updateFields(result); - result.current.verifyJira(); + await act(async () => { + await updateFields(result); + await result.current.verifyJira(); }); const tokenField = result.current.fields.find((field) => field.key === 'Token'); @@ -128,11 +126,8 @@ describe('use verify board state', () => { }); it('should clear all verified error messages when update a verified error field', async () => { - server.use( - rest.post(MOCK_BOARD_URL_FOR_JIRA, (_, res, ctx) => { - return res(ctx.status(HttpStatusCode.Unauthorized)); - }), - ); + const mockedError = new UnauthorizedException('', HttpStatusCode.Unauthorized, ''); + boardClient.getVerifyBoard = jest.fn().mockImplementation(() => Promise.reject(mockedError)); const { result } = renderHook(() => useVerifyBoardEffect()); await act(() => { @@ -148,4 +143,19 @@ describe('use verify board state', () => { expect(emailFiled?.verifiedError).toBe(''); expect(tokenField?.verifiedError).toBe(''); }); + + it('should set timeout is true given getVerifyBoard api is timeout', async () => { + const mockedError = new TimeoutException('', AXIOS_REQUEST_ERROR_CODE.TIMEOUT); + boardClient.getVerifyBoard = jest.fn().mockImplementation(() => Promise.reject(mockedError)); + + const { result } = renderHook(() => useVerifyBoardEffect()); + await act(() => { + result.current.verifyJira(); + }); + + await waitFor(() => { + const isVerifyTimeOut = result.current.isVerifyTimeOut; + expect(isVerifyTimeOut).toBe(true); + }); + }); }); diff --git a/frontend/__tests__/hooks/useVerifyPipelineToolEffect.test.tsx b/frontend/__tests__/hooks/useVerifyPipelineToolEffect.test.tsx index 8df8194857..c75bdab78f 100644 --- a/frontend/__tests__/hooks/useVerifyPipelineToolEffect.test.tsx +++ b/frontend/__tests__/hooks/useVerifyPipelineToolEffect.test.tsx @@ -1,9 +1,13 @@ -import { MOCK_PIPELINE_VERIFY_REQUEST_PARAMS, MOCK_PIPELINE_VERIFY_URL } from '../fixtures'; +import { + MOCK_PIPELINE_VERIFY_FORBIDDEN_ERROR_TEXT, + MOCK_PIPELINE_VERIFY_REQUEST_PARAMS, + MOCK_PIPELINE_VERIFY_UNAUTHORIZED_TEXT, +} from '../fixtures'; import { useVerifyPipelineToolEffect } from '@src/hooks/useVerifyPipelineToolEffect'; +import { pipelineToolClient } from '@src/clients/pipeline/PipelineToolClient'; +import { AXIOS_REQUEST_ERROR_CODE } from '@src/constants/resources'; import { act, renderHook, waitFor } from '@testing-library/react'; -import { setupServer } from 'msw/node'; import { HttpStatusCode } from 'axios'; -import { rest } from 'msw'; const mockDispatch = jest.fn(); jest.mock('react-redux', () => ({ @@ -11,15 +15,6 @@ jest.mock('react-redux', () => ({ useDispatch: () => mockDispatch, })); -const server = setupServer( - rest.post(MOCK_PIPELINE_VERIFY_URL, (req, res, ctx) => { - return res(ctx.status(HttpStatusCode.NoContent)); - }), -); - -beforeAll(() => server.listen()); -afterAll(() => server.close()); - describe('use verify pipelineTool state', () => { it('should return empty error message when call verify feature given client returns 204', async () => { const { result } = renderHook(() => useVerifyPipelineToolEffect()); @@ -35,7 +30,11 @@ describe('use verify pipelineTool state', () => { }); it('should set error message when verifying pipeline given response status 401', async () => { - server.use(rest.post(MOCK_PIPELINE_VERIFY_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Unauthorized)))); + pipelineToolClient.verify = jest.fn().mockResolvedValue({ + code: HttpStatusCode.Unauthorized, + errorTitle: MOCK_PIPELINE_VERIFY_UNAUTHORIZED_TEXT, + }); + const { result } = renderHook(() => useVerifyPipelineToolEffect()); act(() => { @@ -43,12 +42,14 @@ describe('use verify pipelineTool state', () => { }); await waitFor(() => { - expect(result.current.verifiedError).toEqual(`Token is incorrect!`); + expect(result.current.verifiedError).toEqual(MOCK_PIPELINE_VERIFY_UNAUTHORIZED_TEXT); }); }); it('should clear error message when explicitly call clear function given error message exists', async () => { - server.use(rest.post(MOCK_PIPELINE_VERIFY_URL, (req, res, ctx) => res(ctx.status(HttpStatusCode.Forbidden)))); + pipelineToolClient.verify = jest + .fn() + .mockResolvedValue({ code: HttpStatusCode.Forbidden, errorTitle: MOCK_PIPELINE_VERIFY_FORBIDDEN_ERROR_TEXT }); const { result } = renderHook(() => useVerifyPipelineToolEffect()); act(() => { @@ -56,9 +57,7 @@ describe('use verify pipelineTool state', () => { }); await waitFor(() => { - expect(result.current.verifiedError).toEqual( - 'Forbidden request, please change your token with correct access permission.', - ); + expect(result.current.verifiedError).toEqual(MOCK_PIPELINE_VERIFY_FORBIDDEN_ERROR_TEXT); }); result.current.clearVerifiedError(); @@ -67,4 +66,18 @@ describe('use verify pipelineTool state', () => { expect(result.current.verifiedError).toEqual(''); }); }); + + it('should set timeout is true when verify api is timeout', async () => { + pipelineToolClient.verify = jest.fn().mockResolvedValue({ code: AXIOS_REQUEST_ERROR_CODE.TIMEOUT }); + + const { result } = renderHook(() => useVerifyPipelineToolEffect()); + await act(() => { + result.current.verifyPipelineTool(MOCK_PIPELINE_VERIFY_REQUEST_PARAMS); + }); + + await waitFor(() => { + const isVerifyTimeOut = result.current.isVerifyTimeOut; + expect(isVerifyTimeOut).toBe(true); + }); + }); }); diff --git a/frontend/__tests__/hooks/useVerifySourceControlTokenEffect.test.tsx b/frontend/__tests__/hooks/useVerifySourceControlTokenEffect.test.tsx index 3e7d5efde4..13694a3248 100644 --- a/frontend/__tests__/hooks/useVerifySourceControlTokenEffect.test.tsx +++ b/frontend/__tests__/hooks/useVerifySourceControlTokenEffect.test.tsx @@ -2,6 +2,7 @@ import { MOCK_SOURCE_CONTROL_VERIFY_ERROR_CASE_TEXT, MOCK_SOURCE_CONTROL_VERIFY_ import { useVerifySourceControlTokenEffect } from '@src/hooks/useVerifySourceControlTokenEffect'; import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient'; import { ContextProvider } from '@src/hooks/useMetricsStepValidationCheckContext'; +import { AXIOS_REQUEST_ERROR_CODE } from '@src/constants/resources'; import { act, renderHook, waitFor } from '@testing-library/react'; import { setupStore } from '../utils/setupStoreUtil'; import { Provider } from 'react-redux'; @@ -76,4 +77,18 @@ describe('use verify sourceControl token', () => { expect(result.current.verifiedError).toEqual(''); }); }); + + it('should isVerifyTimeOut and isShowAlert is true when api timeout', async () => { + sourceControlClient.verifyToken = jest.fn().mockResolvedValue({ + code: AXIOS_REQUEST_ERROR_CODE.TIMEOUT, + }); + const { result } = setup(); + + await act(() => result.current.verifyToken(MOCK_SOURCE_CONTROL_VERIFY_REQUEST_PARAMS)); + + await waitFor(() => { + expect(result.current.isVerifyTimeOut).toEqual(true); + expect(result.current.isShowAlert).toEqual(true); + }); + }); }); diff --git a/frontend/src/clients/HttpClient.ts b/frontend/src/clients/HttpClient.ts index d599d0d0bd..de35ae5cc7 100644 --- a/frontend/src/clients/HttpClient.ts +++ b/frontend/src/clients/HttpClient.ts @@ -1,4 +1,4 @@ -import { AXIOS_NETWORK_ERROR_CODES, HEARTBEAT_EXCEPTION_CODE } from '@src/constants/resources'; +import { AXIOS_NETWORK_ERROR_CODES, AXIOS_REQUEST_ERROR_CODE } from '@src/constants/resources'; import { InternalServerException } from '@src/exceptions/InternalServerException'; import { UnauthorizedException } from '@src/exceptions/UnauthorizedException'; import { BadRequestException } from '@src/exceptions/BadRequestException'; @@ -23,7 +23,7 @@ export class HttpClient { (error) => { const { code, response } = error; if (AXIOS_NETWORK_ERROR_CODES.some((predefinedCode) => predefinedCode === code)) { - throw new TimeoutException(error?.message, HEARTBEAT_EXCEPTION_CODE.TIMEOUT); + throw new TimeoutException(error?.message, AXIOS_REQUEST_ERROR_CODE.TIMEOUT); } else if (response && response.status && response.status > 0) { const { status, data, statusText } = response; const errorMessage = data?.hintInfo ?? statusText; diff --git a/frontend/src/clients/pipeline/PipelineToolClient.ts b/frontend/src/clients/pipeline/PipelineToolClient.ts index f5ffb5c3d0..cc4e119fde 100644 --- a/frontend/src/clients/pipeline/PipelineToolClient.ts +++ b/frontend/src/clients/pipeline/PipelineToolClient.ts @@ -24,7 +24,11 @@ export interface IGetPipelineToolInfoResult { } export class PipelineToolClient extends HttpClient { - verify = async (params: IPipelineVerifyRequestDTO): Promise => { + verify = async ( + params: IPipelineVerifyRequestDTO, + setIsShowAlert: (value: boolean) => void, + setIsVerifyTimeOut: (value: boolean) => void, + ): Promise => { const result: IVerifyPipelineToolResult = { code: null, errorTitle: '', @@ -32,6 +36,8 @@ export class PipelineToolClient extends HttpClient { try { const response = await this.axiosInstance.post(`/pipelines/${params.type.toLowerCase()}/verify`, params); result.code = response.status; + setIsShowAlert(false); + setIsVerifyTimeOut(false); } catch (e) { if (isHeartBeatException(e)) { const exception = e as IHeartBeatException; diff --git a/frontend/src/clients/report/ReportClient.ts b/frontend/src/clients/report/ReportClient.ts index e1b9e7778f..8b0899f2b3 100644 --- a/frontend/src/clients/report/ReportClient.ts +++ b/frontend/src/clients/report/ReportClient.ts @@ -26,6 +26,19 @@ export class ReportClient extends HttpClient { }, ], }, + rework: { + totalReworkTimes: 0, + reworkState: 'Done', + fromToDo: 0, + fromInDev: 0, + fromBlock: 0, + fromWaitingForTesting: 0, + fromTesting: 0, + fromReview: 0, + fromDone: 0, + totalReworkCards: 0, + reworkCardsRatio: 0, + }, classificationList: [ { fieldName: '', diff --git a/frontend/src/clients/report/dto/request.ts b/frontend/src/clients/report/dto/request.ts index 4a29fd59e9..327b4ebd46 100644 --- a/frontend/src/clients/report/dto/request.ts +++ b/frontend/src/clients/report/dto/request.ts @@ -1,8 +1,4 @@ -export interface ReportRequestDTO { - metrics: string[]; - startTime: string | null; - endTime: string | null; - considerHoliday: boolean; +export interface ReportRequestDTO extends IBasicReportRequestDTO { buildKiteSetting?: { type: string; token: string; @@ -32,24 +28,14 @@ export interface ReportRequestDTO { branches: string[]; }[]; }; - jiraBoardSetting?: { - token: string; - type: string; - site: string; - projectKey: string; - boardId: string; - boardColumns: { name: string; value: string }[]; - treatFlagCardAsBlock: boolean; - users: string[]; - assigneeFilter: string; - targetFields: { key: string; name: string; flag: boolean }[]; - overrideFields: { key: string; name: string; flag: boolean }[]; - doneColumn: string[]; - }; - csvTimeStamp?: number; } -export interface BoardReportRequestDTO { +interface ReworkSettingsRequest { + reworkState?: string | null; + excludedStates?: string[]; +} + +export interface IBasicReportRequestDTO { considerHoliday: boolean; startTime: string | null; endTime: string | null; @@ -66,6 +52,7 @@ export interface BoardReportRequestDTO { assigneeFilter: string; targetFields: { key: string; name: string; flag: boolean }[]; overrideFields: { key: string; name: string; flag: boolean }[]; + reworkTimesSetting: ReworkSettingsRequest; doneColumn: string[]; }; csvTimeStamp?: number; diff --git a/frontend/src/clients/report/dto/response.ts b/frontend/src/clients/report/dto/response.ts index 6c3cb0f732..685943509f 100644 --- a/frontend/src/clients/report/dto/response.ts +++ b/frontend/src/clients/report/dto/response.ts @@ -4,6 +4,7 @@ import { Nullable } from '@src/utils/types'; export interface ReportResponseDTO { velocity: Nullable; cycleTime: Nullable; + rework: Nullable; classificationList: Nullable; deploymentFrequency: Nullable; meanTimeToRecovery: Nullable; @@ -40,6 +41,20 @@ export interface CycleTimeResponse { swimlaneList: Array; } +export interface ReworkTimeResponse { + totalReworkTimes: number; + reworkState: string; + fromToDo: number; + fromInDev: number; + fromBlock: number; + fromWaitingForTesting: number; + fromTesting: number; + fromReview: number; + fromDone: number; + totalReworkCards: number; + reworkCardsRatio: number; +} + export interface ClassificationResponse { fieldName: string; pairList: Array; diff --git a/frontend/src/clients/sourceControl/SourceControlClient.ts b/frontend/src/clients/sourceControl/SourceControlClient.ts index 1b7fad9a86..3328503cd6 100644 --- a/frontend/src/clients/sourceControl/SourceControlClient.ts +++ b/frontend/src/clients/sourceControl/SourceControlClient.ts @@ -10,7 +10,11 @@ export interface SourceControlResult { } export class SourceControlClient extends HttpClient { - verifyToken = async (params: SourceControlVerifyRequestDTO) => { + verifyToken = async ( + params: SourceControlVerifyRequestDTO, + setIsShowAlert: (value: boolean) => void, + setIsVerifyTimeOut: (value: boolean) => void, + ) => { const result: SourceControlResult = {}; const { token, type } = params; try { @@ -18,6 +22,8 @@ export class SourceControlClient extends HttpClient { token, }); result.code = response.status; + setIsShowAlert(false); + setIsVerifyTimeOut(false); } catch (e) { if (isHeartBeatException(e)) { const exception = e as IHeartBeatException; diff --git a/frontend/src/components/Common/Buttons.ts b/frontend/src/components/Common/Buttons.ts index 7792860687..69c8bf401e 100644 --- a/frontend/src/components/Common/Buttons.ts +++ b/frontend/src/components/Common/Buttons.ts @@ -10,7 +10,9 @@ export const BasicButton = styled(Button)({ }); export const VerifyButton = styled(BasicButton)({}); -export const ResetButton = styled(BasicButton)({ +export const ReverifyButton = styled(BasicButton)({ color: theme.components?.errorMessage.color, +}); +export const ResetButton = styled(BasicButton)({ marginLeft: '0.5rem', }); diff --git a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PresentationForErrorCases/index.tsx b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PresentationForErrorCases/index.tsx index 46c3b21457..0edbd8d040 100644 --- a/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PresentationForErrorCases/index.tsx +++ b/frontend/src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PresentationForErrorCases/index.tsx @@ -9,7 +9,7 @@ import { } from '@src/components/Metrics/MetricsStep/DeploymentFrequencySettings/PresentationForErrorCases/style'; import { PIPELINE_TOOL_RETRY_MESSAGE, PIPELINE_TOOL_RETRY_TRIGGER_MESSAGE } from '@src/constants/resources'; import { IGetPipelineToolInfoResult } from '@src/clients/pipeline/PipelineToolClient'; -import { HEARTBEAT_EXCEPTION_CODE } from '@src/constants/resources'; +import { AXIOS_REQUEST_ERROR_CODE } from '@src/constants/resources'; import errorSvg from '@src/assets/PipelineInfoError.svg'; import React, { useCallback } from 'react'; import { Box } from '@mui/material'; @@ -21,7 +21,7 @@ export interface IPresentationForErrorCasesProps extends IGetPipelineToolInfoRes const PresentationForErrorCases = (props: IPresentationForErrorCasesProps) => { const handleRetry = useCallback(() => !props.isLoading && props.retry(), [props]); - const isShowRetryUI = HEARTBEAT_EXCEPTION_CODE.TIMEOUT === props.code; + const isShowRetryUI = AXIOS_REQUEST_ERROR_CODE.TIMEOUT === props.code; return ( diff --git a/frontend/src/constants/resources.ts b/frontend/src/constants/resources.ts index 7eb63d3c19..7d790eca8b 100644 --- a/frontend/src/constants/resources.ts +++ b/frontend/src/constants/resources.ts @@ -51,6 +51,7 @@ export enum METRICS_TITLE { VELOCITY = 'Velocity', CYCLE_TIME = 'Cycle Time', CLASSIFICATION = 'Classification', + REWORK_TIMES = 'Rework times', LEAD_TIME_FOR_CHANGES = 'Lead Time For Changes', DEPLOYMENT_FREQUENCY = 'Deployment Frequency', CHANGE_FAILURE_RATE = 'Change Failure Rate', @@ -85,7 +86,12 @@ export const DORA_METRICS: string[] = [ REQUIRED_DATA.MEAN_TIME_TO_RECOVERY, ]; -export const BOARD_METRICS: string[] = [REQUIRED_DATA.VELOCITY, REQUIRED_DATA.CYCLE_TIME, REQUIRED_DATA.CLASSIFICATION]; +export const BOARD_METRICS: string[] = [ + REQUIRED_DATA.VELOCITY, + REQUIRED_DATA.CYCLE_TIME, + REQUIRED_DATA.CLASSIFICATION, + REQUIRED_DATA.REWORK_TIMES, +]; export enum CONFIG_TITLE { BOARD = 'Board', @@ -152,9 +158,9 @@ export const REWORK_TIME_LIST = [ METRICS_CONSTANTS.analysisValue, METRICS_CONSTANTS.inDevValue, METRICS_CONSTANTS.blockValue, + METRICS_CONSTANTS.reviewValue, METRICS_CONSTANTS.waitingValue, METRICS_CONSTANTS.testingValue, - METRICS_CONSTANTS.reviewValue, ]; export const TOKEN_HELPER_TEXT = { @@ -284,8 +290,8 @@ export const AXIOS_NETWORK_ERROR_CODES = [AxiosError.ECONNABORTED, AxiosError.ET export const NO_PIPELINE_STEP_ERROR = 'No steps for this pipeline!'; -export enum HEARTBEAT_EXCEPTION_CODE { - TIMEOUT = 'HB_TIMEOUT', +export enum AXIOS_REQUEST_ERROR_CODE { + TIMEOUT = 'NETWORK_TIMEOUT', } export const BOARD_CONFIG_INFO_TITLE = { diff --git a/frontend/src/containers/ConfigStep/Board/index.tsx b/frontend/src/containers/ConfigStep/Board/index.tsx index f1dca9761a..ff3406aefc 100644 --- a/frontend/src/containers/ConfigStep/Board/index.tsx +++ b/frontend/src/containers/ConfigStep/Board/index.tsx @@ -1,25 +1,35 @@ import { ConfigSectionContainer, - StyledButtonGroup, StyledForm, StyledTextField, StyledTypeSelections, } from '@src/components/Common/ConfigForms'; import { updateShouldGetBoardConfig } from '@src/context/Metrics/metricsSlice'; import { KEYS, useVerifyBoardEffect } from '@src/hooks/useVerifyBoardEffect'; -import { ResetButton, VerifyButton } from '@src/components/Common/Buttons'; +import { ConfigButtonGrop } from '@src/containers/ConfigStep/ConfigButton'; import { useAppSelector, useAppDispatch } from '@src/hooks/useAppDispatch'; import { InputLabel, ListItemText, MenuItem, Select } from '@mui/material'; import { ConfigSelectionTitle } from '@src/containers/MetricsStep/style'; import { selectIsBoardVerified } from '@src/context/config/configSlice'; +import { TimeoutAlert } from '@src/containers/ConfigStep/TimeoutAlert'; +import { StyledAlterWrapper } from '@src/containers/ConfigStep/style'; import { BOARD_TYPES, CONFIG_TITLE } from '@src/constants/resources'; import { Loading } from '@src/components/Loading'; import { FormEvent, useMemo } from 'react'; - export const Board = () => { const dispatch = useAppDispatch(); const isVerified = useAppSelector(selectIsBoardVerified); - const { verifyJira, isLoading, fields, updateField, validateField, resetFields } = useVerifyBoardEffect(); + const { + verifyJira, + isLoading, + fields, + updateField, + isShowAlert, + setIsShowAlert, + validateField, + resetFields, + isVerifyTimeOut, + } = useVerifyBoardEffect(); const onSubmit = async (e: FormEvent) => { e.preventDefault(); @@ -36,6 +46,14 @@ export const Board = () => { {isLoading && } {CONFIG_TITLE.BOARD} + + + {fields.map(({ key, value, validatedError, verifiedError, col }, index) => !index ? ( @@ -66,16 +84,12 @@ export const Board = () => { /> ), )} - - {isVerified && !isLoading ? ( - Verified - ) : ( - - Verify - - )} - {isVerified && !isLoading && Reset} - + ); diff --git a/frontend/src/containers/ConfigStep/ConfigButton/index.tsx b/frontend/src/containers/ConfigStep/ConfigButton/index.tsx new file mode 100644 index 0000000000..d003c4c1e2 --- /dev/null +++ b/frontend/src/containers/ConfigStep/ConfigButton/index.tsx @@ -0,0 +1,35 @@ +import { ResetButton, ReverifyButton, VerifyButton } from '@src/components/Common/Buttons'; +import { StyledButtonGroup } from '@src/components/Common/ConfigForms'; + +interface PropsInterface { + isVerified: boolean; + isLoading: boolean; + isVerifyTimeOut: boolean; + isDisableVerifyButton: boolean; +} +export const ConfigButtonGrop = ({ isVerified, isLoading, isVerifyTimeOut, isDisableVerifyButton }: PropsInterface) => { + const renderVerifyButton = ( + isVerified: boolean, + isLoading: boolean, + isVerifyTimeOut: boolean, + isDisableVerifyButton: boolean, + ) => { + if (isVerified && !isLoading) { + return Verified; + } else if (isVerifyTimeOut) { + return Reverify; + } else { + return ( + + Verify + + ); + } + }; + return ( + + {renderVerifyButton(isVerified, isLoading, isVerifyTimeOut, isDisableVerifyButton)} + {(isVerified || isVerifyTimeOut) && !isLoading && Reset} + + ); +}; diff --git a/frontend/src/containers/ConfigStep/PipelineTool/index.tsx b/frontend/src/containers/ConfigStep/PipelineTool/index.tsx index ad865c9827..1c271b1130 100644 --- a/frontend/src/containers/ConfigStep/PipelineTool/index.tsx +++ b/frontend/src/containers/ConfigStep/PipelineTool/index.tsx @@ -1,24 +1,25 @@ -import { - ConfigSectionContainer, - StyledButtonGroup, - StyledForm, - StyledTextField, - StyledTypeSelections, -} from '@src/components/Common/ConfigForms'; import { isPipelineToolVerified, selectPipelineTool, updatePipelineTool, updatePipelineToolVerifyState, } from '@src/context/config/configSlice'; +import { + ConfigSectionContainer, + StyledForm, + StyledTextField, + StyledTypeSelections, +} from '@src/components/Common/ConfigForms'; import { CONFIG_TITLE, PIPELINE_TOOL_TYPES, TOKEN_HELPER_TEXT } from '@src/constants/resources'; import { useVerifyPipelineToolEffect } from '@src/hooks/useVerifyPipelineToolEffect'; import { updateShouldGetPipelineConfig } from '@src/context/Metrics/metricsSlice'; -import { ResetButton, VerifyButton } from '@src/components/Common/Buttons'; +import { ConfigButtonGrop } from '@src/containers/ConfigStep/ConfigButton'; import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch'; import { DEFAULT_HELPER_TEXT, EMPTY_STRING } from '@src/constants/commons'; import { InputLabel, ListItemText, MenuItem, Select } from '@mui/material'; import { ConfigSelectionTitle } from '@src/containers/MetricsStep/style'; +import { TimeoutAlert } from '@src/containers/ConfigStep/TimeoutAlert'; +import { StyledAlterWrapper } from '@src/containers/ConfigStep/style'; import { findCaseInsensitiveType } from '@src/utils/util'; import { FormEvent, useMemo, useState } from 'react'; import { Loading } from '@src/components/Loading'; @@ -43,7 +44,15 @@ export const PipelineTool = () => { const dispatch = useAppDispatch(); const pipelineToolFields = useAppSelector(selectPipelineTool); const isVerified = useAppSelector(isPipelineToolVerified); - const { verifyPipelineTool, isLoading, verifiedError, clearVerifiedError } = useVerifyPipelineToolEffect(); + const { + verifyPipelineTool, + isLoading, + verifiedError, + clearVerifiedError, + isVerifyTimeOut, + isShowAlert, + setIsShowAlert, + } = useVerifyPipelineToolEffect(); const type = findCaseInsensitiveType(Object.values(PIPELINE_TOOL_TYPES), pipelineToolFields.type); const [fields, setFields] = useState([ { @@ -121,6 +130,14 @@ export const PipelineTool = () => { {isLoading && } {CONFIG_TITLE.PIPELINE_TOOL} + + + Pipeline Tool @@ -151,16 +168,12 @@ export const PipelineTool = () => { error={!!fields[FIELD_KEY.TOKEN].validatedError || !!verifiedError} helperText={fields[FIELD_KEY.TOKEN].validatedError || verifiedError} /> - - {isVerified && !isLoading ? ( - Verified - ) : ( - - Verify - - )} - {isVerified && !isLoading && Reset} - + ); diff --git a/frontend/src/containers/ConfigStep/SourceControl/index.tsx b/frontend/src/containers/ConfigStep/SourceControl/index.tsx index 0bd99decc5..5279c97bfc 100644 --- a/frontend/src/containers/ConfigStep/SourceControl/index.tsx +++ b/frontend/src/containers/ConfigStep/SourceControl/index.tsx @@ -1,23 +1,24 @@ -import { - ConfigSectionContainer, - StyledButtonGroup, - StyledForm, - StyledTextField, - StyledTypeSelections, -} from '@src/components/Common/ConfigForms'; import { isSourceControlVerified, selectSourceControl, updateSourceControl, updateSourceControlVerifyState, } from '@src/context/config/configSlice'; +import { + ConfigSectionContainer, + StyledForm, + StyledTextField, + StyledTypeSelections, +} from '@src/components/Common/ConfigForms'; import { initDeploymentFrequencySettings, updateShouldGetPipelineConfig } from '@src/context/Metrics/metricsSlice'; import { useVerifySourceControlTokenEffect } from '@src/hooks/useVerifySourceControlTokenEffect'; import { CONFIG_TITLE, SOURCE_CONTROL_TYPES, TOKEN_HELPER_TEXT } from '@src/constants/resources'; -import { ResetButton, VerifyButton } from '@src/components/Common/Buttons'; +import { ConfigButtonGrop } from '@src/containers/ConfigStep/ConfigButton'; import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch'; import { InputLabel, ListItemText, MenuItem, Select } from '@mui/material'; import { ConfigSelectionTitle } from '@src/containers/MetricsStep/style'; +import { TimeoutAlert } from '@src/containers/ConfigStep/TimeoutAlert'; +import { StyledAlterWrapper } from '@src/containers/ConfigStep/style'; import { DEFAULT_HELPER_TEXT } from '@src/constants/commons'; import { findCaseInsensitiveType } from '@src/utils/util'; import { FormEvent, useMemo, useState } from 'react'; @@ -43,7 +44,8 @@ export const SourceControl = () => { const dispatch = useAppDispatch(); const sourceControlFields = useAppSelector(selectSourceControl); const isVerified = useAppSelector(isSourceControlVerified); - const { verifyToken, isLoading, verifiedError, clearVerifiedError } = useVerifySourceControlTokenEffect(); + const { verifyToken, isLoading, verifiedError, clearVerifiedError, isVerifyTimeOut, isShowAlert, setIsShowAlert } = + useVerifySourceControlTokenEffect(); const type = findCaseInsensitiveType(Object.values(SOURCE_CONTROL_TYPES), sourceControlFields.type); const [fields, setFields] = useState([ { @@ -113,6 +115,14 @@ export const SourceControl = () => { {isLoading && } {CONFIG_TITLE.SOURCE_CONTROL} + + + Source Control @@ -138,18 +148,12 @@ export const SourceControl = () => { error={!!fields[FIELD_KEY.TOKEN].validatedError || !!verifiedError} helperText={fields[FIELD_KEY.TOKEN].validatedError || verifiedError} /> - - {isVerified && !isLoading ? ( - <> - Verified - Reset - - ) : ( - - Verify - - )} - + ); diff --git a/frontend/src/containers/ConfigStep/TimeoutAlert/index.tsx b/frontend/src/containers/ConfigStep/TimeoutAlert/index.tsx new file mode 100644 index 0000000000..5ec2e1125f --- /dev/null +++ b/frontend/src/containers/ConfigStep/TimeoutAlert/index.tsx @@ -0,0 +1,29 @@ +import { StyledAlert, StyledModuleType } from '@src/containers/ConfigStep/TimeoutAlert/style'; +import EllipsisText from '@src/components/Common/EllipsisText'; +import CancelIcon from '@mui/icons-material/Cancel'; + +interface PropsInterface { + isVerifyTimeOut: boolean; + isShowAlert: boolean; + setIsShowAlert: (value: boolean) => void; + moduleType: string; +} +export const TimeoutAlert = ({ isVerifyTimeOut, isShowAlert, setIsShowAlert, moduleType }: PropsInterface) => { + return ( + <> + {isVerifyTimeOut && isShowAlert && ( + } + severity='error' + onClose={() => { + setIsShowAlert(false); + }} + > + + Submission timeout on {moduleType} , please reverify! + + + )} + + ); +}; diff --git a/frontend/src/containers/ConfigStep/TimeoutAlert/style.tsx b/frontend/src/containers/ConfigStep/TimeoutAlert/style.tsx new file mode 100644 index 0000000000..4c3a397c2c --- /dev/null +++ b/frontend/src/containers/ConfigStep/TimeoutAlert/style.tsx @@ -0,0 +1,23 @@ +import { styled } from '@mui/material/styles'; +import { Alert } from '@mui/material'; +import { theme } from '@src/theme'; + +export const StyledAlert = styled(Alert)({ + '&.MuiPaper-root': { + flex: 1, + border: `0.07rem solid ${theme.main.alert.error.borderColor}`, + backgroundColor: theme.main.alert.error.backgroundColor, + borderRadius: '0.5rem', + padding: '0 1rem', + maxWidth: '50%', + alignItems: 'center', + justifyContent: 'center', + '& .MuiAlert-icon': { + marginTop: '0.125rem', + }, + }, +}); +export const StyledModuleType = styled('span')({ + color: theme.main.secondColor, + fontWeight: 600, +}); diff --git a/frontend/src/containers/ConfigStep/style.tsx b/frontend/src/containers/ConfigStep/style.tsx index c4685c3f91..9e72053ead 100644 --- a/frontend/src/containers/ConfigStep/style.tsx +++ b/frontend/src/containers/ConfigStep/style.tsx @@ -3,3 +3,12 @@ import { styled } from '@mui/material/styles'; export const ConfigStepWrapper = styled('div')({ width: '100%', }); +export const StyledAlterWrapper = styled('div')({ + display: 'flex', + width: '100%', + alignItems: 'center', + justifyContent: 'center', + height: '2.5rem', + position: 'absolute', + top: '1rem', +}); diff --git a/frontend/src/containers/MetricsStep/Advance/style.tsx b/frontend/src/containers/MetricsStep/Advance/style.tsx index a0507e9c0f..7f6c383fcd 100644 --- a/frontend/src/containers/MetricsStep/Advance/style.tsx +++ b/frontend/src/containers/MetricsStep/Advance/style.tsx @@ -5,6 +5,7 @@ export const AdvancedTitleContainer = styled.div({ fontSize: '1rem', lineHeight: '1.25rem', fontWeight: '600', + marginRight: '1rem', }); export const AdvancedContainer = styled('div')({ diff --git a/frontend/src/containers/MetricsStep/CycleTime/index.tsx b/frontend/src/containers/MetricsStep/CycleTime/index.tsx index ef30dad784..56cb1fa9c8 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/index.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/index.tsx @@ -15,7 +15,7 @@ export const CycleTime = () => { return (
- + diff --git a/frontend/src/containers/MetricsStep/CycleTime/style.tsx b/frontend/src/containers/MetricsStep/CycleTime/style.tsx index 0dcc821331..7aabb8ab63 100644 --- a/frontend/src/containers/MetricsStep/CycleTime/style.tsx +++ b/frontend/src/containers/MetricsStep/CycleTime/style.tsx @@ -22,7 +22,6 @@ export const TitleAndTooltipContainer = styled('div')({ display: 'flex', flexDirection: 'row', alignItems: 'center', - gap: '1rem', }); export const TooltipContainer = styled('div')({ diff --git a/frontend/src/containers/MetricsStep/DeploymentFrequencySettings/BranchSelection/BranchChip/index.tsx b/frontend/src/containers/MetricsStep/DeploymentFrequencySettings/BranchSelection/BranchChip/index.tsx index a10377272b..53f9b92cc9 100644 --- a/frontend/src/containers/MetricsStep/DeploymentFrequencySettings/BranchSelection/BranchChip/index.tsx +++ b/frontend/src/containers/MetricsStep/DeploymentFrequencySettings/BranchSelection/BranchChip/index.tsx @@ -1,4 +1,4 @@ -import { HEARTBEAT_EXCEPTION_CODE, SOURCE_CONTROL_TYPES } from '@src/constants/resources'; +import { AXIOS_REQUEST_ERROR_CODE, SOURCE_CONTROL_TYPES } from '@src/constants/resources'; import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient'; import { SourceControlInfoRequestDTO } from '@src/clients/sourceControl/dto/request'; import { selectSourceControl } from '@src/context/config/configSlice'; @@ -55,7 +55,7 @@ const BranchChip = ({ value, needVerify, error, updateBranchMeta, repository, er label={value} loading={needVerify} error={error} - showRetry={errorDetail === HEARTBEAT_EXCEPTION_CODE.TIMEOUT} + showRetry={errorDetail === AXIOS_REQUEST_ERROR_CODE.TIMEOUT} onRetry={handleRetry} /> ); diff --git a/frontend/src/containers/MetricsStep/ReworkSettings/index.tsx b/frontend/src/containers/MetricsStep/ReworkSettings/index.tsx index 6528de84ad..08fa57a324 100644 --- a/frontend/src/containers/MetricsStep/ReworkSettings/index.tsx +++ b/frontend/src/containers/MetricsStep/ReworkSettings/index.tsx @@ -20,7 +20,7 @@ function ReworkSettings() { const MultiOptions = reworkTimesSettings.rework2State ? [ - ...REWORK_TIME_LIST.slice(REWORK_TIME_LIST.indexOf(reworkTimesSettings.rework2State as string) + 1), + ...REWORK_TIME_LIST.slice(REWORK_TIME_LIST.indexOf(reworkTimesSettings.rework2State) + 1), METRICS_CONSTANTS.doneValue, ] : []; diff --git a/frontend/src/containers/MetricsStep/index.tsx b/frontend/src/containers/MetricsStep/index.tsx index 6229b4b20a..5f6fc9db07 100644 --- a/frontend/src/containers/MetricsStep/index.tsx +++ b/frontend/src/containers/MetricsStep/index.tsx @@ -19,7 +19,7 @@ import { MetricSelectionWrapper, MetricsSelectionTitle, } from '@src/containers/MetricsStep/style'; -import { CYCLE_TIME_SETTINGS_TYPES, DONE, REQUIRED_DATA, HEARTBEAT_EXCEPTION_CODE } from '@src/constants/resources'; +import { CYCLE_TIME_SETTINGS_TYPES, DONE, REQUIRED_DATA, AXIOS_REQUEST_ERROR_CODE } from '@src/constants/resources'; import { DeploymentFrequencySettings } from '@src/containers/MetricsStep/DeploymentFrequencySettings'; import { StyledRetryButton, StyledErrorMessage } from '@src/containers/MetricsStep/style'; import { closeAllNotifications } from '@src/context/notification/NotificationSlice'; @@ -123,7 +123,7 @@ const MetricsStep = () => { diff --git a/frontend/src/containers/ReportStep/BoardMetrics/index.tsx b/frontend/src/containers/ReportStep/BoardMetrics/index.tsx index 40c650f394..2ddcfb03e2 100644 --- a/frontend/src/containers/ReportStep/BoardMetrics/index.tsx +++ b/frontend/src/containers/ReportStep/BoardMetrics/index.tsx @@ -22,7 +22,7 @@ import { getJiraBoardToken, getRealDoneStatus, } from '@src/utils/util'; -import { BoardReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request'; +import { IBasicReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request'; import { ReportTitle } from '@src/components/Common/ReportGrid/ReportTitle'; import { selectMetricsContent } from '@src/context/Metrics/metricsSlice'; import { ReportResponseDTO } from '@src/clients/report/dto/response'; @@ -64,7 +64,7 @@ const BoardMetrics = ({ targetFields, doneColumn, assigneeFilter, - importedData: { importedAdvancedSettings }, + importedData: { importedAdvancedSettings, reworkTimesSettings }, } = useAppSelector(selectMetricsContent); const { metrics, calendarType } = configData.basic; @@ -76,7 +76,7 @@ const BoardMetrics = ({ .map((metric) => BOARD_METRICS_MAPPING[metric]) .every((metric) => boardReport?.[metric] ?? false); - const getBoardReportRequestBody = (): BoardReportRequestDTO => { + const getBoardReportRequestBody = (): IBasicReportRequestDTO => { return { metrics: boardMetrics, startTime: dayjs(startDate).valueOf().toString(), @@ -94,6 +94,10 @@ const BoardMetrics = ({ assigneeFilter, targetFields: formatDuplicatedNameWithSuffix(targetFields), doneColumn: getRealDoneStatus(cycleTimeSettings, cycleTimeSettingsType, doneColumn), + reworkTimesSetting: { + reworkState: reworkTimesSettings.rework2State, + excludedStates: reworkTimesSettings.excludeStates, + }, overrideFields: [ { name: 'Story Points', diff --git a/frontend/src/hooks/useGenerateReportEffect.ts b/frontend/src/hooks/useGenerateReportEffect.ts index 54e94807d3..1b0543b777 100644 --- a/frontend/src/hooks/useGenerateReportEffect.ts +++ b/frontend/src/hooks/useGenerateReportEffect.ts @@ -1,4 +1,4 @@ -import { BoardReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request'; +import { IBasicReportRequestDTO, ReportRequestDTO } from '@src/clients/report/dto/request'; import { exportValidityTimeMapper } from '@src/hooks/reportMapper/exportValidityTime'; import { ReportResponseDTO } from '@src/clients/report/dto/response'; import { TimeoutException } from '@src/exceptions/TimeoutException'; @@ -8,7 +8,7 @@ import { METRIC_TYPES } from '@src/constants/commons'; import { useRef, useState } from 'react'; export interface useGenerateReportEffectInterface { - startToRequestBoardData: (boardParams: BoardReportRequestDTO) => void; + startToRequestBoardData: (boardParams: IBasicReportRequestDTO) => void; startToRequestDoraData: (doraParams: ReportRequestDTO) => void; stopPollingReports: () => void; timeout4Board: string; diff --git a/frontend/src/hooks/useGetBoardInfo.ts b/frontend/src/hooks/useGetBoardInfo.ts index b947bf0e41..9990f245c2 100644 --- a/frontend/src/hooks/useGetBoardInfo.ts +++ b/frontend/src/hooks/useGetBoardInfo.ts @@ -1,7 +1,7 @@ import { BOARD_CONFIG_INFO_ERROR, BOARD_CONFIG_INFO_TITLE } from '@src/constants/resources'; import { boardInfoClient } from '@src/clients/board/BoardInfoClient'; import { BoardInfoRequestDTO } from '@src/clients/board/dto/request'; -import { HEARTBEAT_EXCEPTION_CODE } from '@src/constants/resources'; +import { AXIOS_REQUEST_ERROR_CODE } from '@src/constants/resources'; import { AxiosResponse, HttpStatusCode } from 'axios'; import { ReactNode, useState } from 'react'; import get from 'lodash/get'; @@ -42,10 +42,10 @@ const codeMapping = (code: string | number) => { message: BOARD_CONFIG_INFO_ERROR.NOT_FOUND, code: HttpStatusCode.NotFound, }, - [HEARTBEAT_EXCEPTION_CODE.TIMEOUT]: { + [AXIOS_REQUEST_ERROR_CODE.TIMEOUT]: { title: BOARD_CONFIG_INFO_TITLE.EMPTY, message: BOARD_CONFIG_INFO_ERROR.RETRY, - code: HEARTBEAT_EXCEPTION_CODE.TIMEOUT, + code: AXIOS_REQUEST_ERROR_CODE.TIMEOUT, }, }; return get(codes, code); diff --git a/frontend/src/hooks/useVerifyBoardEffect.ts b/frontend/src/hooks/useVerifyBoardEffect.ts index d4214435e0..cc0afe0a95 100644 --- a/frontend/src/hooks/useVerifyBoardEffect.ts +++ b/frontend/src/hooks/useVerifyBoardEffect.ts @@ -1,5 +1,5 @@ +import { BOARD_TYPES, AXIOS_REQUEST_ERROR_CODE, MESSAGE, UNKNOWN_ERROR_TITLE } from '@src/constants/resources'; import { selectBoard, updateBoard, updateBoardVerifyState } from '@src/context/config/configSlice'; -import { BOARD_TYPES, MESSAGE, UNKNOWN_ERROR_TITLE } from '@src/constants/resources'; import { updateTreatFlagCardAsBlock } from '@src/context/Metrics/metricsSlice'; import { findCaseInsensitiveType, getJiraBoardToken } from '@src/utils/util'; import { useAppDispatch, useAppSelector } from '@src/hooks/useAppDispatch'; @@ -22,12 +22,15 @@ export interface Field { } export interface useVerifyBoardStateInterface { + isVerifyTimeOut: boolean; verifyJira: () => Promise; isLoading: boolean; fields: Field[]; updateField: (key: string, value: string) => void; validateField: (key: string) => void; resetFields: () => void; + setIsShowAlert: (value: boolean) => void; + isShowAlert: boolean; } const ERROR_INFO = { @@ -60,6 +63,8 @@ const getValidatedError = (key: string, value: string, validateRule?: (value: st export const useVerifyBoardEffect = (): useVerifyBoardStateInterface => { const [isLoading, setIsLoading] = useState(false); + const [isVerifyTimeOut, setIsVerifyTimeOut] = useState(false); + const [isShowAlert, setIsShowAlert] = useState(false); const boardFields = useAppSelector(selectBoard); const dispatch = useAppDispatch(); const type = findCaseInsensitiveType(Object.values(BOARD_TYPES), boardFields.type); @@ -185,6 +190,8 @@ export const useVerifyBoardEffect = (): useVerifyBoardStateInterface => { token: getJiraBoardToken(boardInfo.token, boardInfo.email), }); if (res?.response) { + setIsShowAlert(false); + setIsVerifyTimeOut(false); dispatch(updateBoardVerifyState(true)); dispatch(updateBoard({ ...boardInfo, projectKey: res.response.projectKey })); } @@ -200,6 +207,9 @@ export const useVerifyBoardEffect = (): useVerifyBoardStateInterface => { setVerifiedError([KEYS.SITE], [MESSAGE.VERIFY_SITE_FAILED_ERROR]); } else if (code === HttpStatusCode.NotFound && description === ERROR_INFO.BOARD_NOT_FOUND) { setVerifiedError([KEYS.BOARD_ID], [MESSAGE.VERIFY_BOARD_FAILED_ERROR]); + } else if (code === AXIOS_REQUEST_ERROR_CODE.TIMEOUT) { + setIsVerifyTimeOut(true); + setIsShowAlert(true); } else { setVerifiedError([KEYS.TOKEN], [UNKNOWN_ERROR_TITLE]); } @@ -215,5 +225,8 @@ export const useVerifyBoardEffect = (): useVerifyBoardStateInterface => { updateField, validateField, resetFields, + isVerifyTimeOut, + isShowAlert, + setIsShowAlert, }; }; diff --git a/frontend/src/hooks/useVerifyPipelineToolEffect.ts b/frontend/src/hooks/useVerifyPipelineToolEffect.ts index 0aa9f2120d..d4d2c64f36 100644 --- a/frontend/src/hooks/useVerifyPipelineToolEffect.ts +++ b/frontend/src/hooks/useVerifyPipelineToolEffect.ts @@ -2,6 +2,7 @@ import { initDeploymentFrequencySettings } from '@src/context/Metrics/metricsSli import { updatePipelineToolVerifyState } from '@src/context/config/configSlice'; import { pipelineToolClient } from '@src/clients/pipeline/PipelineToolClient'; import { IPipelineVerifyRequestDTO } from '@src/clients/pipeline/dto/request'; +import { AXIOS_REQUEST_ERROR_CODE } from '@src/constants/resources'; import { useAppDispatch } from '@src/hooks'; import { HttpStatusCode } from 'axios'; import { useState } from 'react'; @@ -10,13 +11,17 @@ export const useVerifyPipelineToolEffect = () => { const [isLoading, setIsLoading] = useState(false); const [verifiedError, setVerifiedError] = useState(''); const dispatch = useAppDispatch(); - + const [isVerifyTimeOut, setIsVerifyTimeOut] = useState(false); + const [isShowAlert, setIsShowAlert] = useState(false); const verifyPipelineTool = async (params: IPipelineVerifyRequestDTO): Promise => { setIsLoading(true); - const response = await pipelineToolClient.verify(params); + const response = await pipelineToolClient.verify(params, setIsVerifyTimeOut, setIsShowAlert); if (response.code === HttpStatusCode.NoContent) { dispatch(updatePipelineToolVerifyState(true)); dispatch(initDeploymentFrequencySettings()); + } else if (response.code === AXIOS_REQUEST_ERROR_CODE.TIMEOUT) { + setIsVerifyTimeOut(true); + setIsShowAlert(true); } else { setVerifiedError(response.errorTitle); } @@ -32,5 +37,8 @@ export const useVerifyPipelineToolEffect = () => { isLoading, verifiedError, clearVerifiedError, + isVerifyTimeOut, + isShowAlert, + setIsShowAlert, }; }; diff --git a/frontend/src/hooks/useVerifySourceControlTokenEffect.ts b/frontend/src/hooks/useVerifySourceControlTokenEffect.ts index 332db3b217..04840c652d 100644 --- a/frontend/src/hooks/useVerifySourceControlTokenEffect.ts +++ b/frontend/src/hooks/useVerifySourceControlTokenEffect.ts @@ -1,6 +1,7 @@ import { SourceControlVerifyRequestDTO } from '@src/clients/sourceControl/dto/request'; import { sourceControlClient } from '@src/clients/sourceControl/SourceControlClient'; import { updateSourceControlVerifyState } from '@src/context/config/configSlice'; +import { AXIOS_REQUEST_ERROR_CODE } from '@src/constants/resources'; import { useAppDispatch } from '@src/hooks/index'; import { useCallback, useState } from 'react'; import { HttpStatusCode } from 'axios'; @@ -9,12 +10,16 @@ export const useVerifySourceControlTokenEffect = () => { const dispatch = useAppDispatch(); const [isLoading, setIsLoading] = useState(false); const [verifiedError, setVerifiedError] = useState(); - + const [isVerifyTimeOut, setIsVerifyTimeOut] = useState(false); + const [isShowAlert, setIsShowAlert] = useState(false); const verifyToken = async (params: SourceControlVerifyRequestDTO) => { setIsLoading(true); - const response = await sourceControlClient.verifyToken(params); + const response = await sourceControlClient.verifyToken(params, setIsVerifyTimeOut, setIsShowAlert); if (response.code === HttpStatusCode.NoContent) { dispatch(updateSourceControlVerifyState(true)); + } else if (response.code === AXIOS_REQUEST_ERROR_CODE.TIMEOUT) { + setIsVerifyTimeOut(true); + setIsShowAlert(true); } else { dispatch(updateSourceControlVerifyState(false)); setVerifiedError(response.errorTitle); @@ -32,5 +37,8 @@ export const useVerifySourceControlTokenEffect = () => { isLoading, verifiedError, clearVerifiedError, + isVerifyTimeOut, + isShowAlert, + setIsShowAlert, }; };