diff --git a/img/features/wallet/transaction-receipt-divider.svg b/img/features/wallet/transaction-receipt-divider.svg
new file mode 100644
index 00000000000..292f1a0b753
--- /dev/null
+++ b/img/features/wallet/transaction-receipt-divider.svg
@@ -0,0 +1,3 @@
+
diff --git a/locales/en/index.yml b/locales/en/index.yml
index 050fdb203e0..8b3ac755763 100644
--- a/locales/en/index.yml
+++ b/locales/en/index.yml
@@ -3788,3 +3788,20 @@ fastLogin:
description: every time a login with SPID or CIE is performed, we'll send you an e-mail to let you know.
whatsNew:
title: What's changed?
+transaction:
+ details:
+ title: Dettaglio operazione
+ totalAmount: Totale
+ totalFee: Il totale comprende
+ totalFeePsp: di commissione, applicata da {{pspName}}
+ info:
+ title: Informazioni sulla transazione
+ pspName: Gestore della transazione (PSP)
+ dateAndHour: Data e ora
+ transactionId: ID transazione
+ operation:
+ amount: Importo
+ creditor: Ente creditore
+ debtor: Debitore
+ iuv: IUV
+ subject: Oggetto del pagamento
diff --git a/locales/it/index.yml b/locales/it/index.yml
index 808555b8750..2d966ecc750 100644
--- a/locales/it/index.yml
+++ b/locales/it/index.yml
@@ -3788,3 +3788,20 @@ fastLogin:
description: a ogni nuovo accesso con SPID o CIE, ti invieremo un’email.
whatsNew:
title: Cosa cambia?
+transaction:
+ details:
+ title: Dettaglio operazione
+ totalAmount: Totale
+ totalFee: Il totale comprende
+ totalFeePsp: di commissione, applicata da {{pspName}}
+ info:
+ title: Informazioni sulla transazione
+ pspName: Gestore della transazione (PSP)
+ dateAndHour: Data e ora
+ transactionId: ID transazione
+ operation:
+ amount: Importo
+ creditor: Ente creditore
+ debtor: Debitore
+ iuv: IUV
+ subject: Oggetto del pagamento
diff --git a/ts/components/ui/RNavScreenWithLargeHeader.tsx b/ts/components/ui/RNavScreenWithLargeHeader.tsx
index d901ea0a5ad..c570dfcf176 100644
--- a/ts/components/ui/RNavScreenWithLargeHeader.tsx
+++ b/ts/components/ui/RNavScreenWithLargeHeader.tsx
@@ -1,8 +1,4 @@
-import {
- H3,
- HeaderSecondLevel,
- IOVisualCostants
-} from "@pagopa/io-app-design-system";
+import { H2, HeaderSecondLevel, IOStyles } from "@pagopa/io-app-design-system";
import { useNavigation } from "@react-navigation/native";
import React, { ComponentProps, useLayoutEffect, useState } from "react";
import { LayoutChangeEvent, View } from "react-native";
@@ -79,7 +75,7 @@ export const RNavScreenWithLargeHeader = ({
-
- {title}
+
+ {title}
{children}
diff --git a/ts/features/walletV3/common/saga/index.ts b/ts/features/walletV3/common/saga/index.ts
index a3bc5020ea7..a68ceb485c4 100644
--- a/ts/features/walletV3/common/saga/index.ts
+++ b/ts/features/walletV3/common/saga/index.ts
@@ -9,6 +9,7 @@ import { isPagoPATestEnabledSelector } from "../../../../store/reducers/persiste
import { createWalletClient } from "../api/client";
import { watchWalletOnboardingSaga } from "../../onboarding/saga";
import { watchWalletDetailsSaga } from "../../details/saga";
+import { watchWalletTransactionSaga } from "../../transaction/saga";
export function* watchWalletV3Saga(bpdToken: string): SagaIterator {
const isPagoPATestEnabled = yield* select(isPagoPATestEnabledSelector);
@@ -23,4 +24,6 @@ export function* watchWalletV3Saga(bpdToken: string): SagaIterator {
yield* fork(watchWalletOnboardingSaga, client, token);
yield* fork(watchWalletDetailsSaga, client, token);
+
+ yield* fork(watchWalletTransactionSaga, client, token);
}
diff --git a/ts/features/walletV3/common/store/actions/index.ts b/ts/features/walletV3/common/store/actions/index.ts
index 886d2f6f31e..7c991d4fd70 100644
--- a/ts/features/walletV3/common/store/actions/index.ts
+++ b/ts/features/walletV3/common/store/actions/index.ts
@@ -1,4 +1,8 @@
import { WalletDetailsActions } from "../../../details/store/actions";
import { WalletOnboardingActions } from "../../../onboarding/store/actions";
+import { WalletTransactionActions } from "../../../transaction/store/actions";
-export type WalletV3Actions = WalletOnboardingActions | WalletDetailsActions;
+export type WalletV3Actions =
+ | WalletOnboardingActions
+ | WalletDetailsActions
+ | WalletTransactionActions;
diff --git a/ts/features/walletV3/common/store/reducers/index.ts b/ts/features/walletV3/common/store/reducers/index.ts
index c6778fa3947..90d36d49f2c 100644
--- a/ts/features/walletV3/common/store/reducers/index.ts
+++ b/ts/features/walletV3/common/store/reducers/index.ts
@@ -5,15 +5,20 @@ import walletOnboardingReducer, {
import walletDetailsReducer, {
WalletDetailsState
} from "../../../details/store";
+import walletTransactionReducer, {
+ WalletTransactionState
+} from "../../../transaction/store";
export type WalletV3State = {
onboarding: WalletOnboardingState;
details: WalletDetailsState;
+ transaction: WalletTransactionState;
};
const walletV3Reducer = combineReducers({
onboarding: walletOnboardingReducer,
- details: walletDetailsReducer
+ details: walletDetailsReducer,
+ transaction: walletTransactionReducer
});
export default walletV3Reducer;
diff --git a/ts/features/walletV3/transaction/components/WalletTransactionDetailsList.tsx b/ts/features/walletV3/transaction/components/WalletTransactionDetailsList.tsx
new file mode 100644
index 00000000000..0b9939618f5
--- /dev/null
+++ b/ts/features/walletV3/transaction/components/WalletTransactionDetailsList.tsx
@@ -0,0 +1,79 @@
+import React from "react";
+import { pipe } from "fp-ts/lib/function";
+import * as O from "fp-ts/lib/Option";
+import { View } from "react-native";
+import Placeholder from "rn-placeholder";
+import {
+ Divider,
+ HSpacer,
+ IOStyles,
+ ListItemTransaction,
+ VSpacer
+} from "@pagopa/io-app-design-system";
+import { Transaction } from "../../../../types/pagopa";
+import { formatNumberCentsToAmount } from "../../../../utils/stringBuilder";
+import { Dettaglio } from "../../../../../definitions/pagopa/Dettaglio";
+import {
+ cleanTransactionDescription,
+ getTransactionIUV
+} from "../../../../utils/payment";
+
+type Props = {
+ transaction?: Transaction | null;
+ loading: boolean;
+ onPress: (operationDetails: Dettaglio) => void;
+};
+
+/**
+ * This component renders a list of transaction details which currently is just a single item
+ * TODO: Using the actual information, this component is already arranged to handle a list that will be implemented from the BIZ Event implementation (https://pagopa.atlassian.net/browse/IOBP-440)
+ */
+export const WalletTransactionDetailsList = ({
+ transaction,
+ loading,
+ onPress
+}: Props) => {
+ if (loading) {
+ return ;
+ }
+ if (!transaction) {
+ return <>>;
+ }
+
+ const operationDetails: Dettaglio = {
+ importo: transaction.amount.amount,
+ enteBeneficiario: transaction.merchant,
+ IUV: pipe(getTransactionIUV(transaction.description), O.toUndefined)
+ };
+
+ return (
+ <>
+ onPress?.(operationDetails)}
+ />
+
+ >
+ );
+};
+
+const SkeletonTransactionDetailsList = () => (
+
+
+
+
+
+
+
+
+
+
+);
diff --git a/ts/features/walletV3/transaction/components/WalletTransactionHeadingSection.tsx b/ts/features/walletV3/transaction/components/WalletTransactionHeadingSection.tsx
new file mode 100644
index 00000000000..30534267430
--- /dev/null
+++ b/ts/features/walletV3/transaction/components/WalletTransactionHeadingSection.tsx
@@ -0,0 +1,93 @@
+import { useNavigation } from "@react-navigation/native";
+import Placeholder from "rn-placeholder";
+import React from "react";
+import { View } from "react-native";
+import { Body, IOStyles, VSpacer } from "@pagopa/io-app-design-system";
+import { Psp, Transaction } from "../../../../types/pagopa";
+import { Dettaglio } from "../../../../../definitions/pagopa/Dettaglio";
+import { formatNumberCentsToAmount } from "../../../../utils/stringBuilder";
+import I18n from "../../../../i18n";
+import {
+ WalletTransactionRoutes,
+ WalletTransactionStackNavigation
+} from "../navigation/navigator";
+
+import { WalletTransactionTotalAmount } from "./WalletTransactionTotalAmount";
+import { WalletTransactionDetailsList } from "./WalletTransactionDetailsList";
+
+type Props = {
+ transaction?: Transaction;
+ psp?: Psp;
+ loading: boolean;
+};
+
+export const WalletTransactionHeadingSection = ({
+ transaction,
+ psp,
+ loading
+}: Props) => {
+ const navigation = useNavigation();
+
+ const handlePressTransactionDetails = (operationDetails: Dettaglio) => {
+ if (transaction) {
+ navigation.navigate(
+ WalletTransactionRoutes.WALLET_TRANSACTION_OPERATION_DETAILS,
+ {
+ operationDetails,
+ operationSubject: transaction.description,
+ operationName: transaction.description
+ }
+ );
+ }
+ };
+
+ const FeeAmountSection = () => {
+ if (psp && transaction?.fee && !loading) {
+ const formattedFee = formatNumberCentsToAmount(
+ transaction.fee.amount,
+ true,
+ "right"
+ );
+ return (
+
+ {I18n.t("transaction.details.totalFee")}{" "}
+ {formattedFee}{" "}
+ {I18n.t("transaction.details.totalFeePsp", {
+ pspName: psp.businessName || ""
+ })}
+ .
+
+ );
+ }
+ if (loading) {
+ return (
+
+
+
+
+
+
+ );
+ }
+ return <>>;
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/ts/features/walletV3/transaction/components/WalletTransactionInfoSection.tsx b/ts/features/walletV3/transaction/components/WalletTransactionInfoSection.tsx
new file mode 100644
index 00000000000..b437782e97a
--- /dev/null
+++ b/ts/features/walletV3/transaction/components/WalletTransactionInfoSection.tsx
@@ -0,0 +1,117 @@
+/* eslint-disable functional/immutable-data */
+import * as React from "react";
+import { StyleSheet, View } from "react-native";
+import Placeholder from "rn-placeholder";
+import {
+ Divider,
+ IORadiusScale,
+ IOVisualCostants,
+ ListItemHeader,
+ ListItemInfo,
+ ListItemInfoCopy,
+ VSpacer
+} from "@pagopa/io-app-design-system";
+import { IOStyles } from "../../../../components/core/variables/IOStyles";
+import I18n from "../../../../i18n";
+import { Psp, Transaction } from "../../../../types/pagopa";
+import { format } from "../../../../utils/dates";
+import { clipboardSetStringWithFeedback } from "../../../../utils/clipboard";
+import TransactionReceiptDivider from "../../../../../img/features/wallet/transaction-receipt-divider.svg";
+
+type WalletTransactionInfoSectionProps = {
+ transaction?: Transaction;
+ psp?: Psp;
+ loading?: boolean;
+};
+
+const styles = StyleSheet.create({
+ container: {
+ flexGrow: 1,
+ ...IOStyles.horizontalContentPadding
+ },
+ contentCard: {
+ ...IOStyles.horizontalContentPadding,
+ ...IOStyles.bgWhite,
+ borderRadius: IORadiusScale["1"],
+ marginVertical: IOVisualCostants.appMarginDefault
+ }
+});
+
+/**
+ * Component that shows a success message after the wallet onboarding process is completed
+ * TODO: Define the desired design of this component
+ */
+const WalletTransactionInfoSection = ({
+ transaction,
+ psp,
+ loading
+}: WalletTransactionInfoSectionProps) => (
+ <>
+
+
+
+
+ {loading && (
+ <>
+
+
+
+
+
+ >
+ )}
+ {!loading && transaction && (
+ <>
+ {psp?.businessName && (
+ <>
+
+
+ >
+ )}
+
+
+
+ clipboardSetStringWithFeedback(transaction.id.toString())
+ }
+ accessibilityLabel={I18n.t(
+ "transaction.details.info.transactionId"
+ )}
+ label={I18n.t("transaction.details.info.transactionId")}
+ value={transaction.id.toString()}
+ />
+ >
+ )}
+
+
+ >
+);
+
+const SkeletonItem = () => (
+
+
+
+
+
+);
+
+export default WalletTransactionInfoSection;
diff --git a/ts/features/walletV3/transaction/components/WalletTransactionTotalAmount.tsx b/ts/features/walletV3/transaction/components/WalletTransactionTotalAmount.tsx
new file mode 100644
index 00000000000..08dbbb2d329
--- /dev/null
+++ b/ts/features/walletV3/transaction/components/WalletTransactionTotalAmount.tsx
@@ -0,0 +1,36 @@
+import React from "react";
+import Placeholder from "rn-placeholder";
+import { StyleSheet, View } from "react-native";
+import { H3, H6, IOStyles } from "@pagopa/io-app-design-system";
+import { formatNumberCentsToAmount } from "../../../../utils/stringBuilder";
+import I18n from "../../../../i18n";
+
+type TotalAmountSectionProps = {
+ totalAmount?: number;
+ loading?: boolean;
+};
+
+const styles = StyleSheet.create({
+ container: {
+ ...IOStyles.rowSpaceBetween,
+ ...IOStyles.alignCenter,
+ ...IOStyles.flex
+ }
+});
+
+export const WalletTransactionTotalAmount = ({
+ totalAmount,
+ loading
+}: TotalAmountSectionProps) => (
+
+ {I18n.t("transaction.details.totalAmount")}
+ {loading && (
+
+
+
+ )}
+ {!loading && totalAmount && (
+ {formatNumberCentsToAmount(totalAmount, true, "right")}
+ )}
+
+);
diff --git a/ts/features/walletV3/transaction/navigation/navigator.tsx b/ts/features/walletV3/transaction/navigation/navigator.tsx
new file mode 100644
index 00000000000..402c2f449ff
--- /dev/null
+++ b/ts/features/walletV3/transaction/navigation/navigator.tsx
@@ -0,0 +1,57 @@
+import { ParamListBase } from "@react-navigation/native";
+import {
+ createStackNavigator,
+ StackNavigationProp
+} from "@react-navigation/stack";
+import React from "react";
+import { isGestureEnabled } from "../../../../utils/navigation";
+import WalletTransactionDetailsScreen, {
+ WalletTransactionDetailsScreenParams
+} from "../screens/WalletTransactionDetailsScreen";
+import WalletTransactionOperationDetailsScreen, {
+ WalletTransactionOperationDetailsScreenParams
+} from "../screens/WalletTransactionOperationDetails";
+
+export const WalletTransactionRoutes = {
+ WALLET_TRANSACTION_MAIN: "WALLET_TRANSACTION_MAIN",
+ WALLET_TRANSACTION_DETAILS: "WALLET_TRANSACTION_DETAILS",
+ WALLET_TRANSACTION_OPERATION_DETAILS: "WALLET_TRANSACTION_OPERATION_DETAILS"
+} as const;
+
+export type WalletTransactionParamsList = {
+ [WalletTransactionRoutes.WALLET_TRANSACTION_MAIN]: undefined;
+ [WalletTransactionRoutes.WALLET_TRANSACTION_DETAILS]: WalletTransactionDetailsScreenParams;
+ [WalletTransactionRoutes.WALLET_TRANSACTION_OPERATION_DETAILS]: WalletTransactionOperationDetailsScreenParams;
+};
+
+const Stack = createStackNavigator();
+
+export const WalletTransactionNavigator = () => (
+
+
+
+
+);
+
+export type WalletTransactionStackNavigationProp<
+ ParamList extends ParamListBase,
+ RouteName extends keyof ParamList = string
+> = StackNavigationProp;
+
+export type WalletTransactionStackNavigation =
+ WalletTransactionStackNavigationProp<
+ WalletTransactionParamsList,
+ keyof WalletTransactionParamsList
+ >;
diff --git a/ts/features/walletV3/transaction/saga/handleGetTransactionDetails.ts b/ts/features/walletV3/transaction/saga/handleGetTransactionDetails.ts
new file mode 100644
index 00000000000..db9e4dd858d
--- /dev/null
+++ b/ts/features/walletV3/transaction/saga/handleGetTransactionDetails.ts
@@ -0,0 +1,39 @@
+import * as pot from "@pagopa/ts-commons/lib/pot";
+import { put, select } from "typed-redux-saga/macro";
+import { ActionType } from "typesafe-actions";
+import { walletTransactionDetailsGet } from "../store/actions";
+import { getTransactions } from "../../../../store/reducers/wallet/transactions";
+import { getGenericError } from "../../../../utils/errors";
+
+/**
+ * Handle the remote call to get the transaction details
+ * TODO: This is a temporary implementation to simulate the BIZ Event API, it will be replaced as soon as the BIZ Event API will be available (https://pagopa.atlassian.net/browse/IOBP-440)
+ * @param getPaymentMethods
+ * @param action
+ */
+export function* handleGetTransactionDetails(
+ _getTransactionDetails: any, // TODO: Replace with the real type when the BIZ Event API will be available
+ _token: string,
+ action: ActionType<(typeof walletTransactionDetailsGet)["request"]>
+) {
+ // TODO: Add the whole logic here to call the BIZ Event API as soon as it will be available and replace the following code
+ const transactions = yield* select(getTransactions);
+ const transactionDetails = pot.toUndefined(
+ pot.map(transactions, transactions =>
+ transactions.find(trx => trx.id === action.payload.transactionId)
+ )
+ );
+ if (transactionDetails) {
+ yield* put(walletTransactionDetailsGet.success(transactionDetails));
+ return;
+ }
+ yield* put(
+ walletTransactionDetailsGet.failure({
+ ...getGenericError(
+ new Error(
+ `Transaction details not found for transaction id ${action.payload.transactionId}`
+ )
+ )
+ })
+ );
+}
diff --git a/ts/features/walletV3/transaction/saga/index.ts b/ts/features/walletV3/transaction/saga/index.ts
new file mode 100644
index 00000000000..e86a47517ae
--- /dev/null
+++ b/ts/features/walletV3/transaction/saga/index.ts
@@ -0,0 +1,23 @@
+import { SagaIterator } from "redux-saga";
+import { takeLatest } from "typed-redux-saga/macro";
+
+import { WalletClient } from "../../common/api/client";
+import { walletTransactionDetailsGet } from "../store/actions";
+import { handleGetTransactionDetails } from "./handleGetTransactionDetails";
+
+/**
+ * Handle Wallet transaction requests
+ * @param bearerToken
+ */
+export function* watchWalletTransactionSaga(
+ walletClient: WalletClient,
+ token: string
+): SagaIterator {
+ // TODO: Connect the saga code here to the BIZ Event API as asoon as it will be available (https://pagopa.atlassian.net/browse/IOBP-440)
+ yield* takeLatest(
+ walletTransactionDetailsGet.request,
+ handleGetTransactionDetails,
+ walletClient.getWalletById, // TODO: Add the get transaction details API call here when BIZ Event API will be available
+ token
+ );
+}
diff --git a/ts/features/walletV3/transaction/screens/WalletTransactionDetailsScreen.tsx b/ts/features/walletV3/transaction/screens/WalletTransactionDetailsScreen.tsx
new file mode 100644
index 00000000000..19727e6db2e
--- /dev/null
+++ b/ts/features/walletV3/transaction/screens/WalletTransactionDetailsScreen.tsx
@@ -0,0 +1,108 @@
+import * as React from "react";
+import * as pot from "@pagopa/ts-commons/lib/pot";
+import { Dimensions, StyleSheet, View } from "react-native";
+import { IOColors } from "@pagopa/io-app-design-system";
+import { RouteProp, useRoute } from "@react-navigation/native";
+
+import { WalletTransactionParamsList } from "../navigation/navigator";
+import { useIODispatch, useIOSelector } from "../../../../store/hooks";
+import FocusAwareStatusBar from "../../../../components/ui/FocusAwareStatusBar";
+import { emptyContextualHelp } from "../../../../utils/emptyContextualHelp";
+import { useOnFirstRender } from "../../../../utils/hooks/useOnFirstRender";
+import { walletTransactionDetailsGet } from "../store/actions";
+import {
+ walletTransactionDetailsPotSelector,
+ walletTransactionDetailsSelector
+} from "../store";
+import { fetchPsp } from "../../../../store/actions/wallet/transactions";
+import { Psp } from "../../../../types/pagopa";
+import WalletTransactionInfoSection from "../components/WalletTransactionInfoSection";
+import { WalletTransactionHeadingSection } from "../components/WalletTransactionHeadingSection";
+import { RNavScreenWithLargeHeader } from "../../../../components/ui/RNavScreenWithLargeHeader";
+import I18n from "../../../../i18n";
+
+export type WalletTransactionDetailsScreenParams = {
+ transactionId: number;
+};
+
+export type WalletTransactionDetailsScreenProps = RouteProp<
+ WalletTransactionParamsList,
+ "WALLET_TRANSACTION_DETAILS"
+>;
+
+const windowHeight = Dimensions.get("window").height;
+
+const styles = StyleSheet.create({
+ bottomBackground: {
+ position: "absolute",
+ height: windowHeight,
+ bottom: -windowHeight,
+ left: 0,
+ right: 0,
+ backgroundColor: IOColors["grey-50"]
+ },
+ wrapper: {
+ flexGrow: 1,
+ alignContent: "flex-start",
+ backgroundColor: IOColors["grey-50"]
+ }
+});
+
+const WalletTransactionDetailsScreen = () => {
+ const dispatch = useIODispatch();
+ const route = useRoute();
+ const { transactionId } = route.params;
+ const transactionDetailsPot = useIOSelector(
+ walletTransactionDetailsPotSelector
+ );
+
+ const isLoading = pot.isLoading(transactionDetailsPot);
+
+ const [transactionPsp, setTransactionPsp] = React.useState();
+
+ const transactionDetails = useIOSelector(walletTransactionDetailsSelector);
+
+ useOnFirstRender(() => {
+ dispatch(walletTransactionDetailsGet.request({ transactionId }));
+ });
+
+ React.useEffect(() => {
+ if (transactionDetails && transactionDetails.idPsp) {
+ dispatch(
+ fetchPsp.request({
+ idPsp: transactionDetails.idPsp,
+ onSuccess: ({ payload }) => {
+ setTransactionPsp(payload.psp);
+ }
+ })
+ );
+ }
+ }, [dispatch, transactionDetails]);
+
+ return (
+
+
+
+ {/* The following line is used to show the background color gray that overlay the basic one which is white */}
+
+
+
+
+
+ );
+};
+
+export default WalletTransactionDetailsScreen;
diff --git a/ts/features/walletV3/transaction/screens/WalletTransactionOperationDetails.tsx b/ts/features/walletV3/transaction/screens/WalletTransactionOperationDetails.tsx
new file mode 100644
index 00000000000..92a1db7ab6d
--- /dev/null
+++ b/ts/features/walletV3/transaction/screens/WalletTransactionOperationDetails.tsx
@@ -0,0 +1,123 @@
+import * as React from "react";
+import { ScrollView, StyleSheet } from "react-native";
+import {
+ Divider,
+ H6,
+ IOStyles,
+ ListItemInfo
+} from "@pagopa/io-app-design-system";
+import { RouteProp, useRoute } from "@react-navigation/native";
+
+import { WalletTransactionParamsList } from "../navigation/navigator";
+import { Dettaglio } from "../../../../../definitions/pagopa/Dettaglio";
+import { formatNumberCentsToAmount } from "../../../../utils/stringBuilder";
+import { cleanTransactionDescription } from "../../../../utils/payment";
+import I18n from "../../../../i18n";
+import { RNavScreenWithLargeHeader } from "../../../../components/ui/RNavScreenWithLargeHeader";
+
+const styles = StyleSheet.create({
+ scrollViewContainer: {
+ ...IOStyles.flex,
+ ...IOStyles.horizontalContentPadding
+ }
+});
+
+export type WalletTransactionOperationDetailsScreenParams = {
+ operationName: string;
+ operationSubject: string;
+ operationDetails: Dettaglio;
+};
+
+export type WalletTransactionOperationDetailsScreenProps = RouteProp<
+ WalletTransactionParamsList,
+ "WALLET_TRANSACTION_OPERATION_DETAILS"
+>;
+
+const WalletTransactionOperationDetailsScreen = () => {
+ const route = useRoute();
+ const { operationDetails, operationName, operationSubject } = route.params;
+
+ const getDebtorText = () => {
+ const debtorNameLabel = operationDetails.nomePagatore ? (
+ {operationDetails.nomePagatore}
+ ) : (
+ <>>
+ );
+ const debtorCodeLabel = operationDetails.codicePagatore ? (
+ ({operationDetails.codicePagatore})
+ ) : (
+ <>>
+ );
+ return (
+ <>
+ {debtorNameLabel}
+ {debtorCodeLabel}
+ >
+ );
+ };
+
+ return (
+
+
+ {operationDetails.importo && (
+
+ )}
+
+ {operationDetails.enteBeneficiario && (
+ <>
+
+
+ >
+ )}
+ {(operationDetails.codicePagatore || operationDetails.nomePagatore) && (
+ <>
+
+
+ >
+ )}
+ {operationDetails.IUV && (
+ <>
+
+
+ >
+ )}
+ {operationSubject && (
+
+ )}
+
+
+ );
+};
+
+export default WalletTransactionOperationDetailsScreen;
diff --git a/ts/features/walletV3/transaction/store/actions/index.ts b/ts/features/walletV3/transaction/store/actions/index.ts
new file mode 100644
index 00000000000..50f40edff17
--- /dev/null
+++ b/ts/features/walletV3/transaction/store/actions/index.ts
@@ -0,0 +1,18 @@
+import { ActionType, createAsyncAction } from "typesafe-actions";
+import { NetworkError } from "../../../../../utils/errors";
+import { Transaction } from "../../../../../types/pagopa";
+
+export type WalletTransactionDetailsPayload = {
+ transactionId: number;
+};
+
+export const walletTransactionDetailsGet = createAsyncAction(
+ "WALLET_TRANSACTION_DETAILS_REQUEST",
+ "WALLET_TRANSACTION_DETAILS_SUCCESS",
+ "WALLET_TRANSACTION_DETAILS_FAILURE",
+ "WALLET_TRANSACTION_DETAILS_CANCEL"
+)();
+
+export type WalletTransactionActions = ActionType<
+ typeof walletTransactionDetailsGet
+>;
diff --git a/ts/features/walletV3/transaction/store/index.ts b/ts/features/walletV3/transaction/store/index.ts
new file mode 100644
index 00000000000..f0979f9c0b8
--- /dev/null
+++ b/ts/features/walletV3/transaction/store/index.ts
@@ -0,0 +1,68 @@
+import * as pot from "@pagopa/ts-commons/lib/pot";
+import * as _ from "lodash";
+import { createSelector } from "reselect";
+import { getType } from "typesafe-actions";
+import { Action } from "../../../../store/actions/types";
+import { NetworkError } from "../../../../utils/errors";
+import { GlobalState } from "../../../../store/reducers/types";
+
+import { Transaction } from "../../../../types/pagopa";
+import { walletTransactionDetailsGet } from "./actions";
+
+export type WalletTransactionState = {
+ details: pot.Pot;
+};
+
+const INITIAL_STATE: WalletTransactionState = {
+ details: pot.noneLoading
+};
+
+const walletTransactionReducer = (
+ state: WalletTransactionState = INITIAL_STATE,
+ action: Action
+): WalletTransactionState => {
+ switch (action.type) {
+ // GET TRANSACTION DETAILS
+ case getType(walletTransactionDetailsGet.request):
+ return {
+ ...state,
+ details: pot.toLoading(pot.none)
+ };
+ case getType(walletTransactionDetailsGet.success):
+ return {
+ ...state,
+ details: pot.some(action.payload)
+ };
+ case getType(walletTransactionDetailsGet.failure):
+ return {
+ ...state,
+ details: pot.toError(state.details, action.payload)
+ };
+ case getType(walletTransactionDetailsGet.cancel):
+ return {
+ ...state,
+ details: pot.none
+ };
+ }
+ return state;
+};
+
+const walletTransactionSelector = (state: GlobalState) =>
+ state.features.wallet.transaction;
+
+export const walletTransactionDetailsPotSelector = createSelector(
+ walletTransactionSelector,
+ transaction => transaction.details
+);
+
+export const walletTransactionDetailsSelector = createSelector(
+ walletTransactionDetailsPotSelector,
+ details => pot.toUndefined(pot.map(details, el => el))
+);
+
+export const isLoadingTransactionDetailsSelector = createSelector(
+ walletTransactionDetailsPotSelector,
+ details => pot.isLoading(details)
+);
+
+export default walletTransactionReducer;
diff --git a/ts/navigation/AuthenticatedStackNavigator.tsx b/ts/navigation/AuthenticatedStackNavigator.tsx
index d7e75f447fa..8f2c279d6e7 100644
--- a/ts/navigation/AuthenticatedStackNavigator.tsx
+++ b/ts/navigation/AuthenticatedStackNavigator.tsx
@@ -55,6 +55,10 @@ import { WalletPaymentRoutes } from "../features/walletV3/payment/navigation/rou
import { WalletPaymentBarcodeScanScreen } from "../features/walletV3/payment/screens/WalletPaymentBarcodeScanScreen";
import { ZendeskStackNavigator } from "../features/zendesk/navigation/navigator";
import ZENDESK_ROUTES from "../features/zendesk/navigation/routes";
+import {
+ WalletTransactionNavigator,
+ WalletTransactionRoutes
+} from "../features/walletV3/transaction/navigation/navigator";
import { useIOSelector } from "../store/hooks";
import {
isCdcEnabledSelector,
@@ -285,6 +289,14 @@ const AuthenticatedStackNavigator = () => {
...hideHeaderOptions
}}
/>
+
{/*
This screen is outside the WalletPaymentNavigator to enable the slide from bottom animation.
FIXME IOBP-383: Using react-navigation 6.x we can achive this using a Stack.Group inside the WalletPaymentNavigator
diff --git a/ts/navigation/params/AppParamsList.ts b/ts/navigation/params/AppParamsList.ts
index 617a8d41781..9a42410f75d 100644
--- a/ts/navigation/params/AppParamsList.ts
+++ b/ts/navigation/params/AppParamsList.ts
@@ -51,6 +51,10 @@ import {
WalletDetailsParamsList,
WalletDetailsRoutes
} from "../../features/walletV3/details/navigation/navigator";
+import {
+ WalletTransactionParamsList,
+ WalletTransactionRoutes
+} from "../../features/walletV3/transaction/navigation/navigator";
import { WalletPaymentParamsList } from "../../features/walletV3/payment/navigation/params";
import { WalletPaymentRoutes } from "../../features/walletV3/payment/navigation/routes";
import { ZendeskParamsList } from "../../features/zendesk/navigation/params";
@@ -107,6 +111,7 @@ export type AppParamsList = {
[WalletPaymentRoutes.WALLET_PAYMENT_MAIN]: NavigatorScreenParams;
[WalletPaymentRoutes.WALLET_PAYMENT_BARCODE_SCAN]: undefined; // FIXME IOBP-383: remove after react-navigation 6.x upgrade. This should be insde WALLET_PAYMENT_MAIN
[WalletDetailsRoutes.WALLET_DETAILS_MAIN]: NavigatorScreenParams;
+ [WalletTransactionRoutes.WALLET_TRANSACTION_MAIN]: NavigatorScreenParams;
};
/**
diff --git a/ts/screens/wallet/WalletHomeScreen.tsx b/ts/screens/wallet/WalletHomeScreen.tsx
index aeeb6ee85ea..6cce2659799 100644
--- a/ts/screens/wallet/WalletHomeScreen.tsx
+++ b/ts/screens/wallet/WalletHomeScreen.tsx
@@ -86,7 +86,10 @@ import {
isIdPayEnabledSelector
} from "../../store/reducers/backendStatus";
import { paymentsHistorySelector } from "../../store/reducers/payments/history";
-import { isPagoPATestEnabledSelector } from "../../store/reducers/persistedPreferences";
+import {
+ isDesignSystemEnabledSelector,
+ isPagoPATestEnabledSelector
+} from "../../store/reducers/persistedPreferences";
import { GlobalState } from "../../store/reducers/types";
import { creditCardAttemptsSelector } from "../../store/reducers/wallet/creditCard";
import {
@@ -103,6 +106,7 @@ import customVariables from "../../theme/variables";
import { Transaction, Wallet } from "../../types/pagopa";
import { isStrictSome } from "../../utils/pot";
import { showToast } from "../../utils/showToast";
+import { WalletTransactionRoutes } from "../../features/walletV3/transaction/navigation/navigator";
export type WalletHomeNavigationParams = Readonly<{
newMethodAdded: boolean;
@@ -432,6 +436,24 @@ class WalletHomeScreen extends React.PureComponent {
this.props.loadTransactions(this.props.transactionsLoadedLength);
};
+ private navigateToWalletTransactionDetailsScreen = (
+ transaction: Transaction
+ ) => {
+ if (this.props.isDesignSystemEnabled) {
+ this.props.navigation.navigate(
+ WalletTransactionRoutes.WALLET_TRANSACTION_MAIN,
+ {
+ screen: WalletTransactionRoutes.WALLET_TRANSACTION_DETAILS,
+ params: {
+ transactionId: transaction.id
+ }
+ }
+ );
+ } else {
+ this.props.navigateToTransactionDetailsScreen(transaction);
+ }
+ };
+
private transactionList(
potTransactions: pot.Pot, Error>
) {
@@ -442,7 +464,7 @@ class WalletHomeScreen extends React.PureComponent {
areMoreTransactionsAvailable={this.props.areMoreTransactionsAvailable}
onLoadMoreTransactions={this.handleLoadMoreTransactions}
navigateToTransactionDetails={
- this.props.navigateToTransactionDetailsScreen
+ this.navigateToWalletTransactionDetailsScreen
}
ListEmptyComponent={this.listEmptyComponent()}
/>
@@ -556,6 +578,7 @@ const mapStateToProps = (state: GlobalState) => ({
bancomatListVisibleInWallet: bancomatListVisibleInWalletSelector(state),
coBadgeListVisibleInWallet: cobadgeListVisibleInWalletSelector(state),
bpdConfig: bpdRemoteConfigSelector(state),
+ isDesignSystemEnabled: isDesignSystemEnabledSelector(state),
isIdPayEnabled: isIdPayEnabledSelector(state)
});
diff --git a/ts/utils/stringBuilder.ts b/ts/utils/stringBuilder.ts
index d350642d0f4..14ed286392d 100644
--- a/ts/utils/stringBuilder.ts
+++ b/ts/utils/stringBuilder.ts
@@ -15,13 +15,18 @@ export const centsToAmount = (cents: number): number =>
export const formatNumberAmount = (
amount: number,
- displayCurrency: boolean = false
+ displayCurrency: boolean = false,
+ currencyPosition: "left" | "right" = "left"
): string =>
I18n.toCurrency(amount, {
precision: DISPLAYED_DIGITS,
delimiter: I18n.t("global.localization.delimiterSeparator"),
separator: I18n.t("global.localization.decimalSeparator"),
- format: displayCurrency ? "€ %n" : "%n"
+ format: displayCurrency
+ ? currencyPosition === "left"
+ ? "€ %n"
+ : "%n €"
+ : "%n"
});
/**
@@ -42,8 +47,10 @@ export const formatNumberWithNoDigits = (
export const formatNumberCentsToAmount = (
cents: number,
- displayCurrency: boolean = false
-): string => formatNumberAmount(centsToAmount(cents), displayCurrency);
+ displayCurrency: boolean = false,
+ currencyPosition: "left" | "right" = "left"
+): string =>
+ formatNumberAmount(centsToAmount(cents), displayCurrency, currencyPosition);
export const buildExpirationDate = (creditCard: CardInfo): string =>
`${creditCard.expireMonth}/${creditCard.expireYear}`;