From 58557e7a6776f6a613ae12ee51a01746593cd8f0 Mon Sep 17 00:00:00 2001 From: Cristian Matteu <94987118+ChrisMattew@users.noreply.github.com> Date: Fri, 17 Jan 2025 10:59:49 +0100 Subject: [PATCH 1/3] [IOPID-2598] Tracking offline event (#6606) ## Short description This PR adds the tracking of the `INGRESS_NO_INTERNET_CONNECTION` event, logged when the user opens the app with the internet connection disabled. ## List of changes proposed in this pull request - Added and implemented the `INGRESS_NO_INTERNET_CONNECTION` event - Added the `event_category: KO` to the already existing `INGRESS_SERVICES_SLOW_DOWN`, `INGRESS_TIMEOUT` and `INGRESS_CDN_SYSTEM_ERROR` events ## Demo |iOS|Android| |---|--------| ||| ## How to test Start the app ensuring that the internet connection is disabled (as shown in the demo). Once the blocking screen appears, close the app, re-enable the internet connection, and reopen the app. >[!Note] >In this demos, I'm using [Proxyman](https://proxyman.io/) to monitor internet traffic, but any tool of your choice can be used. --- ts/features/ingress/analytics/index.ts | 20 +++++++++++-- ts/features/ingress/screens/IngressScreen.tsx | 30 ++++++++++++++----- 2 files changed, 39 insertions(+), 11 deletions(-) diff --git a/ts/features/ingress/analytics/index.ts b/ts/features/ingress/analytics/index.ts index 8e8acd7efb1..a2cc1e94eec 100644 --- a/ts/features/ingress/analytics/index.ts +++ b/ts/features/ingress/analytics/index.ts @@ -1,13 +1,27 @@ import { mixpanelTrack } from "../../../mixpanel"; +import { buildEventProperties } from "../../../utils/analytics"; export function trackIngressServicesSlowDown() { - void mixpanelTrack("INGRESS_SERVICES_SLOW_DOWN"); + void mixpanelTrack( + "INGRESS_SERVICES_SLOW_DOWN", + buildEventProperties("KO", undefined) + ); } export function trackIngressTimeout() { - void mixpanelTrack("INGRESS_TIMEOUT"); + void mixpanelTrack("INGRESS_TIMEOUT", buildEventProperties("KO", undefined)); } export function trackIngressCdnSystemError() { - void mixpanelTrack("INGRESS_CDN_SYSTEM_ERROR"); + void mixpanelTrack( + "INGRESS_CDN_SYSTEM_ERROR", + buildEventProperties("KO", undefined) + ); +} + +export function trackIngressNoInternetConnection() { + void mixpanelTrack( + "INGRESS_NO_INTERNET_CONNECTION", + buildEventProperties("KO", undefined) + ); } diff --git a/ts/features/ingress/screens/IngressScreen.tsx b/ts/features/ingress/screens/IngressScreen.tsx index 63b4d132e49..86fbd3b9b46 100644 --- a/ts/features/ingress/screens/IngressScreen.tsx +++ b/ts/features/ingress/screens/IngressScreen.tsx @@ -21,6 +21,7 @@ import ModalSectionStatusComponent from "../../../components/SectionStatus/modal import { isMixpanelInitializedSelector } from "../../mixpanel/store/selectors"; import { trackIngressCdnSystemError, + trackIngressNoInternetConnection, trackIngressServicesSlowDown, trackIngressTimeout } from "../analytics"; @@ -84,14 +85,7 @@ export const IngressScreen = () => { }, [dispatch]); if (netInfo && !netInfo.isConnected) { - return ( - - ); + return ; } if (showBlockingScreen) { @@ -114,6 +108,26 @@ export const IngressScreen = () => { ); }; +const IngressScreenNoInternetConnection = memo(() => { + const isMixpanelEnabled = useIOSelector(isMixpanelEnabledSelector); + const isMixpanelInitialized = useIOSelector(isMixpanelInitializedSelector); + + useEffect(() => { + if (isMixpanelInitialized && isMixpanelEnabled !== false) { + void trackIngressNoInternetConnection(); + } + }, [isMixpanelEnabled, isMixpanelInitialized]); + + return ( + + ); +}); + const IngressScreenBlockingError = memo(() => { const operationRef = useRef(null); const isBackendStatusLoaded = useIOSelector(isBackendStatusLoadedSelector); From d76c8c9ce48c6f81ac36eb48cf30e7c71bea76aa Mon Sep 17 00:00:00 2001 From: Emanuele Dall'Ara <71103219+LeleDallas@users.noreply.github.com> Date: Fri, 17 Jan 2025 11:44:31 +0100 Subject: [PATCH 2/3] refactor[IOBP-1119]: Adapt IDPay custom table and list to design system components (#6597) ## Short description > [!WARNING] > This PR depends on https://github.com/pagopa/io-app/pull/6586 This pull request focuses on enhancing the integration of the design system list components for IDPay screens currently utilizing custom tables and lists ## List of changes proposed in this pull request - Apply `ListItem` to IDPay timeline screens - Apply `ListItem` to IDPay initiative screens - Delete unused custom `Table` component moving its type in main file ## How to test Ensure that all replaced components are properly aligned with design standards ## Preview https://github.com/user-attachments/assets/f5da985d-9c37-43ef-b80f-558645997f5c --------- Co-authored-by: Alessandro --- ts/features/idpay/common/components/Table.tsx | 50 ----- .../components/BeneficiaryDetailsContent.tsx | 192 ++++++++++-------- .../InitiativeDiscountSettingsComponent.tsx | 9 +- .../InitiativeRefundSettingsComponent.tsx | 12 +- .../InitiativeTimelineComponent.tsx | 19 +- .../screens/IdPayInitiativeDetailsScreen.tsx | 3 - ...ineDiscountTransactionDetailsComponent.tsx | 117 ++++------- .../TimelineRefundDetailsComponent.tsx | 87 ++++---- .../TimelineTransactionDetailsComponent.tsx | 114 +++++------ 9 files changed, 257 insertions(+), 346 deletions(-) delete mode 100644 ts/features/idpay/common/components/Table.tsx diff --git a/ts/features/idpay/common/components/Table.tsx b/ts/features/idpay/common/components/Table.tsx deleted file mode 100644 index ff37696e971..00000000000 --- a/ts/features/idpay/common/components/Table.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import React, { Fragment } from "react"; -import { StyleSheet, View } from "react-native"; -import { Body, Divider, H6, WithTestID } from "@pagopa/io-app-design-system"; - -export type TableRow = WithTestID<{ - label: string; - value: string | React.ReactNode; -}>; - -type TableProps = { - title: string; - rows: ReadonlyArray; -}; - -const renderTable = (data: ReadonlyArray): React.ReactNode => - data.map((item, index) => { - const isLast = data.length === index + 1; - - return ( - - - {item.label} - {item.value} - - {!isLast && } - - ); - }); - -export const Table = (props: TableProps) => ( - <> - -
{props.title}
-
- {renderTable(props.rows)} - -); - -const styles = StyleSheet.create({ - sectionHeader: { - paddingTop: 16, - paddingBottom: 8 - }, - infoRow: { - flex: 1, - flexDirection: "row", - justifyContent: "space-between", - paddingVertical: 12 - } -}); diff --git a/ts/features/idpay/details/components/BeneficiaryDetailsContent.tsx b/ts/features/idpay/details/components/BeneficiaryDetailsContent.tsx index 71349c27c27..38350bca942 100644 --- a/ts/features/idpay/details/components/BeneficiaryDetailsContent.tsx +++ b/ts/features/idpay/details/components/BeneficiaryDetailsContent.tsx @@ -1,11 +1,20 @@ -import { Body, BodySmall, VSpacer } from "@pagopa/io-app-design-system"; +import { + Body, + BodySmall, + Divider, + ListItemHeader, + ListItemInfo, + ListItemTransaction, + VSpacer, + WithTestID +} from "@pagopa/io-app-design-system"; import { NonEmptyString } from "@pagopa/ts-commons/lib/strings"; import { useNavigation } from "@react-navigation/native"; import { sequenceS } from "fp-ts/lib/Apply"; import * as O from "fp-ts/lib/Option"; import { pipe } from "fp-ts/lib/function"; import React from "react"; -import { StyleSheet, View } from "react-native"; +import { View } from "react-native"; import Placeholder from "rn-placeholder"; import { InitiativeDTO, @@ -17,7 +26,6 @@ import { RewardValueDTO, RewardValueTypeEnum } from "../../../../../definitions/idpay/RewardValueDTO"; -import { IOStyles } from "../../../../components/core/variables/IOStyles"; import I18n from "../../../../i18n"; import { AppParamsList, @@ -25,7 +33,6 @@ import { } from "../../../../navigation/params/AppParamsList"; import { format } from "../../../../utils/dates"; import { SERVICES_ROUTES } from "../../../services/common/navigation/routes"; -import { Table, TableRow } from "../../common/components/Table"; import { formatNumberCurrencyOrDefault } from "../../common/utils/strings"; import { IdPayUnsubscriptionRoutes } from "../../unsubscription/navigation/routes"; import { @@ -33,6 +40,11 @@ import { InitiativeRulesInfoBoxSkeleton } from "./InitiativeRulesInfoBox"; +type TableRow = WithTestID<{ + label: string; + value: string | React.ReactNode; +}>; + export type BeneficiaryDetailsProps = | { isLoading?: false; @@ -210,80 +222,95 @@ const BeneficiaryDetailsContent = (props: BeneficiaryDetailsProps) => { ); }; + const summaryData = [ + { + label: I18n.t("idpay.initiative.beneficiaryDetails.status"), + value: statusString, + testID: "statusTestID" + }, + { + label: I18n.t("idpay.initiative.beneficiaryDetails.endDate"), + value: endDateString, + testID: "endDateTestID" + }, + { + label: I18n.t("idpay.initiative.beneficiaryDetails.amount"), + value: formatNumberCurrencyOrDefault(initiativeDetails.amountCents), + testID: "amountTestID" + }, + ...getTypeDependantTableRows() + ]; + + const enrollmentData = [ + { + label: I18n.t("idpay.initiative.beneficiaryDetails.enrollmentDate"), + value: onboardingDateString, + testID: "onboardingDateTestID" + }, + { + label: I18n.t("idpay.initiative.beneficiaryDetails.protocolNumber"), + value: "-", + testID: "protocolTestID" + } + ]; + + const spendingRulesData = [ + { + label: I18n.t("idpay.initiative.beneficiaryDetails.spendFrom"), + value: fruitionStartDateString, + testID: "fruitionStartDateTestID" + }, + { + label: I18n.t("idpay.initiative.beneficiaryDetails.spendTo"), + value: fruitionEndDateString, + testID: "fruitionEndDateTestID" + }, + rewardRuleRow + ]; + + const renderTableRow = (data: Array) => + data.map((row, i) => ( + <> + + {i !== data.length - 1 && } + + )); + return ( <> {ruleInfoBox} - + {renderTableRow(summaryData)} {lastUpdateString} -
- -
- - - - {I18n.t("idpay.initiative.beneficiaryDetails.buttons.privacy")} - - - - - {I18n.t("idpay.initiative.beneficiaryDetails.buttons.unsubscribe", { - initiativeName - })} - - + {renderTableRow(enrollmentData)} + + + {I18n.t("idpay.initiative.beneficiaryDetails.buttons.privacy")} + + + + {I18n.t("idpay.initiative.beneficiaryDetails.buttons.unsubscribe", { + initiativeName + })} + ); @@ -297,22 +324,17 @@ const BeneficiaryDetailsContentSkeleton = () => ( - {Array.from({ length: 5 }).map((_, j) => ( + {Array.from({ length: 2 }).map((_, j) => ( - - - - + ))} @@ -321,10 +343,4 @@ const BeneficiaryDetailsContentSkeleton = () => ( ); -const styles = StyleSheet.create({ - linkRow: { - paddingVertical: 16 - } -}); - export { BeneficiaryDetailsContent }; diff --git a/ts/features/idpay/details/components/InitiativeDiscountSettingsComponent.tsx b/ts/features/idpay/details/components/InitiativeDiscountSettingsComponent.tsx index 583cae36552..47681b5fa31 100644 --- a/ts/features/idpay/details/components/InitiativeDiscountSettingsComponent.tsx +++ b/ts/features/idpay/details/components/InitiativeDiscountSettingsComponent.tsx @@ -1,4 +1,4 @@ -import { H6, ListItemNav, VSpacer } from "@pagopa/io-app-design-system"; +import { ListItemHeader, ListItemNav } from "@pagopa/io-app-design-system"; import { useNavigation } from "@react-navigation/core"; import * as O from "fp-ts/lib/Option"; import { pipe } from "fp-ts/lib/function"; @@ -84,12 +84,11 @@ const InitiativeDiscountSettingsComponent = (props: Props) => { return ( -
- {I18n.t( + - + /> {instrumentsSettingsButton} ); diff --git a/ts/features/idpay/details/components/InitiativeRefundSettingsComponent.tsx b/ts/features/idpay/details/components/InitiativeRefundSettingsComponent.tsx index ea40f1fbd0b..f72463ef26b 100644 --- a/ts/features/idpay/details/components/InitiativeRefundSettingsComponent.tsx +++ b/ts/features/idpay/details/components/InitiativeRefundSettingsComponent.tsx @@ -1,8 +1,7 @@ import { - H6, + ListItemHeader, ListItemNav, - ListItemNavAlert, - VSpacer + ListItemNavAlert } from "@pagopa/io-app-design-system"; import { useNavigation } from "@react-navigation/core"; import * as O from "fp-ts/lib/Option"; @@ -171,12 +170,11 @@ const InitiativeRefundSettingsComponent = (props: Props) => { return ( -
- {I18n.t( + - + /> {instrumentsSettingsButton} {ibanSettingsButton} diff --git a/ts/features/idpay/details/components/InitiativeTimelineComponent.tsx b/ts/features/idpay/details/components/InitiativeTimelineComponent.tsx index dfc3214da2a..b5106087d4c 100644 --- a/ts/features/idpay/details/components/InitiativeTimelineComponent.tsx +++ b/ts/features/idpay/details/components/InitiativeTimelineComponent.tsx @@ -3,7 +3,8 @@ import { Divider, H6, BodySmall, - VSpacer + VSpacer, + ListItemHeader } from "@pagopa/io-app-design-system"; import * as pot from "@pagopa/ts-commons/lib/pot"; import { useNavigation } from "@react-navigation/native"; @@ -73,8 +74,20 @@ const InitiativeTimelineComponent = ({ initiativeId, size = 3 }: Props) => { return ( - - + {renderTimelineContent()} {detailsBottomSheet.bottomSheet} diff --git a/ts/features/idpay/details/screens/IdPayInitiativeDetailsScreen.tsx b/ts/features/idpay/details/screens/IdPayInitiativeDetailsScreen.tsx index cba8831c2e0..ff89835112b 100644 --- a/ts/features/idpay/details/screens/IdPayInitiativeDetailsScreen.tsx +++ b/ts/features/idpay/details/screens/IdPayInitiativeDetailsScreen.tsx @@ -203,18 +203,15 @@ const IdPayInitiativeDetailsScreen = () => { case InitiativeRewardTypeEnum.DISCOUNT: return ( - - - ); diff --git a/ts/features/idpay/timeline/components/TimelineDiscountTransactionDetailsComponent.tsx b/ts/features/idpay/timeline/components/TimelineDiscountTransactionDetailsComponent.tsx index 9bea069faea..f6b19aae345 100644 --- a/ts/features/idpay/timeline/components/TimelineDiscountTransactionDetailsComponent.tsx +++ b/ts/features/idpay/timeline/components/TimelineDiscountTransactionDetailsComponent.tsx @@ -1,20 +1,19 @@ import { Alert, - Body, - H6, - HSpacer, + Divider, + ListItemHeader, + ListItemInfo, ListItemInfoCopy, VSpacer } 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 { StyleSheet, View } from "react-native"; +import { View } from "react-native"; import { TransactionDetailDTO, StatusEnum as TransactionStatusEnum } from "../../../../../definitions/idpay/TransactionDetailDTO"; -import ItemSeparatorComponent from "../../../../components/ItemSeparatorComponent"; import { IOStyles } from "../../../../components/core/variables/IOStyles"; import I18n from "../../../../i18n"; import { clipboardSetStringWithFeedback } from "../../../../utils/clipboard"; @@ -69,65 +68,46 @@ const TimelineDiscountTransactionDetailsComponent = (props: Props) => { {statusAlertComponent} - - - {I18n.t( - "idpay.initiative.operationDetails.discount.details.labels.totalAmount" - )} - - {formattedAmount} - - - - {I18n.t( - "idpay.initiative.operationDetails.discount.details.labels.idpayAmount" - )} - - - {formatNumberCentsToAmount(transaction.accruedCents, true)} - - - - -
- {I18n.t("idpay.initiative.operationDetails.transaction.infoTitle")} -
- - - - {I18n.t( - "idpay.initiative.operationDetails.discount.details.labels.business" - )} - - - - {businessName} - - - - - {I18n.t( - "idpay.initiative.operationDetails.discount.details.labels.status" - )} - - - {I18n.t( - `idpay.initiative.operationDetails.discount.labels.${transaction.status}` - )} - - - - - {I18n.t("idpay.initiative.operationDetails.transaction.date")} - - - {format(transaction.operationDate, "DD MMM YYYY, HH:mm")} - - + + + + + + + + + + { ); }; -const styles = StyleSheet.create({ - detailRow: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - paddingVertical: 8 - } -}); - export { TimelineDiscountTransactionDetailsComponent }; diff --git a/ts/features/idpay/timeline/components/TimelineRefundDetailsComponent.tsx b/ts/features/idpay/timeline/components/TimelineRefundDetailsComponent.tsx index d9a7f1bc60a..24bf7f7660f 100644 --- a/ts/features/idpay/timeline/components/TimelineRefundDetailsComponent.tsx +++ b/ts/features/idpay/timeline/components/TimelineRefundDetailsComponent.tsx @@ -1,8 +1,9 @@ import { useBottomSheet } from "@gorhom/bottom-sheet"; import { Alert, - Badge, - Body, + Divider, + ListItemHeader, + ListItemInfo, ListItemInfoCopy, VSpacer } from "@pagopa/io-app-design-system"; @@ -11,13 +12,11 @@ import { format } from "date-fns"; import * as O from "fp-ts/lib/Option"; import { pipe } from "fp-ts/lib/function"; import React from "react"; -import { StyleSheet, View } from "react-native"; import { RefundDetailDTO } from "../../../../../definitions/idpay/RefundDetailDTO"; import { OperationTypeEnum } from "../../../../../definitions/idpay/RefundOperationDTO"; import I18n from "../../../../i18n"; import NavigationService from "../../../../navigation/NavigationService"; import { useIOSelector } from "../../../../store/hooks"; -import themeVariables from "../../../../theme/variables"; import { clipboardSetStringWithFeedback } from "../../../../utils/clipboard"; import { formatNumberCentsToAmount } from "../../../../utils/stringBuilder"; import { IdPayConfigurationRoutes } from "../../configuration/navigation/routes"; @@ -85,44 +84,41 @@ const TimelineRefundDetailsComponent = (props: Props) => { return ( <> {rejectedAlertComponent} - - {I18n.t("idpay.initiative.operationDetails.refund.iban")} - {refund.iban} - - - {I18n.t("idpay.initiative.operationDetails.refund.amount")} - {formattedAmount} - - - - {I18n.t("idpay.initiative.operationDetails.refund.resultLabel")} - - {refund.operationType === OperationTypeEnum.REJECTED_REFUND ? ( - - ) : ( - + + + + - )} - - - {I18n.t("idpay.initiative.operationDetails.refund.period")} - {getRefundPeriodDateString(refund)} - - - Data rimborso - - {format(refund.operationDate, "DD MMM YYYY, HH:mm")} - - + ) + } + }} + /> + + + + { ); }; -const styles = StyleSheet.create({ - detailRow: { - flexDirection: "row", - justifyContent: "space-between", - paddingTop: themeVariables.spacerSmallHeight, - paddingBottom: themeVariables.spacerSmallHeight - } -}); - export { TimelineRefundDetailsComponent }; diff --git a/ts/features/idpay/timeline/components/TimelineTransactionDetailsComponent.tsx b/ts/features/idpay/timeline/components/TimelineTransactionDetailsComponent.tsx index ef9085d4130..694785c6d57 100644 --- a/ts/features/idpay/timeline/components/TimelineTransactionDetailsComponent.tsx +++ b/ts/features/idpay/timeline/components/TimelineTransactionDetailsComponent.tsx @@ -1,22 +1,21 @@ import { Alert, - Body, - H6, - HSpacer, + Divider, + IOLogoPaymentType, + ListItemHeader, + ListItemInfo, ListItemInfoCopy, VSpacer } 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 { StyleSheet, View } from "react-native"; +import { View } from "react-native"; import { TransactionDetailDTO, OperationTypeEnum as TransactionTypeEnum } from "../../../../../definitions/idpay/TransactionDetailDTO"; -import ItemSeparatorComponent from "../../../../components/ItemSeparatorComponent"; import { IOStyles } from "../../../../components/core/variables/IOStyles"; -import { LogoPaymentWithFallback } from "../../../../components/ui/utils/components/LogoPaymentWithFallback"; import I18n from "../../../../i18n"; import { clipboardSetStringWithFeedback } from "../../../../utils/clipboard"; import { format } from "../../../../utils/dates"; @@ -66,59 +65,47 @@ const TimelineTransactionDetailsComponent = (props: Props) => { return ( {reversalAlertComponent} - - - {I18n.t("idpay.initiative.operationDetails.transaction.instrument")} - - - - - - {I18n.t("idpay.initiative.operationDetails.transaction.maskedPan", { - lastDigits: transaction.maskedPan - })} - - - - - - {I18n.t("idpay.initiative.operationDetails.transaction.amountLabel")} - - {formattedAmount} - - - - {I18n.t( - "idpay.initiative.operationDetails.transaction.accruedAmountLabel" - )} - - {formattedAccrued} - - - -
- {I18n.t("idpay.initiative.operationDetails.transaction.infoTitle")} -
- - - - {I18n.t("idpay.initiative.operationDetails.transaction.date")} - - - {format(transaction.operationDate, "DD MMM YYYY, HH:mm")} - - - - - {I18n.t("idpay.initiative.operationDetails.transaction.circuit")} - - - {getLabelForCircuitType(transaction.circuitType)} - - + + + + + + + + + + { clipboardSetStringWithFeedback(idTrxAcquirer); }} /> + { ); }; -const styles = StyleSheet.create({ - detailRow: { - flexDirection: "row", - justifyContent: "space-between", - paddingVertical: 8 - } -}); - export { TimelineTransactionDetailsComponent }; From eddd677034d88c164279fc9a23cd7128a4145056 Mon Sep 17 00:00:00 2001 From: Andrea Date: Fri, 17 Jan 2025 13:14:23 +0100 Subject: [PATCH 3/3] [IOCOM-1914] Paid payment badge from message details (#6610) ## Short description This PR adds the PAID badge to a message in the messages home if the related check happens on the message details.