Skip to content

Commit

Permalink
feat: [IOBP-801] Add transactions category tab filters (#6353)
Browse files Browse the repository at this point in the history
## Short description
This PR implements category filters for the receipt list, allowing users
to view receipts "paid by me," "addressed to me," or both. The selected
filter is applied directly to the backend request, enhancing result
customization. Additionally, this PR improves list performance by
leveraging React memoization, reducing unnecessary re-renders and
significantly enhancing overall efficiency.

## List of changes proposed in this pull request
- Added a new component `PaymentsBizEventsFilterTabs` that handles the
tabs filter;
- Added `noticeCategory` attribute to the
`getPaymentsBizEventsTransactionsAction` that can be "all", "payer" or
"debtor". Based on the type of the value it will send to the backend
request `is_debtor` or `is_payer` flag;
- Wrapped the `PaymentsBizEventsListItemTransaction` component inside a
`React.memo` to avoid unnecessary re-rendering and allow the
re-rendering only if the new attributes are different.

## How to test
- Open the "Payments" screen;
- Tap on the "See all" CTA on the top-right of the latest transactions
to open the transactions list;
- You can see three tabs to filter the elements backend-side;
- If you're testing it on dev-server please check that the request
populate the right query parameter;
- If you're testing it on UAT env. you can see the right result returned
in the list;

## Preview


https://github.com/user-attachments/assets/26007dce-1631-48cb-8ce8-f1220eeca6aa

---------

Co-authored-by: Emanuele Dall'Ara <71103219+LeleDallas@users.noreply.github.com>
  • Loading branch information
Hantex9 and LeleDallas authored Oct 31, 2024
1 parent c57f072 commit c14925b
Show file tree
Hide file tree
Showing 12 changed files with 270 additions and 94 deletions.
9 changes: 9 additions & 0 deletions locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3212,6 +3212,15 @@ features:
banner:
label: "The loading of the receipts failed."
retryButton: "Retry"
filters:
tabs:
all: Tutte
payer: Pagate da me
debtor: Intestate a me
list:
empty:
title: Nessuna ricevuta trovata
subtitle: Se stai cercando la ricevuta di un avviso pagoPA che hai pagato in passato, rivolgiti all’ente creditore.
details:
payPal:
banner:
Expand Down
9 changes: 9 additions & 0 deletions locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3212,6 +3212,15 @@ features:
banner:
label: "Il caricamento delle ricevute è fallito."
retryButton: "Prova di nuovo"
filters:
tabs:
all: Tutte
payer: Pagate da me
debtor: Intestate a me
list:
empty:
title: Nessuna ricevuta trovata
subtitle: Se stai cercando la ricevuta di un avviso pagoPA che hai pagato in passato, rivolgiti all’ente creditore.
details:
payPal:
banner:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";
import { IOStyles } from "@pagopa/io-app-design-system";
import Animated, { FadeIn, FadeOut } from "react-native-reanimated";

export const PaymentsBizEventsFadeInOutAnimationView = React.memo(
({ children }: { children: React.ReactNode }) => (
<Animated.View
style={IOStyles.flex}
exiting={FadeOut.duration(200)}
entering={FadeIn.duration(200)}
>
{children}
</Animated.View>
),
() => true
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import {
IOVisualCostants,
TabItem,
TabNavigation,
VSpacer
} from "@pagopa/io-app-design-system";
import React from "react";
import { StyleSheet, View } from "react-native";
import {
PaymentBizEventsCategoryFilter,
paymentsBizEventsCategoryFilters
} from "../types";
import I18n from "../../../../i18n";

type PaymentsBizEventsFilterTabsProps = {
selectedCategory: PaymentBizEventsCategoryFilter;
onCategorySelected?: (category: PaymentBizEventsCategoryFilter) => void;
};

const PaymentsBizEventsFilterTabs = ({
selectedCategory,
onCategorySelected
}: PaymentsBizEventsFilterTabsProps) => {
const selectedIndexOfCategory =
paymentsBizEventsCategoryFilters.indexOf(selectedCategory);

const handleFilterSelected = (index: number) => {
const categoryByIndex = paymentsBizEventsCategoryFilters[index];
onCategorySelected?.(categoryByIndex);
};

return (
<View style={styles.container}>
<TabNavigation
tabAlignment="start"
onItemPress={handleFilterSelected}
selectedIndex={selectedIndexOfCategory}
>
{paymentsBizEventsCategoryFilters.map(category => (
<TabItem
testID={`CategoryTabTestID-${category}`}
key={category}
label={I18n.t(
`features.payments.transactions.filters.tabs.${category}`
)}
accessibilityLabel={I18n.t(
`features.payments.transactions.filters.tabs.${category}`
)}
/>
))}
</TabNavigation>
<VSpacer size={16} />
</View>
);
};

const styles = StyleSheet.create({
container: {
marginHorizontal: -IOVisualCostants.appMarginDefault * 2,
paddingHorizontal: IOVisualCostants.appMarginDefault
}
});

export { PaymentsBizEventsFilterTabs };
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
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 React, { useMemo } from "react";
import { getAccessibleAmountText } from "../../../../utils/accessibility";
import { format } from "../../../../utils/dates";
import { getTransactionLogo } from "../../common/utils";
Expand All @@ -14,73 +14,73 @@ type Props = {
onPress?: () => void;
};

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

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

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

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

const transactionPayeeLogoUri = getTransactionLogo(transaction);
const transactionPayeeLogoUri = getTransactionLogo(transaction);

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

const TransactionEmptyIcon = () => <Avatar size="small" />;
const TransactionEmptyIcon = useMemo(() => <Avatar size="small" />, []);

const transactionLogo = pipe(
transactionPayeeLogoUri,
O.map(uri => ({ uri })),
O.getOrElseW(() => <TransactionEmptyIcon />)
);
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"
/>
);
}

if (transaction.isCart) {
return (
<ListItemTransaction
paymentLogoIcon={<TransactionEmptyIcon />}
paymentLogoIcon={transactionLogo}
onPress={onPress}
accessible={true}
title={I18n.t("features.payments.transactions.multiplePayment")}
title={recipient}
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"
/>
);
};
},
(prevProps, nextProps) => prevProps.transaction === nextProps.transaction
);

export { PaymentsBizEventsListItemTransaction };
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { ListItemTransaction, VSpacer } from "@pagopa/io-app-design-system";
import * as React from "react";
import Placeholder from "rn-placeholder";
import { PaymentsBizEventsFadeInOutAnimationView } from "./PaymentsBizEventsFadeInOutAnimationView";

export type PaymentsBizEventsTransactionLoadingListProps = {
showSectionTitleSkeleton?: boolean;
};

export const PaymentsBizEventsTransactionLoadingList = ({
showSectionTitleSkeleton
}: PaymentsBizEventsTransactionLoadingListProps) => (
<>
{showSectionTitleSkeleton && (
<>
<VSpacer size={16} />
<Placeholder.Box animate="fade" radius={8} width={62} height={16} />
<VSpacer size={16} />
</>
)}

{Array.from({ length: 5 }).map((_, index) => (
<PaymentsBizEventsFadeInOutAnimationView key={index}>
<ListItemTransaction
isLoading={true}
transactionStatus="success"
transactionAmount=""
title=""
subtitle=""
/>
</PaymentsBizEventsFadeInOutAnimationView>
))}
</>
);
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export function* handleGetBizEventsTransactions(
action,
{
size: action.payload.size || DEFAULT_TRANSACTION_LIST_SIZE,
"x-continuation-token": action.payload.continuationToken
"x-continuation-token": action.payload.continuationToken,
is_debtor: action.payload.noticeCategory === "debtor" || undefined,
is_payer: action.payload.noticeCategory === "payer" || undefined
},
"Authorization"
);
Expand Down
Loading

0 comments on commit c14925b

Please sign in to comment.