Skip to content

Commit

Permalink
Merge pull request #1484 from pagopa/feature/messaggi-cortesia-banche
Browse files Browse the repository at this point in the history
  • Loading branch information
AndreaCimini90 authored Feb 24, 2025
2 parents fafd998 + 5f2da71 commit f8ad602
Show file tree
Hide file tree
Showing 74 changed files with 1,265 additions and 569 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { Fragment, memo, useEffect, useState } from 'react';
import React, { memo, useEffect, useState } from 'react';

import { Download } from '@mui/icons-material/';
import { Alert, Box, Button, Link, RadioGroup, Typography } from '@mui/material';
Expand All @@ -14,6 +14,7 @@ import {
PaymentStatus,
PaymentsData,
} from '../../models';
import { PaymentTpp } from '../../models/NotificationDetail';
import { formatEurocentToCurrency } from '../../utility';
import { getLocalizedOrDefaultLabel } from '../../utility/localization.utility';
import { getPaymentCache, setPaymentCache } from '../../utility/paymentCaching.utility';
Expand All @@ -26,6 +27,7 @@ const FAQ_NOTIFICATION_CANCELLED_REFUND = '/faq#notifica-pagata-rimborso';

type Props = {
payments: PaymentsData;
paymentTpp?: PaymentTpp;
isCancelled: boolean;
timerF24: number;
landingSiteUrl: string;
Expand All @@ -38,18 +40,21 @@ type Props = {
unwrap: () => Promise<PaymentAttachment>;
};
onPayClick: (noticeCode?: string, creditorTaxId?: string, amount?: number) => void;
onPayTppClick?: (noticeCode?: string, creditorTaxId?: string, retrievalId?: string) => void;
handleTrackEvent?: (event: EventPaymentRecipientType, param?: object) => void;
handleFetchPaymentsInfo: (payment: Array<PaymentDetails | NotificationDetailPayment>) => void;
};

const NotificationPaymentRecipient: React.FC<Props> = ({
payments,
paymentTpp,
isCancelled,
timerF24,
landingSiteUrl,
iun,
getPaymentAttachmentAction,
onPayClick,
onPayTppClick,
handleTrackEvent,
handleFetchPaymentsInfo,
}) => {
Expand Down Expand Up @@ -137,9 +142,17 @@ const NotificationPaymentRecipient: React.FC<Props> = ({
}
};

const handleCheckPaymentSelected = () => {
const handleCheckPaymentSelected = (paymentType: 'default' | 'tpp') => {
if (selectedPayment.pagoPa) {
setErrorOnPayment(false);
if (paymentType === 'tpp') {
onPayTppClick?.(
selectedPayment?.pagoPa?.noticeCode,
selectedPayment?.pagoPa?.creditorTaxId,
paymentTpp?.retrievalId
);
return;
}
onPayClick(
selectedPayment.pagoPa.noticeCode,
selectedPayment.pagoPa.creditorTaxId,
Expand Down Expand Up @@ -233,57 +246,19 @@ const NotificationPaymentRecipient: React.FC<Props> = ({
</Alert>
)}
{!allPaymentsIsPaid && (
<Fragment>
<Button
color={errorOnPayment ? 'error' : 'primary'}
fullWidth
variant={errorOnPayment ? 'outlined' : 'contained'}
data-testid="pay-button"
onClick={handleCheckPaymentSelected}
>
{getLocalizedOrDefaultLabel('notifications', 'detail.payment.submit')}
&nbsp;
{selectedPayment?.pagoPa?.amount
? formatEurocentToCurrency(selectedPayment.pagoPa?.amount)
: null}
</Button>
{selectedPayment?.pagoPa?.attachment && (
<Button
fullWidth
variant="outlined"
data-testid="download-pagoPA-notice-button"
disabled={!selectedPayment.pagoPa}
onClick={() => downloadAttachment(PaymentAttachmentSName.PAGOPA)}
>
<Download fontSize="small" sx={{ mr: 1 }} />
{getLocalizedOrDefaultLabel(
'notifications',
'detail.payment.download-pagoPA-notice'
)}
</Button>
)}
{selectedPayment?.f24 ? (
<Box key="attachment" data-testid="f24-download">
<NotificationPaymentF24Item
f24Item={selectedPayment?.f24}
getPaymentAttachmentAction={getPaymentAttachmentAction}
isPagoPaAttachment
handleTrackDownloadF24={() =>
handleTrackEventFn(EventPaymentRecipientType.SEND_F24_DOWNLOAD)
}
handleTrackDownloadF24Success={() =>
handleTrackEventFn(EventPaymentRecipientType.SEND_F24_DOWNLOAD_SUCCESS)
}
handleTrackDownloadF24Timeout={() =>
handleTrackEventFn(EventPaymentRecipientType.SEND_F24_DOWNLOAD_TIMEOUT)
}
timerF24={timerF24}
disableDownload={areOtherDowloading}
handleDownload={setAreOtherDowloading}
/>
</Box>
) : null}
</Fragment>
<PaymentButtons
paymentTpp={paymentTpp}
iun={iun}
selectedPayment={selectedPayment}
downloadAttachment={downloadAttachment}
getPaymentAttachmentAction={getPaymentAttachmentAction}
handleTrackEventFn={handleTrackEventFn}
timerF24={timerF24}
areOtherDowloading={areOtherDowloading}
errorOnPayment={errorOnPayment}
setAreOtherDowloading={setAreOtherDowloading}
handleCheckPaymentSelected={handleCheckPaymentSelected}
/>
)}
</>
)}
Expand Down Expand Up @@ -323,3 +298,101 @@ const NotificationPaymentRecipient: React.FC<Props> = ({
};

export default memo(NotificationPaymentRecipient);

type PaymentButtonsProps = Pick<
Props,
'paymentTpp' | 'iun' | 'getPaymentAttachmentAction' | 'timerF24'
> & {
selectedPayment?: PaymentDetails | { pagoPa: null; f24?: null };
areOtherDowloading: boolean;
errorOnPayment: boolean;
setAreOtherDowloading: (value: boolean) => void;
downloadAttachment: (attachmentName: PaymentAttachmentSName) => void;
handleTrackEventFn: (event: EventPaymentRecipientType, param?: object) => void;
handleCheckPaymentSelected: (paymentType: 'default' | 'tpp') => void;
};

const PaymentButtons = ({
paymentTpp,
iun,
selectedPayment,
timerF24,
areOtherDowloading,
errorOnPayment,
setAreOtherDowloading,
downloadAttachment,
getPaymentAttachmentAction,
handleTrackEventFn,
handleCheckPaymentSelected,
}: PaymentButtonsProps) => {
const hasPaymentTpp = paymentTpp?.iun === iun;
return (
<>
{hasPaymentTpp && (
<Button
color={errorOnPayment ? 'error' : 'primary'}
fullWidth
variant={errorOnPayment ? 'outlined' : 'contained'}
data-testid="tpp-pay-button"
onClick={() => handleCheckPaymentSelected('tpp')}
>
{getLocalizedOrDefaultLabel('notifications', 'detail.payment.submit-tpp', undefined, {
name: paymentTpp?.paymentButton,
})}
</Button>
)}
<Button
color={errorOnPayment ? 'error' : 'primary'}
fullWidth
variant={errorOnPayment || hasPaymentTpp ? 'outlined' : 'contained'}
data-testid="pay-button"
onClick={() => handleCheckPaymentSelected('default')}
>
{hasPaymentTpp &&
getLocalizedOrDefaultLabel('notifications', 'detail.payment.pay-with-other-methods')}
{!hasPaymentTpp && (
<>
{getLocalizedOrDefaultLabel('notifications', 'detail.payment.submit')}
&nbsp;
{selectedPayment?.pagoPa?.amount
? formatEurocentToCurrency(selectedPayment.pagoPa?.amount)
: null}
</>
)}
</Button>
{selectedPayment?.pagoPa?.attachment && (
<Button
fullWidth
variant={hasPaymentTpp ? 'text' : 'outlined'}
data-testid="download-pagoPA-notice-button"
disabled={!selectedPayment.pagoPa}
onClick={() => downloadAttachment(PaymentAttachmentSName.PAGOPA)}
>
<Download fontSize="small" sx={{ mr: 1 }} />
{getLocalizedOrDefaultLabel('notifications', 'detail.payment.download-pagoPA-notice')}
</Button>
)}
{selectedPayment?.f24 && (
<Box key="attachment" data-testid="f24-download">
<NotificationPaymentF24Item
f24Item={selectedPayment?.f24}
getPaymentAttachmentAction={getPaymentAttachmentAction}
isPagoPaAttachment
handleTrackDownloadF24={() =>
handleTrackEventFn(EventPaymentRecipientType.SEND_F24_DOWNLOAD)
}
handleTrackDownloadF24Success={() =>
handleTrackEventFn(EventPaymentRecipientType.SEND_F24_DOWNLOAD_SUCCESS)
}
handleTrackDownloadF24Timeout={() =>
handleTrackEventFn(EventPaymentRecipientType.SEND_F24_DOWNLOAD_TIMEOUT)
}
timerF24={timerF24}
disableDownload={areOtherDowloading}
handleDownload={setAreOtherDowloading}
/>
</Box>
)}
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -432,4 +432,52 @@ describe('NotificationPaymentRecipient Component', () => {
fireEvent.click(f24ButtonToCheck);
expect(getPaymentAttachmentActionMk).toBeCalledTimes(1);
});

it('should show tpp button if payments tpp is present and click onPayTppClick', () => {
const onPayTppClick = vi.fn();
const paymentTpp = {
retrievalId: 'retrievalId',
iun,
paymentButton: 'paymentButton',
};
const { getByTestId, queryAllByTestId } = render(
<NotificationPaymentRecipient
payments={paymentsData}
paymentTpp={paymentTpp}
isCancelled={false}
timerF24={F24TIMER}
iun={iun}
getPaymentAttachmentAction={vi.fn()}
onPayClick={() => {}}
onPayTppClick={onPayTppClick}
handleFetchPaymentsInfo={() => {}}
landingSiteUrl=""
/>
);
const payTppButton = getByTestId('tpp-pay-button');
expect(payTppButton).toBeInTheDocument();

const payButton = getByTestId('pay-button');
expect(payButton).toHaveTextContent('detail.payment.pay-with-other-methods');

const pageSelector = getByTestId('pageSelector');
const pageButtons = pageSelector?.querySelectorAll('button');
fireEvent.click(pageButtons[1]);

// select payment
const paymentIndex = paymentsData.pagoPaF24.findIndex(
(payment) => payment.pagoPa?.status === PaymentStatus.REQUIRED
);
const item = queryAllByTestId('pagopa-item')[paymentIndex];
const radioButton = item.querySelector('[data-testid="radio-button"] input');
fireEvent.click(radioButton!);

// click pay
fireEvent.click(payTppButton);
expect(onPayTppClick).toHaveBeenCalledWith(
paymentsData.pagoPaF24[paymentIndex].pagoPa?.noticeCode,
paymentsData.pagoPaF24[paymentIndex].pagoPa?.creditorTaxId,
paymentTpp.retrievalId
);
});
});
4 changes: 3 additions & 1 deletion packages/pn-commons/src/models/MixpanelEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ export type EventNotificationsListType = {
cancelled_count: number;
};

export type EventNotificationSource = '3Papp' | 'QRcode' | 'LISTA_NOTIFICHE';

export type EventNotificationDetailType = {
notification_owner: boolean;
notification_status: NotificationStatus;
Expand All @@ -88,7 +90,7 @@ export type EventNotificationDetailType = {
count_payment: number;
contains_f24: 'yes' | 'no';
first_time_opening: boolean;
source: 'QRcode' | 'LISTA_NOTIFICHE';
source: EventNotificationSource;
};

export type EventMandateNotificationsListType = {
Expand Down
6 changes: 6 additions & 0 deletions packages/pn-commons/src/models/NotificationDetail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ export interface NotificationDetail {
radd?: INotificationDetailTimeline;
}

export type PaymentTpp = {
paymentButton: string;
retrievalId: string;
iun: string;
};

export type PaymentsData = {
pagoPaF24: Array<PaymentDetails>;
f24Only: Array<F24PaymentDetails>;
Expand Down
39 changes: 38 additions & 1 deletion packages/pn-commons/src/utility/__test__/routes.utility.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { compileRoute } from '../routes.utility';
import { AppRouteParams, compileRoute, getRapidAccessParam } from '../routes.utility';

describe('Routes utility', () => {
it('Route with no params and no path', () => {
Expand Down Expand Up @@ -76,4 +76,41 @@ describe('Routes utility', () => {
});
expect(route).toEqual('/prefix/v1');
});

// Tests for getRapidAccessParam
it('getRapidAccessParam returns correct param when present', () => {
const params = new URLSearchParams({ aar: 'value' });
const result = getRapidAccessParam(params);
expect(result).toEqual([AppRouteParams.AAR, 'value']);
});

it('getRapidAccessParam returns undefined when no params are present', () => {
const params = new URLSearchParams();
const result = getRapidAccessParam(params);
expect(result).toBeUndefined();
});

it('getRapidAccessParam returns correct param when multiple params are present', () => {
const params = new URLSearchParams({ aar: 'value', retrievalId: '123' });
const result = getRapidAccessParam(params);
expect(result).toEqual([AppRouteParams.AAR, 'value']);
});

it('getRapidAccessParam returns correct param when only retrievalId is present', () => {
const params = new URLSearchParams({ retrievalId: '123' });
const result = getRapidAccessParam(params);
expect(result).toEqual([AppRouteParams.RETRIEVAL_ID, '123']);
});

it('getRapidAccessParam returns undefined when param is empty', () => {
const params = new URLSearchParams({ aar: '' });
const result = getRapidAccessParam(params);
expect(result).toBeUndefined();
});

it('getRapidAccessParam returns undefined when parameters is unknown', () => {
const params = new URLSearchParams({ unknownParam: 'test-param' });
const result = getRapidAccessParam(params);
expect(result).toBeUndefined();
});
});
3 changes: 2 additions & 1 deletion packages/pn-commons/src/utility/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ import {
setPaymentsInCache,
} from './paymentCaching.utility';
import { parseError } from './redux.utility';
import { AppRouteParams, compileRoute } from './routes.utility';
import { AppRouteParams, compileRoute, getRapidAccessParam } from './routes.utility';
import { searchStringLimitReachedText, useSearchStringChangeInput } from './searchString.utility';
import { storageOpsBuilder } from './storage.utility';
import { dataRegex, formatFiscalCode, fromStringToBase64, sanitizeString } from './string.utility';
Expand Down Expand Up @@ -158,4 +158,5 @@ export {
fromStringToBase64,
IS_DEVELOP,
APP_VERSION,
getRapidAccessParam,
};
8 changes: 8 additions & 0 deletions packages/pn-commons/src/utility/routes.utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,12 @@ export function compileRoute(route: Route) {

export enum AppRouteParams {
AAR = 'aar',
RETRIEVAL_ID = 'retrievalId',
}

export function getRapidAccessParam(params: URLSearchParams): [AppRouteParams, string] | undefined {
const keys = Object.values(AppRouteParams);
const key = keys.find((k) => params.has(k));
const param = key ? params.get(key) : undefined;
return key && param ? [key, param] : undefined;
}
Loading

0 comments on commit f8ad602

Please sign in to comment.