diff --git a/locales/en/index.yml b/locales/en/index.yml
index 03d4eb990c8..2efc4b2d729 100644
--- a/locales/en/index.yml
+++ b/locales/en/index.yml
@@ -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:
diff --git a/locales/it/index.yml b/locales/it/index.yml
index 2a7391e2fdc..a1c36642162 100644
--- a/locales/it/index.yml
+++ b/locales/it/index.yml
@@ -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:
diff --git a/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsFadeInOutAnimationView.tsx b/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsFadeInOutAnimationView.tsx
new file mode 100644
index 00000000000..4c1087d6708
--- /dev/null
+++ b/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsFadeInOutAnimationView.tsx
@@ -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 }) => (
+
+ {children}
+
+ ),
+ () => true
+);
diff --git a/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsFilterTabs.tsx b/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsFilterTabs.tsx
new file mode 100644
index 00000000000..b8f9d2da1e5
--- /dev/null
+++ b/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsFilterTabs.tsx
@@ -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 (
+
+
+ {paymentsBizEventsCategoryFilters.map(category => (
+
+ ))}
+
+
+
+ );
+};
+
+const styles = StyleSheet.create({
+ container: {
+ marginHorizontal: -IOVisualCostants.appMarginDefault * 2,
+ paddingHorizontal: IOVisualCostants.appMarginDefault
+ }
+});
+
+export { PaymentsBizEventsFilterTabs };
diff --git a/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsListItemTransaction.tsx b/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsListItemTransaction.tsx
index c59887a5225..2320acb3112 100644
--- a/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsListItemTransaction.tsx
+++ b/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsListItemTransaction.tsx
@@ -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";
@@ -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 = () => ;
+ const TransactionEmptyIcon = useMemo(() => , []);
- const transactionLogo = pipe(
- transactionPayeeLogoUri,
- O.map(uri => ({ uri })),
- O.getOrElseW(() => )
- );
+ const transactionLogo = pipe(
+ transactionPayeeLogoUri,
+ O.map(uri => ({ uri })),
+ O.getOrElseW(() => TransactionEmptyIcon)
+ );
+
+ if (transaction.isCart) {
+ return (
+
+ );
+ }
- if (transaction.isCart) {
return (
}
+ paymentLogoIcon={transactionLogo}
onPress={onPress}
accessible={true}
- title={I18n.t("features.payments.transactions.multiplePayment")}
+ title={recipient}
subtitle={datetime}
transactionAmount={amountText}
accessibilityLabel={accessibilityLabel}
transactionStatus="success"
/>
);
- }
-
- return (
-
- );
-};
+ },
+ (prevProps, nextProps) => prevProps.transaction === nextProps.transaction
+);
export { PaymentsBizEventsListItemTransaction };
diff --git a/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsTransactionLoadingList.tsx b/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsTransactionLoadingList.tsx
new file mode 100644
index 00000000000..66fed3e5aee
--- /dev/null
+++ b/ts/features/payments/bizEventsTransaction/components/PaymentsBizEventsTransactionLoadingList.tsx
@@ -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 && (
+ <>
+
+
+
+ >
+ )}
+
+ {Array.from({ length: 5 }).map((_, index) => (
+
+
+
+ ))}
+ >
+);
diff --git a/ts/features/payments/bizEventsTransaction/saga/handleGetBizEventsTransactions.ts b/ts/features/payments/bizEventsTransaction/saga/handleGetBizEventsTransactions.ts
index 8fd748714c1..34454505a18 100644
--- a/ts/features/payments/bizEventsTransaction/saga/handleGetBizEventsTransactions.ts
+++ b/ts/features/payments/bizEventsTransaction/saga/handleGetBizEventsTransactions.ts
@@ -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"
);
diff --git a/ts/features/payments/bizEventsTransaction/screens/PaymentsTransactionBizEventsListScreen.tsx b/ts/features/payments/bizEventsTransaction/screens/PaymentsTransactionBizEventsListScreen.tsx
index 41acb4548b8..7eb39287251 100644
--- a/ts/features/payments/bizEventsTransaction/screens/PaymentsTransactionBizEventsListScreen.tsx
+++ b/ts/features/payments/bizEventsTransaction/screens/PaymentsTransactionBizEventsListScreen.tsx
@@ -4,7 +4,6 @@ import {
H2,
IOStyles,
ListItemHeader,
- ListItemTransaction,
VSpacer
} from "@pagopa/io-app-design-system";
import * as pot from "@pagopa/ts-commons/lib/pot";
@@ -28,7 +27,6 @@ import { walletTransactionBizEventsListPotSelector } from "../store/selectors";
import { useIONavigation } from "../../../../navigation/params/AppParamsList";
import { isPaymentsTransactionsEmptySelector } from "../../home/store/selectors";
import { PaymentsBizEventsListItemTransaction } from "../components/PaymentsBizEventsListItemTransaction";
-import { PaymentsHomeEmptyScreenContent } from "../../home/components/PaymentsHomeEmptyScreenContent";
import { useHeaderSecondLevel } from "../../../../hooks/useHeaderSecondLevel";
import { useOnFirstRender } from "../../../../utils/hooks/useOnFirstRender";
import { groupTransactionsByMonth } from "../utils";
@@ -37,6 +35,11 @@ import { PaymentsTransactionBizEventsRoutes } from "../navigation/routes";
import { PaymentsTransactionRoutes } from "../../transaction/navigation/routes";
import { NoticeListItem } from "../../../../../definitions/pagopa/biz-events/NoticeListItem";
import * as analytics from "../analytics";
+import { PaymentsBizEventsFilterTabs } from "../components/PaymentsBizEventsFilterTabs";
+import { PaymentBizEventsCategoryFilter } from "../types";
+import { OperationResultScreenContent } from "../../../../components/screens/OperationResultScreenContent";
+import { PaymentsBizEventsFadeInOutAnimationView } from "../components/PaymentsBizEventsFadeInOutAnimationView";
+import { PaymentsBizEventsTransactionLoadingList } from "../components/PaymentsBizEventsTransactionLoadingList";
export type PaymentsTransactionBizEventsListScreenProps = RouteProp<
PaymentsTransactionBizEventsParamsList,
@@ -57,6 +60,8 @@ const PaymentsTransactionBizEventsListScreen = () => {
const [continuationToken, setContinuationToken] = React.useState<
string | undefined
>();
+ const [noticeCategory, setNoticeCategory] =
+ React.useState("all");
const [groupedTransactions, setGroupedTransactions] =
React.useState>>();
const insets = useSafeAreaInsets();
@@ -105,6 +110,19 @@ const PaymentsTransactionBizEventsListScreen = () => {
setTitleHeight(height);
};
+ const fetchNextPage = () => {
+ if (!continuationToken || isLoading) {
+ return;
+ }
+ dispatch(
+ getPaymentsBizEventsTransactionsAction.request({
+ noticeCategory,
+ continuationToken,
+ onSuccess: handleOnSuccess
+ })
+ );
+ };
+
const handleOnSuccess = (paginationToken?: string) => {
setContinuationToken(paginationToken);
setIsRefreshing(false);
@@ -115,6 +133,18 @@ const PaymentsTransactionBizEventsListScreen = () => {
dispatch(
getPaymentsBizEventsTransactionsAction.request({
firstLoad: true,
+ noticeCategory,
+ onSuccess: handleOnSuccess
+ })
+ );
+ };
+
+ const handleCategorySelected = (category: PaymentBizEventsCategoryFilter) => {
+ setNoticeCategory(category);
+ dispatch(
+ getPaymentsBizEventsTransactionsAction.request({
+ firstLoad: true,
+ noticeCategory: category,
onSuccess: handleOnSuccess
})
);
@@ -132,12 +162,6 @@ const PaymentsTransactionBizEventsListScreen = () => {
}, [dispatch])
);
- React.useEffect(() => {
- if (pot.isSome(transactionsPot)) {
- setGroupedTransactions(groupTransactionsByMonth(transactionsPot.value));
- }
- }, [transactionsPot]);
-
useHeaderSecondLevel({
title: I18n.t("features.payments.transactions.title"),
supportRequest: true,
@@ -147,6 +171,12 @@ const PaymentsTransactionBizEventsListScreen = () => {
}
});
+ React.useEffect(() => {
+ if (pot.isSome(transactionsPot)) {
+ setGroupedTransactions(groupTransactionsByMonth(transactionsPot.value));
+ }
+ }, [transactionsPot]);
+
const SectionListHeaderTitle = (
{
>
{I18n.t("features.payments.transactions.title")}
+
+
);
@@ -171,36 +206,27 @@ const PaymentsTransactionBizEventsListScreen = () => {
const renderLoadingFooter = () => (
<>
- {isLoading &&
- Array.from({ length: 5 }).map((_, index) => (
-
- ))}
- {!isLoading && !continuationToken && }
+ {isLoading && (
+
+ )}
+ {!isLoading && !continuationToken && noticeCategory === "all" && (
+
+ )}
>
);
- if (isEmpty) {
- return ;
- }
-
- const fetchNextPage = () => {
- if (!continuationToken || isLoading) {
- return;
- }
- dispatch(
- getPaymentsBizEventsTransactionsAction.request({
- continuationToken,
- onSuccess: handleOnSuccess
- })
- );
- };
+ const EmptyStateList = isEmpty ? (
+
+
+
+ ) : undefined;
return (
{
scrollIndicatorInsets={{ right: 0 }}
contentContainerStyle={{
...IOStyles.horizontalContentPadding,
+ minHeight: isEmpty ? "100%" : undefined,
paddingBottom: insets.bottom + 24
}}
onEndReached={fetchNextPage}
@@ -226,13 +253,16 @@ const PaymentsTransactionBizEventsListScreen = () => {
renderSectionHeader={({ section }) => (
)}
+ ListEmptyComponent={EmptyStateList}
ListFooterComponent={renderLoadingFooter}
keyExtractor={item => `transaction_${item.eventId}`}
renderItem={({ item }) => (
- handleNavigateToTransactionDetails(item)}
- transaction={item}
- />
+
+ handleNavigateToTransactionDetails(item)}
+ transaction={item}
+ />
+
)}
/>
);
diff --git a/ts/features/payments/bizEventsTransaction/store/actions/index.ts b/ts/features/payments/bizEventsTransaction/store/actions/index.ts
index 64eb3810229..e05f953f989 100644
--- a/ts/features/payments/bizEventsTransaction/store/actions/index.ts
+++ b/ts/features/payments/bizEventsTransaction/store/actions/index.ts
@@ -2,9 +2,11 @@ import { ActionType, createAsyncAction } from "typesafe-actions";
import { NetworkError } from "../../../../../utils/errors";
import { NoticeListWrapResponse } from "../../../../../../definitions/pagopa/biz-events/NoticeListWrapResponse";
import { NoticeDetailResponse } from "../../../../../../definitions/pagopa/biz-events/NoticeDetailResponse";
+import { PaymentBizEventsCategoryFilter } from "../../types";
export type PaymentsGetBizEventsTransactionPayload = {
firstLoad?: boolean;
+ noticeCategory?: PaymentBizEventsCategoryFilter;
size?: number;
continuationToken?: string;
onSuccess?: (continuationToken?: string) => void;
diff --git a/ts/features/payments/bizEventsTransaction/store/reducers/index.ts b/ts/features/payments/bizEventsTransaction/store/reducers/index.ts
index f41c1750685..6b5a415b319 100644
--- a/ts/features/payments/bizEventsTransaction/store/reducers/index.ts
+++ b/ts/features/payments/bizEventsTransaction/store/reducers/index.ts
@@ -1,3 +1,4 @@
+/* eslint-disable complexity */
import * as pot from "@pagopa/ts-commons/lib/pot";
import { getType } from "typesafe-actions";
import { Action } from "../../../../../store/actions/types";
@@ -58,9 +59,12 @@ const reducer = (
};
// GET TRANSACTIONS LIST
case getType(getPaymentsBizEventsTransactionsAction.request):
+ const transactions = action.payload.firstLoad
+ ? pot.noneLoading
+ : pot.toLoading(state.transactions);
return {
...state,
- transactions: pot.toLoading(state.transactions)
+ transactions
};
case getType(getPaymentsBizEventsTransactionsAction.success):
const previousTransactions = pot.getOrElse(state.transactions, []);
diff --git a/ts/features/payments/bizEventsTransaction/types/index.ts b/ts/features/payments/bizEventsTransaction/types/index.ts
new file mode 100644
index 00000000000..6c90e0004b5
--- /dev/null
+++ b/ts/features/payments/bizEventsTransaction/types/index.ts
@@ -0,0 +1,7 @@
+export const paymentsBizEventsCategoryFilters = [
+ "all",
+ "payer",
+ "debtor"
+] as const;
+export type PaymentBizEventsCategoryFilter =
+ (typeof paymentsBizEventsCategoryFilters)[number];
diff --git a/ts/features/payments/bizEventsTransaction/utils/types.ts b/ts/features/payments/bizEventsTransaction/utils/types.ts
index 3466b0794b9..5cccf916caa 100644
--- a/ts/features/payments/bizEventsTransaction/utils/types.ts
+++ b/ts/features/payments/bizEventsTransaction/utils/types.ts
@@ -7,8 +7,7 @@ import * as t from "io-ts";
*/
export const BizEventsHeaders = t.type({
map: t.type({
- "x-continuation-token": t.string,
- "content-disposition": t.string
+ "x-continuation-token": t.string
})
});