From 72688e1e150046c22ebfc8a6ffc7954df9149b8c Mon Sep 17 00:00:00 2001 From: svariant Date: Tue, 27 Aug 2024 09:59:52 +0200 Subject: [PATCH 01/18] [PPANTT-99] feat: Add new columns to bundle list table --- src/locale/it.json | 2 + .../list/CommissionBundlesTableColumns.tsx | 61 +++++++++++++++++-- .../CommissionBundlesTableColumns.test.tsx | 59 ++++++++++++++---- src/utils/common-utils.ts | 10 +++ 4 files changed, 115 insertions(+), 17 deletions(-) diff --git a/src/locale/it.json b/src/locale/it.json index abe9abb14..b7578c0fa 100644 --- a/src/locale/it.json +++ b/src/locale/it.json @@ -1087,6 +1087,8 @@ "endDate": "Fine validità", "touchpoint": "Touchpoint", "paymentType": "Tipo di pagamento", + "payment": "Importo", + "commission": "Commissione", "state": "Stato" }, "states": { diff --git a/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx b/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx index c2bb9e998..dbe87929d 100644 --- a/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx +++ b/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx @@ -1,14 +1,19 @@ /* eslint-disable sonarjs/cognitive-complexity */ -import { Chip, FormControl, MenuItem, Select } from '@mui/material'; +import { Box, FormControl, MenuItem, Select } from '@mui/material'; import ChevronRightIcon from '@mui/icons-material/ChevronRight'; import { GridColDef, GridRenderCellParams } from '@mui/x-data-grid'; import { TFunction, useTranslation } from 'react-i18next'; import { generatePath } from 'react-router-dom'; +import { Euro } from '@mui/icons-material'; import GridLinkAction from '../../../components/Table/GridLinkAction'; import ROUTES from '../../../routes'; import { bundleDetailsActions } from '../../../redux/slices/bundleDetailsSlice'; import { useAppDispatch } from '../../../redux/hooks'; -import { dateDifferenceInDays, datesAreOnSameDay } from '../../../utils/common-utils'; +import { + dateDifferenceInDays, + datesAreOnSameDay, + formatCurrencyWithoutSymbol, +} from '../../../utils/common-utils'; import { renderCell, renderStatusChip, @@ -89,7 +94,7 @@ export function buildColumnDefs( renderHeader: showCustomHeader, renderCell: (params) => renderCell({ value: t(params.row.touchpoint) }), sortable: false, - flex: 4, + flex: 3, }, { field: 'paymentType', @@ -103,8 +108,46 @@ export function buildColumnDefs( renderHeader: showCustomHeader, renderCell: (params) => renderCell({ value: t(params.row.paymentType) }), sortable: false, + flex: 3, + }, + { + field: 'payment', + cellClassName: 'justifyContentNormal', + headerName: t('commissionBundlesPage.list.headerFields.payment'), + align: 'left', + headerAlign: 'left', + width: 145, + editable: false, + disableColumnMenu: true, + renderHeader: showCustomHeader, + renderCell: (params) => + renderCell({ + value: ( + + {`${formatCurrencyWithoutSymbol(params.row.minPaymentAmount) || ' '} - ${formatCurrencyWithoutSymbol(params.row.maxPaymentAmount) || ' '}`} + + ), + }), + sortable: false, flex: 4, }, + { + field: 'commission', + cellClassName: 'justifyContentNormal', + headerName: t('commissionBundlesPage.list.headerFields.commission'), + align: 'left', + headerAlign: 'left', + width: 145, + editable: false, + disableColumnMenu: true, + renderHeader: showCustomHeader, + renderCell: (params) => + renderCell({ + value: {formatCurrencyWithoutSymbol(params.row.paymentAmount) || '-'}, + }), + sortable: false, + flex: 3, + }, { field: 'state', cellClassName: 'justifyContentNormal', @@ -229,7 +272,10 @@ const getCIStatusChip = ( bundleType: TypeEnum | undefined, bundleStatus: CiBundleStatusEnum | undefined ) => { - if (bundleStatus === CiBundleStatusEnum.AVAILABLE || bundleStatus === CiBundleStatusEnum.AVAILABLE_EXPIRED) { + if ( + bundleStatus === CiBundleStatusEnum.AVAILABLE || + bundleStatus === CiBundleStatusEnum.AVAILABLE_EXPIRED + ) { return renderStatusChip({ chipColor: 'default', chipLabel: t('commissionBundlesPage.list.states.toBeActivated'), @@ -301,3 +347,10 @@ export const SelectStatusFilter = ({ ); }; + +const EuroCell = ({ children, small }: { children: React.ReactNode; small?:boolean }) => ( + + {children} + + +); diff --git a/src/pages/commisionalBundles/list/__tests__/CommissionBundlesTableColumns.test.tsx b/src/pages/commisionalBundles/list/__tests__/CommissionBundlesTableColumns.test.tsx index c92e118f2..1899ac7a7 100644 --- a/src/pages/commisionalBundles/list/__tests__/CommissionBundlesTableColumns.test.tsx +++ b/src/pages/commisionalBundles/list/__tests__/CommissionBundlesTableColumns.test.tsx @@ -13,14 +13,12 @@ import { mockedCommissionBundleCiDetailPrivate, mockedCommissionBundleCiDetailPublic, mockedCommissionBundlePspDetailGlobal, - mockedCommissionBundlePspDetailPrivate, } from '../../../../services/__mocks__/bundleService'; import { store } from '../../../../redux/store'; import { Provider } from 'react-redux'; import { Router } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; import add from 'date-fns/add'; -import { showCustomHeader } from '../../../../components/Table/TableUtils'; import { BundleResource, SubscriptionStateType } from '../../../../model/CommissionBundle'; import { CiBundleStatusEnum } from '../../../../api/generated/portal/CIBundleResource'; @@ -100,6 +98,10 @@ const mockTFunction = (key: string) => { return 'Payment Type'; case 'commissionBundlesPage.list.headerFields.amountRange': return 'Amount Range'; + case 'commissionBundlesPage.list.headerFields.commission': + return 'Commission'; + case 'commissionBundlesPage.list.headerFields.payment': + return 'Payment Range'; default: return ''; } @@ -171,7 +173,7 @@ describe(' for PSPs', () => { renderHeader: expect.any(Function), renderCell: expect.any(Function), sortable: false, - flex: 4, + flex: 3, }, { field: 'paymentType', @@ -185,8 +187,36 @@ describe(' for PSPs', () => { renderHeader: expect.any(Function), renderCell: expect.any(Function), sortable: false, + flex: 3, + }, + { + field: 'payment', + cellClassName: 'justifyContentNormal', + headerName: 'Payment Range', + align: 'left', + headerAlign: 'left', + width: 145, + editable: false, + disableColumnMenu: true, + renderHeader: expect.any(Function), + renderCell: expect.any(Function), + sortable: false, flex: 4, }, + { + field: 'commission', + cellClassName: 'justifyContentNormal', + headerName: 'Commission', + align: 'left', + headerAlign: 'left', + width: 145, + editable: false, + disableColumnMenu: true, + renderHeader: expect.any(Function), + renderCell: expect.any(Function), + sortable: false, + flex: 3, + }, { field: 'state', cellClassName: 'justifyContentNormal', @@ -586,7 +616,7 @@ describe(' for ECs', () => { let bundle = { ...mockedCommissionBundleCiDetailPrivate }; bundle.ciBundleStatus = CiBundleStatusEnum.ENABLED; bundle.validityDateFrom = new Date('01/01/2020'); - bundle.validityDateTo = add(new Date(), {days: 6}); + bundle.validityDateTo = add(new Date(), { days: 6 }); render( @@ -620,14 +650,17 @@ describe(' for ECs', () => { expect(screen.queryByTestId('primary-state-chip')).not.toBeInTheDocument(); expect(screen.queryByTestId('default-state-chip')).not.toBeInTheDocument(); }); - test("Bundle state filter", () => { - render( - - - - - - ); - }) + test('Bundle state filter', () => { + render( + + + + + + ); + }); }); }); diff --git a/src/utils/common-utils.ts b/src/utils/common-utils.ts index 575865d6a..6a0afe6eb 100644 --- a/src/utils/common-utils.ts +++ b/src/utils/common-utils.ts @@ -55,6 +55,16 @@ export const formatCurrencyEur = (value: number | undefined): string => { }); }; +export const formatCurrencyWithoutSymbol = (value: number | undefined): string => { + if (value === undefined) { + return ''; + } + const newValue = value / 100; + return newValue.toLocaleString('it-IT', { + minimumFractionDigits: 2, + }); +}; + export const formatBooleanValueToYesOrNo = (value: boolean, t: TFunction<'translation'>): string => value ? t('general.yes') : t('general.no'); From 2dd912003936e1eadad19cb26f02487bc3bc029f Mon Sep 17 00:00:00 2001 From: svariant Date: Tue, 27 Aug 2024 10:07:25 +0200 Subject: [PATCH 02/18] [PPANTT-99] feat: Add new bundle list columns "channel" --- src/locale/it.json | 3 +- .../list/CommissionBundlesTableColumns.tsx | 32 +++++++++++++++---- .../CommissionBundlesTableColumns.test.tsx | 19 ++++++++--- 3 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/locale/it.json b/src/locale/it.json index b7578c0fa..171ff01a0 100644 --- a/src/locale/it.json +++ b/src/locale/it.json @@ -1086,9 +1086,10 @@ "startDate": "Inizio validità", "endDate": "Fine validità", "touchpoint": "Touchpoint", - "paymentType": "Tipo di pagamento", + "paymentType": "Pagamento con", "payment": "Importo", "commission": "Commissione", + "channel": "Canale", "state": "Stato" }, "states": { diff --git a/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx b/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx index dbe87929d..dc8b6f1a7 100644 --- a/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx +++ b/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx @@ -40,12 +40,24 @@ export function buildColumnDefs( headerName: t('commissionBundlesPage.list.headerFields.bundleName'), align: 'left', headerAlign: 'left', - minWidth: 400, editable: false, disableColumnMenu: true, renderHeader: showCustomHeader, renderCell: (params: any) => renderCell({ value: params.row.name, mainCell: true }), sortable: true, + flex: 5, + }, + { + field: 'channel', + cellClassName: 'justifyContentBold', + headerName: t('commissionBundlesPage.list.headerFields.channel'), + align: 'left', + headerAlign: 'left', + editable: false, + disableColumnMenu: true, + renderHeader: showCustomHeader, + renderCell: (params: any) => renderCell({ value: params.row.idChannel ?? '-' }), + sortable: true, flex: 4, }, ...(isPsp @@ -88,7 +100,6 @@ export function buildColumnDefs( headerName: t('commissionBundlesPage.list.headerFields.touchpoint'), align: 'left', headerAlign: 'left', - maxWidth: 220, editable: false, disableColumnMenu: true, renderHeader: showCustomHeader, @@ -102,7 +113,6 @@ export function buildColumnDefs( headerName: t('commissionBundlesPage.list.headerFields.paymentType'), align: 'left', headerAlign: 'left', - width: 145, editable: false, disableColumnMenu: true, renderHeader: showCustomHeader, @@ -143,7 +153,11 @@ export function buildColumnDefs( renderHeader: showCustomHeader, renderCell: (params) => renderCell({ - value: {formatCurrencyWithoutSymbol(params.row.paymentAmount) || '-'}, + value: ( + + {formatCurrencyWithoutSymbol(params.row.paymentAmount) || '-'} + + ), }), sortable: false, flex: 3, @@ -154,7 +168,6 @@ export function buildColumnDefs( headerName: t('commissionBundlesPage.list.headerFields.state'), align: 'left', headerAlign: 'left', - width: 200, editable: false, disableColumnMenu: true, renderHeader: (params) => @@ -348,8 +361,13 @@ export const SelectStatusFilter = ({ ); }; -const EuroCell = ({ children, small }: { children: React.ReactNode; small?:boolean }) => ( - +const EuroCell = ({ children, small }: { children: React.ReactNode; small?: boolean }) => ( + {children} diff --git a/src/pages/commisionalBundles/list/__tests__/CommissionBundlesTableColumns.test.tsx b/src/pages/commisionalBundles/list/__tests__/CommissionBundlesTableColumns.test.tsx index 1899ac7a7..ed8900cab 100644 --- a/src/pages/commisionalBundles/list/__tests__/CommissionBundlesTableColumns.test.tsx +++ b/src/pages/commisionalBundles/list/__tests__/CommissionBundlesTableColumns.test.tsx @@ -102,6 +102,8 @@ const mockTFunction = (key: string) => { return 'Commission'; case 'commissionBundlesPage.list.headerFields.payment': return 'Payment Range'; + case 'commissionBundlesPage.list.headerFields.channel': + return 'Channel'; default: return ''; } @@ -125,7 +127,19 @@ describe(' for PSPs', () => { headerName: 'Bundle Name', align: 'left', headerAlign: 'left', - minWidth: 400, + editable: false, + disableColumnMenu: true, + renderHeader: expect.any(Function), + renderCell: expect.any(Function), + sortable: true, + flex: 5, + }, + { + field: 'channel', + cellClassName: 'justifyContentBold', + headerName: 'Channel', + align: 'left', + headerAlign: 'left', editable: false, disableColumnMenu: true, renderHeader: expect.any(Function), @@ -167,7 +181,6 @@ describe(' for PSPs', () => { headerName: 'Touch Point', align: 'left', headerAlign: 'left', - maxWidth: 220, editable: false, disableColumnMenu: true, renderHeader: expect.any(Function), @@ -181,7 +194,6 @@ describe(' for PSPs', () => { headerName: 'Payment Type', align: 'left', headerAlign: 'left', - width: 145, editable: false, disableColumnMenu: true, renderHeader: expect.any(Function), @@ -223,7 +235,6 @@ describe(' for PSPs', () => { headerName: '', align: 'left', headerAlign: 'left', - width: 200, editable: false, disableColumnMenu: true, renderHeader: expect.any(Function), From 9f0a0f168c19a466acac6b3071f87709cf5f297e Mon Sep 17 00:00:00 2001 From: svariant Date: Wed, 28 Aug 2024 10:26:23 +0200 Subject: [PATCH 03/18] [PPANTT-99] feat: Delete validity columns for bundles' table --- .../list/CommissionBundlesTableColumns.tsx | 86 +++++++------------ .../CommissionBundlesTableColumns.test.tsx | 28 ------ 2 files changed, 30 insertions(+), 84 deletions(-) diff --git a/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx b/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx index dc8b6f1a7..646390541 100644 --- a/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx +++ b/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx @@ -47,49 +47,19 @@ export function buildColumnDefs( sortable: true, flex: 5, }, - { - field: 'channel', - cellClassName: 'justifyContentBold', - headerName: t('commissionBundlesPage.list.headerFields.channel'), - align: 'left', - headerAlign: 'left', - editable: false, - disableColumnMenu: true, - renderHeader: showCustomHeader, - renderCell: (params: any) => renderCell({ value: params.row.idChannel ?? '-' }), - sortable: true, - flex: 4, - }, ...(isPsp ? [ { - field: 'validityDateFrom', - cellClassName: 'justifyContentNormal', - headerName: t('commissionBundlesPage.list.headerFields.startDate'), - align: 'left', - headerAlign: 'left', - maxWidth: 150, - editable: false, - disableColumnMenu: true, - renderHeader: showCustomHeader, - renderCell: (params: any) => - renderCell({ value: params.row.validityDateFrom?.toLocaleDateString('en-GB') }), - sortable: false, - flex: 4, - }, - { - field: 'validityDateTo', - cellClassName: 'justifyContentNormal', - headerName: t('commissionBundlesPage.list.headerFields.endDate'), + field: 'channel', + cellClassName: 'justifyContentBold', + headerName: t('commissionBundlesPage.list.headerFields.channel'), align: 'left', headerAlign: 'left', - maxWidth: 150, editable: false, disableColumnMenu: true, renderHeader: showCustomHeader, - renderCell: (params: any) => - renderCell({ value: params.row.validityDateTo?.toLocaleDateString('en-GB') }), - sortable: false, + renderCell: (params: any) => renderCell({ value: params.row.idChannel ?? '-' }), + sortable: true, flex: 4, }, ] @@ -141,27 +111,31 @@ export function buildColumnDefs( sortable: false, flex: 4, }, - { - field: 'commission', - cellClassName: 'justifyContentNormal', - headerName: t('commissionBundlesPage.list.headerFields.commission'), - align: 'left', - headerAlign: 'left', - width: 145, - editable: false, - disableColumnMenu: true, - renderHeader: showCustomHeader, - renderCell: (params) => - renderCell({ - value: ( - - {formatCurrencyWithoutSymbol(params.row.paymentAmount) || '-'} - - ), - }), - sortable: false, - flex: 3, - }, + ...(isPsp + ? [ + { + field: 'commission', + cellClassName: 'justifyContentNormal', + headerName: t('commissionBundlesPage.list.headerFields.commission'), + align: 'left', + headerAlign: 'left', + width: 145, + editable: false, + disableColumnMenu: true, + renderHeader: showCustomHeader, + renderCell: (params: any) => + renderCell({ + value: ( + + {formatCurrencyWithoutSymbol(params.row.paymentAmount) || '-'} + + ), + }), + sortable: false, + flex: 3, + }, + ] + : []), { field: 'state', cellClassName: 'justifyContentNormal', diff --git a/src/pages/commisionalBundles/list/__tests__/CommissionBundlesTableColumns.test.tsx b/src/pages/commisionalBundles/list/__tests__/CommissionBundlesTableColumns.test.tsx index ed8900cab..137a40b36 100644 --- a/src/pages/commisionalBundles/list/__tests__/CommissionBundlesTableColumns.test.tsx +++ b/src/pages/commisionalBundles/list/__tests__/CommissionBundlesTableColumns.test.tsx @@ -147,34 +147,6 @@ describe(' for PSPs', () => { sortable: true, flex: 4, }, - { - field: 'validityDateFrom', - cellClassName: 'justifyContentNormal', - headerName: 'Activation Date', - align: 'left', - headerAlign: 'left', - maxWidth: 150, - editable: false, - disableColumnMenu: true, - renderHeader: expect.any(Function), - renderCell: expect.any(Function), - sortable: false, - flex: 4, - }, - { - field: 'validityDateTo', - cellClassName: 'justifyContentNormal', - headerName: 'Due Date', - align: 'left', - headerAlign: 'left', - maxWidth: 150, - editable: false, - disableColumnMenu: true, - renderHeader: expect.any(Function), - renderCell: expect.any(Function), - sortable: false, - flex: 4, - }, { field: 'touchpoint', cellClassName: 'justifyContentNormal', From a1b7a70f31111849a8586b8db14a470299e41596 Mon Sep 17 00:00:00 2001 From: svariant Date: Thu, 29 Aug 2024 17:05:12 +0200 Subject: [PATCH 04/18] [PPANTT-99] feat: Add new filters to bundles table search bar --- package.json | 2 +- src/api/BackofficeClient.ts | 23 +- src/locale/it.json | 27 +- .../CommissionBundlesPage.tsx | 128 +++++----- .../list/CommissionBundlesSearchBar.tsx | 239 ++++++++++++++++++ .../list/CommissionBundlesTable.tsx | 18 +- src/services/bundleService.ts | 21 ++ src/utils/common-utils.ts | 6 + 8 files changed, 393 insertions(+), 71 deletions(-) create mode 100644 src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx diff --git a/package.json b/package.json index c203b3af0..df647d229 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "clean:api-portal": "rimraf src/api/generated/portal && rimraf openApi/generated", "generate:api-portal": "wget https://raw.githubusercontent.com/pagopa/pagopa-selfcare-ms-backoffice-backend/main/openapi/openapi.json -O ./openApi/portal-api-docs.json && npm run generate:client", "generate:api-portal-next": "wget https://raw.githubusercontent.com/pagopa/pagopa-selfcare-ms-backoffice-backend/next/openapi/openapi.json -O ./openApi/portal-api-docs.json && npm run generate:client", - "generate:api-portal-pr": "wget https://raw.githubusercontent.com/pagopa/pagopa-selfcare-ms-backoffice-backend/PPANTT-93-new-api-key-aca/openapi/openapi.json -O ./openApi/portal-api-docs.json && npm run generate:client", + "generate:api-portal-pr": "wget https://raw.githubusercontent.com/pagopa/pagopa-selfcare-ms-backoffice-backend/PPANTT-99-new-filters-bundle-list/openapi/openapi.json -O ./openApi/portal-api-docs.json && npm run generate:client", "generate:api-portal-local": "npm run generate:client", "generate:client": "jq 'walk(if type == \"object\" and has(\"parameters\") then .parameters |= map(select(.name != \"X-Request-Id\")) else . end)' ./openApi/portal-api-docs.json > ./openApi/portal-api-docs.json.temp && mv ./openApi/portal-api-docs.json.temp ./openApi/portal-api-docs.json && yarn run clean:api-portal && mkdirp openApi/generated && gen-api-models --api-spec openApi/portal-api-docs.json --out-dir src/api/generated/portal --no-strict --request-types --response-decoders --client && node openApi/scripts/api-portal_fixPostGen.js" }, diff --git a/src/api/BackofficeClient.ts b/src/api/BackofficeClient.ts index 7e9a0f851..fd17be174 100644 --- a/src/api/BackofficeClient.ts +++ b/src/api/BackofficeClient.ts @@ -399,7 +399,7 @@ export const BackofficeApi = { const result = await backofficeClient.getChannels({ status: String(status), brokerCode, - ...(channelCode ? {channelCode} : {}), + ...(channelCode ? { channelCode } : {}), limit, page, }); @@ -941,12 +941,26 @@ export const BackofficeApi = { page, pspCode, bundleName, + maxPaymentAmountOrder, + paymentAmountMinRange, + paymentAmountMaxRange, + validBefore, + validAfter, + expireBefore, + expireAfter, }: { bundleType: string; pageLimit: number; page: number; pspCode: string; bundleName?: string; + maxPaymentAmountOrder?: string; + paymentAmountMinRange?: number; + paymentAmountMaxRange?: number; + validBefore?: string; + validAfter?: string; + expireBefore?: string; + expireAfter?: string; }): Promise => { const result = await backofficeClient.getBundlesByPSP({ 'bundle-type': [bundleType], @@ -954,6 +968,13 @@ export const BackofficeApi = { ...(bundleName ? { name: bundleName } : {}), page, 'psp-tax-code': pspCode, + ...(maxPaymentAmountOrder ? { maxPaymentAmountOrder } : {}), + ...(paymentAmountMinRange ? { paymentAmountMinRange } : {}), + ...(paymentAmountMaxRange ? { paymentAmountMaxRange } : {}), + ...(validBefore ? { validBefore } : {}), + ...(validAfter ? { validAfter } : {}), + ...(expireBefore ? { expireBefore } : {}), + ...(expireAfter ? { expireAfter } : {}), }); return extractResponse(result, 200, onRedirectToLogin); }, diff --git a/src/locale/it.json b/src/locale/it.json index 171ff01a0..2039d5fd8 100644 --- a/src/locale/it.json +++ b/src/locale/it.json @@ -1066,8 +1066,31 @@ "globalBundles": "Per tutti", "list": { "search": { - "placeholder": "Cerca per Nome", - "createButton": "Crea pacchetto" + "placeholder": "Cerca per nome pacchetto", + "createButton": "Crea nuovo pacchetto", + "downloadButton": "Scarica lista", + "paymentRangeFilter": "Importo", + "paymentAmountFilter": "Commissione", + "stateFilter": "Stato", + "paymentRangeOptions": { + "noOrder": "Tutti", + "asc": "Prima il più basso", + "desc": "Prima il più alto" + }, + "paymentAmountOptions": { + "all": "Tutti", + "section1": "Fino a 1€", + "section2": "Da 1€ a 2,50€", + "section3": "Da 2,50€ a 5€", + "section4": "Oltre i 5€" + }, + "stateOptions": { + "all": "Tutti", + "active": "Attivi", + "toBeActivated": "In attivazione", + "expiring": "In scadenza", + "deleting": "In eliminazione" + } }, "table": { "emptyState": "Non sono ancora presenti pacchetti commissioni {{bundleType}}{{status}}.", diff --git a/src/pages/commisionalBundles/CommissionBundlesPage.tsx b/src/pages/commisionalBundles/CommissionBundlesPage.tsx index 3ecfc42fd..6a5051dcf 100644 --- a/src/pages/commisionalBundles/CommissionBundlesPage.tsx +++ b/src/pages/commisionalBundles/CommissionBundlesPage.tsx @@ -1,13 +1,6 @@ -import { - Alert, - Button, - FormControl, - InputLabel, - MenuItem, - Select, - Typography, -} from '@mui/material'; -import { Box } from '@mui/system'; +import { Alert, Button, Typography } from '@mui/material'; +import { Download } from '@mui/icons-material'; +import { Box, Stack } from '@mui/system'; import { TitleBox } from '@pagopa/selfcare-common-frontend'; import { useTranslation } from 'react-i18next'; import { useEffect, useState } from 'react'; @@ -16,14 +9,13 @@ import { generatePath, Link as RouterLink } from 'react-router-dom'; import ROUTES from '../../routes'; import { bundleDetailsSelectors } from '../../redux/slices/bundleDetailsSlice'; import { useAppSelector } from '../../redux/hooks'; -import { useFlagValue } from '../../hooks/useFeatureFlags'; -import { BundleResource, SubscriptionStateType } from '../../model/CommissionBundle'; +import { BundleResource } from '../../model/CommissionBundle'; import { TypeEnum } from '../../api/generated/portal/PSPBundleResource'; -import TableSearchBar from '../../components/Table/TableSearchBar'; import SideMenuLayout from '../../components/SideMenu/SideMenuLayout'; import { useUserRole } from '../../hooks/useUserRole'; import { useOrganizationType } from '../../hooks/useOrganizationType'; import CommissionBundlesTable from './list/CommissionBundlesTable'; +import CommissionBundlesSearchBar from './list/CommissionBundlesSearchBar'; type Props = { children?: React.ReactNode; @@ -61,20 +53,38 @@ function getTabValue(bundle: BundleResource | Record) { return 2; } +export const emptyFiltersValues = { + name: '', + paymentRange: { + value: undefined, + name: 'noOrder', + }, + paymentAmount: { + name: 'all', + paymentAmountMax: undefined, + paymentAmountMin: undefined, + }, + state: { + name: 'all', + validityBefore: undefined, + validityAfter: undefined, + expireBefore: undefined, + expireAfter: undefined, + }, +}; + const CommissionBundlesPage = () => { const { t } = useTranslation(); const history = useHistory(); + const { userIsAdmin } = useUserRole(); const { orgInfo } = useOrganizationType(); - const isPrivateEnabled = useFlagValue('commission-bundles-private'); - const isPublicEnabled = useFlagValue('commission-bundles-public'); - const commissionBundleDetail: BundleResource | Record = useAppSelector( bundleDetailsSelectors.selectBundleDetails ); const [tabValue, setTabValue] = useState(getTabValue(commissionBundleDetail)); - const [bundleNameInput, setBundleNameInput] = useState(''); + const [filtersValues, setFiltersValues] = useState(emptyFiltersValues); useEffect(() => { window.addEventListener('beforeunload', clearLocationState); @@ -89,72 +99,66 @@ const CommissionBundlesPage = () => { return ( - - {history.location.state && (history.location.state as any).alertSuccessMessage && ( - - {(history.location.state as any).alertSuccessMessage} - - )} - + + {orgInfo.types.isPsp && userIsAdmin && ( + <> + - ) - } - setActiveTab={(value) => { - setTabValue(value); - }} - activeTab={tabValue} - listTabFilter={[ - { - label: t('commissionBundlesPage.privateBundles'), - disabled: !isPrivateEnabled, - 'data-testid': 'private', - }, - { - label: t('commissionBundlesPage.publicBundles'), - disabled: !isPublicEnabled, - 'data-testid': 'public', - }, - { - label: t('commissionBundlesPage.globalBundles'), - 'data-testid': 'global', - }, - ]} - > + + )} + + + {history.location.state && (history.location.state as any).alertSuccessMessage && ( + + {(history.location.state as any).alertSuccessMessage} + + )} + diff --git a/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx b/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx new file mode 100644 index 000000000..7e643e74f --- /dev/null +++ b/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx @@ -0,0 +1,239 @@ +import { FormControl, InputLabel, Select, MenuItem } from '@mui/material'; +import { useTranslation } from 'react-i18next'; +import { useState } from 'react'; +import { add } from 'date-fns'; +import { useFlagValue } from '../../../hooks/useFeatureFlags'; +import TableSearchBar from '../../../components/Table/TableSearchBar'; +import { useOrganizationType } from '../../../hooks/useOrganizationType'; +import { useUserRole } from '../../../hooks/useUserRole'; +import { emptyFiltersValues } from '../CommissionBundlesPage'; +import { formatDateToYYYYMMDD } from '../../../utils/common-utils'; + +const paymentRangeOptions = [ + { + value: undefined, + name: 'noOrder', + }, + { + value: 'DESC', + name: 'desc', + }, + { + value: 'ASC', + name: 'asc', + }, +]; + +const paymentAmountOptions = [ + { + name: 'all', + paymentAmountMax: undefined, + paymentAmountMin: undefined, + }, + { + name: 'section1', + paymentAmountMax: 100, + paymentAmountMin: undefined, + }, + { + name: 'section2', + paymentAmountMax: 250, + paymentAmountMin: 100, + }, + { + name: 'section3', + paymentAmountMax: 500, + paymentAmountMin: 250, + }, + { + name: 'section4', + paymentAmountMax: undefined, + paymentAmountMin: 500, + }, +]; + +const stateOptions = [ + { + name: 'all', + validityBefore: undefined, + validityAfter: undefined, + expireBefore: undefined, + expireAfter: undefined, + }, + { + name: 'active', + validityBefore: formatDateToYYYYMMDD(new Date()), + validityAfter: undefined, + expireBefore: undefined, + expireAfter: formatDateToYYYYMMDD(new Date()), + }, + { + name: 'toBeActivated', + validityBefore: undefined, + validityAfter: formatDateToYYYYMMDD(new Date()), + expireBefore: undefined, + expireAfter: formatDateToYYYYMMDD(new Date()), + }, + { + name: 'expiring', + validityBefore: formatDateToYYYYMMDD(new Date()), + validityAfter: undefined, + expireBefore: formatDateToYYYYMMDD(add(new Date(), { days: 7 })), + expireAfter: undefined, + }, + { + name: 'deleting', + validityBefore: formatDateToYYYYMMDD(new Date()), + validityAfter: undefined, + expireBefore: formatDateToYYYYMMDD(add(new Date(), { days: 1 })), + expireAfter: undefined, + }, +]; + +const componentPath = 'commissionBundlesPage.list.search'; +export default function CommissionBundlesSearchBar({ + tabValue, + setFiltersValues, + setTabValue, +}: Readonly<{ + tabValue: any; + setFiltersValues: (value: any) => void; + setTabValue: (value: any) => void; +}>) { + const { t } = useTranslation(); + + const { userIsAdmin } = useUserRole(); + const { orgInfo } = useOrganizationType(); + + const isPrivateEnabled = useFlagValue('commission-bundles-private'); + const isPublicEnabled = useFlagValue('commission-bundles-public'); + + const [paymentRangeFilter, setPaymentRangeFilter] = useState(paymentRangeOptions[0]); + const [paymentAmountFilter, setPaymentAmountFilter] = useState(paymentAmountOptions[0]); + const [stateFilter, setStateFilter] = useState(stateOptions[0]); + const [bundleNameInput, setBundleNameInput] = useState(''); + + const resetFilter = () => { + setPaymentRangeFilter(paymentRangeOptions[0]); + setPaymentAmountFilter(paymentAmountOptions[0]); + setStateFilter(stateOptions[0]); + setFiltersValues(emptyFiltersValues); + }; + + return ( + { + setTabValue(value); + }} + handleSearchTrigger={() => + setFiltersValues({ + name: bundleNameInput, + paymentRange: paymentRangeFilter, + paymentAmount: paymentAmountFilter, + state: stateFilter, + }) + } + activeTab={tabValue} + resetFilters={resetFilter} + listTabFilter={[ + { + label: t('commissionBundlesPage.privateBundles'), + disabled: !isPrivateEnabled, + 'data-testid': 'private', + }, + { + label: t('commissionBundlesPage.publicBundles'), + disabled: !isPublicEnabled, + 'data-testid': 'public', + }, + { + label: t('commissionBundlesPage.globalBundles'), + 'data-testid': 'global', + }, + ]} + > + {orgInfo.types.isPsp && userIsAdmin ? ( + <> + + + {t(`${componentPath}.paymentRangeFilter`)} + + + + + + {t(`${componentPath}.paymentAmountFilter`)} + + + + + {t(`${componentPath}.stateFilter`)} + + + + ) : ( + <> + )} + + ); +} diff --git a/src/pages/commisionalBundles/list/CommissionBundlesTable.tsx b/src/pages/commisionalBundles/list/CommissionBundlesTable.tsx index 7c828e27b..166fd6761 100644 --- a/src/pages/commisionalBundles/list/CommissionBundlesTable.tsx +++ b/src/pages/commisionalBundles/list/CommissionBundlesTable.tsx @@ -20,7 +20,7 @@ import { TypeEnum } from '../../../api/generated/portal/PSPBundleResource'; import { buildColumnDefs } from './CommissionBundlesTableColumns'; type Props = { - bundleNameFilter?: string; + filtersValue?: any; bundleType: string; }; @@ -48,7 +48,7 @@ const mapBundle = (bundleType: string) => { }; const componentPath = 'commissionBundlesPage.list'; -const CommissionBundlesTable = ({ bundleNameFilter, bundleType }: Props) => { +const CommissionBundlesTable = ({ filtersValue, bundleType }: Props) => { const { t } = useTranslation(); const { orgInfo } = useOrganizationType(); const addError = useErrorDispatcher(); @@ -91,18 +91,26 @@ const CommissionBundlesTable = ({ bundleNameFilter, bundleType }: Props) => { // eslint-disable-next-line functional/no-let let promise; if (orgInfo.types.isPsp) { + console.log("SAMU", filtersValue); promise = getBundleListByPSP({ bundleType: mappedBundleType, pageLimit, - bundleName: bundleNameFilter ?? undefined, + bundleName: filtersValue?.name ?? undefined, page: newPage ?? page, pspCode: brokerCode, + maxPaymentAmountOrder: filtersValue?.paymentRange?.value, + paymentAmountMinRange: filtersValue?.paymentAmount?.paymentAmountMin, + paymentAmountMaxRange: filtersValue?.paymentAmount?.paymentAmountMax, + validBefore: filtersValue?.state?.validityBefore, + validAfter: filtersValue?.state?.validityAfter, + expireBefore: filtersValue?.state?.expireBefore, + expireAfter: filtersValue?.state?.expireAfter, }); } else if (orgInfo.types.isEc) { promise = getCisBundles({ bundleType: mappedBundleType, pageLimit, - bundleName: bundleNameFilter ?? undefined, + bundleName: filtersValue?.name ?? undefined, page: newPage ?? page, ciTaxCode: mappedBundleType === TypeEnum.GLOBAL ? undefined : brokerCode, bundleStatus: mappedBundleType === TypeEnum.PRIVATE ? privateBundleStatus : undefined, @@ -153,7 +161,7 @@ const CommissionBundlesTable = ({ bundleNameFilter, bundleType }: Props) => { }; } return () => {}; - }, [bundleNameFilter, brokerCode]); + }, [filtersValue, brokerCode]); useEffect(() => { getBundleList(0); diff --git a/src/services/bundleService.ts b/src/services/bundleService.ts index c100812e6..028eaf333 100644 --- a/src/services/bundleService.ts +++ b/src/services/bundleService.ts @@ -34,12 +34,26 @@ export const getBundleListByPSP = ({ page, pspCode, bundleName, + maxPaymentAmountOrder, + paymentAmountMinRange, + paymentAmountMaxRange, + validBefore, + validAfter, + expireBefore, + expireAfter, }: { bundleType: string; pageLimit: number; page: number; pspCode: string; bundleName?: string; + maxPaymentAmountOrder?: string; + paymentAmountMinRange?: number; + paymentAmountMaxRange?: number; + validBefore?: string; + validAfter?: string; + expireBefore?: string; + expireAfter?: string; }): Promise => { if (process.env.REACT_APP_API_MOCK_BACKOFFICE === 'true') { return getCommissionBundlePsp(); @@ -50,6 +64,13 @@ export const getBundleListByPSP = ({ page, pspCode, bundleName, + maxPaymentAmountOrder, + paymentAmountMinRange, + paymentAmountMaxRange, + validBefore, + validAfter, + expireBefore, + expireAfter, }); } }; diff --git a/src/utils/common-utils.ts b/src/utils/common-utils.ts index 6a0afe6eb..bdc7aef38 100644 --- a/src/utils/common-utils.ts +++ b/src/utils/common-utils.ts @@ -111,6 +111,12 @@ export const formatDateToDDMMYYYYhhmmWithTimezone = (value: any): string => timeZone: 'Europe/Rome', }); +export const formatDateToYYYYMMDD = (value: any): string => { + const offset = value.getTimezoneOffset(); + const date = new Date(value.getTime() - offset * 60 * 1000); + return date.toISOString().split('T')[0]; +}; + export function fromHoursFormattedToNumbers(hour: string): number { const separator = hour.indexOf(':'); if (separator >= 0) { From 133c2e04de0ab2405b9280cdae54f668686ad969 Mon Sep 17 00:00:00 2001 From: svariant Date: Fri, 30 Aug 2024 11:04:26 +0200 Subject: [PATCH 05/18] [PPANTT-99] fix: New bundle date filters --- .../list/CommissionBundlesSearchBar.tsx | 10 +++++----- .../commisionalBundles/list/CommissionBundlesTable.tsx | 1 - .../list/CommissionBundlesTableColumns.tsx | 4 ++-- src/utils/common-utils.ts | 6 +----- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx b/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx index 7e643e74f..9544962af 100644 --- a/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx +++ b/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx @@ -65,27 +65,27 @@ const stateOptions = [ validityBefore: formatDateToYYYYMMDD(new Date()), validityAfter: undefined, expireBefore: undefined, - expireAfter: formatDateToYYYYMMDD(new Date()), + expireAfter: formatDateToYYYYMMDD(add(new Date(), {days: 7})), }, { name: 'toBeActivated', validityBefore: undefined, - validityAfter: formatDateToYYYYMMDD(new Date()), + validityAfter: formatDateToYYYYMMDD(add(new Date(), {days:1})), expireBefore: undefined, - expireAfter: formatDateToYYYYMMDD(new Date()), + expireAfter: undefined }, { name: 'expiring', validityBefore: formatDateToYYYYMMDD(new Date()), validityAfter: undefined, expireBefore: formatDateToYYYYMMDD(add(new Date(), { days: 7 })), - expireAfter: undefined, + expireAfter: formatDateToYYYYMMDD(add(new Date(), {days:1})), }, { name: 'deleting', validityBefore: formatDateToYYYYMMDD(new Date()), validityAfter: undefined, - expireBefore: formatDateToYYYYMMDD(add(new Date(), { days: 1 })), + expireBefore: formatDateToYYYYMMDD(new Date()), expireAfter: undefined, }, ]; diff --git a/src/pages/commisionalBundles/list/CommissionBundlesTable.tsx b/src/pages/commisionalBundles/list/CommissionBundlesTable.tsx index 166fd6761..bef001ab6 100644 --- a/src/pages/commisionalBundles/list/CommissionBundlesTable.tsx +++ b/src/pages/commisionalBundles/list/CommissionBundlesTable.tsx @@ -91,7 +91,6 @@ const CommissionBundlesTable = ({ filtersValue, bundleType }: Props) => { // eslint-disable-next-line functional/no-let let promise; if (orgInfo.types.isPsp) { - console.log("SAMU", filtersValue); promise = getBundleListByPSP({ bundleType: mappedBundleType, pageLimit, diff --git a/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx b/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx index 646390541..8b6782747 100644 --- a/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx +++ b/src/pages/commisionalBundles/list/CommissionBundlesTableColumns.tsx @@ -95,7 +95,7 @@ export function buildColumnDefs( cellClassName: 'justifyContentNormal', headerName: t('commissionBundlesPage.list.headerFields.payment'), align: 'left', - headerAlign: 'left', + headerAlign: 'center', width: 145, editable: false, disableColumnMenu: true, @@ -340,7 +340,7 @@ const EuroCell = ({ children, small }: { children: React.ReactNode; small?: bool display="flex" alignItems="center" width={small ? '80px' : '170px'} - justifyContent="space-between" + justifyContent="flex-end" > {children} diff --git a/src/utils/common-utils.ts b/src/utils/common-utils.ts index bdc7aef38..3d2b0e6f4 100644 --- a/src/utils/common-utils.ts +++ b/src/utils/common-utils.ts @@ -111,11 +111,7 @@ export const formatDateToDDMMYYYYhhmmWithTimezone = (value: any): string => timeZone: 'Europe/Rome', }); -export const formatDateToYYYYMMDD = (value: any): string => { - const offset = value.getTimezoneOffset(); - const date = new Date(value.getTime() - offset * 60 * 1000); - return date.toISOString().split('T')[0]; -}; +export const formatDateToYYYYMMDD = (value: any): string => value.toISOString().split('T')[0]; export function fromHoursFormattedToNumbers(hour: string): number { const separator = hour.indexOf(':'); From 21ef818110d4ea5b415428ffa4ea0627ff39a821 Mon Sep 17 00:00:00 2001 From: svariant Date: Fri, 30 Aug 2024 11:07:05 +0200 Subject: [PATCH 06/18] [PPANTT-99] feat: Disabled download button --- src/pages/commisionalBundles/CommissionBundlesPage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/pages/commisionalBundles/CommissionBundlesPage.tsx b/src/pages/commisionalBundles/CommissionBundlesPage.tsx index 6a5051dcf..5b9406522 100644 --- a/src/pages/commisionalBundles/CommissionBundlesPage.tsx +++ b/src/pages/commisionalBundles/CommissionBundlesPage.tsx @@ -117,6 +117,7 @@ const CommissionBundlesPage = () => { /* TODO */ }} endIcon={} + disabled={true} > {t('commissionBundlesPage.list.search.downloadButton')} From ec3f5bfaa3e43ffb95165b8cbd18484af30bdaa3 Mon Sep 17 00:00:00 2001 From: pagopa-github-bot Date: Fri, 30 Aug 2024 09:15:51 +0000 Subject: [PATCH 07/18] Bump to version 1.30.3-7-PPANTT-99-new-features-bundle-list [skip ci] --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index df647d229..a93cbadea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pagopa-selfcare-backoffice-frontend", - "version": "1.30.3-6-next", + "version": "1.30.3-7-PPANTT-99-new-features-bundle-list", "homepage": "ui", "private": true, "scripts": { From a337f16bd7b30b2167eb4eac17f0aff420ef6f34 Mon Sep 17 00:00:00 2001 From: svariant Date: Fri, 30 Aug 2024 11:19:43 +0200 Subject: [PATCH 08/18] [PPANTT-99] fix: Reset bundle name input --- src/locale/it.json | 2 +- .../commisionalBundles/list/CommissionBundlesSearchBar.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/locale/it.json b/src/locale/it.json index 2039d5fd8..7a333a300 100644 --- a/src/locale/it.json +++ b/src/locale/it.json @@ -1109,7 +1109,7 @@ "startDate": "Inizio validità", "endDate": "Fine validità", "touchpoint": "Touchpoint", - "paymentType": "Pagamento con", + "paymentType": "Tipo di pagamento", "payment": "Importo", "commission": "Commissione", "channel": "Canale", diff --git a/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx b/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx index 9544962af..2ace48510 100644 --- a/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx +++ b/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx @@ -117,6 +117,7 @@ export default function CommissionBundlesSearchBar({ setPaymentRangeFilter(paymentRangeOptions[0]); setPaymentAmountFilter(paymentAmountOptions[0]); setStateFilter(stateOptions[0]); + setBundleNameInput(""); setFiltersValues(emptyFiltersValues); }; From d1bd96d0826408448e5d4ee2e0af5f2148f5094d Mon Sep 17 00:00:00 2001 From: svariant Date: Fri, 30 Aug 2024 11:21:36 +0200 Subject: [PATCH 09/18] [PPANTT-99] fix: Filter deleting bundle --- .../commisionalBundles/list/CommissionBundlesSearchBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx b/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx index 2ace48510..3da1c201e 100644 --- a/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx +++ b/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx @@ -83,7 +83,7 @@ const stateOptions = [ }, { name: 'deleting', - validityBefore: formatDateToYYYYMMDD(new Date()), + validityBefore: undefined, validityAfter: undefined, expireBefore: formatDateToYYYYMMDD(new Date()), expireAfter: undefined, From d7875db15584cf46067e1d37f341b89dbabeba23 Mon Sep 17 00:00:00 2001 From: svariant Date: Fri, 30 Aug 2024 11:45:19 +0200 Subject: [PATCH 10/18] [PPANTT-99] fix: Filter bundle to be activated --- .../commisionalBundles/list/CommissionBundlesSearchBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx b/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx index 3da1c201e..4709d1ceb 100644 --- a/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx +++ b/src/pages/commisionalBundles/list/CommissionBundlesSearchBar.tsx @@ -72,7 +72,7 @@ const stateOptions = [ validityBefore: undefined, validityAfter: formatDateToYYYYMMDD(add(new Date(), {days:1})), expireBefore: undefined, - expireAfter: undefined + expireAfter: formatDateToYYYYMMDD(add(new Date(), {days:1})) }, { name: 'expiring', From 289a113b2f73e9773f867e7db9fad469905d747e Mon Sep 17 00:00:00 2001 From: svariant Date: Fri, 30 Aug 2024 15:04:21 +0200 Subject: [PATCH 11/18] [PPANTT-99] feat: Refactor bundle APIs & defined export bundle list API --- package.json | 2 +- src/api/BackofficeClient.ts | 693 ++++++++++--------- src/services/__tests__/bundleService.test.ts | 75 +- src/services/bundleService.ts | 61 +- src/services/taxonomyService.ts | 4 +- 5 files changed, 452 insertions(+), 383 deletions(-) diff --git a/package.json b/package.json index a93cbadea..7f0486564 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "clean:api-portal": "rimraf src/api/generated/portal && rimraf openApi/generated", "generate:api-portal": "wget https://raw.githubusercontent.com/pagopa/pagopa-selfcare-ms-backoffice-backend/main/openapi/openapi.json -O ./openApi/portal-api-docs.json && npm run generate:client", "generate:api-portal-next": "wget https://raw.githubusercontent.com/pagopa/pagopa-selfcare-ms-backoffice-backend/next/openapi/openapi.json -O ./openApi/portal-api-docs.json && npm run generate:client", - "generate:api-portal-pr": "wget https://raw.githubusercontent.com/pagopa/pagopa-selfcare-ms-backoffice-backend/PPANTT-99-new-filters-bundle-list/openapi/openapi.json -O ./openApi/portal-api-docs.json && npm run generate:client", + "generate:api-portal-pr": "wget https://raw.githubusercontent.com/pagopa/pagopa-selfcare-ms-backoffice-backend/PPANTT-101-add-export-psp-bundle-api/openapi/openapi.json -O ./openApi/portal-api-docs.json && npm run generate:client", "generate:api-portal-local": "npm run generate:client", "generate:client": "jq 'walk(if type == \"object\" and has(\"parameters\") then .parameters |= map(select(.name != \"X-Request-Id\")) else . end)' ./openApi/portal-api-docs.json > ./openApi/portal-api-docs.json.temp && mv ./openApi/portal-api-docs.json.temp ./openApi/portal-api-docs.json && yarn run clean:api-portal && mkdirp openApi/generated && gen-api-models --api-spec openApi/portal-api-docs.json --out-dir src/api/generated/portal --no-strict --request-types --response-decoders --client && node openApi/scripts/api-portal_fixPostGen.js" }, diff --git a/src/api/BackofficeClient.ts b/src/api/BackofficeClient.ts index fd17be174..969f20a3f 100644 --- a/src/api/BackofficeClient.ts +++ b/src/api/BackofficeClient.ts @@ -32,7 +32,7 @@ import { BrokerPspDetailsDto } from './generated/portal/BrokerPspDetailsDto'; import { BrokerPspDetailsResource } from './generated/portal/BrokerPspDetailsResource'; import { BrokerResource } from './generated/portal/BrokerResource'; import { BundleCreateResponse } from './generated/portal/BundleCreateResponse'; -import { BundleRequest } from './generated/portal/BundleRequest'; +import { BundleRequest, TypeEnum } from './generated/portal/BundleRequest'; import { CIBrokerDelegationPage } from './generated/portal/CIBrokerDelegationPage'; import { CIBrokerStationPage } from './generated/portal/CIBrokerStationPage'; import { CIBundleAttributeResource } from './generated/portal/CIBundleAttributeResource'; @@ -935,125 +935,370 @@ export const BackofficeApi = { return extractResponse(result, 200, onRedirectToLogin); }, - getBundlesByPsp: async ({ - bundleType, - pageLimit, - page, - pspCode, - bundleName, - maxPaymentAmountOrder, - paymentAmountMinRange, - paymentAmountMaxRange, - validBefore, - validAfter, - expireBefore, - expireAfter, - }: { - bundleType: string; - pageLimit: number; - page: number; - pspCode: string; - bundleName?: string; - maxPaymentAmountOrder?: string; - paymentAmountMinRange?: number; - paymentAmountMaxRange?: number; - validBefore?: string; - validAfter?: string; - expireBefore?: string; - expireAfter?: string; - }): Promise => { - const result = await backofficeClient.getBundlesByPSP({ - 'bundle-type': [bundleType], - limit: pageLimit, - ...(bundleName ? { name: bundleName } : {}), - page, - 'psp-tax-code': pspCode, - ...(maxPaymentAmountOrder ? { maxPaymentAmountOrder } : {}), - ...(paymentAmountMinRange ? { paymentAmountMinRange } : {}), - ...(paymentAmountMaxRange ? { paymentAmountMaxRange } : {}), - ...(validBefore ? { validBefore } : {}), - ...(validAfter ? { validAfter } : {}), - ...(expireBefore ? { expireBefore } : {}), - ...(expireAfter ? { expireAfter } : {}), - }); - return extractResponse(result, 200, onRedirectToLogin); - }, + taxonomies: { + getTaxonomyGroups: async (): Promise => { + const result = await backofficeClient.getTaxonomyGroups({}); + return extractResponse(result, 200, onRedirectToLogin); + }, - createBundle: async ( - pspTaxCode: string, - bundle: BundleRequest - ): Promise => { - const result = await backofficeClient.createBundle({ - 'psp-tax-code': pspTaxCode, - body: bundle, - }); - return extractResponse(result, 201, onRedirectToLogin); + getTaxonomies: async ( + ec: string | undefined, + area: string | undefined, + code: string | undefined, + onlyValid: boolean + ): Promise => { + const result = await backofficeClient.getTaxonomies({ + code, + ec, + macro_area: area, + only_valid: onlyValid, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, }, - getTouchpoints: async (page: number, limit: number): Promise => { - const result = await backofficeClient.getTouchpoints({ page, limit }); - return extractResponse(result, 200, onRedirectToLogin); - }, + bundles: { + getBundlesByPsp: async ({ + bundleType, + pageLimit, + page, + pspCode, + bundleName, + maxPaymentAmountOrder, + paymentAmountMinRange, + paymentAmountMaxRange, + validBefore, + validAfter, + expireBefore, + expireAfter, + }: { + bundleType: string; + pageLimit: number; + page: number; + pspCode: string; + bundleName?: string; + maxPaymentAmountOrder?: string; + paymentAmountMinRange?: number; + paymentAmountMaxRange?: number; + validBefore?: string; + validAfter?: string; + expireBefore?: string; + expireAfter?: string; + }): Promise => { + const result = await backofficeClient.getBundlesByPSP({ + 'bundle-type': [bundleType], + limit: pageLimit, + ...(bundleName ? { name: bundleName } : {}), + page, + 'psp-tax-code': pspCode, + ...(maxPaymentAmountOrder ? { maxPaymentAmountOrder } : {}), + ...(paymentAmountMinRange ? { paymentAmountMinRange } : {}), + ...(paymentAmountMaxRange ? { paymentAmountMaxRange } : {}), + ...(validBefore ? { validBefore } : {}), + ...(validAfter ? { validAfter } : {}), + ...(expireBefore ? { expireBefore } : {}), + ...(expireAfter ? { expireAfter } : {}), + }); + return extractResponse(result, 200, onRedirectToLogin); + }, - getTaxonomyGroups: async (): Promise => { - const result = await backofficeClient.getTaxonomyGroups({}); - return extractResponse(result, 200, onRedirectToLogin); - }, + createBundle: async ( + pspTaxCode: string, + bundle: BundleRequest + ): Promise => { + const result = await backofficeClient.createBundle({ + 'psp-tax-code': pspTaxCode, + body: bundle, + }); + return extractResponse(result, 201, onRedirectToLogin); + }, - getTaxonomies: async ( - ec: string | undefined, - area: string | undefined, - code: string | undefined, - onlyValid: boolean - ): Promise => { - const result = await backofficeClient.getTaxonomies({ - code, - ec, - macro_area: area, - only_valid: onlyValid, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, + getTouchpoints: async (page: number, limit: number): Promise => { + const result = await backofficeClient.getTouchpoints({ page, limit }); + return extractResponse(result, 200, onRedirectToLogin); + }, - getBundleDetailByPSP: async ( - pspTaxCode: string, - bundleId: string - ): Promise => { - const result = await backofficeClient.getBundleDetailByPSP({ - 'psp-tax-code': pspTaxCode, - 'id-bundle': bundleId, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, + getBundleDetailByPSP: async ( + pspTaxCode: string, + bundleId: string + ): Promise => { + const result = await backofficeClient.getBundleDetailByPSP({ + 'psp-tax-code': pspTaxCode, + 'id-bundle': bundleId, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, - deletePSPBundle: async ( - pspTaxCode: string, - bundleId: string, - bundleName: string, - pspName: string, - bundleType: string - ): Promise => { - const result = await backofficeClient.deletePSPBundle({ - 'psp-tax-code': pspTaxCode, - 'id-bundle': bundleId, + deletePSPBundle: async ( + pspTaxCode: string, + bundleId: string, + bundleName: string, + pspName: string, + bundleType: string + ): Promise => { + const result = await backofficeClient.deletePSPBundle({ + 'psp-tax-code': pspTaxCode, + 'id-bundle': bundleId, + bundleName, + pspName, + bundleType, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + + updatePSPBundle: async ( + pspTaxCode: string, + bundleId: string, + bundle: BundleRequest + ): Promise => { + const result = await backofficeClient.updatePSPBundle({ + 'psp-tax-code': pspTaxCode, + 'id-bundle': bundleId, + body: bundle, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + + getCisBundles: async ({ + bundleType, + pageLimit, + page, bundleName, - pspName, + ciTaxCode, + bundleStatus, + }: { + bundleType: string; + pageLimit: number; + page: number; + bundleName?: string; + ciTaxCode?: string; + bundleStatus?: SubscriptionStateType; + }): Promise => { + const result = await backofficeClient.getCisBundles({ + bundleType, + limit: pageLimit, + ...(bundleName ? { name: bundleName } : {}), + page, + ciTaxCode, + status: bundleStatus, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + getBundleCISubscriptions: async ({ + idBundle, + pspTaxCode, + ciTaxCode, + limit, + page, + status, bundleType, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, + }: BundleCISubscriptionsMethodParams): Promise => { + // eslint-disable-next-line functional/no-let + let params: BundleCISubscriptionsBodyRequest = { + 'id-bundle': idBundle, + 'psp-tax-code': pspTaxCode, + limit, + page, + status, + bundleType, + }; + if (ciTaxCode) { + params = { ...params, ciTaxCode }; + } + const result = await backofficeClient.getBundleCISubscriptions(params); + return extractResponse(result, 200, onRedirectToLogin); + }, - updatePSPBundle: async ( - pspTaxCode: string, - bundleId: string, - bundle: BundleRequest - ): Promise => { - const result = await backofficeClient.updatePSPBundle({ - 'psp-tax-code': pspTaxCode, - 'id-bundle': bundleId, - body: bundle, - }); - return extractResponse(result, 200, onRedirectToLogin); + getBundleCISubscriptionsDetail: async ({ + idBundle, + pspTaxCode, + ciTaxCode, + status, + bundleType, + }: BundleCiSubscriptionsDetailMethodParams): Promise => { + const result = await backofficeClient.getBundleCISubscriptionsDetail({ + 'id-bundle': idBundle, + 'psp-tax-code': pspTaxCode, + 'ci-tax-code': ciTaxCode, + bundleType, + status, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + + acceptBundleSubscriptionRequest: async ( + pspTaxCode: string, + idBundleRequest: string, + ciTaxCode: string, + bundleName: string + ): Promise => { + const result = await backofficeClient.acceptPublicBundleSubscriptions({ + 'psp-tax-code': pspTaxCode, + 'bundle-request-id': idBundleRequest, + ciTaxCode, + bundleName, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + + rejectPublicBundleSubscription: async ( + pspTaxCode: string, + bundleRequestId: string, + ciTaxCode: string, + bundleName: string + ): Promise => { + const result = await backofficeClient.rejectPublicBundleSubscription({ + 'psp-tax-code': pspTaxCode, + 'bundle-request-id': bundleRequestId, + ciTaxCode, + bundleName, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + + deleteCIBundleSubscription: async ( + ciBundleId: string, + ciTaxCode: string, + bundleName: string + ): Promise => { + const result = await backofficeClient.deleteCIBundleSubscription({ + 'ci-bundle-id': ciBundleId, + 'ci-tax-code': ciTaxCode, + bundleName, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + + deleteCIBundleRequest: async ({ + idBundleRequest, + ciTaxCode, + }: { + idBundleRequest: string; + ciTaxCode: string; + }): Promise => { + const result = await backofficeClient.deleteCIBundleRequest({ + 'bundle-request-id': idBundleRequest, + 'ci-tax-code': ciTaxCode, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + + createCIBundleRequest: async ({ + ciTaxCode, + bundleRequest, + bundleName, + }: { + ciTaxCode: string; + bundleRequest: Partial; + bundleName: string; + }): Promise => { + const result = await backofficeClient.createCIBundleRequest({ + 'ci-tax-code': ciTaxCode, + body: bundleRequest as PublicBundleRequest, + bundleName, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + + deletePrivateBundleOffer: async ({ + idBundle, + pspTaxCode, + bundleOfferId, + ciTaxCode, + bundleName, + }: { + idBundle: string; + pspTaxCode: string; + bundleOfferId: string; + ciTaxCode: string; + bundleName: string; + }): Promise => { + const result = await backofficeClient.deletePrivateBundleOffer({ + 'id-bundle': idBundle, + 'psp-tax-code': pspTaxCode, + 'bundle-offer-id': bundleOfferId, + ciTaxCode, + bundleName, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + + createCIBundleOffers: async ({ + idBundle, + pspTaxCode, + bundleName, + ciTaxCodeList, + }: { + idBundle: string; + pspTaxCode: string; + bundleName: string; + ciTaxCodeList: Array; + }): Promise => { + const result = await backofficeClient.createCIBundleOffers({ + 'id-bundle': idBundle, + 'psp-tax-code': pspTaxCode, + bundleName, + body: { ciFiscalCodeList: ciTaxCodeList }, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + + acceptPrivateBundleOffer: async ({ + ciTaxCode, + idBundleOffer, + pspTaxCode, + bundleName, + ciBundleAttributes, + }: { + ciTaxCode: string; + idBundleOffer: string; + pspTaxCode: string; + bundleName: string; + ciBundleAttributes: CIBundleAttributeResource; + }): Promise => { + const result = await backofficeClient.acceptPrivateBundleOffer({ + 'ci-tax-code': ciTaxCode, + 'id-bundle-offer': idBundleOffer, + pspTaxCode, + bundleName, + body: ciBundleAttributes, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + + rejectPrivateBundleOffer: async ({ + ciTaxCode, + idBundleOffer, + pspTaxCode, + bundleName, + }: { + ciTaxCode: string; + idBundleOffer: string; + pspTaxCode: string; + bundleName: string; + }): Promise => { + const result = await backofficeClient.rejectPrivateBundleOffer({ + 'ci-tax-code': ciTaxCode, + 'id-bundle-offer': idBundleOffer, + pspTaxCode, + bundleName, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, + + exportPSPBundleList: async ({ + pspTaxCode, + bundleType, + }: { + pspTaxCode: string; + bundleType: Array; + }): Promise => { + const result = await backofficeClient.exportPSPBundleList({ + 'psp-tax-code': pspTaxCode, + bundleTypeList: bundleType, + }); + return extractResponse(result, 200, onRedirectToLogin); + }, }, getFeatureFlags: async (): Promise => { @@ -1061,32 +1306,6 @@ export const BackofficeApi = { return extractResponse(result, 200, onRedirectToLogin); }, - getCisBundles: async ({ - bundleType, - pageLimit, - page, - bundleName, - ciTaxCode, - bundleStatus, - }: { - bundleType: string; - pageLimit: number; - page: number; - bundleName?: string; - ciTaxCode?: string; - bundleStatus?: SubscriptionStateType; - }): Promise => { - const result = await backofficeClient.getCisBundles({ - bundleType, - limit: pageLimit, - ...(bundleName ? { name: bundleName } : {}), - page, - ciTaxCode, - status: bundleStatus, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, - getCIBrokerDelegation: async ( brokerTaxCode: string, brokerId: string, @@ -1229,145 +1448,6 @@ export const BackofficeApi = { return extractResponse(result, 200, onRedirectToLogin); }, - getBundleCISubscriptions: async ({ - idBundle, - pspTaxCode, - ciTaxCode, - limit, - page, - status, - bundleType, - }: BundleCISubscriptionsMethodParams): Promise => { - // eslint-disable-next-line functional/no-let - let params: BundleCISubscriptionsBodyRequest = { - 'id-bundle': idBundle, - 'psp-tax-code': pspTaxCode, - limit, - page, - status, - bundleType, - }; - if (ciTaxCode) { - params = { ...params, ciTaxCode }; - } - const result = await backofficeClient.getBundleCISubscriptions(params); - return extractResponse(result, 200, onRedirectToLogin); - }, - - getBundleCISubscriptionsDetail: async ({ - idBundle, - pspTaxCode, - ciTaxCode, - status, - bundleType, - }: BundleCiSubscriptionsDetailMethodParams): Promise => { - const result = await backofficeClient.getBundleCISubscriptionsDetail({ - 'id-bundle': idBundle, - 'psp-tax-code': pspTaxCode, - 'ci-tax-code': ciTaxCode, - bundleType, - status, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, - - acceptBundleSubscriptionRequest: async ( - pspTaxCode: string, - idBundleRequest: string, - ciTaxCode: string, - bundleName: string - ): Promise => { - const result = await backofficeClient.acceptPublicBundleSubscriptions({ - 'psp-tax-code': pspTaxCode, - 'bundle-request-id': idBundleRequest, - ciTaxCode, - bundleName, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, - - rejectPublicBundleSubscription: async ( - pspTaxCode: string, - bundleRequestId: string, - ciTaxCode: string, - bundleName: string - ): Promise => { - const result = await backofficeClient.rejectPublicBundleSubscription({ - 'psp-tax-code': pspTaxCode, - 'bundle-request-id': bundleRequestId, - ciTaxCode, - bundleName, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, - - deleteCIBundleSubscription: async ( - ciBundleId: string, - ciTaxCode: string, - bundleName: string - ): Promise => { - const result = await backofficeClient.deleteCIBundleSubscription({ - 'ci-bundle-id': ciBundleId, - 'ci-tax-code': ciTaxCode, - bundleName, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, - - deleteCIBundleRequest: async ({ - idBundleRequest, - ciTaxCode, - }: { - idBundleRequest: string; - ciTaxCode: string; - }): Promise => { - const result = await backofficeClient.deleteCIBundleRequest({ - 'bundle-request-id': idBundleRequest, - 'ci-tax-code': ciTaxCode, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, - - createCIBundleRequest: async ({ - ciTaxCode, - bundleRequest, - bundleName, - }: { - ciTaxCode: string; - bundleRequest: Partial; - bundleName: string; - }): Promise => { - const result = await backofficeClient.createCIBundleRequest({ - 'ci-tax-code': ciTaxCode, - body: bundleRequest as PublicBundleRequest, - bundleName, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, - - deletePrivateBundleOffer: async ({ - idBundle, - pspTaxCode, - bundleOfferId, - ciTaxCode, - bundleName, - }: { - idBundle: string; - pspTaxCode: string; - bundleOfferId: string; - ciTaxCode: string; - bundleName: string; - }): Promise => { - const result = await backofficeClient.deletePrivateBundleOffer({ - 'id-bundle': idBundle, - 'psp-tax-code': pspTaxCode, - 'bundle-offer-id': bundleOfferId, - ciTaxCode, - bundleName, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, - getMaintenanceMessage: async (): Promise => { const result = await backofficeClient.getMaintenanceMessage({}); return extractResponse(result, 200, onRedirectToLogin); @@ -1415,69 +1495,6 @@ export const BackofficeApi = { return extractResponse(result, 200, onRedirectToLogin); }, - createCIBundleOffers: async ({ - idBundle, - pspTaxCode, - bundleName, - ciTaxCodeList, - }: { - idBundle: string; - pspTaxCode: string; - bundleName: string; - ciTaxCodeList: Array; - }): Promise => { - const result = await backofficeClient.createCIBundleOffers({ - 'id-bundle': idBundle, - 'psp-tax-code': pspTaxCode, - bundleName, - body: { ciFiscalCodeList: ciTaxCodeList }, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, - - acceptPrivateBundleOffer: async ({ - ciTaxCode, - idBundleOffer, - pspTaxCode, - bundleName, - ciBundleAttributes, - }: { - ciTaxCode: string; - idBundleOffer: string; - pspTaxCode: string; - bundleName: string; - ciBundleAttributes: CIBundleAttributeResource; - }): Promise => { - const result = await backofficeClient.acceptPrivateBundleOffer({ - 'ci-tax-code': ciTaxCode, - 'id-bundle-offer': idBundleOffer, - pspTaxCode, - bundleName, - body: ciBundleAttributes, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, - - rejectPrivateBundleOffer: async ({ - ciTaxCode, - idBundleOffer, - pspTaxCode, - bundleName, - }: { - ciTaxCode: string; - idBundleOffer: string; - pspTaxCode: string; - bundleName: string; - }): Promise => { - const result = await backofficeClient.rejectPrivateBundleOffer({ - 'ci-tax-code': ciTaxCode, - 'id-bundle-offer': idBundleOffer, - pspTaxCode, - bundleName, - }); - return extractResponse(result, 200, onRedirectToLogin); - }, - updateWrapperChannelWithOperatorReview: async ({ channelCode, brokerPspCode, diff --git a/src/services/__tests__/bundleService.test.ts b/src/services/__tests__/bundleService.test.ts index cf34623f1..5377870a0 100644 --- a/src/services/__tests__/bundleService.test.ts +++ b/src/services/__tests__/bundleService.test.ts @@ -34,6 +34,7 @@ import { createCIBundleOffers, acceptPrivateBundleOffer, rejectPrivateBundleOffer, + exportPSPBundleList, } from '../bundleService'; describe('BundleService test mocked', () => { @@ -60,7 +61,9 @@ describe('BundleService test mocked', () => { expect(response).toMatchObject(mockedCommissionBundlePspDetailGlobal); }); test('Test deletePSPBundle', async () => { - expect(deletePSPBundle('pspTaxCode', 'bundleId', 'bundleName', 'pspName', 'PUBLIC')).resolves.not.toThrow(); + expect( + deletePSPBundle('pspTaxCode', 'bundleId', 'bundleName', 'pspName', 'PUBLIC') + ).resolves.not.toThrow(); }); test('Test updatePSPBundle', async () => { expect(updatePSPBundle('pspTaxCode', 'bundleId', mockedBundleRequest)).resolves.not.toThrow(); @@ -132,7 +135,9 @@ describe('BundleService test mocked', () => { deletePrivateBundleOffer({ idBundle: 'idBundle', pspTaxCode: 'pspTaxCode', - bundleOfferId: 'ciTaxCode', + bundleOfferId: 'bundleOfferId', + ciTaxCode: 'ciTaxCode', + bundleName: 'bundleName', }) ).resolves.not.toThrow(); }); @@ -167,6 +172,14 @@ describe('BundleService test mocked', () => { }) ).resolves.not.toThrow(); }); + test('Test exportPSPBundleList', async () => { + expect( + exportPSPBundleList({ + pspTaxCode: 'pspTaxCode', + bundleType: [TypeEnum.GLOBAL], + }) + ).resolves.not.toThrow(); + }); }); describe('BundleService test client', () => { @@ -182,7 +195,7 @@ describe('BundleService test client', () => { test('Test getBundlesByPsp', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'getBundlesByPsp') + .spyOn(BackofficeApi.bundles, 'getBundlesByPsp') .mockReturnValue(new Promise((resolve) => resolve({}))); expect( getBundleListByPSP({ @@ -197,42 +210,44 @@ describe('BundleService test client', () => { }); test('Test createBundle', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'createBundle') + .spyOn(BackofficeApi.bundles, 'createBundle') .mockReturnValue(new Promise((resolve) => resolve({}))); expect(createBundle('pspTaxCode', mockedBundleRequest)).resolves.not.toThrow(); expect(spyOn).toBeCalledTimes(1); }); test('Test getTouchpoints', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'getTouchpoints') + .spyOn(BackofficeApi.bundles, 'getTouchpoints') .mockReturnValue(new Promise((resolve) => resolve({}))); expect(getTouchpoints(0, 0)).resolves.not.toThrow(); expect(spyOn).toBeCalledTimes(1); }); test('Test getBundleDetailByPSP', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'getBundleDetailByPSP') + .spyOn(BackofficeApi.bundles, 'getBundleDetailByPSP') .mockReturnValue(new Promise((resolve) => resolve(mockedCommissionBundlePspDetailGlobal))); expect(getBundleDetailByPSP('pspTaxCode', 'bundleId')).resolves.not.toThrow(); expect(spyOn).toBeCalledTimes(1); }); test('Test deletePSPBundle', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'deletePSPBundle') + .spyOn(BackofficeApi.bundles, 'deletePSPBundle') .mockReturnValue(new Promise((resolve) => resolve())); - expect(deletePSPBundle('pspTaxCode', 'bundleId', 'bundleName', 'pspName', 'PUBLIC')).resolves.not.toThrow(); + expect( + deletePSPBundle('pspTaxCode', 'bundleId', 'bundleName', 'pspName', 'PUBLIC') + ).resolves.not.toThrow(); expect(spyOn).toBeCalledTimes(1); }); test('Test updatePSPBundle', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'updatePSPBundle') + .spyOn(BackofficeApi.bundles, 'updatePSPBundle') .mockReturnValue(new Promise((resolve) => resolve())); expect(updatePSPBundle('pspTaxCode', 'bundleId', mockedBundleRequest)).resolves.not.toThrow(); expect(spyOn).toBeCalledTimes(1); }); test('Test getCisBundles', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'getCisBundles') + .spyOn(BackofficeApi.bundles, 'getCisBundles') .mockReturnValue(new Promise((resolve) => resolve({}))); expect( getCisBundles({ @@ -248,7 +263,7 @@ describe('BundleService test client', () => { }); test('Test acceptBundleSubscriptionRequest', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'acceptBundleSubscriptionRequest') + .spyOn(BackofficeApi.bundles, 'acceptBundleSubscriptionRequest') .mockReturnValue(new Promise((resolve) => resolve())); expect( acceptBundleSubscriptionRequest('pspTaxCode', 'bundleRequestId', 'ciTaxCode', 'bundleName') @@ -257,7 +272,7 @@ describe('BundleService test client', () => { }); test('Test rejectPublicBundleSubscription', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'rejectPublicBundleSubscription') + .spyOn(BackofficeApi.bundles, 'rejectPublicBundleSubscription') .mockReturnValue(new Promise((resolve) => resolve())); expect( rejectPublicBundleSubscription('pspTaxCode', 'bundleRequestId', 'ciTaxCode', 'bundleName') @@ -266,7 +281,7 @@ describe('BundleService test client', () => { }); test('Test getPublicBundleCISubscriptions', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'getBundleCISubscriptions') + .spyOn(BackofficeApi.bundles, 'getBundleCISubscriptions') .mockReturnValue(new Promise((resolve) => resolve({}))); expect( getBundleCISubscriptions({ @@ -283,7 +298,7 @@ describe('BundleService test client', () => { }); test('Test getPublicBundleCISubscriptionsDetail', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'getBundleCISubscriptionsDetail') + .spyOn(BackofficeApi.bundles, 'getBundleCISubscriptionsDetail') .mockReturnValue(new Promise((resolve) => resolve({}))); expect( getBundleCISubscriptionsDetail({ @@ -298,7 +313,7 @@ describe('BundleService test client', () => { }); test('Test deleteCIBundleSubscription', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'deleteCIBundleSubscription') + .spyOn(BackofficeApi.bundles, 'deleteCIBundleSubscription') .mockReturnValue(Promise.resolve()); expect( deleteCIBundleSubscription('idBundle', 'ciTaxCode', 'bundleName') @@ -307,7 +322,7 @@ describe('BundleService test client', () => { }); test('Test deleteCIBundleRequest', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'deleteCIBundleRequest') + .spyOn(BackofficeApi.bundles, 'deleteCIBundleRequest') .mockReturnValue(Promise.resolve()); expect( deleteCIBundleRequest({ idBundleRequest: 'idBundleRequest', ciTaxCode: 'ciTaxCode' }) @@ -316,7 +331,7 @@ describe('BundleService test client', () => { }); test('Test createCIBundleRequest', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'createCIBundleRequest') + .spyOn(BackofficeApi.bundles, 'createCIBundleRequest') .mockReturnValue(Promise.resolve()); expect( createCIBundleRequest({ @@ -329,20 +344,22 @@ describe('BundleService test client', () => { }); test('Test deletePrivateBundleOffer', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'deletePrivateBundleOffer') + .spyOn(BackofficeApi.bundles, 'deletePrivateBundleOffer') .mockReturnValue(Promise.resolve()); expect( deletePrivateBundleOffer({ idBundle: 'idBundle', pspTaxCode: 'pspTaxCode', - bundleOfferId: 'ciTaxCode', + bundleOfferId: 'bundleOfferId', + ciTaxCode: 'ciTaxCode', + bundleName: 'bundleName', }) ).resolves.not.toThrow(); expect(spyOn).toBeCalledTimes(1); }); test('Test createCIBundleOffers', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'createCIBundleOffers') + .spyOn(BackofficeApi.bundles, 'createCIBundleOffers') .mockReturnValue(Promise.resolve()); expect( createCIBundleOffers({ @@ -357,8 +374,8 @@ describe('BundleService test client', () => { test('Test acceptPrivateBundleOffer', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'acceptPrivateBundleOffer') - .mockReturnValue(Promise.resolve({idCiBundle: "idCiBundle"})); + .spyOn(BackofficeApi.bundles, 'acceptPrivateBundleOffer') + .mockReturnValue(Promise.resolve({ idCiBundle: 'idCiBundle' })); expect( acceptPrivateBundleOffer({ ciTaxCode: 'ciTaxCode', @@ -372,7 +389,7 @@ describe('BundleService test client', () => { }); test('Test rejectPrivateBundleOffer', async () => { const spyOn = jest - .spyOn(BackofficeApi, 'rejectPrivateBundleOffer') + .spyOn(BackofficeApi.bundles, 'rejectPrivateBundleOffer') .mockReturnValue(Promise.resolve()); expect( rejectPrivateBundleOffer({ @@ -384,6 +401,18 @@ describe('BundleService test client', () => { ).resolves.not.toThrow(); expect(spyOn).toBeCalledTimes(1); }); + test('Test exportPSPBundleList', async () => { + const spyOn = jest + .spyOn(BackofficeApi.bundles, 'exportPSPBundleList') + .mockReturnValue(Promise.resolve(new Buffer(''))); + expect( + exportPSPBundleList({ + pspTaxCode: 'pspTaxCode', + bundleType: [TypeEnum.GLOBAL], + }) + ).resolves.not.toThrow(); + expect(spyOn).toBeCalledTimes(1); + }); }); describe('Test BundleService utils', () => { diff --git a/src/services/bundleService.ts b/src/services/bundleService.ts index 028eaf333..46169da2d 100644 --- a/src/services/bundleService.ts +++ b/src/services/bundleService.ts @@ -1,7 +1,7 @@ import { TFunction } from 'react-i18next'; import { BackofficeApi } from '../api/BackofficeClient'; import { BundleCreateResponse } from '../api/generated/portal/BundleCreateResponse'; -import { BundleRequest } from '../api/generated/portal/BundleRequest'; +import { BundleRequest, TypeEnum } from '../api/generated/portal/BundleRequest'; import { CIBundleAttributeResource } from '../api/generated/portal/CIBundleAttributeResource'; import { CIBundleId } from '../api/generated/portal/CIBundleId'; import { CIBundlesResource } from '../api/generated/portal/CIBundlesResource'; @@ -58,7 +58,7 @@ export const getBundleListByPSP = ({ if (process.env.REACT_APP_API_MOCK_BACKOFFICE === 'true') { return getCommissionBundlePsp(); } else { - return BackofficeApi.getBundlesByPsp({ + return BackofficeApi.bundles.getBundlesByPsp({ bundleType, pageLimit, page, @@ -82,7 +82,7 @@ export const createBundle = ( if (process.env.REACT_APP_API_MOCK_BACKOFFICE === 'true') { return createCommissionBundle(bundle); } else { - return BackofficeApi.createBundle(pspTaxCode, bundle); + return BackofficeApi.bundles.createBundle(pspTaxCode, bundle); } }; @@ -90,7 +90,7 @@ export const getTouchpoints = (page: number, pageLimit: number): Promise; +}): Promise => { + if (process.env.REACT_APP_API_MOCK_BACKOFFICE === 'true') { + return Promise.resolve(new Buffer('')); + } else { + return BackofficeApi.bundles.exportPSPBundleList({ + pspTaxCode, + bundleType, + }); + } +}; + export const getSpecificBuiltInData = (t: TFunction, specificBuiltInData?: string) => specificBuiltInData ? specificBuiltInData : t('commissionBundlesPage.allTaxonomies'); diff --git a/src/services/taxonomyService.ts b/src/services/taxonomyService.ts index fbf0b9b1b..c016a35d2 100644 --- a/src/services/taxonomyService.ts +++ b/src/services/taxonomyService.ts @@ -11,7 +11,7 @@ export const getTaxonomies = (ec: string | undefined, area: string | undefined, if (process.env.REACT_APP_API_MOCK_BACKOFFICE === 'true') { return getTaxonomiesMock(); } else { - return BackofficeApi.getTaxonomies(ec, area, code, onlyValid); + return BackofficeApi.taxonomies.getTaxonomies(ec, area, code, onlyValid); } }; @@ -19,6 +19,6 @@ export const getTaxonomyGroups = (): Promise => { if (process.env.REACT_APP_API_MOCK_BACKOFFICE === 'true') { return getTaxonomyGroupsMock(); } else { - return BackofficeApi.getTaxonomyGroups(); + return BackofficeApi.taxonomies.getTaxonomyGroups(); } }; From 9999ed28e80d3556edccf5db73b5a9a74722f1fd Mon Sep 17 00:00:00 2001 From: svariant Date: Fri, 30 Aug 2024 15:47:24 +0200 Subject: [PATCH 12/18] [PPANTT-99] feat: Implement export bundle list --- src/components/Form/GenericModal.tsx | 3 + src/locale/it.json | 12 ++ .../CommissionBundlesPage.tsx | 148 +++++++++++++++++- 3 files changed, 155 insertions(+), 8 deletions(-) diff --git a/src/components/Form/GenericModal.tsx b/src/components/Form/GenericModal.tsx index 5ec832357..012258731 100644 --- a/src/components/Form/GenericModal.tsx +++ b/src/components/Form/GenericModal.tsx @@ -11,6 +11,7 @@ type Props = { handleCloseModal?: MouseEventHandler; handleConfirm?: MouseEventHandler; renderContent?: () => any; + children?: React.ReactNode; }; const GenericModal = ({ @@ -22,6 +23,7 @@ const GenericModal = ({ handleCloseModal, handleConfirm, renderContent, + children }: Props) => ( {message} + {children && {children}} ): string => { + // eslint-disable-next-line functional/no-let + let type = ''; + if (bundleType.length === 3) { + type = 'tutti'; + } else { + if (bundleType[0] === TypeEnum.GLOBAL) { + type = 'per-tutti'; + } else if (bundleType[0] === TypeEnum.PUBLIC) { + type = 'su-richiesta'; + } else if (bundleType[0] === TypeEnum.PRIVATE) { + type = 'su-invito'; + } + } + return `${pspName}_${formatDateToYYYYMMDD(new Date())}_${type}_bundle-export.csv`; +}; + +const optionsExportBundleTypes = [ + { + name: 'all', + types: [TypeEnum.GLOBAL, TypeEnum.PRIVATE, TypeEnum.PUBLIC], + }, + { + name: 'private', + types: [TypeEnum.PRIVATE], + }, + { + name: 'public', + types: [TypeEnum.PUBLIC], + }, + { + name: 'global', + types: [TypeEnum.GLOBAL], + }, +]; + const CommissionBundlesPage = () => { const { t } = useTranslation(); const history = useHistory(); + const addError = useErrorDispatcher(); + const selectedParty = useAppSelector(partiesSelectors.selectPartySelected); const { userIsAdmin } = useUserRole(); const { orgInfo } = useOrganizationType(); @@ -86,6 +137,10 @@ const CommissionBundlesPage = () => { const [tabValue, setTabValue] = useState(getTabValue(commissionBundleDetail)); const [filtersValues, setFiltersValues] = useState(emptyFiltersValues); + const [loadingExport, setLoadingExport] = useState(false); + const [modalExport, setModalExport] = useState(false); + const [exportBundleTypes, setExportBundleTypes] = useState(optionsExportBundleTypes[0]); + useEffect(() => { window.addEventListener('beforeunload', clearLocationState); return () => { @@ -97,6 +152,41 @@ const CommissionBundlesPage = () => { window.history.replaceState({}, document.title); }; + const exportBundleList = () => { + setLoadingExport(true); + exportPSPBundleList({ + pspTaxCode: selectedParty?.fiscalCode ?? '', + bundleType: exportBundleTypes.types, + }) + .then((csv: Buffer) => { + console.log(selectedParty); + const filename = getCsvFileName( + selectedParty?.description?.replace(' ', '-') ?? '', + exportBundleTypes.types + ); + const download = document.createElement('a'); + const blob = new Blob([csv], { type: 'text/plain' }); + download.setAttribute('href', window.URL.createObjectURL(blob)); + download.setAttribute('download', filename); + // eslint-disable-next-line functional/immutable-data + download.dataset.downloadurl = ['text/plain', download.download, download.href].join(':'); + download.click(); + }) + .catch((reason) => { + addError({ + component: 'Toast', + id: 'EXPORT_BUNDLE_LIST', + displayableTitle: t('general.errorTitle'), + techDescription: 'An error occured exporting the bundle lists', + blocking: false, + error: reason, + toNotify: true, + displayableDescription: t('commissionBundlesPage.exportError'), + }); + }) + .finally(() => setLoadingExport(false)); + }; + return ( @@ -113,13 +203,14 @@ const CommissionBundlesPage = () => { variant="outlined" sx={{ mr: 1, minWidth: '150px', fontWeight: 'bold', padding: 0 }} data-testid={'download-bundle-button'} - onClick={() => { - /* TODO */ - }} - endIcon={} - disabled={true} + onClick={() => setModalExport(true)} + endIcon={loadingExport ? undefined : } > - {t('commissionBundlesPage.list.search.downloadButton')} + {loadingExport ? ( + + ) : ( + t('commissionBundlesPage.list.search.downloadButton') + )}