-
Notifications
You must be signed in to change notification settings - Fork 105
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: [IOBP-637,IOBP-440] New transactions list and details with biz …
…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
Showing
35 changed files
with
1,704 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
86 changes: 86 additions & 0 deletions
86
...eatures/payments/bizEventsTransaction/components/PaymentsBizEventsListItemTransaction.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }; |
64 changes: 64 additions & 0 deletions
64
...eatures/payments/bizEventsTransaction/components/PaymentsBizEventsTransactionCartList.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); |
99 changes: 99 additions & 0 deletions
99
...s/payments/bizEventsTransaction/components/PaymentsBizEventsTransactionHeadingSection.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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> | ||
); | ||
}; |
Oops, something went wrong.