Skip to content

Commit

Permalink
feat: [IOBP-637,IOBP-440] New transactions list and details with biz …
Browse files Browse the repository at this point in the history
…events integration (#5768)

## Short description
This PR implements the new transaction list and transaction details
powered by biz-events by managing more information such as logo, payment
method used, cart type transactions and so on.

## List of changes proposed in this pull request
- Upgraded the `io-app-design-system` library to the latest version in
order to support the `ListItemInfo` with a payment method logo.
- Added a new feature-folder within the “payments” folder;
- Added sagas, reducers, store, screens;
- Added selectors for getting latest transactions;
- Added a transaction list with server-side pagination;
- Added biz-events API definitions reference;
<details><summary>Why is a new feature-folder being created? </summary>
<p>
Because the old transactions feature will still have to be available
since in addition to biz-events in an early transition there will also
be dated transactions
</p>
</details> 
 

## How to test
- Checkout this PR on the io-dev-api-server:
pagopa/io-dev-api-server#369
- Now You can test this feature on the main landing page of the new
payment section. You should be able to initially see the last 10
transactions made. In the upper right-hand corner, you can see all
transactions performed via the “See All” call-to-action, which will show
a list of transactions paged 10 per page.
If you tap on a transaction, you can access its detail

## Preview


https://github.com/pagopa/io-app/assets/34343582/9a089ac1-7e25-4d5f-b161-4dc6b82f414c
  • Loading branch information
Hantex9 authored Jun 10, 2024
1 parent 7ea4af1 commit aaa9d2e
Show file tree
Hide file tree
Showing 35 changed files with 1,704 additions and 52 deletions.
12 changes: 11 additions & 1 deletion locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3068,7 +3068,8 @@ features:
status:
expired: Scaduta
transactions:
title: Storico operazioni
multiplePayment: Pagamento multiplo
title: Ricevute pagoPA
button: Vedi tutte
empty:
title: Qui vedrai le tue ricevute pagoPA
Expand Down Expand Up @@ -3778,6 +3779,8 @@ bonusCard:
removed: Rimossa
transaction:
details:
error:
title: É stato riscontrato un errore
title: Dettaglio operazione
totalAmount: Totale
totalFee: Il totale comprende
Expand All @@ -3788,12 +3791,19 @@ transaction:
pspName: Gestore della transazione (PSP)
dateAndHour: Data e ora
transactionId: ID transazione
paymentMethod: Metodo di pagamento
authCode: Codice autorizzativo
rrn: RRN
executedBy: Eseguito da
headedTo: Intestato a
operation:
amount: Importo
creditor: Ente creditore
debtor: Debitore
iuv: IUV
subject: Oggetto del pagamento
noticeCode: Codice avviso
taxCode: Codice Fiscale Ente
permissionRequest:
gallery:
title: Consenti a IO di accedere alle tue foto
Expand Down
12 changes: 11 additions & 1 deletion locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3068,7 +3068,8 @@ features:
status:
expired: Scaduta
transactions:
title: Storico operazioni
multiplePayment: Pagamento multiplo
title: Ricevute pagoPA
button: Vedi tutte
empty:
title: Qui vedrai le tue ricevute pagoPA
Expand Down Expand Up @@ -3778,6 +3779,8 @@ bonusCard:
removed: Rimossa
transaction:
details:
error:
title: É stato riscontrato un errore
title: Dettaglio operazione
totalAmount: Totale
totalFee: Il totale comprende
Expand All @@ -3788,12 +3791,19 @@ transaction:
pspName: Gestore della transazione (PSP)
dateAndHour: Data e ora
transactionId: ID transazione
paymentMethod: Metodo di pagamento
authCode: Codice autorizzativo
rrn: RRN
executedBy: Eseguito da
headedTo: Intestato a
operation:
amount: Importo
creditor: Ente creditore
debtor: Debitore
iuv: IUV
subject: Oggetto del pagamento
noticeCode: Codice avviso
taxCode: Codice Fiscale Ente
permissionRequest:
gallery:
title: Consenti a IO di accedere alle tue foto
Expand Down
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"fast_login_api": "https://raw.githubusercontent.com/pagopa/io-backend/v13.35.0-RELEASE/openapi/generated/api_fast_login.yaml",
"pagopa_api_walletv3": "https://raw.githubusercontent.com/pagopa/pagopa-infra/v1.35.1/src/domains/wallet-app/api/payment-wallet/v1/_openapi.json.tpl",
"pagopa_api_ecommerce": "https://raw.githubusercontent.com/pagopa/pagopa-infra/v1.35.1/src/domains/ecommerce-app/api/ecommerce-io/v1/_openapi.json.tpl",
"pagopa_api_biz_events": "https://raw.githubusercontent.com/pagopa/pagopa-infra/0a6784276fd43aaff7709dd90e0d379e77326f28/src/domains/bizevents-app/api/transaction-service/v1/_openapi-jwt.json.tpl",
"trial_system": "https://raw.githubusercontent.com/pagopa/io-backend/features/add-openapi-trial-service/api_trial_system.yaml",
"private": true,
"scripts": {
Expand Down Expand Up @@ -82,7 +83,8 @@
"generate:trial-system-api": "rimraf definitions/trial_systwem && mkdir -p definitions/trial_systwem && gen-api-models --api-spec $npm_package_trial_system --out-dir ./definitions/trial_systwem --no-strict --response-decoders --request-types --client",
"generate:pagopa-walletv3-api": "rimraf definitions/pagopa/walletv3 && mkdir -p definitions/pagopa/walletv3 && gen-api-models --api-spec $npm_package_pagopa_api_walletv3 --out-dir ./definitions/pagopa/walletv3 --no-strict --response-decoders --request-types --client",
"generate:pagopa-ecommerce-api": "rimraf definitions/pagopa/ecommerce && mkdir -p definitions/pagopa/ecommerce && gen-api-models --api-spec $npm_package_pagopa_api_ecommerce --out-dir ./definitions/pagopa/ecommerce --no-strict --response-decoders --request-types --client",
"generate:payments": "npm-run-all generate:pagopa-walletv3-api generate:pagopa-ecommerce-api",
"generate:pagopa-biz-events-api": "rimraf definitions/pagopa/biz-events && mkdir -p definitions/pagopa/biz-events && gen-api-models --api-spec $npm_package_pagopa_api_biz_events --out-dir ./definitions/pagopa/biz-events --no-strict --response-decoders --request-types --client",
"generate:payments": "npm-run-all generate:pagopa-walletv3-api generate:pagopa-ecommerce-api generate:pagopa-biz-events-api",
"generate": "npm-run-all generate:*",
"locales_unused": "ts-node --skip-project -O '{\"lib\":[\"es2015\"]}' scripts/unused-locales.ts",
"remove_unused_locales": "ts-node --skip-project -O '{\"lib\":[\"es2015\"]}' scripts/remove-unused-locales.ts",
Expand Down Expand Up @@ -227,7 +229,7 @@
"@babel/preset-typescript": "^7.16.7",
"@babel/runtime": "^7.15.3",
"@jambit/eslint-plugin-typed-redux-saga": "^0.4.0",
"@pagopa/openapi-codegen-ts": "^12.2.1",
"@pagopa/openapi-codegen-ts": "^13.2.0",
"@react-native-community/eslint-config": "^3.0.1",
"@testing-library/jest-native": "^3.4.3",
"@testing-library/react-native": "^8.0.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { Avatar, ListItemTransaction } from "@pagopa/io-app-design-system";
import * as O from "fp-ts/lib/Option";
import { pipe } from "fp-ts/lib/function";
import React from "react";
import { getAccessibleAmountText } from "../../../../utils/accessibility";
import { format } from "../../../../utils/dates";
import { TransactionListItem } from "../../../../../definitions/pagopa/biz-events/TransactionListItem";
import { getTransactionLogo } from "../../common/utils";
import { formatAmountText } from "../utils";
import I18n from "../../../../i18n";

type Props = {
transaction: TransactionListItem;
onPress?: () => void;
};

const PaymentsBizEventsListItemTransaction = ({
transaction,
onPress
}: Props) => {
const recipient = transaction.payeeName || "";

const amountText = pipe(
transaction.amount,
O.fromNullable,
O.map(amount => formatAmountText(amount)),
O.getOrElse(() => "")
);

const datetime: string = pipe(
transaction.transactionDate,
O.fromNullable,
O.map(transactionDate => format(transactionDate, "DD MMM YYYY, HH:mm")),
O.getOrElse(() => "")
);

const accessibleDatetime: string = pipe(
transaction.transactionDate,
O.fromNullable,
O.map(transactionDate => format(transactionDate, "DD MMMM YYYY, HH:mm")),
O.getOrElse(() => "")
);

const transactionPayeeLogoUri = getTransactionLogo(transaction);

const accessibleAmountText = getAccessibleAmountText(amountText);
const accessibilityLabel = `${recipient}; ${accessibleDatetime}; ${accessibleAmountText}`;

const TransactionEmptyIcon = () => <Avatar size="small" />;

const transactionLogo = pipe(
transactionPayeeLogoUri,
O.map(uri => ({ uri })),
O.getOrElseW(() => <TransactionEmptyIcon />)
);

if (transaction.isCart) {
return (
<ListItemTransaction
paymentLogoIcon={<TransactionEmptyIcon />}
onPress={onPress}
accessible={true}
title={I18n.t("features.payments.transactions.multiplePayment")}
subtitle={datetime}
transactionAmount={amountText}
accessibilityLabel={accessibilityLabel}
transactionStatus="success"
/>
);
}

return (
<ListItemTransaction
paymentLogoIcon={transactionLogo}
onPress={onPress}
accessible={true}
title={recipient}
subtitle={datetime}
transactionAmount={amountText}
accessibilityLabel={accessibilityLabel}
transactionStatus="success"
/>
);
};

export { PaymentsBizEventsListItemTransaction };
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import React from "react";
import { View } from "react-native";
import Placeholder from "rn-placeholder";
import {
HSpacer,
IOStyles,
ListItemTransaction,
VSpacer
} from "@pagopa/io-app-design-system";
import { CartItem } from "../../../../../definitions/pagopa/biz-events/CartItem";
import { formatAmountText } from "../utils";

type Props = {
carts?: ReadonlyArray<CartItem>;
loading: boolean;
onPress: (cartItem: CartItem) => void;
};

/**
* This component renders a list of transaction cart details
*/
export const PaymentsBizEventsTransactionCartList = ({
carts,
loading,
onPress
}: Props) => {
if (loading) {
return <SkeletonTransactionDetailsList />;
}
if (!carts) {
return null;
}

return (
<>
{carts.map((cartItem, index) => (
<ListItemTransaction
key={cartItem.refNumberValue ?? index.toString()}
title={cartItem.subject ?? ""}
subtitle={cartItem.payee?.name ?? ""}
transactionStatus="success"
transactionAmount={
cartItem.amount ? formatAmountText(cartItem.amount) : ""
}
hasChevronRight
onPress={() => onPress(cartItem)}
/>
))}
</>
);
};

const SkeletonTransactionDetailsList = () => (
<View style={[IOStyles.flex, IOStyles.rowSpaceBetween, IOStyles.alignCenter]}>
<View style={[IOStyles.flex, { paddingVertical: 12 }]}>
<Placeholder.Box height={16} width="90%" radius={4} />
<VSpacer size={8} />
<Placeholder.Box height={16} width="30%" radius={4} />
</View>
<Placeholder.Box height={16} width={48} radius={4} />
<HSpacer size={16} />
<Placeholder.Box height={16} width={16} radius={4} />
</View>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import _ from "lodash";
import { Body, IOStyles, VSpacer } from "@pagopa/io-app-design-system";
import { useNavigation } from "@react-navigation/native";
import React from "react";
import { View } from "react-native";
import Placeholder from "rn-placeholder";
import { CartItem } from "../../../../../definitions/pagopa/biz-events/CartItem";
import I18n from "../../../../i18n";
import { TransactionDetailResponse } from "../../../../../definitions/pagopa/biz-events/TransactionDetailResponse";
import { Psp } from "../../../../types/pagopa";
import { formatAmountText } from "../utils";
import { PaymentsTransactionBizEventsStackNavigation } from "../navigation/navigator";
import { PaymentsTransactionBizEventsRoutes } from "../navigation/routes";
import { PaymentsBizEventsTransactionCartList } from "./PaymentsBizEventsTransactionCartList";
import { PaymentsBizEventsTransactionTotalAmount } from "./PaymentsBizEventsTransactionTotalAmount";

type Props = {
transaction?: TransactionDetailResponse;
psp?: Psp;
isLoading: boolean;
};

export const PaymentsBizEventsTransactionHeadingSection = ({
transaction,
isLoading
}: Props) => {
const navigation =
useNavigation<PaymentsTransactionBizEventsStackNavigation>();

const transactionInfo = transaction?.infoTransaction;

const handlePressTransactionDetails = (cartItem: CartItem) => {
if (transaction) {
navigation.navigate(
PaymentsTransactionBizEventsRoutes.PAYMENT_TRANSACTION_BIZ_EVENTS_CART_ITEM_DETAILS,
{
cartItem
}
);
}
};

const FeeAmountSection = () => {
if (isLoading) {
return (
<View style={IOStyles.flex}>
<VSpacer size={4} />
<Placeholder.Line width="100%" animate="fade" />
<VSpacer size={8} />
<Placeholder.Line width="50%" animate="fade" />
</View>
);
}
if (transactionInfo?.fee !== undefined) {
const formattedFee = formatAmountText(transactionInfo.fee);
return (
<Body>
{I18n.t("transaction.details.totalFee")}{" "}
<Body weight="Medium">{formattedFee}</Body>{" "}
{transactionInfo?.pspName
? // we want to make sure no empty string is passed either
I18n.t("transaction.details.totalFeePsp", {
pspName: transactionInfo.pspName
})
: I18n.t("transaction.details.totalFeeNoPsp")}
</Body>
);
}
return null;
};

const calculateTotalAmount = () => {
if (transactionInfo?.amount && transactionInfo?.fee) {
return (
_.toNumber(transactionInfo.amount) + _.toNumber(transactionInfo.fee)
).toString();
}
return transactionInfo?.amount;
};

return (
<View style={[IOStyles.horizontalContentPadding, IOStyles.bgWhite]}>
<VSpacer size={16} />
<PaymentsBizEventsTransactionCartList
carts={transaction?.carts}
loading={isLoading}
onPress={handlePressTransactionDetails}
/>
<VSpacer size={8} />
<PaymentsBizEventsTransactionTotalAmount
loading={isLoading}
totalAmount={calculateTotalAmount()}
/>
<VSpacer size={8} />
<FeeAmountSection />
<VSpacer size={8} />
</View>
);
};
Loading

0 comments on commit aaa9d2e

Please sign in to comment.