diff --git a/img/bonus/bpd/bonus_bg.png b/img/bonus/bpd/bonus_bg.png deleted file mode 100644 index 3fe6eb3a393..00000000000 Binary files a/img/bonus/bpd/bonus_bg.png and /dev/null differ diff --git a/img/bonus/bpd/bonus_preview_bg.png b/img/bonus/bpd/bonus_preview_bg.png deleted file mode 100644 index 8306a480fc3..00000000000 Binary files a/img/bonus/bpd/bonus_preview_bg.png and /dev/null differ diff --git a/img/bonus/bpd/fireworks.png b/img/bonus/bpd/fireworks.png deleted file mode 100644 index 5bec88cd5aa..00000000000 Binary files a/img/bonus/bpd/fireworks.png and /dev/null differ diff --git a/img/bonus/bpd/logo_BonusCashback_White.png b/img/bonus/bpd/logo_BonusCashback_White.png deleted file mode 100644 index b131968a391..00000000000 Binary files a/img/bonus/bpd/logo_BonusCashback_White.png and /dev/null differ diff --git a/img/bonus/bpd/logo_cashback_blue.png b/img/bonus/bpd/logo_cashback_blue.png deleted file mode 100644 index 03ba047b77a..00000000000 Binary files a/img/bonus/bpd/logo_cashback_blue.png and /dev/null differ diff --git a/ts/features/bonus/bpd/api/award-period/v1.ts b/ts/features/bonus/bpd/api/award-period/v1.ts deleted file mode 100644 index b63dfb1432b..00000000000 --- a/ts/features/bonus/bpd/api/award-period/v1.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { - findAllUsingGETDefaultDecoder, - FindAllUsingGETT -} from "../../../../../../definitions/bpd/award_periods/requestTypes"; -import { bpdHeadersProducers } from "../common"; - -export const awardPeriodsGET: FindAllUsingGETT = { - method: "get", - url: () => `/bpd/io/award-periods`, - query: _ => ({}), - headers: bpdHeadersProducers(), - response_decoder: findAllUsingGETDefaultDecoder() -}; diff --git a/ts/features/bonus/bpd/api/backendBpdClient.ts b/ts/features/bonus/bpd/api/backendBpdClient.ts deleted file mode 100644 index 314f15ecb20..00000000000 --- a/ts/features/bonus/bpd/api/backendBpdClient.ts +++ /dev/null @@ -1,109 +0,0 @@ -import { createFetchRequestForApi } from "@pagopa/ts-commons/lib/requests"; -import { Iban } from "../../../../../definitions/backend/Iban"; -import { InitializedProfile } from "../../../../../definitions/backend/InitializedProfile"; -import { PayoffInstrTypeEnum } from "../../../../../definitions/bpd/citizen/CitizenPatchDTO"; -import { fetchPaymentManagerLongTimeout } from "../../../../config"; -import { defaultRetryingFetch } from "../../../../utils/fetch"; -import { awardPeriodsGET } from "./award-period/v1"; -import { - citizenDELETE, - citizenPaymentMethodPATCH, - PatchOptions -} from "./citizen/v1"; -import { - citizenV2EnrollPUT, - citizenV2FindGET, - citizenV2RankingGET -} from "./citizen/v2"; -import { - paymentInstrumentsDELETE, - paymentInstrumentsEnrollPUT, - paymentInstrumentsFindGET -} from "./payment-instrument/v1"; -import { winningTransactionsTotalCashbackGET } from "./winning-transactions/v1"; -import { - winningTransactionsV2CountByDayGET, - winningTransactionsV2GET -} from "./winning-transactions/v2"; - -const jsonContentType = "application/json; charset=utf-8"; - -export function BackendBpdClient( - baseUrl: string, - token: string, - fetchApi: typeof fetch = defaultRetryingFetch( - fetchPaymentManagerLongTimeout, - 0 - ) -) { - const options: PatchOptions = { - baseUrl, - fetchApi - }; - // withBearerToken injects the header "Bearer" with token - // and "Ocp-Apim-Subscription-Key" with an hard-coded value (perhaps it won't be used) - type extendHeaders = { - readonly apiKeyHeader?: string; - readonly Authorization?: string; - readonly Bearer?: string; - ["Ocp-Apim-Subscription-Key"]?: string; - }; - - const withBearerToken = -

(f: (p: P) => Promise) => - async (po: P): Promise => { - const params = Object.assign({ Bearer: token }, po) as P; - return f(params); - }; - - return { - findV2: withBearerToken( - createFetchRequestForApi(citizenV2FindGET, options) - ), - enrollCitizenV2IO: withBearerToken( - createFetchRequestForApi(citizenV2EnrollPUT, options) - ), - deleteCitizenIO: withBearerToken( - createFetchRequestForApi(citizenDELETE, options) - ), - updatePaymentMethod: (iban: Iban, profile: InitializedProfile) => - withBearerToken( - citizenPaymentMethodPATCH( - options, - token, - { - payoffInstr: iban, - payoffInstrType: PayoffInstrTypeEnum.IBAN, - accountHolderCF: profile.fiscal_code as string, - accountHolderName: profile.name, - accountHolderSurname: profile.family_name - }, - { ["Content-Type"]: jsonContentType } - ) - ), - findPayment: withBearerToken( - createFetchRequestForApi(paymentInstrumentsFindGET, options) - ), - enrollPayment: withBearerToken( - createFetchRequestForApi(paymentInstrumentsEnrollPUT, options) - ), - deletePayment: withBearerToken( - createFetchRequestForApi(paymentInstrumentsDELETE, options) - ), - awardPeriods: withBearerToken( - createFetchRequestForApi(awardPeriodsGET, options) - ), - totalCashback: withBearerToken( - createFetchRequestForApi(winningTransactionsTotalCashbackGET, options) - ), - winningTransactionsV2: withBearerToken( - createFetchRequestForApi(winningTransactionsV2GET, options) - ), - winningTransactionsV2CountByDay: withBearerToken( - createFetchRequestForApi(winningTransactionsV2CountByDayGET, options) - ), - getRankingV2: withBearerToken( - createFetchRequestForApi(citizenV2RankingGET, options) - ) - }; -} diff --git a/ts/features/bonus/bpd/api/citizen/v1.ts b/ts/features/bonus/bpd/api/citizen/v1.ts deleted file mode 100644 index 7e2b7d2835a..00000000000 --- a/ts/features/bonus/bpd/api/citizen/v1.ts +++ /dev/null @@ -1,117 +0,0 @@ -import * as E from "fp-ts/lib/Either"; -import * as t from "io-ts"; -import * as r from "@pagopa/ts-commons/lib/requests"; -import { CitizenPatchDTO } from "../../../../../../definitions/bpd/citizen/CitizenPatchDTO"; -import { - findRankingUsingGETDefaultDecoder, - FindRankingUsingGETT -} from "../../../../../../definitions/bpd/citizen/requestTypes"; -import { bpdHeadersProducers } from "../common"; - -const deleteResponseDecoders = r.composeResponseDecoders( - r.composeResponseDecoders( - r.constantResponseDecoder(204, undefined), - r.constantResponseDecoder(401, undefined) - ), - r.constantResponseDecoder(404, undefined) -); - -// these responses code/codec are built from api usage and not from API spec -type DeleteUsingDELETETExtra = r.IDeleteApiRequestType< - { - readonly Authorization: string; - readonly x_request_id?: string; - }, - never, - never, - | r.IResponseType<204, undefined> - | r.IResponseType<401, undefined> - | r.IResponseType<404, undefined> - | r.IResponseType<500, undefined> ->; - -export const citizenDELETE: DeleteUsingDELETETExtra = { - method: "delete", - url: () => `/bpd/io/citizen`, - query: _ => ({}), - headers: bpdHeadersProducers(), - response_decoder: deleteResponseDecoders -}; - -/** - * @deprecated - * citizen ranking (super cashback) - */ -export const citizenRankingGET: FindRankingUsingGETT = { - method: "get", - url: () => `/bpd/io/citizen/ranking`, - query: (_: { awardPeriodId?: string }) => ({}), - headers: bpdHeadersProducers(), - response_decoder: findRankingUsingGETDefaultDecoder() -}; - -export type PatchOptions = { - baseUrl: string; - fetchApi: typeof fetch; -}; - -/* Patch IBAN */ -const PatchIban = t.interface({ validationStatus: t.string }); -type PatchIban = t.TypeOf; - -type finalType = - | r.IResponseType<200, PatchIban> - | r.IResponseType<401, undefined> - | r.IResponseType<404, undefined> - | r.IResponseType<400, undefined> - | r.IResponseType<500, undefined>; - -// decoders composition to handle updatePaymentMethod response -export function patchIbanDecoders(type: t.Type) { - return r.composeResponseDecoders( - r.composeResponseDecoders( - r.composeResponseDecoders( - r.ioResponseDecoder<200, (typeof type)["_A"], (typeof type)["_O"]>( - 200, - type - ), - r.composeResponseDecoders( - r.constantResponseDecoder(400, undefined), - r.constantResponseDecoder(401, undefined) - ) - ), - r.constantResponseDecoder(404, undefined) - ), - r.constantResponseDecoder(500, undefined) - ); -} - -// custom implementation of patch request -// TODO abstract the usage of fetch -export const citizenPaymentMethodPATCH = - ( - options: PatchOptions, - token: string, - payload: CitizenPatchDTO, - headers: Record - ): (() => Promise>) => - async () => { - const response = await options.fetchApi( - `${options.baseUrl}/bpd/io/citizen`, - { - method: "patch", - headers: { ...headers, Authorization: `Bearer ${token}` }, - body: JSON.stringify(payload) - } - ); - const decode = await patchIbanDecoders(PatchIban)(response); - return ( - decode ?? - E.left([ - { - context: [], - value: response - } - ]) - ); - }; diff --git a/ts/features/bonus/bpd/api/citizen/v2.ts b/ts/features/bonus/bpd/api/citizen/v2.ts deleted file mode 100644 index 8106223ef77..00000000000 --- a/ts/features/bonus/bpd/api/citizen/v2.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { - ApiHeaderJson, - composeHeaderProducers, - MapResponseType -} from "@pagopa/ts-commons/lib/requests"; -import { - enrollmentDecoder, - EnrollmentT as EnrollmentTV2, - findRankingUsingGETDefaultDecoder, - FindRankingUsingGETT, - findUsingGETDecoder, - FindUsingGETT as FindUsingGETTV2 -} from "../../../../../../definitions/bpd/citizen_v2/requestTypes"; -import { bpdHeadersProducers } from "../common"; -import { PatchedCitizenV2Resource } from "../patchedTypes"; - -type FindV2UsingGETTExtra = MapResponseType< - FindUsingGETTV2, - 200, - PatchedCitizenV2Resource ->; - -const findUsingGETCustomDecoder = findUsingGETDecoder({ - 200: PatchedCitizenV2Resource -}); - -export const citizenV2FindGET: FindV2UsingGETTExtra = { - method: "get", - url: () => `/bpd/io/citizen/v2`, - query: _ => ({}), - headers: bpdHeadersProducers(), - response_decoder: findUsingGETCustomDecoder -}; - -type EnrollmentV2TTExtra = MapResponseType< - EnrollmentTV2, - 200, - PatchedCitizenV2Resource ->; - -const enrollmentCustomDecoder = enrollmentDecoder({ - 200: PatchedCitizenV2Resource -}); - -export const citizenV2EnrollPUT: EnrollmentV2TTExtra = { - method: "put", - url: () => `/bpd/io/citizen/v2`, - query: _ => ({}), - body: ({ optInStatus }) => JSON.stringify(optInStatus ? { optInStatus } : {}), - headers: composeHeaderProducers(bpdHeadersProducers(), ApiHeaderJson), - response_decoder: enrollmentCustomDecoder -}; - -/** - * Request the user ranking + milestone information, containing the trxPivot - */ -export const citizenV2RankingGET: FindRankingUsingGETT = { - method: "get", - url: () => `/bpd/io/citizen/v2/ranking`, - query: (_: { awardPeriodId?: string }) => ({}), - headers: bpdHeadersProducers(), - response_decoder: findRankingUsingGETDefaultDecoder() -}; diff --git a/ts/features/bonus/bpd/api/common.ts b/ts/features/bonus/bpd/api/common.ts deleted file mode 100644 index 724ce4d892b..00000000000 --- a/ts/features/bonus/bpd/api/common.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { RequestHeaderProducer } from "@pagopa/ts-commons/lib/requests"; - -/** - * Produce a base header for the BPD API requests - */ -export const bpdHeadersProducers = < - P extends { - readonly Authorization: string; - } ->() => - ((p: P) => ({ - // since these headers are not correctly autogenerated we have to access them as an anonymous object - Authorization: `Bearer ${(p as any).Bearer}` - })) as RequestHeaderProducer; diff --git a/ts/features/bonus/bpd/api/patchedTypes.ts b/ts/features/bonus/bpd/api/patchedTypes.ts deleted file mode 100644 index 55475e84a8c..00000000000 --- a/ts/features/bonus/bpd/api/patchedTypes.ts +++ /dev/null @@ -1,49 +0,0 @@ -import * as t from "io-ts"; -import { enumType } from "@pagopa/ts-commons/lib/types"; -import { CitizenOptInStatusEnum } from "../../../../../definitions/bpd/citizen_v2/CitizenOptInStatus"; - -/** - * patched version of CitizenResource - * - payoffInstr and payoffInstrType must be optional - */ -// required attributes -const PatchedCitizenResourceR = t.interface({ - enabled: t.boolean, - - fiscalCode: t.string -}); - -// optional attributes -const PatchedCitizenResourceO = t.partial({ - payoffInstr: t.string, - payoffInstrType: t.string, - optInStatus: enumType( - CitizenOptInStatusEnum, - "optInStatus" - ) -}); - -export const PatchedCitizenResource = t.intersection( - [PatchedCitizenResourceR, PatchedCitizenResourceO], - "PatchedCitizenResource" -); - -export type PatchedCitizenResource = t.TypeOf; - -/** - * patched version of CitizenV2Resource - * - technicalAccount must be optional - */ -// required attributes -const PatchedCitizenV2ResourceO = t.partial({ - technicalAccount: t.string -}); - -export const PatchedCitizenV2Resource = t.intersection( - [PatchedCitizenResourceR, PatchedCitizenResourceO, PatchedCitizenV2ResourceO], - "PatchedCitizenResourceV2" -); - -export type PatchedCitizenV2Resource = t.TypeOf< - typeof PatchedCitizenV2Resource ->; diff --git a/ts/features/bonus/bpd/api/payment-instrument/v1.ts b/ts/features/bonus/bpd/api/payment-instrument/v1.ts deleted file mode 100644 index baad7fed209..00000000000 --- a/ts/features/bonus/bpd/api/payment-instrument/v1.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* PAYMENT (status, enroll, delete) */ -import * as r from "@pagopa/ts-commons/lib/requests"; -import { - ApiHeaderJson, - composeHeaderProducers -} from "@pagopa/ts-commons/lib/requests"; -import { - DeleteUsingDELETET, - enrollmentPaymentInstrumentIOUsingPUTDefaultDecoder, - EnrollmentPaymentInstrumentIOUsingPUTT, - findUsingGETDefaultDecoder, - FindUsingGETT as FindPaymentUsingGETT -} from "../../../../../../definitions/bpd/payment/requestTypes"; -import { bpdHeadersProducers } from "../common"; - -export const paymentInstrumentsFindGET: FindPaymentUsingGETT = { - method: "get", - url: ({ id }) => `/bpd/io/payment-instruments/${id}`, - query: _ => ({}), - headers: bpdHeadersProducers(), - response_decoder: findUsingGETDefaultDecoder() -}; - -export const paymentInstrumentsEnrollPUT: EnrollmentPaymentInstrumentIOUsingPUTT = - { - method: "put", - url: ({ id }) => `/bpd/io/payment-instruments/${id}`, - query: _ => ({}), - body: () => "", - headers: composeHeaderProducers(ApiHeaderJson, bpdHeadersProducers()), - response_decoder: enrollmentPaymentInstrumentIOUsingPUTDefaultDecoder() - }; - -const deletePaymentResponseDecoders = r.composeResponseDecoders( - r.composeResponseDecoders( - r.constantResponseDecoder(204, undefined), - r.constantResponseDecoder(400, undefined) - ), - r.composeResponseDecoders( - r.constantResponseDecoder(401, undefined), - r.constantResponseDecoder(500, undefined) - ) -); - -export const paymentInstrumentsDELETE: DeleteUsingDELETET = { - method: "delete", - url: ({ id }) => `/bpd/io/payment-instruments/${id}`, - query: _ => ({}), - headers: bpdHeadersProducers(), - response_decoder: deletePaymentResponseDecoders -}; diff --git a/ts/features/bonus/bpd/api/winning-transactions/patchedWinningTransactionPageResource.ts b/ts/features/bonus/bpd/api/winning-transactions/patchedWinningTransactionPageResource.ts deleted file mode 100644 index 1ea68522929..00000000000 --- a/ts/features/bonus/bpd/api/winning-transactions/patchedWinningTransactionPageResource.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as t from "io-ts"; -import { DateFromString } from "@pagopa/ts-commons/lib/dates"; -import { DateFromISOString } from "../../../../../utils/dates"; - -/** - * We need to use a patched type because the response contains a date-time translated with the codec UTCISODateFromString - * that fails to recognize the received format (used instead {@link DateFromISOString}) - */ - -// required attributes -const WinningTransactionMilestoneResourceR = t.interface({ - amount: t.number, - - awardPeriodId: t.Integer, - - cashback: t.number, - - circuitType: t.string, - - hashPan: t.string, - - idTrx: t.string, - - idTrxAcquirer: t.string, - - idTrxIssuer: t.string, - // replaced from UTCISODateFromString - trxDate: DateFromISOString -}); - -// optional attributes -const WinningTransactionMilestoneResourceO = t.partial({}); - -export const PatchedWinningTransactionMilestoneResource = t.intersection( - [WinningTransactionMilestoneResourceR, WinningTransactionMilestoneResourceO], - "PatchedWinningTransactionMilestoneResource" -); - -export type PatchedWinningTransactionMilestoneResource = t.TypeOf< - typeof PatchedWinningTransactionMilestoneResource ->; - -// required attributes -const WinningTransactionsOfTheDayResourceR = t.interface({ - date: DateFromString, - - transactions: t.readonlyArray( - PatchedWinningTransactionMilestoneResource, - "array of WinningTransactionMilestoneResource" - ) -}); - -// optional attributes -const WinningTransactionsOfTheDayResourceO = t.partial({}); - -export const PatchedWinningTransactionsOfTheDayResource = t.intersection( - [WinningTransactionsOfTheDayResourceR, WinningTransactionsOfTheDayResourceO], - "PatchedWinningTransactionsOfTheDayResource" -); - -export type PatchedWinningTransactionsOfTheDayResource = t.TypeOf< - typeof PatchedWinningTransactionsOfTheDayResource ->; - -const WinningTransactionPageResourceR = t.interface({ - transactions: t.readonlyArray( - PatchedWinningTransactionsOfTheDayResource, - "array of WinningTransactionsOfTheDayResource" - ) -}); - -// optional attributes -const WinningTransactionPageResourceO = t.partial({ - nextCursor: t.Integer, - - prevCursor: t.Integer -}); - -export const PatchedWinningTransactionPageResource = t.intersection( - [WinningTransactionPageResourceR, WinningTransactionPageResourceO], - "PatchedWinningTransactionPageResource" -); - -export type PatchedWinningTransactionPageResource = t.TypeOf< - typeof PatchedWinningTransactionPageResource ->; diff --git a/ts/features/bonus/bpd/api/winning-transactions/v1.ts b/ts/features/bonus/bpd/api/winning-transactions/v1.ts deleted file mode 100644 index 93c1316b7b0..00000000000 --- a/ts/features/bonus/bpd/api/winning-transactions/v1.ts +++ /dev/null @@ -1,56 +0,0 @@ -/* TOTAL CASHBACK */ -import * as O from "fp-ts/lib/Option"; -import * as r from "@pagopa/ts-commons/lib/requests"; -import { pipe } from "fp-ts/lib/function"; -import { - findWinningTransactionsUsingGETDecoder, - getTotalScoreUsingGETDefaultDecoder, - GetTotalScoreUsingGETT -} from "../../../../../../definitions/bpd/winning_transactions/requestTypes"; -import { PatchedBpdWinningTransactions } from "../../types/PatchedWinningTransactionResource"; -import { bpdHeadersProducers } from "../common"; - -export const winningTransactionsTotalCashbackGET: GetTotalScoreUsingGETT = { - method: "get", - url: ({ awardPeriodId }) => - `/bpd/io/winning-transactions/total-cashback?awardPeriodId=${awardPeriodId}`, - query: _ => ({}), - headers: bpdHeadersProducers(), - response_decoder: getTotalScoreUsingGETDefaultDecoder() -}; - -export type FindWinningTransactionsUsingGETTExtra = r.IGetApiRequestType< - { - readonly awardPeriodId: number; - readonly hpan: string; - readonly Authorization: string; - }, - never, - never, - | r.IResponseType<200, PatchedBpdWinningTransactions> - | r.IResponseType<401, undefined> - | r.IResponseType<500, undefined> ->; - -const hPanToQueryString = (hPan: string) => `&hpan=${hPan}`; - -const findWinningTransactionsUsingGETCustomDecoder = - findWinningTransactionsUsingGETDecoder({ - 200: PatchedBpdWinningTransactions - }); -/** - * @deprecated - */ -export const winningTransactionsGET: FindWinningTransactionsUsingGETTExtra = { - method: "get", - url: ({ awardPeriodId, hpan }) => - `/bpd/io/winning-transactions?awardPeriodId=${awardPeriodId}${pipe( - hpan, - O.fromNullable, - O.map(hPanToQueryString), - O.getOrElse(() => "") - )}`, - query: _ => ({}), - headers: bpdHeadersProducers(), - response_decoder: findWinningTransactionsUsingGETCustomDecoder -}; diff --git a/ts/features/bonus/bpd/api/winning-transactions/v2.ts b/ts/features/bonus/bpd/api/winning-transactions/v2.ts deleted file mode 100644 index 6b4bb9f91ac..00000000000 --- a/ts/features/bonus/bpd/api/winning-transactions/v2.ts +++ /dev/null @@ -1,60 +0,0 @@ -import * as r from "@pagopa/ts-commons/lib/requests"; -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import { - findWinningTransactionsUsingGETDecoder, - getCountByDayGETDefaultDecoder, - GetCountByDayGETT -} from "../../../../../../definitions/bpd/winning_transactions_v2/requestTypes"; -import { bpdHeadersProducers } from "../common"; -import { PatchedWinningTransactionPageResource } from "./patchedWinningTransactionPageResource"; - -export const winningTransactionsV2CountByDayGET: GetCountByDayGETT = { - method: "get", - url: ({ awardPeriodId }) => - `/bpd/io/winning-transactions/v2/countbyday?awardPeriodId=${awardPeriodId}`, - query: _ => ({}), - headers: bpdHeadersProducers(), - response_decoder: getCountByDayGETDefaultDecoder() -}; - -/** - * We need to use a patched type because the response contains a date-time translated with the codec UTCISODateFromString - * that fails to recognize the received format (used instead {@link DateFromISOString}) - */ -export type PatchedFindWinningTransactionsUsingGETT = r.IGetApiRequestType< - { - readonly hpan?: string; - readonly awardPeriodId: number; - readonly limit?: number; - readonly nextCursor?: number; - readonly Authorization: string; - }, - never, - never, - | r.IResponseType<200, PatchedWinningTransactionPageResource> - | r.IResponseType<401, undefined> - | r.IResponseType<500, undefined> ->; - -const cursorToQueryString = (cursor: number) => `&nextCursor=${cursor}`; - -const findWinningTransactionsUsingGETCustomDecoder = - findWinningTransactionsUsingGETDecoder({ - 200: PatchedWinningTransactionPageResource - }); - -export const winningTransactionsV2GET: PatchedFindWinningTransactionsUsingGETT = - { - method: "get", - url: ({ awardPeriodId, nextCursor }) => - `/bpd/io/winning-transactions/v2?awardPeriodId=${awardPeriodId}${pipe( - nextCursor, - O.fromNullable, - O.map(cursorToQueryString), - O.getOrElse(() => "") - )}`, - query: _ => ({}), - headers: bpdHeadersProducers(), - response_decoder: findWinningTransactionsUsingGETCustomDecoder - }; diff --git a/ts/features/bonus/bpd/components/BaseDailyTransactionHeader.tsx b/ts/features/bonus/bpd/components/BaseDailyTransactionHeader.tsx deleted file mode 100644 index c6decd1a018..00000000000 --- a/ts/features/bonus/bpd/components/BaseDailyTransactionHeader.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import * as React from "react"; -import { View, StyleSheet } from "react-native"; -import { IOColors, Icon } from "@pagopa/io-app-design-system"; -import I18n from "../../../../i18n"; -import { H3 } from "../../../../components/core/typography/H3"; -import { H5 } from "../../../../components/core/typography/H5"; -import { formatIntegerNumber } from "../../../../utils/stringBuilder"; - -type Props = { - date: string; - transactionsNumber: number; -}; - -const styles = StyleSheet.create({ - row: { - flexDirection: "row", - alignItems: "center" - }, - container: { - justifyContent: "space-between" - }, - whiteBg: { - backgroundColor: IOColors.white - } -}); - -const BaseDailyTransactionHeader: React.FunctionComponent = ( - props: Props -) => ( - -

- {props.date} -

- - - -
- {` ${I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.label", - { - defaultValue: I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.label.other", - { - transactions: formatIntegerNumber(props.transactionsNumber) - } - ), - count: props.transactionsNumber, - transactions: formatIntegerNumber(props.transactionsNumber) - } - )}`} -
-
-
{`${I18n.t("bonus.bpd.name")} (€)`}
-
- -); - -export default BaseDailyTransactionHeader; diff --git a/ts/features/bonus/bpd/components/BpdLastUpdateComponent.tsx b/ts/features/bonus/bpd/components/BpdLastUpdateComponent.tsx deleted file mode 100644 index ecc7ae592df..00000000000 --- a/ts/features/bonus/bpd/components/BpdLastUpdateComponent.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as React from "react"; -import { useEffect, useState } from "react"; -import { View, StyleSheet } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { H4 } from "../../../../components/core/typography/H4"; -import { IOStyles } from "../../../../components/core/variables/IOStyles"; -import I18n from "../../../../i18n"; -import { GlobalState } from "../../../../store/reducers/types"; -import { format, formatDateAsLocal } from "../../../../utils/dates"; -import { showToast } from "../../../../utils/showToast"; -import { bpdAllData } from "../store/actions/details"; -import { bpdLastUpdateSelector } from "../store/reducers/details/lastUpdate"; - -type Props = ReturnType & - ReturnType; - -const styles = StyleSheet.create({ - row: { - flexDirection: "row", - justifyContent: "center" - } -}); - -/** - * This component show the last time in which the bpd periods are loaded correctly - * and allow to request a refresh of the bpd periods data. - * @param props - */ -const BpdLastUpdateComponent: React.FunctionComponent = ( - props: Props -) => { - const [isFirstRender, setIsFirstRender] = useState(true); - const { potLastUpdate } = props; - useEffect(() => { - if (!isFirstRender) { - if (pot.isError(potLastUpdate)) { - showToast(I18n.t("global.genericError"), "danger"); - } - } else { - setIsFirstRender(false); - } - }, [potLastUpdate, isFirstRender]); - - return ( - - {!pot.isNone(props.potLastUpdate) && ( -

- {I18n.t("bonus.bpd.details.lastUpdate", { - hour: format(props.potLastUpdate.value, "HH:mm"), - date: formatDateAsLocal(props.potLastUpdate.value, true, true) - })} -

- )} -
- ); -}; - -const mapStateToProps = (state: GlobalState) => ({ - potLastUpdate: bpdLastUpdateSelector(state) -}); - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - loadBonus: () => dispatch(bpdAllData.request()) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(BpdLastUpdateComponent); diff --git a/ts/features/bonus/bpd/components/BpdTestOverlay.tsx b/ts/features/bonus/bpd/components/BpdTestOverlay.tsx deleted file mode 100644 index 7db3b49f45c..00000000000 --- a/ts/features/bonus/bpd/components/BpdTestOverlay.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import * as React from "react"; -import { useState } from "react"; -import { View, Platform, StyleSheet } from "react-native"; - -import { getStatusBarHeight, isIphoneX } from "react-native-iphone-x-helper"; -import { IOColors, hexToRgba } from "@pagopa/io-app-design-system"; -import { Body } from "../../../../components/core/typography/Body"; -import { Label } from "../../../../components/core/typography/Label"; -import { - bpdApiSitUrlPrefix, - bpdApiUatUrlPrefix, - bpdApiUrlPrefix, - pagoPaApiUrlPrefix, - pagoPaApiUrlPrefixTest -} from "../../../../config"; -import { getAppVersion } from "../../../../utils/appVersion"; - -const opaqueBgColor = hexToRgba(IOColors.white, 0.67); - -const styles = StyleSheet.create({ - versionContainer: { - position: "absolute", - top: Platform.select({ - ios: 20 + (isIphoneX() ? getStatusBarHeight() : 0), - android: 0 - }), - left: 0, - right: 0, - bottom: 0, - justifyContent: "flex-start", - alignItems: "center", - zIndex: 1000 - }, - versionText: { - padding: 2, - backgroundColor: opaqueBgColor - } -}); - -/** - * Temp overlay created to avoid ambiguity when test bpd versions are released. - * TODO: remove after the release of bpd - * @constructor - */ -export const BpdTestOverlay: React.FunctionComponent = () => { - const [enabled, setEnabled] = useState(true); - const bpdEndpointStr = - bpdApiUrlPrefix === bpdApiSitUrlPrefix - ? "SIT" - : bpdApiUrlPrefix === bpdApiUatUrlPrefix - ? "UAT" - : "PROD"; - - const pmEndpointStr = - pagoPaApiUrlPrefix === pagoPaApiUrlPrefixTest ? "UAT" : "PROD"; - - return ( - - {enabled ? ( - <> - - setEnabled(!enabled)} - >{`${getAppVersion()} - bpd: ${bpdEndpointStr} - PM: ${pmEndpointStr}`} - - ) : null} - - ); -}; diff --git a/ts/features/bonus/bpd/components/BpdTransactionSummaryComponent.tsx b/ts/features/bonus/bpd/components/BpdTransactionSummaryComponent.tsx deleted file mode 100644 index 9fbe8b8a9e5..00000000000 --- a/ts/features/bonus/bpd/components/BpdTransactionSummaryComponent.tsx +++ /dev/null @@ -1,155 +0,0 @@ -import * as React from "react"; -import { View, StyleSheet } from "react-native"; -import { TouchableWithoutFeedback } from "@gorhom/bottom-sheet"; -import { IOColors, VSpacer } from "@pagopa/io-app-design-system"; -import { Body } from "../../../../components/core/typography/Body"; -import { H4 } from "../../../../components/core/typography/H4"; -import { InfoBox } from "../../../../components/box/InfoBox"; -import { useLegacyIOBottomSheetModal } from "../../../../utils/hooks/bottomSheet"; -import { Link } from "../../../../components/core/typography/Link"; -import { openWebUrl } from "../../../../utils/url"; -import Markdown from "../../../../components/ui/Markdown"; -import I18n from "../../../../i18n"; -import { localeDateFormat } from "../../../../utils/locale"; -import { - formatIntegerNumber, - formatNumberAmount -} from "../../../../utils/stringBuilder"; -import { BpdAmount } from "../saga/networking/amount"; -import { BpdPeriod } from "../store/actions/periods"; - -type Props = { - lastUpdateDate: string; - period: BpdPeriod; - totalAmount: BpdAmount; -}; - -const styles = StyleSheet.create({ - row: { - flexDirection: "row", - alignItems: "center" - }, - readMore: { marginLeft: 31, marginBottom: 24 } -}); - -const readMoreLink = "https://io.italia.it/cashback/acquirer/"; - -const CSS_STYLE = ` -body { - font-size: 16; - color: ${IOColors.black} -} -`; - -export const BottomSheetBpdTransactionsBody: React.FunctionComponent = () => { - const [CTAVisibility, setCTAVisibility] = React.useState(false); - - const setCTAVisible = () => setCTAVisibility(true); - - return ( - <> - - {I18n.t( - "bonus.bpd.details.transaction.detail.summary.bottomSheet.body" - )} - - {CTAVisibility && ( - openWebUrl(readMoreLink)}> - - {I18n.t( - "bonus.bpd.details.transaction.detail.summary.bottomSheet.readMore" - )} - - - )} - - ); -}; - -const BpdTransactionSummaryComponent: React.FunctionComponent = ( - props: Props -) => { - const { present, bottomSheet } = useLegacyIOBottomSheetModal( - <> - - -

- {I18n.t( - "bonus.bpd.details.transaction.detail.summary.calendarBlock.text1" - )} -

- {" "} - {I18n.t( - "bonus.bpd.details.transaction.detail.summary.calendarBlock.text2" - )} -

- {I18n.t( - "bonus.bpd.details.transaction.detail.summary.calendarBlock.text3" - )} - -
- - - - , - I18n.t("bonus.bpd.details.transaction.detail.summary.bottomSheet.title"), - 600 - ); - - return ( - <> - - -

- {I18n.t("bonus.bpd.details.transaction.detail.summary.lastUpdated")} -

{props.lastUpdateDate}

- - - {I18n.t("bonus.bpd.details.transaction.detail.summary.link")} - -
-
- - - - - {I18n.t("bonus.bpd.details.transaction.detail.summary.body.text1")} -

{`${localeDateFormat( - props.period.startDate, - I18n.t("global.dateFormats.fullFormatFullMonthLiteral") - )} - ${localeDateFormat( - props.period.endDate, - I18n.t("global.dateFormats.fullFormatFullMonthLiteral") - )} `}

- {I18n.t("bonus.bpd.details.transaction.detail.summary.body.text2")} -

- {I18n.t("bonus.bpd.details.transaction.detail.summary.body.text3", { - defaultValue: I18n.t( - "bonus.bpd.details.transaction.detail.summary.body.text3.other", - { - transactions: formatIntegerNumber( - props.totalAmount.transactionNumber - ) - } - ), - count: props.totalAmount.transactionNumber, - transactions: formatIntegerNumber( - props.totalAmount.transactionNumber - ) - })} -

- {I18n.t("bonus.bpd.details.transaction.detail.summary.body.text4")} -

{`${I18n.t( - "bonus.bpd.details.transaction.detail.summary.body.text5" - )}${formatNumberAmount(props.totalAmount.totalCashback)} euro.`}

- - {bottomSheet} - - ); -}; - -export default BpdTransactionSummaryComponent; diff --git a/ts/features/bonus/bpd/components/__test__/BottomSheetMethodsToDelete.test.tsx b/ts/features/bonus/bpd/components/__test__/BottomSheetMethodsToDelete.test.tsx deleted file mode 100644 index 83fbf71e329..00000000000 --- a/ts/features/bonus/bpd/components/__test__/BottomSheetMethodsToDelete.test.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { render } from "@testing-library/react-native"; -import React from "react"; -import { BottomSheetMethodsToDelete } from "../optInStatus/BottomSheetMethodsToDelete"; -import { mockCreditCardPaymentMethod } from "../../../../../store/reducers/wallet/__mocks__/wallets"; - -describe("BottomSheetMethodsToDelete", () => { - jest.useFakeTimers(); - it(`component should be defined`, () => { - const renderComponent = render( - - ); - expect( - renderComponent.queryByTestId("BottomSheetMethodsToDeleteTestID") - ).not.toBeNull(); - }); - - describe("when some methods are available", () => { - it(`should shown all these items`, () => { - const paymentMethods = [ - { ...mockCreditCardPaymentMethod, idWallet: 1 }, - { ...mockCreditCardPaymentMethod, idWallet: 2 }, - { ...mockCreditCardPaymentMethod, idWallet: 3 } - ]; - const renderComponent = render( - - ); - expect( - renderComponent.queryByTestId("BottomSheetMethodsToDeleteTestID") - ).not.toBeNull(); - paymentMethods.forEach(pm => { - expect( - renderComponent.queryByTestId(`payment_method_${pm.idWallet}`) - ).not.toBeNull(); - }); - }); - }); -}); diff --git a/ts/features/bonus/bpd/components/bpdCardComponent/BpdCardComponent.tsx b/ts/features/bonus/bpd/components/bpdCardComponent/BpdCardComponent.tsx deleted file mode 100644 index 1e3977c604f..00000000000 --- a/ts/features/bonus/bpd/components/bpdCardComponent/BpdCardComponent.tsx +++ /dev/null @@ -1,440 +0,0 @@ -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import * as React from "react"; -import { - Text, - View, - Image, - ImageBackground, - Platform, - StyleSheet -} from "react-native"; -import { widthPercentageToDP } from "react-native-responsive-screen"; -import { - Icon, - hexToRgba, - IOColors, - HSpacer -} from "@pagopa/io-app-design-system"; -import bpdCardBgFull from "../../../../../../img/bonus/bpd/bonus_bg.png"; -import bpdCardBgPreview from "../../../../../../img/bonus/bpd/bonus_preview_bg.png"; -import bpdBonusLogo from "../../../../../../img/bonus/bpd/logo_BonusCashback_White.png"; -import { H2 } from "../../../../../components/core/typography/H2"; -import { H4 } from "../../../../../components/core/typography/H4"; -import { H5 } from "../../../../../components/core/typography/H5"; -import TouchableDefaultOpacity from "../../../../../components/TouchableDefaultOpacity"; -import I18n from "../../../../../i18n"; -import { localeDateFormat } from "../../../../../utils/locale"; -import { formatNumberAmount } from "../../../../../utils/stringBuilder"; -import { BpdAmount } from "../../saga/networking/amount"; -import { BpdPeriod, BpdPeriodStatus } from "../../store/actions/periods"; -import { makeFontStyleObject } from "../../../../../components/core/fonts"; -import { IOBadge } from "../../../../../components/core/IOBadge"; - -type Props = { - period: BpdPeriod; - totalAmount: BpdAmount; - preview?: boolean; - onPress?: () => void; -}; - -const opaqueBorderColor = hexToRgba(IOColors.black, 0.1); - -const styles = StyleSheet.create({ - flex1: { - flex: 1 - }, - flex2: { - flex: 2 - }, - container: { - flex: 1, - height: 192 - }, - paddedContentFull: { - paddingLeft: 16, - paddingTop: 24, - paddingRight: 20, - paddingBottom: 16 - }, - paddedContentPreview: { - paddingLeft: 18, - paddingTop: 8, - paddingRight: 22 - }, - row: { - flexDirection: "row" - }, - column: { - flexDirection: "column" - }, - spaced: { - justifyContent: "space-between" - }, - fullLogo: { - resizeMode: "contain", - height: 56, - width: 56, - alignSelf: "flex-end" - }, - previewLogo: { - resizeMode: "contain", - height: 40, - width: 40, - alignSelf: "center" - }, - preview: { - marginBottom: -20, - height: 88 - }, - imageFull: { - resizeMode: "stretch", - height: "100%" - }, - imagePreview: { - resizeMode: "stretch", - height: 88, - width: "100%" - }, - amountTextBaseFull: { - color: IOColors.white, - fontSize: 24, - lineHeight: 35, - // solution taken from https://github.com/facebook/react-native/issues/7687#issuecomment-309168661 - paddingTop: Platform.select({ - ios: 0, - android: 10 - }), - marginBottom: -8, - ...makeFontStyleObject("Bold") - }, - amountTextUpperFull: { - color: IOColors.white, - fontSize: 32, - ...makeFontStyleObject("Bold") - }, - amountTextBasePreview: { - fontSize: 16, - lineHeight: 32, - color: IOColors.white, - ...makeFontStyleObject("Bold") - }, - amountTextUpperPreview: { - color: IOColors.white, - fontSize: 24, - ...makeFontStyleObject("Bold") - }, - alignItemsCenter: { - alignItems: "center" - }, - justifyContentCenter: { - justifyContent: "center" - }, - upperShadowBox: { - marginBottom: -13, - borderRadius: 8, - borderTopWidth: 13, - borderTopColor: opaqueBorderColor, - height: 17, - width: "100%" - }, - bottomShadowBox: { - marginBottom: 6, - borderRadius: 8, - borderBottomWidth: 15, - borderBottomColor: opaqueBorderColor, - width: "100%" - } -}); - -type BadgeDefinition = { - label: string; -}; - -type IconType = "locked" | "unlocked" | "ok"; - -type GraphicalState = { - amount: ReadonlyArray; - isInGracePeriod: boolean; - iconName: IconType; - statusBadge: BadgeDefinition; -}; - -const initialGraphicalState: GraphicalState = { - amount: ["0", "00"], - isInGracePeriod: false, - iconName: "locked", - statusBadge: { - label: "-" - } -}; - -/** - * Closed lock must be shown if period is Inactive or the transactionNumber didn't reach the minimum target - * Open lock must be shown if period is Closed or Active and the transactionNumber reach the minimum target - * "Ok" (was Fireworks) must be shown if period is Closed or Active and the totalCashback reach the maxAmount - * - * @param period - * @param totalAmount - */ -const iconHandler = (period: BpdPeriod, totalAmount: BpdAmount): IconType => { - const reachMinTransaction = - totalAmount.transactionNumber >= period.minTransactionNumber; - const reachMaxAmount = totalAmount.totalCashback >= period.maxPeriodCashback; - switch (period.status) { - case "Active": - case "Closed": - return reachMinTransaction && reachMaxAmount - ? "ok" - : reachMinTransaction - ? "unlocked" - : "locked"; - default: - return "locked"; - } -}; - -const statusClosedHandler = (props: Props): GraphicalState => { - const { period, totalAmount } = props; - - const actualDate = new Date(); - const endDate = new Date(period.endDate.getTime()); - endDate.setDate(endDate.getDate() + period.gracePeriod); - - const isInGracePeriod = - actualDate.getTime() >= period.endDate.getTime() && - actualDate.getTime() <= endDate.getTime(); - - return { - ...initialGraphicalState, - amount: - totalAmount.transactionNumber < period.minTransactionNumber && - !isInGracePeriod - ? ["0", "00"] - : formatNumberAmount(props.totalAmount.totalCashback).split( - I18n.t("global.localization.decimalSeparator") - ), - isInGracePeriod, - iconName: iconHandler(props.period, props.totalAmount), - // TODO: Add supercashback business logic - statusBadge: isInGracePeriod - ? { - label: I18n.t("profile.preferences.list.wip") - } - : { - label: I18n.t("bonus.bpd.details.card.status.closed") - } - }; -}; - -const statusActiveHandler = (props: Props): GraphicalState => ({ - ...initialGraphicalState, - statusBadge: { - label: I18n.t("bonus.bpd.details.card.status.active") - }, - amount: formatNumberAmount(props.totalAmount.totalCashback).split( - I18n.t("global.localization.decimalSeparator") - ), - iconName: iconHandler(props.period, props.totalAmount) -}); - -const statusInactiveHandler = (props: Props): GraphicalState => ({ - ...initialGraphicalState, - statusBadge: { - label: I18n.t("bonus.bpd.details.card.status.inactive") - }, - amount: formatNumberAmount(props.totalAmount.totalCashback).split( - I18n.t("global.localization.decimalSeparator") - ) -}); - -const statusHandlersMap = new Map< - BpdPeriodStatus, - (props: Props) => GraphicalState ->([ - ["Closed", statusClosedHandler], - ["Active", statusActiveHandler], - ["Inactive", statusInactiveHandler] -]); - -/** - * if the period is Closed we must check if minimum number of transactions has been reached - * Unless we'll show a Zero amount value - * - * GracePeriod: check if we are in the grace period to show an alert instead of the value - * grace period is given adding the gracePeriod value of days to period.endDate - */ -const calculateGraphicalState = (props: Props) => - pipe( - statusHandlersMap.get(props.period.status), - O.fromNullable, - O.fold( - () => initialGraphicalState, - handler => handler(props) - ) - ); - -export const BpdCardComponent: React.FunctionComponent = ( - props: Props -) => { - const { amount, isInGracePeriod, iconName, statusBadge } = - calculateGraphicalState(props); - - const isPeriodClosed = props.period.status === "Closed" && !isInGracePeriod; - const isPeriodInactive = props.period.status === "Inactive"; - - const FullCard = () => ( - - - -

- {I18n.t("bonus.bpd.title")} -

-

- {`${localeDateFormat( - props.period.startDate, - I18n.t("global.dateFormats.fullFormatShortMonthLiteral") - )} - ${localeDateFormat( - props.period.endDate, - I18n.t("global.dateFormats.fullFormatShortMonthLiteral") - )}`} -

-
- - - {/* NBCard Text component */} - - {"€ "} - - {`${amount[0]}${I18n.t( - "global.localization.decimalSeparator" - )}`} - - {amount[1]} - - - - -
- {I18n.t("bonus.bpd.earned")} -
-
-
- - - - -
- ); - - const PreviewCard = () => ( - - - -
- {`${localeDateFormat( - props.period.startDate, - I18n.t("global.dateFormats.dayFullMonth") - )} - ${localeDateFormat( - props.period.endDate, - I18n.t("global.dateFormats.fullFormatFullMonthLiteral") - )}`} -
- - {isPeriodClosed && } -
- -

- {I18n.t("bonus.bpd.name")} -

- - - - {isInGracePeriod || isPeriodInactive ? ( - - ) : ( - - {"€ "} - - {`${amount[0]}${I18n.t( - "global.localization.decimalSeparator" - )}`} - - {amount[1]} - - )} - -
-
- -
- ); - - return ( - <> - {Platform.OS === "android" && ( - - )} - - {props.preview ? : } - - {Platform.OS === "android" && !props.preview && ( - - )} - - ); -}; diff --git a/ts/features/bonus/bpd/components/optInPaymentMethods/BpdOptInPaymentMethodsContainer.tsx b/ts/features/bonus/bpd/components/optInPaymentMethods/BpdOptInPaymentMethodsContainer.tsx deleted file mode 100644 index 811bfaa47ae..00000000000 --- a/ts/features/bonus/bpd/components/optInPaymentMethods/BpdOptInPaymentMethodsContainer.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { useNavigation } from "@react-navigation/native"; -import * as React from "react"; -import { useContext, useEffect, useState } from "react"; -import { useDispatch } from "react-redux"; -import { isError, isReady } from "../../../../../common/model/RemoteValue"; -import LoadingSpinnerOverlay from "../../../../../components/LoadingSpinnerOverlay"; -import { LightModalContext } from "../../../../../components/ui/LightModal"; -import I18n from "../../../../../i18n"; -import { useIOSelector } from "../../../../../store/hooks"; -import { optInPaymentMethodsShowChoice } from "../../store/actions/optInPaymentMethods"; -import { showOptInChoiceSelector } from "../../store/reducers/details/activation/ui"; -import { bpdLastUpdateSelector } from "../../store/reducers/details/lastUpdate"; - -const BpdOptInPaymentMethodsContainer = () => { - const dispatch = useDispatch(); - const navigation = useNavigation(); - const { showModal, hideModal } = useContext(LightModalContext); - const [showOptInChecked, setShowOptInChecked] = useState(false); - const showOptInChoice = useIOSelector(showOptInChoiceSelector); - const bpdLastUpdate = useIOSelector(bpdLastUpdateSelector); - - useEffect(() => { - if (!showOptInChecked && !isReady(showOptInChoice)) { - setShowOptInChecked(true); - // Starts the optInShouldShowChoiceHandler saga - dispatch(optInPaymentMethodsShowChoice.request()); - showModal( - - ); - } - }, [dispatch, showOptInChecked, bpdLastUpdate, showModal, showOptInChoice]); - - useEffect(() => { - if (isReady(showOptInChoice) || isError(showOptInChoice)) { - hideModal(); - } - }, [hideModal, showOptInChoice, navigation]); - - return <>; -}; - -export default BpdOptInPaymentMethodsContainer; diff --git a/ts/features/bonus/bpd/components/optInPaymentMethods/RetryAfterDeletionFailsComponent.tsx b/ts/features/bonus/bpd/components/optInPaymentMethods/RetryAfterDeletionFailsComponent.tsx deleted file mode 100644 index 1ce4d767b83..00000000000 --- a/ts/features/bonus/bpd/components/optInPaymentMethods/RetryAfterDeletionFailsComponent.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import React from "react"; -import { SafeAreaView } from "react-native"; -import { useDispatch } from "react-redux"; -import { InfoScreenComponent } from "../../../../../components/infoScreen/InfoScreenComponent"; -import Error from "../../../../../../img/wallet/errors/generic-error-icon.svg"; -import { IOStyles } from "../../../../../components/core/variables/IOStyles"; -import { - cancelButtonProps, - confirmButtonProps -} from "../../../../../components/buttons/ButtonConfigurations"; -import { - optInPaymentMethodsCompleted, - optInPaymentMethodsDeletionChoice -} from "../../store/actions/optInPaymentMethods"; -import FooterWithButtons from "../../../../../components/ui/FooterWithButtons"; -import I18n from "../../../../../i18n"; - -const RetryAfterDeletionFailsComponent = () => { - const dispatch = useDispatch(); - - return ( - - } - title={I18n.t( - "bonus.bpd.optInPaymentMethods.thankYouPage.retryAfterDeletion.title" - )} - body={I18n.t( - "bonus.bpd.optInPaymentMethods.thankYouPage.retryAfterDeletion.body" - )} - /> - dispatch(optInPaymentMethodsDeletionChoice()), - I18n.t("bonus.bpd.optInPaymentMethods.thankYouPage.cta.retry"), - undefined, - "retryButton" - )} - rightButton={cancelButtonProps( - () => dispatch(optInPaymentMethodsCompleted()), - I18n.t("bonus.bpd.optInPaymentMethods.thankYouPage.cta.goToWallet"), - undefined, - "goToWalletButton" - )} - /> - - ); -}; - -export default RetryAfterDeletionFailsComponent; diff --git a/ts/features/bonus/bpd/components/optInPaymentMethods/ThankYouSuccessComponent.tsx b/ts/features/bonus/bpd/components/optInPaymentMethods/ThankYouSuccessComponent.tsx deleted file mode 100644 index a0fe882e4ca..00000000000 --- a/ts/features/bonus/bpd/components/optInPaymentMethods/ThankYouSuccessComponent.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { useNavigation } from "@react-navigation/native"; -import React from "react"; -import { SafeAreaView } from "react-native"; -import Completed from "../../../../../../img/pictograms/payment-completed.svg"; -import { IOStyles } from "../../../../../components/core/variables/IOStyles"; -import { InfoScreenComponent } from "../../../../../components/infoScreen/InfoScreenComponent"; -import FooterWithButtons from "../../../../../components/ui/FooterWithButtons"; -import I18n from "../../../../../i18n"; -import ROUTES from "../../../../../navigation/routes"; -import { useIODispatch } from "../../../../../store/hooks"; -import { cancelButtonProps } from "../../../../../components/buttons/ButtonConfigurations"; -import { - optInPaymentMethodsCompleted, - optInPaymentMethodsShowChoice -} from "../../store/actions/optInPaymentMethods"; - -const ThankYouSuccessComponent = () => { - const navigation = useNavigation(); - const dispatch = useIODispatch(); - return ( - - } - title={I18n.t( - "bonus.bpd.optInPaymentMethods.thankYouPage.success.title" - )} - body={I18n.t("bonus.bpd.optInPaymentMethods.thankYouPage.success.body")} - /> - { - dispatch(optInPaymentMethodsCompleted()); - dispatch(optInPaymentMethodsShowChoice.success(false)); - navigation.navigate(ROUTES.WALLET_HOME); - }, - I18n.t("bonus.bpd.optInPaymentMethods.thankYouPage.cta.goToWallet"), - undefined, - "goToWalletButton" - )} - /> - - ); -}; - -export default ThankYouSuccessComponent; diff --git a/ts/features/bonus/bpd/components/optInPaymentMethods/__tests__/RetryAfterDeletionFailsComponent.test.ts b/ts/features/bonus/bpd/components/optInPaymentMethods/__tests__/RetryAfterDeletionFailsComponent.test.ts deleted file mode 100644 index 61d07b36bf4..00000000000 --- a/ts/features/bonus/bpd/components/optInPaymentMethods/__tests__/RetryAfterDeletionFailsComponent.test.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { createStore, Store } from "redux"; - -import { fireEvent, RenderAPI } from "@testing-library/react-native"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { renderScreenWithNavigationStoreContext } from "../../../../../../utils/testWrapper"; -import ROUTES from "../../../../../../navigation/routes"; -import RetryAfterDeletionFailsComponent from "../RetryAfterDeletionFailsComponent"; -import { appReducer } from "../../../../../../store/reducers"; -import { applicationChangeState } from "../../../../../../store/actions/application"; -import * as optInPaymentMethodsActions from "../../../store/actions/optInPaymentMethods"; - -jest.useFakeTimers(); - -describe("the RetryAfterDeletionFailsComponent screen", () => { - const globalState = appReducer(undefined, applicationChangeState("active")); - - it("Should call the optInPaymentMethodsCompleted functions when the goToWalletButton is pressed", () => { - const optInPaymentMethodsCompletedSpy = jest.spyOn( - optInPaymentMethodsActions, - "optInPaymentMethodsCompleted" - ); - const store: Store = createStore( - appReducer, - globalState as any - ); - const component: RenderAPI = renderComponent(store); - fireEvent.press(component.getByTestId("goToWalletButton")); - expect(optInPaymentMethodsCompletedSpy).toBeCalledTimes(1); - }); - it("Should call the optInPaymentMethodsDeletionChoice functions when the retryButton is pressed", () => { - const optInPaymentMethodsDeletionChoiceSpy = jest.spyOn( - optInPaymentMethodsActions, - "optInPaymentMethodsDeletionChoice" - ); - const store: Store = createStore( - appReducer, - globalState as any - ); - const component: RenderAPI = renderComponent(store); - fireEvent.press(component.getByTestId("retryButton")); - expect(optInPaymentMethodsDeletionChoiceSpy).toBeCalledTimes(1); - }); -}); - -function renderComponent(store: Store) { - return renderScreenWithNavigationStoreContext( - RetryAfterDeletionFailsComponent, - ROUTES.MAIN, - {}, - store - ); -} diff --git a/ts/features/bonus/bpd/components/optInPaymentMethods/__tests__/ThankYouSuccessComponent.test.ts b/ts/features/bonus/bpd/components/optInPaymentMethods/__tests__/ThankYouSuccessComponent.test.ts deleted file mode 100644 index 01aafce5dc8..00000000000 --- a/ts/features/bonus/bpd/components/optInPaymentMethods/__tests__/ThankYouSuccessComponent.test.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { createStore, Store } from "redux"; - -import { fireEvent, RenderAPI } from "@testing-library/react-native"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { renderScreenWithNavigationStoreContext } from "../../../../../../utils/testWrapper"; -import ROUTES from "../../../../../../navigation/routes"; -import { appReducer } from "../../../../../../store/reducers"; -import { applicationChangeState } from "../../../../../../store/actions/application"; -import * as optInPaymentMethodsActions from "../../../store/actions/optInPaymentMethods"; -import ThankYouSuccessComponent from "../ThankYouSuccessComponent"; - -jest.useFakeTimers(); - -describe("the ThankYouSuccessComponent screen", () => { - const globalState = appReducer(undefined, applicationChangeState("active")); - - it("Should call the optInPaymentMethodsCompleted functions when the goToWalletButton is pressed", () => { - const optInPaymentMethodsCompletedSpy = jest.spyOn( - optInPaymentMethodsActions, - "optInPaymentMethodsCompleted" - ); - const store: Store = createStore( - appReducer, - globalState as any - ); - const component: RenderAPI = renderComponent(store); - fireEvent.press(component.getByTestId("goToWalletButton")); - expect(optInPaymentMethodsCompletedSpy).toBeCalledTimes(1); - }); -}); - -function renderComponent(store: Store) { - return renderScreenWithNavigationStoreContext( - ThankYouSuccessComponent, - ROUTES.MAIN, - {}, - store - ); -} diff --git a/ts/features/bonus/bpd/components/optInStatus/BottomSheetMethodsToDelete.tsx b/ts/features/bonus/bpd/components/optInStatus/BottomSheetMethodsToDelete.tsx deleted file mode 100644 index 5c8d037a7ea..00000000000 --- a/ts/features/bonus/bpd/components/optInStatus/BottomSheetMethodsToDelete.tsx +++ /dev/null @@ -1,100 +0,0 @@ -import { ListItem } from "native-base"; -import * as React from "react"; -import { View, Dimensions } from "react-native"; -import { useSelector } from "react-redux"; -import { IOColors, VSpacer } from "@pagopa/io-app-design-system"; -import { H3 } from "../../../../../components/core/typography/H3"; -import { Label } from "../../../../../components/core/typography/Label"; -import { IOStyles } from "../../../../../components/core/variables/IOStyles"; -import FooterWithButtons from "../../../../../components/ui/FooterWithButtons"; -import I18n from "../../../../../i18n"; -import { getBPDMethodsVisibleInWalletSelector } from "../../../../../store/reducers/wallet/wallets"; -import { PaymentMethod } from "../../../../../types/pagopa"; -import { useLegacyIOBottomSheetModal } from "../../../../../utils/hooks/bottomSheet"; -import { PaymentMethodRepresentationComponent } from "../paymentMethodActivationToggle/base/PaymentMethodRepresentationComponent"; - -type Props = { - paymentMethods: ReadonlyArray; -}; - -export const BottomSheetMethodsToDelete = (props: Props) => ( - - - - {props.paymentMethods.map(pm => ( - - - - ))} - -); - -type BottomSheetReturnType = { - presentBottomSheet: () => void; - bottomSheet: React.ReactNode; -}; - -/** - * return an hook that exposes presentBottomSheet function to imperative show a bottomsheet - * containing the list of those payment methods that have BPD as function enabled - * @param props - */ -export const useBottomSheetMethodsToDelete = (props: { - onDeletePress: () => void; -}): BottomSheetReturnType => { - const paymentMethods = useSelector(getBPDMethodsVisibleInWalletSelector); - const snapPoint = Math.min( - Dimensions.get("window").height * 0.8, - // (subtitle + footer) + items - 280 + paymentMethods.length * 58 - ); - const { present, bottomSheet, dismiss } = useLegacyIOBottomSheetModal( - , - -

- {I18n.t( - "bonus.bpd.optInPaymentMethods.deletePaymentMethodsBottomSheet.title" - )} -

- -
, - snapPoint, - { - props.onDeletePress(); - dismiss(); - }, - title: I18n.t("global.buttons.delete") - }} - rightButton={{ - testID: "cancelButtonTestID", - bordered: true, - onPressWithGestureHandler: true, - onPress: () => dismiss(), - title: I18n.t("global.buttons.cancel") - }} - /> - ); - - return { - presentBottomSheet: present, - bottomSheet - }; -}; diff --git a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/BpdPaymentMethodToggleFactory.tsx b/ts/features/bonus/bpd/components/paymentMethodActivationToggle/BpdPaymentMethodToggleFactory.tsx deleted file mode 100644 index 5c677805e53..00000000000 --- a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/BpdPaymentMethodToggleFactory.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as React from "react"; -import { PaymentMethod } from "../../../../../types/pagopa"; -import { hasFunctionEnabled } from "../../../../../utils/walletv2"; -import { HPan } from "../../store/actions/paymentMethods"; -import { getPaymentMethodHash } from "../../../../../utils/paymentMethod"; -import { EnableableFunctionsEnum } from "../../../../../../definitions/pagopa/EnableableFunctions"; -import PaymentMethodBpdToggle from "./base/PaymentMethodBpdToggle"; - -/** - * Return a specific toggle based on the WalletTypeEnum - * @param paymentMethod - */ -export const bpdToggleFactory = (paymentMethod: PaymentMethod) => { - const hash = getPaymentMethodHash(paymentMethod); - return hash ? ( - - ) : null; -}; diff --git a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/base/BpdToggle.tsx b/ts/features/bonus/bpd/components/paymentMethodActivationToggle/base/BpdToggle.tsx deleted file mode 100644 index ed23b0c70c8..00000000000 --- a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/base/BpdToggle.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import * as React from "react"; -import { ActivityIndicator } from "react-native"; -import { Icon } from "@pagopa/io-app-design-system"; -import Switch from "../../../../../../components/ui/Switch"; -import TouchableDefaultOpacity from "../../../../../../components/TouchableDefaultOpacity"; -import { GraphicalValue } from "./PaymentMethodBpdToggle"; - -type Props = { - graphicalValue: GraphicalValue; - // The use choose to change the value of the toggle - onValueChanged?: (b: boolean) => void; - // The user tap the "notice" icon when the graphical state is "notActivable" - onPress?: () => void; -}; - -const iconSize = 24; - -/** - * The toggle used in {@link PaymentMethodBpdToggle}. - * @param props - * @constructor - */ -export const BpdToggle: React.FunctionComponent = props => { - switch (props.graphicalValue.state) { - case "loading": - return ( - - ); - case "ready": - case "update": - return props.graphicalValue.value === "notActivable" ? ( - - - - ) : ( - - ); - } -}; diff --git a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/base/PaymentMethodBpdToggle.tsx b/ts/features/bonus/bpd/components/paymentMethodActivationToggle/base/PaymentMethodBpdToggle.tsx deleted file mode 100644 index 50a54b535ab..00000000000 --- a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/base/PaymentMethodBpdToggle.tsx +++ /dev/null @@ -1,217 +0,0 @@ -/* eslint-disable functional/immutable-data */ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { useNavigation } from "@react-navigation/native"; -import * as React from "react"; -import { useCallback, useEffect, useRef, useState } from "react"; -import { View, ImageSourcePropType, StyleSheet } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { fetchPaymentManagerLongTimeout } from "../../../../../../config"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { - bpdPaymentMethodActivation, - BpdPaymentMethodActivation, - BpdPmActivationStatus, - bpdUpdatePaymentMethodActivation, - HPan -} from "../../../store/actions/paymentMethods"; -import { bpdPaymentMethodValueSelector } from "../../../store/reducers/details/paymentMethods"; -import { useChangeActivationConfirmationBottomSheet } from "../bottomsheet/BpdChangeActivationConfirmationScreen"; -import { - NotActivableType, - useNotActivableInformationBottomSheet -} from "../bottomsheet/BpdNotActivableInformation"; -import { BpdToggle } from "./BpdToggle"; -import { PaymentMethodRepresentationComponent } from "./PaymentMethodRepresentationComponent"; - -// TODO: accept only hpan, read all the other information with a selector from payment methods -export type BpdToggleProps = { - hPan: HPan; - icon: ImageSourcePropType; - caption: string; - hasBpdCapability: boolean; -}; - -export type Props = ReturnType & - ReturnType & - BpdToggleProps; - -type GraphicalState = "loading" | "ready" | "update"; - -export type GraphicalValue = { - state: GraphicalState; - value: BpdPmActivationStatus | undefined; -}; - -const styles = StyleSheet.create({ - row: { - flex: 1, - flexDirection: "row", - alignItems: "center", - height: 48 - } -}); - -/** - * This custom hook handles the load of the initial state and the retry in case of error. - * TODO: refactor with {@link useLoadPotValue} - * @deprecated - * @param props - */ -const useInitialValue = (props: Props) => { - const timerRetry = useRef(undefined); - const navigation = useNavigation(); - const isFocused = navigation.isFocused(); - const { bpdPotActivation, hPan, loadActualValue, hasBpdCapability } = props; - - const retry = useCallback(() => { - timerRetry.current = undefined; - if (hasBpdCapability) { - loadActualValue(hPan); - } - }, [loadActualValue, hPan, hasBpdCapability]); - - /** - * When the focus change, clear the timer (if any) and reset the value to undefined - * focus: true -> a new schedule is allowed - * focus: false -> clear all the pending schedule - */ - - useEffect(() => { - clearTimeout(timerRetry.current); - timerRetry.current = undefined; - }, [isFocused]); - - useEffect(() => { - if (!hasBpdCapability) { - return; - } - // Initial state, request the state - if (bpdPotActivation === pot.none) { - loadActualValue(hPan); - } else if ( - pot.isNone(bpdPotActivation) && - pot.isError(bpdPotActivation) && - timerRetry.current === undefined && - isFocused - ) { - // If the pot is NoneError, the navigation focus is on the element - // and no other retry are scheduled - timerRetry.current = setTimeout(retry, fetchPaymentManagerLongTimeout); - } - }, [ - bpdPotActivation, - hPan, - loadActualValue, - retry, - isFocused, - hasBpdCapability - ]); - - // Component unmount, clear scheduled - useEffect( - () => () => { - clearTimeout(timerRetry.current); - }, - [] - ); -}; - -const loading: GraphicalValue = { state: "loading", value: undefined }; - -/** - * Calculate the graphical state based on the pot possible states - * @param potBpdActivation - */ -export const calculateBpdToggleGraphicalState = ( - potBpdActivation: pot.Pot -): GraphicalValue => - pot.fold( - potBpdActivation, - () => loading, - () => loading, - _ => loading, - _ => loading, - value => ({ state: "ready", value: value.activationStatus }), - value => ({ state: "loading", value: value.activationStatus }), - (_, newValue) => ({ - state: "update", - value: newValue.activationStatus - }), - value => ({ state: "ready", value: value.activationStatus }) - ); - -/** - * This component represents the activation state of bpd on a payment method. - * - Load the initial value (is bpd active on the payment method) - * - The toggle allows the user to enable or disable bpd on the payment method - * - Sync the remote communication with the graphical states - * Bpd can also be "not activable" on the payment method: - * - The payment method doesn't have the capability - * - Bpd is already activate on the payment method by another user - * @constructor - */ -const PaymentMethodActivationToggle: React.FunctionComponent = props => { - // Calculate the graphical state based on the potActivation and capability - const graphicalState: GraphicalValue = props.hasBpdCapability - ? calculateBpdToggleGraphicalState(props.bpdPotActivation) - : { state: "ready", value: "notActivable" }; - - const [toggleValue, setToggleValue] = useState( - graphicalState.value === "active" - ); - // trigger the initial loading / retry - useInitialValue(props); - - // a simplification because the onPress is dispatched only when is not activable / compatible - const notActivableType: NotActivableType = !props.hasBpdCapability - ? "NotCompatible" - : "NotActivable"; - - const { - present: askConfirmation, - bottomSheet: changeActivationConfirmationBS - } = useChangeActivationConfirmationBottomSheet(props, toggleValue, () => - props.updateValue(props.hPan, toggleValue) - ); - - const { present: showExplanation, bottomSheet: notAvailableInformationBS } = - useNotActivableInformationBottomSheet(props, notActivableType); - - return ( - <> - - - { - setToggleValue(newVal); - askConfirmation(); - }} - onPress={showExplanation} - /> - - {changeActivationConfirmationBS} - {notAvailableInformationBS} - - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - loadActualValue: (hPan: HPan) => - dispatch(bpdPaymentMethodActivation.request(hPan)), - updateValue: (hPan: HPan, value: boolean) => - dispatch(bpdUpdatePaymentMethodActivation.request({ hPan, value })) -}); - -const mapStateToProps = (state: GlobalState, props: BpdToggleProps) => ({ - bpdPotActivation: bpdPaymentMethodValueSelector(state, props.hPan) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(PaymentMethodActivationToggle); diff --git a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/base/PaymentMethodRepresentationComponent.tsx b/ts/features/bonus/bpd/components/paymentMethodActivationToggle/base/PaymentMethodRepresentationComponent.tsx deleted file mode 100644 index 15606135b8f..00000000000 --- a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/base/PaymentMethodRepresentationComponent.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import * as React from "react"; -import { View, Image, StyleSheet } from "react-native"; -import { HSpacer } from "@pagopa/io-app-design-system"; -import { Body } from "../../../../../../components/core/typography/Body"; -import { PaymentMethodRepresentation } from "../../../../../../types/pagopa"; - -const styles = StyleSheet.create({ - row: { - flex: 1, - flexDirection: "row" - }, - cardIcon: { - width: 40, - height: 25, - overflow: "hidden", - resizeMode: "contain" - }, - text: { - flex: 1, - paddingRight: 16 - } -}); - -/** - * Icon + text representation of a payment method - * @param props - * @constructor - */ -export const PaymentMethodRepresentationComponent: React.FunctionComponent< - PaymentMethodRepresentation -> = props => ( - - - - - {props.caption} - - -); diff --git a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/bottomsheet/BpdChangeActivationConfirmationScreen.tsx b/ts/features/bonus/bpd/components/paymentMethodActivationToggle/bottomsheet/BpdChangeActivationConfirmationScreen.tsx deleted file mode 100644 index 8ea2b5df5fb..00000000000 --- a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/bottomsheet/BpdChangeActivationConfirmationScreen.tsx +++ /dev/null @@ -1,110 +0,0 @@ -import { View } from "react-native"; -import * as React from "react"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { InfoBox } from "../../../../../../components/box/InfoBox"; -import { Body } from "../../../../../../components/core/typography/Body"; -import FooterWithButtons from "../../../../../../components/ui/FooterWithButtons"; -import Markdown from "../../../../../../components/ui/Markdown"; -import I18n from "../../../../../../i18n"; -import { PaymentMethodRepresentation } from "../../../../../../types/pagopa"; -import { useLegacyIOBottomSheetModal } from "../../../../../../utils/hooks/bottomSheet"; -import { - cancelButtonProps, - confirmButtonProps -} from "../../../../../../components/buttons/ButtonConfigurations"; -import { PaymentMethodRepresentationComponent } from "../base/PaymentMethodRepresentationComponent"; - -export type ConfirmationType = "Activation" | "Deactivation"; - -type Props = { - onCancel: () => void; - onConfirm: () => void; - type: ConfirmationType; - representation: PaymentMethodRepresentation; -}; - -const getText = (confirmationType: ConfirmationType) => ({ - cta: - confirmationType === "Activation" - ? I18n.t("global.buttons.activate") - : I18n.t("global.buttons.deactivate"), - body: - confirmationType === "Activation" - ? I18n.t("bonus.bpd.details.paymentMethods.activate.body") - : I18n.t("bonus.bpd.details.paymentMethods.deactivate.body") -}); - -/** - * Confirmation bottom sheet that informs the user about the consequences about the activation / deactivation - * @param props - * @constructor - */ -export const BpdChangeActivationConfirmationScreen: React.FunctionComponent< - Props -> = props => { - const { body } = getText(props.type); - - return ( - - - - - {body} - {props.type === "Activation" && ( - <> - - - - {I18n.t("bonus.bpd.details.paymentMethods.activate.disclaimer", { - activate: I18n.t("global.buttons.activate") - })} - - - - )} - - ); -}; - -export const useChangeActivationConfirmationBottomSheet = ( - representation: PaymentMethodRepresentation, - newVal: boolean, - onConfirm: () => void -) => { - const { cta } = getText(newVal ? "Activation" : "Deactivation"); - - const { present, bottomSheet, dismiss } = useLegacyIOBottomSheetModal( - dismiss()} - onConfirm={() => { - dismiss(); - onConfirm(); - }} - type={newVal ? "Activation" : "Deactivation"} - representation={{ - caption: representation.caption, - icon: representation.icon - }} - />, - newVal - ? I18n.t("bonus.bpd.details.paymentMethods.activate.title") - : I18n.t("bonus.bpd.details.paymentMethods.deactivate.title"), - 466, - dismiss()), - onPressWithGestureHandler: true - }} - rightButton={{ - ...confirmButtonProps(() => { - dismiss(); - onConfirm(); - }, cta), - onPressWithGestureHandler: true - }} - /> - ); - - return { present, bottomSheet, dismiss }; -}; diff --git a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/bottomsheet/BpdNotActivableInformation.tsx b/ts/features/bonus/bpd/components/paymentMethodActivationToggle/bottomsheet/BpdNotActivableInformation.tsx deleted file mode 100644 index d3384635993..00000000000 --- a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/bottomsheet/BpdNotActivableInformation.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import * as React from "react"; -import { View, StyleSheet } from "react-native"; -import { IOColors, VSpacer } from "@pagopa/io-app-design-system"; -import Markdown from "../../../../../../components/ui/Markdown"; -import I18n from "../../../../../../i18n"; -import { PaymentMethodRepresentation } from "../../../../../../types/pagopa"; -import { PaymentMethodRepresentationComponent } from "../base/PaymentMethodRepresentationComponent"; -import { useLegacyIOBottomSheetModal } from "../../../../../../utils/hooks/bottomSheet"; - -// NotActivable: already activated by someone else -// NotCompatible: missing bpd capability -export type NotActivableType = "NotActivable" | "NotCompatible"; - -type Props = { - type: NotActivableType; - representation: PaymentMethodRepresentation; -}; - -const styles = StyleSheet.create({ - body: { - flex: 1, - backgroundColor: IOColors.white - } -}); - -const getBody = (type: NotActivableType) => { - switch (type) { - case "NotActivable": - return I18n.t("bonus.bpd.details.paymentMethods.notActivable.body"); - case "NotCompatible": - return I18n.t("bonus.bpd.details.paymentMethods.notCompatible.body"); - } -}; - -const getTitle = (type: NotActivableType) => { - switch (type) { - case "NotActivable": - return I18n.t("bonus.bpd.details.paymentMethods.notActivable.title"); - case "NotCompatible": - return I18n.t("bonus.bpd.details.paymentMethods.notCompatible.title"); - } -}; - -export const BpdNotActivableInformation: React.FunctionComponent< - Props -> = props => ( - - - - - {getBody(props.type)} - -); - -export const useNotActivableInformationBottomSheet = ( - representation: PaymentMethodRepresentation, - type: NotActivableType -) => { - const { present, bottomSheet, dismiss } = useLegacyIOBottomSheetModal( - , - getTitle(type), - 310 - ); - - return { present, bottomSheet, dismiss }; -}; diff --git a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/bottomsheet/OtherChannelInformation.tsx b/ts/features/bonus/bpd/components/paymentMethodActivationToggle/bottomsheet/OtherChannelInformation.tsx deleted file mode 100644 index 8931210b3d3..00000000000 --- a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/bottomsheet/OtherChannelInformation.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { none } from "fp-ts/lib/Option"; -import { View } from "react-native"; -import * as React from "react"; -import { IOColors, VSpacer } from "@pagopa/io-app-design-system"; -import BlockButtons from "../../../../../../components/ui/BlockButtons"; -import Markdown from "../../../../../../components/ui/Markdown"; -import I18n from "../../../../../../i18n"; -import { navigateToWalletAddPaymentMethod } from "../../../../../../store/actions/navigation"; -import { useLegacyIOBottomSheetModal } from "../../../../../../utils/hooks/bottomSheet"; - -// NotActivable: already activated by someone else -// NotCompatible: missing bpd capability -export type NotActivableType = "NotActivable" | "NotCompatible"; - -type Props = { onAddPayment: () => void }; - -const addPaymentMethodButton = (onPress: () => void) => ( - -); - -export const OtherChannelInformation: React.FunctionComponent< - Props -> = props => ( - - - - {I18n.t("bonus.bpd.details.paymentMethods.activateOnOthersChannel.body")} - - - {addPaymentMethodButton(props.onAddPayment)} - -); - -export const useOtherChannelInformationBottomSheet = () => { - const { present, bottomSheet, dismiss } = useLegacyIOBottomSheetModal( - { - dismiss(); - navigateToWalletAddPaymentMethod({ inPayment: none }); - }} - />, - I18n.t("bonus.bpd.details.paymentMethods.activateOnOthersChannel.title"), - 350 - ); - return { present, bottomSheet, dismiss }; -}; diff --git a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/list/PaymentMethodGroupedList.tsx b/ts/features/bonus/bpd/components/paymentMethodActivationToggle/list/PaymentMethodGroupedList.tsx deleted file mode 100644 index adabe6ae390..00000000000 --- a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/list/PaymentMethodGroupedList.tsx +++ /dev/null @@ -1,122 +0,0 @@ -import * as React from "react"; -import { View, StyleSheet } from "react-native"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { Body } from "../../../../../../components/core/typography/Body"; -import { H4 } from "../../../../../../components/core/typography/H4"; -import { Link } from "../../../../../../components/core/typography/Link"; -import I18n from "../../../../../../i18n"; -import { hasFunctionEnabled } from "../../../../../../utils/walletv2"; -import { useWhyOtherCardsBottomSheet } from "../../../screens/details/components/bottomsheet/WhyOtherCards"; -import { PaymentMethodWithActivation } from "../../../store/reducers/details/combiner"; -import { useOtherChannelInformationBottomSheet } from "../bottomsheet/OtherChannelInformation"; -import { EnableableFunctionsEnum } from "../../../../../../../definitions/pagopa/EnableableFunctions"; -import { PaymentMethodRawList } from "./PaymentMethodRawList"; - -type Props = { paymentList: ReadonlyArray }; - -const styles = StyleSheet.create({ - row: { flexDirection: "row", justifyContent: "space-between" } -}); - -const isOtherChannel = (w: PaymentMethodWithActivation) => - w.onboardingChannel === "EXT"; - -const isNotActivable = (w: PaymentMethodWithActivation) => - w.activationStatus === "notActivable" || - !hasFunctionEnabled(w, EnableableFunctionsEnum.BPD); - -/** - * A quick solution, not the best but the cardinality of the entities - * involved doesn't requires more efficient solutions - * @param paymentList - */ -const clusterizePaymentMethods = ( - paymentList: ReadonlyArray -) => ({ - otherChannels: paymentList.filter(isOtherChannel), - notActivable: paymentList.filter(isNotActivable), - activables: paymentList.filter(v => !isNotActivable(v) && !isOtherChannel(v)) -}); - -const OtherChannelsSection = (props: { - paymentMethods: ReadonlyArray; -}) => { - const { - present: presentOtherChannel, - bottomSheet: otherChannelInformationBottomSheet - } = useOtherChannelInformationBottomSheet(); - const { - present: presentWhyOthersCard, - bottomSheet: whyOtherCardsBottomSheet - } = useWhyOtherCardsBottomSheet(); - - return ( - - - - - {I18n.t( - "bonus.bpd.details.paymentMethods.activateOnOthersChannel.text1" - )} -

- {I18n.t( - "bonus.bpd.details.paymentMethods.activateOnOthersChannel.text2" - )} -

- - - {I18n.t("global.buttons.info").toLowerCase()} - -
- - - - - {I18n.t( - "bonus.bpd.details.paymentMethods.activateOnOthersChannel.whyOtherCards.title" - )} - - {otherChannelInformationBottomSheet} - {whyOtherCardsBottomSheet} -
- ); -}; - -const NotActivablesSection = (props: { - paymentMethods: ReadonlyArray; -}) => ( - - - - - {I18n.t("bonus.bpd.details.paymentMethods.notActivable.header")} - - - - - -); - -/** - * Render an array of WalletV2WithActivation, grouping different category of payment methods. - * @param props - * @constructor - */ -export const PaymentMethodGroupedList: React.FunctionComponent< - Props -> = props => { - const { activables, otherChannels, notActivable } = clusterizePaymentMethods( - props.paymentList - ); - return ( - - - {otherChannels.length > 0 && ( - - )} - {notActivable.length > 0 && ( - - )} - - ); -}; diff --git a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/list/PaymentMethodRawList.tsx b/ts/features/bonus/bpd/components/paymentMethodActivationToggle/list/PaymentMethodRawList.tsx deleted file mode 100644 index 68a6ebce03e..00000000000 --- a/ts/features/bonus/bpd/components/paymentMethodActivationToggle/list/PaymentMethodRawList.tsx +++ /dev/null @@ -1,20 +0,0 @@ -import * as React from "react"; -import { View } from "react-native"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import { PaymentMethod } from "../../../../../../types/pagopa"; -import { bpdToggleFactory } from "../BpdPaymentMethodToggleFactory"; - -type Props = { - paymentList: ReadonlyArray; -}; - -/** - * Render a {@link ReadOnlyArray} of {@link EnhancedPaymentMethod} using {@link PaymentMethodBpdToggle} - * @param props - * @constructor - */ -export const PaymentMethodRawList: React.FunctionComponent = props => ( - - {props.paymentList.map(pm => bpdToggleFactory(pm))} - -); diff --git a/ts/features/bonus/bpd/components/superCashbackRanking/LastPositionItem.tsx b/ts/features/bonus/bpd/components/superCashbackRanking/LastPositionItem.tsx deleted file mode 100644 index dea180116a5..00000000000 --- a/ts/features/bonus/bpd/components/superCashbackRanking/LastPositionItem.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import * as React from "react"; -import { formatNumberWithNoDigits } from "../../../../../utils/stringBuilder"; -import I18n from "../../../../../i18n"; -import RankPositionItem from "./RankPositionItem"; - -type Props = { - transactionsNumber: number; - superCashbackAmount: number; - lastAvailablePosition: number; -}; - -const THOUSAND_UNIT = 1000; - -const retrieveBoxedLabel = (lastAvailablePosition: number) => - lastAvailablePosition >= THOUSAND_UNIT - ? `${Math.floor(lastAvailablePosition / THOUSAND_UNIT)}K` - : `${lastAvailablePosition}`; - -export const LastPositionItem: React.FunctionComponent = ( - props: Props -) => ( - -); diff --git a/ts/features/bonus/bpd/components/superCashbackRanking/RankPositionItem.tsx b/ts/features/bonus/bpd/components/superCashbackRanking/RankPositionItem.tsx deleted file mode 100644 index 7bc8bf1fb63..00000000000 --- a/ts/features/bonus/bpd/components/superCashbackRanking/RankPositionItem.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import * as React from "react"; -import { View, StyleSheet } from "react-native"; -import { IOColors, HSpacer, VSpacer } from "@pagopa/io-app-design-system"; -import { IOStyles } from "../../../../../components/core/variables/IOStyles"; -import { H5 } from "../../../../../components/core/typography/H5"; -import { H4 } from "../../../../../components/core/typography/H4"; -import { - formatIntegerNumber, - formatNumberWithNoDigits -} from "../../../../../utils/stringBuilder"; -import I18n from "../../../../../i18n"; -import { IOBadge } from "../../../../../components/core/IOBadge"; - -type Props = { - transactionsNumber: number; - superCashbackAmount: number; - boxedLabel: string; - rankingLabel: string; - currentUserPosition?: boolean; - hideBadge?: boolean; -}; - -const style = StyleSheet.create({ - positionBox: { - paddingVertical: 8, - width: 48, - textAlign: "center" - } -}); - -const RankPositionItem = (props: Props): React.ReactElement => ( - <> - - -

- {props.boxedLabel} -

-
- - - -

{props.rankingLabel}

- {!props.hideBadge && ( - - - - )} -
-
- {I18n.t("bonus.bpd.details.transaction.label", { - defaultValue: I18n.t("bonus.bpd.details.transaction.label.other", { - transactions: formatIntegerNumber(props.transactionsNumber) - }), - count: props.transactionsNumber, - transactions: formatIntegerNumber(props.transactionsNumber) - })} -
-
-
- - -); - -export default RankPositionItem; diff --git a/ts/features/bonus/bpd/components/superCashbackRanking/SuperCashbackHeader.tsx b/ts/features/bonus/bpd/components/superCashbackRanking/SuperCashbackHeader.tsx deleted file mode 100644 index 3bee88f4be4..00000000000 --- a/ts/features/bonus/bpd/components/superCashbackRanking/SuperCashbackHeader.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import * as React from "react"; -import { View } from "react-native"; -import { connect } from "react-redux"; -import { HSpacer, VSpacer } from "@pagopa/io-app-design-system"; -import { IOBadge } from "../../../../../components/core/IOBadge"; -import { H3 } from "../../../../../components/core/typography/H3"; -import { IOStyles } from "../../../../../components/core/variables/IOStyles"; -import I18n from "../../../../../i18n"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { BpdPeriodWithInfo } from "../../store/reducers/details/periods"; -import { bpdSelectedPeriodSelector } from "../../store/reducers/details/selectedPeriod"; - -type Props = ReturnType; - -const isPeriodOnGoing = (period: BpdPeriodWithInfo | undefined) => - pipe( - period, - O.fromNullable, - O.fold( - () => false, - p => { - if (p.status !== "Closed") { - return true; - } - - const actualDate = new Date(); - const endDate = new Date(p.endDate.getTime()); - endDate.setDate(endDate.getDate() + p.gracePeriod); - - return ( - actualDate.getTime() >= p.endDate.getTime() && - actualDate.getTime() <= endDate.getTime() - ); - } - ) - ); - -const SuperCashbackHeader: React.FunctionComponent = (props: Props) => ( - - {isPeriodOnGoing(props.selectedPeriod) && ( - <> - - - - )} - -

{I18n.t("bonus.bpd.details.superCashback.rankTitle")}

-
-); - -const mapStateToProps = (state: GlobalState) => ({ - selectedPeriod: bpdSelectedPeriodSelector(state) -}); - -export default connect(mapStateToProps)(SuperCashbackHeader); diff --git a/ts/features/bonus/bpd/components/superCashbackRanking/SuperCashbackRanking.tsx b/ts/features/bonus/bpd/components/superCashbackRanking/SuperCashbackRanking.tsx deleted file mode 100644 index 110da22ce20..00000000000 --- a/ts/features/bonus/bpd/components/superCashbackRanking/SuperCashbackRanking.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import * as React from "react"; -import { connect } from "react-redux"; -import { IOColors, VSpacer } from "@pagopa/io-app-design-system"; -import { H3 } from "../../../../../components/core/typography/H3"; -import { H4 } from "../../../../../components/core/typography/H4"; -import ItemSeparatorComponent from "../../../../../components/ItemSeparatorComponent"; -import Markdown from "../../../../../components/ui/Markdown"; -import I18n from "../../../../../i18n"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { useLegacyIOBottomSheetModal } from "../../../../../utils/hooks/bottomSheet"; -import { - formatIntegerNumber, - formatNumberWithNoDigits -} from "../../../../../utils/stringBuilder"; -import { isBpdRankingReady } from "../../store/reducers/details/periods"; -import { bpdSelectedPeriodSelector } from "../../store/reducers/details/selectedPeriod"; -import { isInGracePeriod } from "../../utils/dates"; -import { LastPositionItem } from "./LastPositionItem"; -import SuperCashbackHeader from "./SuperCashbackHeader"; -import UserPositionItem from "./UserPositionItem"; - -type Props = ReturnType; - -const RankingItems: React.FunctionComponent = (props: Props) => { - if (props.selectedPeriod && isBpdRankingReady(props.selectedPeriod.ranking)) { - const mapRankingItems: Map = new Map< - number, - React.ReactNode - >([ - [ - props.selectedPeriod.minPosition, - - ], - [ - props.selectedPeriod.ranking.ranking, - - props.selectedPeriod.minPosition - } - userPosition={props.selectedPeriod.ranking.ranking} - /> - ] - ]); - - const key = [...mapRankingItems.keys()].sort((a, b) => a - b); - - return <>{key.map(k => mapRankingItems.get(k))}; - } - - return null; -}; - -const CSS_STYLE = ` -body { - color: ${IOColors.bluegreyDark} -} -`; - -const SuperCashbackBottomSheet: React.FunctionComponent = ( - props: Props -) => ( - <> - - - - - -

{I18n.t("bonus.bpd.details.superCashback.howItWorks.title")}

- - {props.selectedPeriod && ( - <> - - {I18n.t("bonus.bpd.details.superCashback.howItWorks.body", { - citizens: formatIntegerNumber(props.selectedPeriod.minPosition), - amount: formatNumberWithNoDigits( - props.selectedPeriod.superCashbackAmount - ) - })} - - - {props.selectedPeriod.status === "Active" || - isInGracePeriod( - props.selectedPeriod.endDate, - props.selectedPeriod.gracePeriod - ) ? ( - - {I18n.t("bonus.bpd.details.superCashback.howItWorks.status.active")} - - ) : ( -

- {I18n.t("bonus.bpd.details.superCashback.howItWorks.status.closed")} -

- )} - - )} - -); - -const mapStateToProps = (state: GlobalState) => ({ - selectedPeriod: bpdSelectedPeriodSelector(state) -}); - -const SuperCashbackRanking = connect(mapStateToProps)(SuperCashbackBottomSheet); - -export default SuperCashbackRanking; - -export const useSuperCashbackRankingBottomSheet = () => - useLegacyIOBottomSheetModal( - , - , - 470 - ); diff --git a/ts/features/bonus/bpd/components/superCashbackRanking/UserPositionItem.tsx b/ts/features/bonus/bpd/components/superCashbackRanking/UserPositionItem.tsx deleted file mode 100644 index 2dd1e69ecf5..00000000000 --- a/ts/features/bonus/bpd/components/superCashbackRanking/UserPositionItem.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import * as React from "react"; -import { connect } from "react-redux"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { profileNameSelector } from "../../../../../store/reducers/profile"; -import { formatNumberWithNoDigits } from "../../../../../utils/stringBuilder"; -import I18n from "../../../../../i18n"; -import RankPositionItem from "./RankPositionItem"; - -type Props = { - transactionsNumber: number; - superCashbackAmount: number; - userPosition: number; - hideBadge?: boolean; -} & ReturnType; - -const UserPositionItem: React.FunctionComponent = (props: Props) => ( - -); - -const mapStateToProps = (state: GlobalState) => ({ - currentUser: profileNameSelector(state) -}); - -export default connect(mapStateToProps)(UserPositionItem); diff --git a/ts/features/bonus/bpd/components/superCashbackRanking/__test__/RankPositionItem.test.tsx b/ts/features/bonus/bpd/components/superCashbackRanking/__test__/RankPositionItem.test.tsx deleted file mode 100644 index 3a4b89ac6ac..00000000000 --- a/ts/features/bonus/bpd/components/superCashbackRanking/__test__/RankPositionItem.test.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { render } from "@testing-library/react-native"; -import * as React from "react"; -import { IOColors } from "@pagopa/io-app-design-system"; -import RankPositionItem from "../RankPositionItem"; -import I18n from "../../../../../../i18n"; -import { formatNumberWithNoDigits } from "../../../../../../utils/stringBuilder"; - -describe("RankingPositionItem", () => { - it("Expect an item representing user position with Super Cashback amount badge", () => { - const userLabel = I18n.t("global.you"); - const rankingLabel = `${formatNumberWithNoDigits(5000)}º: Maria`; - - const component = render( - - ); - - expect(component).not.toBeNull(); - expect(component.queryByTestId("PositionBoxContainer")).toHaveStyle({ - backgroundColor: IOColors.blue - }); - expect(component.queryByTestId("PositionBoxedLabel")).toHaveTextContent( - userLabel - ); - expect(component.queryByTestId("RankingLabel")).toHaveTextContent( - rankingLabel - ); - expect(component.queryByTestId("SuperCashbackAmountBadge")).toBeDefined(); - }); - - it("Expect an item representing user position without Super Cashback amount badge", () => { - const userLabel = I18n.t("global.you"); - const rankingLabel = `${formatNumberWithNoDigits(5000)}º: Maria`; - - const component = render( - - ); - - expect(component).not.toBeNull(); - expect(component.queryByTestId("PositionBoxContainer")).toHaveStyle({ - backgroundColor: IOColors.blue - }); - expect(component.queryByTestId("PositionBoxedLabel")).toHaveTextContent( - userLabel - ); - expect(component.queryByTestId("RankingLabel")).toHaveTextContent( - rankingLabel - ); - expect(component.queryByTestId("SuperCashbackAmountBadge")).toBeNull(); - }); - - it("Expect an item representing generic position", () => { - const boxedLabel = "100K"; - - const rankingLabel = I18n.t("bonus.bpd.details.superCashback.rankLabel", { - position: formatNumberWithNoDigits(1000) - }); - - const component = render( - - ); - - expect(component.queryByTestId("PositionBoxedLabel")).toHaveTextContent( - boxedLabel - ); - expect(component.queryByTestId("RankingLabel")).toHaveTextContent( - rankingLabel - ); - expect(component.queryByTestId("SuperCashbackAmountBadge")).toBeNull(); - }); -}); diff --git a/ts/features/bonus/bpd/components/transactionItem/BaseBpdTransactionItem.tsx b/ts/features/bonus/bpd/components/transactionItem/BaseBpdTransactionItem.tsx deleted file mode 100644 index 3e8467db04b..00000000000 --- a/ts/features/bonus/bpd/components/transactionItem/BaseBpdTransactionItem.tsx +++ /dev/null @@ -1,82 +0,0 @@ -import * as React from "react"; -import { - View, - Image, - ImageSourcePropType, - StyleSheet, - TouchableOpacity, - TouchableWithoutFeedbackProps -} from "react-native"; -import { IOColors, HSpacer } from "@pagopa/io-app-design-system"; -import { H4 } from "../../../../../components/core/typography/H4"; -import { H5 } from "../../../../../components/core/typography/H5"; -import { IOStyles } from "../../../../../components/core/variables/IOStyles"; -import { ShadowBox } from "../../screens/details/components/summary/base/ShadowBox"; - -type Props = { - image: ImageSourcePropType; - title: string; - subtitle: string; - rightText: string; -} & Pick; - -const styles = StyleSheet.create({ - baseRow: { - flex: 1, - flexDirection: "row", - justifyContent: "space-between", - backgroundColor: IOColors.white - }, - leftRow: { - flex: 1, - flexDirection: "row", - paddingRight: 16 - }, - cardIcon: { - width: 40, - height: 25, - overflow: "hidden", - resizeMode: "contain", - alignSelf: "center" - }, - rightText: { alignSelf: "center" } -}); - -/** - * Graphical settings and layout for the BpdTransactionItem - * @param props - * @constructor - */ -export const BaseBpdTransactionItem: React.FunctionComponent = props => ( - - - {/* The base row */} - - {/* The left side of the row (icon, space, top text, bottom text */} - - - - -

- {props.title} -

-
{props.subtitle}
-
-
- {/* The right side of the row */} -

- {props.rightText} -

-
-
-
-); diff --git a/ts/features/bonus/bpd/components/transactionItem/BpdTransactionItem.tsx b/ts/features/bonus/bpd/components/transactionItem/BpdTransactionItem.tsx deleted file mode 100644 index d0cd2f803d4..00000000000 --- a/ts/features/bonus/bpd/components/transactionItem/BpdTransactionItem.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import * as React from "react"; -import { ImageSourcePropType } from "react-native"; -import I18n from "../../../../../i18n"; -import { useLegacyIOBottomSheetModal } from "../../../../../utils/hooks/bottomSheet"; -import { localeDateFormat } from "../../../../../utils/locale"; -import { formatNumberAmount } from "../../../../../utils/stringBuilder"; -import { - BpdTransactionDetailComponent, - BpdTransactionDetailRepresentation -} from "../../screens/details/transaction/detail/BpdTransactionDetailComponent"; -import { BpdTransaction } from "../../store/actions/transactions"; -import { BaseBpdTransactionItem } from "./BaseBpdTransactionItem"; - -export type EnhancedBpdTransaction = { - image: ImageSourcePropType; - title: string; - keyId: string; - maxCashbackForTransactionAmount: number | undefined; -} & BpdTransaction; - -type Props = { - transaction: BpdTransactionDetailRepresentation; -}; - -/** - * Create the subtitle for the transaction item, based on the trx date and trx amount - * TODO: move the get subtitle in the combiner and remove this component? - * @param transaction - */ -export const getSubtitle = (transaction: BpdTransaction) => { - const isMidNight = - transaction.trxDate.getHours() + - transaction.trxDate.getMinutes() + - transaction.trxDate.getSeconds() === - 0; - return isMidNight - ? `€ ${formatNumberAmount(transaction.amount)} · ${localeDateFormat( - transaction.trxDate, - I18n.t("global.dateFormats.dayMonthWithoutTime") - )} ` - : `€ ${formatNumberAmount(transaction.amount)} · ${localeDateFormat( - transaction.trxDate, - I18n.t("global.dateFormats.dayMonthWithTime") - )} `; -}; - -export const BpdTransactionItem: React.FunctionComponent = props => { - const { present: openBottomSheet, bottomSheet } = useLegacyIOBottomSheetModal( - , - I18n.t("bonus.bpd.details.transaction.detail.title"), - 522 - ); - - return ( - <> - - {bottomSheet} - - ); -}; diff --git a/ts/features/bonus/bpd/components/transactionItem/__test__/BdpTransactionItem.test.tsx b/ts/features/bonus/bpd/components/transactionItem/__test__/BdpTransactionItem.test.tsx deleted file mode 100644 index 18d354b89c8..00000000000 --- a/ts/features/bonus/bpd/components/transactionItem/__test__/BdpTransactionItem.test.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { getSubtitle } from "../BpdTransactionItem"; -import { EnhancedBpdTransaction } from "../../../components/transactionItem/BpdTransactionItem"; -import { HPan } from "../../../store/actions/paymentMethods"; -import { AwardPeriodId } from "../../../store/actions/periods"; - -describe("Test how the transaction subtitle changes with different timestamps", () => { - it("Subtitle shouldn't contain hours and minutes, when the transaction has a timestamp 00:00", () => { - const myTransaction: EnhancedBpdTransaction = { - hashPan: - "0d4194712c5d820fcbbb2e7ba199e15f73cfd43f8fe49f0aa62e7901253506df" as HPan, - idTrxAcquirer: "10126487773E", - idTrxIssuer: "R64692", - amount: 87.79, - awardPeriodId: 2 as AwardPeriodId, - image: 29, - maxCashbackForTransactionAmount: 15, - title: "xxxxxxx", - trxDate: new Date("2100-12-17T00:00"), - keyId: "xxxxxxxxx", - cashback: 8.779, - circuitType: "Mastercard / Maestro" - }; - - expect(getSubtitle(myTransaction)).toMatch("€ 87.79 · 17 Dec "); - }); - - it("Subtitle should contain hours and minutes when the transaction has a timestamp 00:00", () => { - const myTransaction: EnhancedBpdTransaction = { - hashPan: - "0d4194712c5d820fcbbb2e7ba199e15f73cfd43f8fe49f0aa62e7901253506df" as HPan, - idTrxAcquirer: "10126487773E", - idTrxIssuer: "R64692", - amount: 100000.79, - awardPeriodId: 2 as AwardPeriodId, - image: 29, - maxCashbackForTransactionAmount: 15, - title: "xxxxxxx", - trxDate: new Date("2100-12-19T12:39"), - keyId: "xxxxxxxxx", - cashback: 8.779, - circuitType: "Mastercard / Maestro" - }; - - expect(getSubtitle(myTransaction)).toMatch("€ 100,000.79 · 19 Dec, 12:39 "); - }); -}); diff --git a/ts/features/bonus/bpd/components/walletCardContainer/BpdCardsInWalletComponent.tsx b/ts/features/bonus/bpd/components/walletCardContainer/BpdCardsInWalletComponent.tsx deleted file mode 100644 index 5bf2e4268ae..00000000000 --- a/ts/features/bonus/bpd/components/walletCardContainer/BpdCardsInWalletComponent.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import _ from "lodash"; -import { View } from "react-native"; -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { bpdSelectPeriod } from "../../store/actions/selectedPeriod"; -import { bpdPeriodsAmountWalletVisibleSelector } from "../../store/reducers/details/combiner"; -import { BpdPeriodWithInfo } from "../../store/reducers/details/periods"; -import { BpdCardComponent } from "../bpdCardComponent/BpdCardComponent"; - -type Props = ReturnType & - ReturnType; - -const BpdCardList = (props: Props) => ( - - {pot.isSome(props.periodsWithAmount) && - props.periodsWithAmount.value.map(pa => ( - { - props.navigateToCashbackDetails(pa); - }} - /> - ))} - -); - -const BpdCardListMemo = React.memo( - BpdCardList, - (prev: Props, curr: Props) => - pot.isSome(prev.periodsWithAmount) && - pot.isSome(curr.periodsWithAmount) && - _.isEqual(curr.periodsWithAmount.value, prev.periodsWithAmount.value) -); - -/** - * Render the bpd card in the wallet - * //TODO: handle error, render only if some - * @constructor - */ -const BpdCardsInWalletContainer = (props: Props) => ( - -); - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - navigateToCashbackDetails: (period: BpdPeriodWithInfo) => { - dispatch(bpdSelectPeriod(period)); - } -}); - -const mapStateToProps = (state: GlobalState) => ({ - periodsWithAmount: bpdPeriodsAmountWalletVisibleSelector(state) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(BpdCardsInWalletContainer); diff --git a/ts/features/bonus/bpd/saga/index.ts b/ts/features/bonus/bpd/saga/index.ts deleted file mode 100644 index f2ce5a2cdca..00000000000 --- a/ts/features/bonus/bpd/saga/index.ts +++ /dev/null @@ -1,132 +0,0 @@ -import { SagaIterator } from "redux-saga"; -import { takeEvery, takeLatest } from "typed-redux-saga/macro"; -import { getType } from "typesafe-actions"; -import { bpdApiUrlPrefix } from "../../../../config"; -import { BackendBpdClient } from "../api/backendBpdClient"; -import { bpdAllData, bpdLoadActivationStatus } from "../store/actions/details"; -import { bpdIbanInsertionStart, bpdUpsertIban } from "../store/actions/iban"; -import { - bpdDeleteUserFromProgram, - bpdEnrollUserToProgram, - bpdOnboardingAcceptDeclaration, - bpdOnboardingStart -} from "../store/actions/onboarding"; -import { - bpdPaymentMethodActivation, - bpdUpdatePaymentMethodActivation -} from "../store/actions/paymentMethods"; -import { bpdPeriodsAmountLoad } from "../store/actions/periods"; -import { - bpdTransactionsLoadCountByDay, - bpdTransactionsLoadMilestone, - bpdTransactionsLoadPage, - bpdTransactionsLoadRequiredData -} from "../store/actions/transactions"; -import { deleteCitizen, getCitizenV2, putEnrollCitizenV2 } from "./networking"; -import { loadBpdData } from "./networking/loadBpdData"; -import { loadPeriodsWithInfo } from "./networking/loadPeriodsWithInfo"; -import { patchCitizenIban } from "./networking/patchCitizenIban"; -import { - bpdLoadPaymentMethodActivationSaga, - bpdUpdatePaymentMethodActivationSaga -} from "./networking/paymentMethod"; -import { handleLoadMilestone } from "./networking/ranking"; -import { handleCountByDay } from "./networking/winning-transactions/countByDay"; -import { handleTransactionsLoadRequiredData } from "./networking/winning-transactions/loadTransactionsRequiredData"; -import { handleTransactionsPage } from "./networking/winning-transactions/transactionsPage"; -import { handleBpdIbanInsertion } from "./orchestration/insertIban"; -import { handleBpdEnroll } from "./orchestration/onboarding/enrollToBpd"; -import { handleBpdStartOnboardingSaga } from "./orchestration/onboarding/startOnboarding"; - -// watch all events about bpd -export function* watchBonusBpdSaga(bpdBearerToken: string): SagaIterator { - const bpdBackendClient = BackendBpdClient(bpdApiUrlPrefix, bpdBearerToken); - - // load citizen details - yield* takeLatest( - bpdLoadActivationStatus.request, - getCitizenV2, - bpdBackendClient.findV2 - ); - // enroll citizen to the bpd - yield* takeLatest( - bpdEnrollUserToProgram.request, - putEnrollCitizenV2, - bpdBackendClient.enrollCitizenV2IO - ); - - // delete citizen from the bpd - yield* takeLatest( - bpdDeleteUserFromProgram.request, - deleteCitizen, - bpdBackendClient.deleteCitizenIO - ); - - // upsert citizen iban - yield* takeLatest( - bpdUpsertIban.request, - patchCitizenIban, - bpdBackendClient.updatePaymentMethod - ); - - // load bpd activation status for a specific payment method - yield* takeEvery( - bpdPaymentMethodActivation.request, - bpdLoadPaymentMethodActivationSaga, - bpdBackendClient.findPayment - ); - - // update bpd activation status for a specific payment method - yield* takeEvery( - bpdUpdatePaymentMethodActivation.request, - bpdUpdatePaymentMethodActivationSaga, - bpdBackendClient.enrollPayment, - bpdBackendClient.deletePayment - ); - - // prefetch all the bpd data - yield* takeEvery(bpdAllData.request, loadBpdData); - - // Load bpd periods with amount - yield* takeEvery( - bpdPeriodsAmountLoad.request, - loadPeriodsWithInfo, - bpdBackendClient - ); - - // Load count by day info for a period - yield* takeEvery( - bpdTransactionsLoadCountByDay.request, - handleCountByDay, - bpdBackendClient.winningTransactionsV2CountByDay - ); - - // Load the milestone (pivot) information for a period - yield* takeEvery( - bpdTransactionsLoadMilestone.request, - handleLoadMilestone, - bpdBackendClient.getRankingV2 - ); - - // Load a transactions page for a period - yield* takeEvery( - bpdTransactionsLoadPage.request, - handleTransactionsPage, - bpdBackendClient.winningTransactionsV2 - ); - - // Load all the required transactions data, for a period - yield* takeEvery( - bpdTransactionsLoadRequiredData.request, - handleTransactionsLoadRequiredData - ); - - // First step of the onboarding workflow; check if the user is enrolled to the bpd program - yield* takeLatest(getType(bpdOnboardingStart), handleBpdStartOnboardingSaga); - - // The user accepts the declaration, enroll the user to the bpd program - yield* takeLatest(getType(bpdOnboardingAcceptDeclaration), handleBpdEnroll); - - // The user start the insertion / modification of the IBAN associated with bpd program - yield* takeLatest(getType(bpdIbanInsertionStart), handleBpdIbanInsertion); -} diff --git a/ts/features/bonus/bpd/saga/networking/__test__/loadBpdData.test.ts b/ts/features/bonus/bpd/saga/networking/__test__/loadBpdData.test.ts deleted file mode 100644 index 428152d161d..00000000000 --- a/ts/features/bonus/bpd/saga/networking/__test__/loadBpdData.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { testSaga } from "redux-saga-test-plan"; -import { abiSelector } from "../../../../../wallet/onboarding/store/abi"; -import { remoteReady } from "../../../../../../common/model/RemoteValue"; -import { - bpdAllData, - bpdLoadActivationStatus -} from "../../../store/actions/details"; -import { bpdPeriodsAmountLoad } from "../../../store/actions/periods"; -import { loadBpdData, testableFunctions } from "../loadBpdData"; - -// TODO: tested only two base case, add more if needed -describe("loadBpdData", () => { - it("Dispatch bpdAllData.success if the user is not enrolled", () => { - testSaga(loadBpdData) - .next() - .select(abiSelector) - .next(remoteReady({})) - .call(testableFunctions.checkPreviousFailures!) - .next() - .put(bpdLoadActivationStatus.request()) - .next() - .take([bpdLoadActivationStatus.success, bpdLoadActivationStatus.failure]) - .next( - bpdLoadActivationStatus.success({ - enabled: false, - activationStatus: "never", - payoffInstr: undefined - }) - ) - .put(bpdAllData.success()) - .next() - .isDone(); - }); - it("Dispatch bpdAllData.success if the user is enrolled and all subsequent requests are successful", () => { - testSaga(loadBpdData) - .next() - .select(abiSelector) - .next(remoteReady({})) - .call(testableFunctions.checkPreviousFailures!) - .next() - .put(bpdLoadActivationStatus.request()) - .next() - .take([bpdLoadActivationStatus.success, bpdLoadActivationStatus.failure]) - .next( - bpdLoadActivationStatus.success({ - enabled: true, - activationStatus: "subscribed", - payoffInstr: undefined - }) - ) - .put(bpdPeriodsAmountLoad.request()) - .next() - .take([bpdPeriodsAmountLoad.success, bpdPeriodsAmountLoad.failure]) - .next(bpdPeriodsAmountLoad.success([])) - .put(bpdAllData.success()) - .next() - .isDone(); - }); -}); diff --git a/ts/features/bonus/bpd/saga/networking/__test__/loadPeriodsAmount.test.ts b/ts/features/bonus/bpd/saga/networking/__test__/loadPeriodsAmount.test.ts deleted file mode 100644 index 940eed1c7a5..00000000000 --- a/ts/features/bonus/bpd/saga/networking/__test__/loadPeriodsAmount.test.ts +++ /dev/null @@ -1,249 +0,0 @@ -import * as E from "fp-ts/lib/Either"; -import { expectSaga } from "redux-saga-test-plan"; -import { call } from "redux-saga-test-plan/matchers"; -import { - AwardPeriodId, - BpdPeriod, - bpdPeriodsAmountLoad -} from "../../../store/actions/periods"; -import { - notEligibleAmount, - zeroAmount -} from "../../../store/reducers/__mock__/amount"; -import { - activePeriod, - closedPeriod, - inactivePeriod, - withAwardPeriodId -} from "../../../store/reducers/__mock__/periods"; -import { - notReadyRanking, - readyRanking -} from "../../../store/reducers/__mock__/ranking"; -import { BpdAmount, bpdLoadAmountSaga } from "../amount"; -import { loadPeriodsWithInfo } from "../loadPeriodsWithInfo"; -import { bpdLoadPeriodsSaga } from "../periods"; -import { bpdLoadRakingV2 } from "../ranking"; - -describe("loadPeriodsAmount, mock networking saga", () => { - it("Dispatch failure if awardsPeriods fails", async () => { - const awardPeriodFailure = new Error("Error while loading periods"); - const backendClient = { - totalCashback: jest.fn(), - awardPeriods: jest.fn(), - getRankingV2: jest.fn() - }; - await expectSaga(loadPeriodsWithInfo, backendClient) - .provide([ - [ - call(bpdLoadPeriodsSaga, backendClient.awardPeriods), - E.left(awardPeriodFailure) - ] - ]) - .put(bpdPeriodsAmountLoad.failure(awardPeriodFailure)) - .run(); - }); - - it("Dispatch failure if a single totalCashback is left", async () => { - const totalCashbackFailure = new Error("Error for a single amount"); - const backendClient = { - totalCashback: jest.fn(), - awardPeriods: jest.fn(), - getRankingV2: jest.fn() - }; - await expectSaga(loadPeriodsWithInfo, backendClient) - .provide([ - [ - call(bpdLoadPeriodsSaga, backendClient.awardPeriods), - E.right>([activePeriod, closedPeriod]) - ], - [ - call(bpdLoadAmountSaga, backendClient.totalCashback, 0), - E.right(zeroAmount) - ], - [ - call(bpdLoadAmountSaga, backendClient.totalCashback, 1), - E.left(totalCashbackFailure) - ], - [ - call(bpdLoadRakingV2, backendClient.getRankingV2), - E.right([readyRanking]) - ] - ]) - .put( - bpdPeriodsAmountLoad.failure(new Error("Error while loading amounts")) - ) - .run(); - }); - it("Dispatch failure if all the totalCashback are E.left", async () => { - const totalCashbackFailure = new Error("Error for a single amount"); - const backendClient = { - totalCashback: jest.fn(), - awardPeriods: jest.fn(), - getRankingV2: jest.fn() - }; - await expectSaga(loadPeriodsWithInfo, backendClient) - .provide([ - [ - call(bpdLoadPeriodsSaga, backendClient.awardPeriods), - E.right>([activePeriod, closedPeriod]) - ], - [ - call(bpdLoadAmountSaga, backendClient.totalCashback, 0), - E.left(totalCashbackFailure) - ], - [ - call(bpdLoadAmountSaga, backendClient.totalCashback, 1), - E.left(totalCashbackFailure) - ], - [ - call(bpdLoadRakingV2, backendClient.getRankingV2), - E.right([readyRanking]) - ] - ]) - .put( - bpdPeriodsAmountLoad.failure(new Error("Error while loading amounts")) - ) - .run(); - }); - - it("Dispatch failure if load ranking is E.left", async () => { - const totalCashbackFailure = new Error("Error for a single amount"); - const backendClient = { - totalCashback: jest.fn(), - awardPeriods: jest.fn(), - getRankingV2: jest.fn() - }; - await expectSaga(loadPeriodsWithInfo, backendClient) - .provide([ - [ - call(bpdLoadPeriodsSaga, backendClient.awardPeriods), - E.right>([activePeriod, closedPeriod]) - ], - [ - call(bpdLoadAmountSaga, backendClient.totalCashback, 0), - E.left(totalCashbackFailure) - ], - [ - call(bpdLoadAmountSaga, backendClient.totalCashback, 1), - E.left(totalCashbackFailure) - ], - [ - call(bpdLoadRakingV2, backendClient.getRankingV2), - E.left(new Error("error")) - ] - ]) - .put( - bpdPeriodsAmountLoad.failure(new Error("Error while loading rankings")) - ) - .run(); - }); - - it("Dispatch success if all the totalCashback are E.right", async () => { - const amountForPeriod0: BpdAmount = { - ...zeroAmount, - awardPeriodId: 0 as AwardPeriodId - }; - const amountForPeriod1: BpdAmount = { - ...notEligibleAmount, - awardPeriodId: 1 as AwardPeriodId - }; - const backendClient = { - totalCashback: jest.fn(), - awardPeriods: jest.fn(), - getRankingV2: jest.fn() - }; - await expectSaga(loadPeriodsWithInfo, backendClient) - .provide([ - [ - call(bpdLoadPeriodsSaga, backendClient.awardPeriods), - E.right>([activePeriod, closedPeriod]) - ], - [ - call(bpdLoadAmountSaga, backendClient.totalCashback, 0), - E.right(amountForPeriod0) - ], - [ - call(bpdLoadAmountSaga, backendClient.totalCashback, 1), - E.right(amountForPeriod1) - ], - [ - call(bpdLoadRakingV2, backendClient.getRankingV2), - E.right([readyRanking]) - ] - ]) - .put( - bpdPeriodsAmountLoad.success([ - { ...activePeriod, amount: amountForPeriod1, ranking: readyRanking }, - { - ...closedPeriod, - amount: amountForPeriod0, - ranking: notReadyRanking - } - ]) - ) - .run(); - }); - it("Dispatch success if all the totalCashback are E.right, do not request inactive periods", async () => { - const amountForPeriod0: BpdAmount = { - ...zeroAmount, - awardPeriodId: 0 as AwardPeriodId - }; - const amountForPeriod1: BpdAmount = { - ...notEligibleAmount, - awardPeriodId: 1 as AwardPeriodId - }; - const amountForPeriod2: BpdAmount = { - ...notEligibleAmount, - awardPeriodId: 2 as AwardPeriodId - }; - const backendClient = { - totalCashback: jest.fn(), - awardPeriods: jest.fn(), - getRankingV2: jest.fn() - }; - await expectSaga(loadPeriodsWithInfo, backendClient) - .provide([ - [ - call(bpdLoadPeriodsSaga, backendClient.awardPeriods), - E.right>([ - activePeriod, - closedPeriod, - inactivePeriod - ]) - ], - [ - call(bpdLoadAmountSaga, backendClient.totalCashback, 0), - E.right(amountForPeriod0) - ], - [ - call(bpdLoadAmountSaga, backendClient.totalCashback, 1), - E.right(amountForPeriod1) - ], - [ - call(bpdLoadAmountSaga, backendClient.totalCashback, 2), - E.right(amountForPeriod2) - ], - [ - call(bpdLoadRakingV2, backendClient.getRankingV2), - E.right([readyRanking]) - ] - ]) - .put( - bpdPeriodsAmountLoad.success([ - { - ...inactivePeriod, - amount: { ...zeroAmount, awardPeriodId: 2 as AwardPeriodId }, - ranking: withAwardPeriodId(notReadyRanking, 2 as AwardPeriodId) - }, - { ...activePeriod, amount: amountForPeriod1, ranking: readyRanking }, - { - ...closedPeriod, - amount: amountForPeriod0, - ranking: notReadyRanking - } - ]) - ) - .run(); - }); -}); diff --git a/ts/features/bonus/bpd/saga/networking/amount.ts b/ts/features/bonus/bpd/saga/networking/amount.ts deleted file mode 100644 index 3df0552cfc5..00000000000 --- a/ts/features/bonus/bpd/saga/networking/amount.ts +++ /dev/null @@ -1,78 +0,0 @@ -import * as E from "fp-ts/lib/Either"; -import { readableReport } from "@pagopa/ts-commons/lib/reporters"; -import { call } from "typed-redux-saga/macro"; -import { TotalCashbackResource } from "../../../../../../definitions/bpd/winning_transactions/TotalCashbackResource"; -import { mixpanelTrack } from "../../../../../mixpanel"; -import { - ReduxSagaEffect, - SagaCallReturnType -} from "../../../../../types/utils"; -import { convertUnknownToError, getError } from "../../../../../utils/errors"; -import { BackendBpdClient } from "../../api/backendBpdClient"; -import { AwardPeriodId, WithAwardPeriodId } from "../../store/actions/periods"; - -export type BpdAmount = WithAwardPeriodId & { - // The total cashback amount gained by the user in the period (without super cashback) - totalCashback: number; - // The total transaction number in the period for the user - transactionNumber: number; -}; - -export type BpdAmountError = WithAwardPeriodId & { - error: Error; -}; - -// convert a network payload amount into the relative app domain model -const convertAmount = ( - networkAmount: TotalCashbackResource, - awardPeriodId: AwardPeriodId -): BpdAmount => ({ - totalCashback: networkAmount.totalCashback, - transactionNumber: networkAmount.transactionNumber, - awardPeriodId -}); - -const mixpanelActionRequest = "BPD_AMOUNT_REQUEST"; -const mixpanelActionSuccess = "BPD_AMOUNT_SUCCESS"; -const mixpanelActionFailure = "BPD_AMOUNT_FAILURE"; - -/** - * Networking code to request the amount for a specified period. - * @param totalCashback - * @param awardPeriodId - */ -export function* bpdLoadAmountSaga( - totalCashback: ReturnType["totalCashback"], - awardPeriodId: AwardPeriodId -): Generator< - ReduxSagaEffect, - E.Either, - SagaCallReturnType -> { - void mixpanelTrack(mixpanelActionRequest, { awardPeriodId }); - try { - const totalCashbackResult: SagaCallReturnType = - yield* call(totalCashback, { awardPeriodId } as any); - if (E.isRight(totalCashbackResult)) { - if (totalCashbackResult.right.status === 200) { - void mixpanelTrack(mixpanelActionSuccess, { awardPeriodId }); - return E.right( - convertAmount(totalCashbackResult.right.value, awardPeriodId) - ); - } else { - throw new Error(`response status ${totalCashbackResult.right.status}`); - } - } else { - throw new Error(readableReport(totalCashbackResult.left)); - } - } catch (e) { - void mixpanelTrack(mixpanelActionFailure, { - awardPeriodId, - reason: convertUnknownToError(e).message - }); - return E.left({ - error: getError(e), - awardPeriodId - }); - } -} diff --git a/ts/features/bonus/bpd/saga/networking/index.ts b/ts/features/bonus/bpd/saga/networking/index.ts deleted file mode 100644 index af53a972b07..00000000000 --- a/ts/features/bonus/bpd/saga/networking/index.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { readableReport } from "@pagopa/ts-commons/lib/reporters"; -import { SagaIterator } from "@redux-saga/core"; -import * as E from "fp-ts/lib/Either"; -import { call, put } from "typed-redux-saga/macro"; -import { ActionType } from "typesafe-actions"; -import { SagaCallReturnType } from "../../../../../types/utils"; -import { convertUnknownToError, getError } from "../../../../../utils/errors"; -import { BackendBpdClient } from "../../api/backendBpdClient"; -import { bpdLoadActivationStatus } from "../../store/actions/details"; -import { - bpdDeleteUserFromProgram, - bpdEnrollUserToProgram, - bpdUpdateOptInStatusMethod -} from "../../store/actions/onboarding"; - -export function* executeAndDispatchV2( - remoteCall: - | ReturnType["enrollCitizenV2IO"] - | ReturnType["findV2"], - action: typeof bpdEnrollUserToProgram | typeof bpdLoadActivationStatus -) { - try { - const enrollCitizenIOResult: SagaCallReturnType = - yield* call( - remoteCall, - // due to avoid required headers coming from code autogenerate - // (note the required header will be injected automatically) - {} as any - ); - if (E.isRight(enrollCitizenIOResult)) { - if (enrollCitizenIOResult.right.status === 200) { - const { enabled, payoffInstr, technicalAccount, optInStatus } = - enrollCitizenIOResult.right.value; - yield* put( - action.success({ - enabled, - activationStatus: enabled ? "subscribed" : "unsubscribed", - payoffInstr, - technicalAccount, - optInStatus - }) - ); - return; - } else if (enrollCitizenIOResult.right.status === 404) { - yield* put( - action.success({ - enabled: false, - activationStatus: "never", - payoffInstr: undefined, - technicalAccount: undefined - }) - ); - return; - } - throw new Error(`response status ${enrollCitizenIOResult.right.status}`); - } else { - throw new Error(readableReport(enrollCitizenIOResult.left)); - } - } catch (e) { - yield* put(action.failure(convertUnknownToError(e))); - } -} - -export function* getCitizenV2( - findCitizen: ReturnType["findV2"] -): SagaIterator { - yield* call(executeAndDispatchV2, findCitizen, bpdLoadActivationStatus); -} - -export function* putEnrollCitizenV2( - enrollCitizenIO: ReturnType["enrollCitizenV2IO"] -): SagaIterator { - yield* call(executeAndDispatchV2, enrollCitizenIO, bpdEnrollUserToProgram); -} - -/** - * update the citizen OptInStatus - * @param updateCitizenIO - * @param action - */ -export function* putOptInStatusCitizenV2( - updateCitizenIO: ReturnType["enrollCitizenV2IO"], - action: ActionType -) { - try { - const updateCitizenIOResult: SagaCallReturnType = - yield* call( - updateCitizenIO, - // due to avoid required headers coming from code autogenerate - // (note the required header will be injected automatically) - { optInStatus: action.payload } as any - ); - - if (E.isRight(updateCitizenIOResult)) { - if (updateCitizenIOResult.right.status === 200) { - if (updateCitizenIOResult.right.value.optInStatus) { - const { optInStatus } = updateCitizenIOResult.right.value; - yield* put(bpdUpdateOptInStatusMethod.success(optInStatus)); - return; - } else { - // it should never happen - bpdUpdateOptInStatusMethod.failure( - new Error(`optInStatus is undefined`) - ); - } - } else { - yield* put( - bpdUpdateOptInStatusMethod.failure( - new Error(`response status ${updateCitizenIOResult.right.status}`) - ) - ); - } - } else { - yield* put( - bpdUpdateOptInStatusMethod.failure( - new Error(readableReport(updateCitizenIOResult.left)) - ) - ); - } - } catch (e) { - yield* put(bpdUpdateOptInStatusMethod.failure(getError(e))); - } -} - -/** - * make a request to delete citizen from bpd program - */ -export function* deleteCitizen( - deleteCitizenIO: ReturnType["deleteCitizenIO"] -): SagaIterator { - try { - const deleteCitizenIOResult: SagaCallReturnType = - yield* call(deleteCitizenIO, {} as any); - if (E.isRight(deleteCitizenIOResult)) { - if (deleteCitizenIOResult.right.status === 204) { - yield* put(bpdDeleteUserFromProgram.success()); - return; - } - throw new Error(`response status ${deleteCitizenIOResult.right.status}`); - } else { - throw new Error(readableReport(deleteCitizenIOResult.left)); - } - } catch (e) { - yield* put(bpdDeleteUserFromProgram.failure(convertUnknownToError(e))); - } -} diff --git a/ts/features/bonus/bpd/saga/networking/loadBpdData.ts b/ts/features/bonus/bpd/saga/networking/loadBpdData.ts deleted file mode 100644 index a0e9907905f..00000000000 --- a/ts/features/bonus/bpd/saga/networking/loadBpdData.ts +++ /dev/null @@ -1,93 +0,0 @@ -import { call, delay, put, select, take } from "typed-redux-saga/macro"; -import { ActionType, getType } from "typesafe-actions"; -import { SagaCallReturnType } from "../../../../../types/utils"; -import { getBackoffTime } from "../../../../../utils/backoffError"; -import { isTestEnv } from "../../../../../utils/environment"; -import { loadAbi } from "../../../../wallet/onboarding/bancomat/store/actions"; -import { abiSelector } from "../../../../wallet/onboarding/store/abi"; -import { isReady } from "../../../../../common/model/RemoteValue"; -import { - bpdAllData, - bpdLoadActivationStatus -} from "../../store/actions/details"; -import { bpdPeriodsAmountLoad } from "../../store/actions/periods"; - -/** - * retrieve possible backoff waiting time and if there is, wait that time - */ -function* checkPreviousFailures() { - // wait if some previous errors occurred - const loadActivationBackOff: SagaCallReturnType = - yield* call(getBackoffTime, bpdLoadActivationStatus.failure); - const loadPeriodsBackOff: SagaCallReturnType = - yield* call(getBackoffTime, bpdPeriodsAmountLoad.failure); - const waitingTime = Math.max(loadActivationBackOff, loadPeriodsBackOff); - if (waitingTime > 0) { - yield* delay(waitingTime); - } -} - -/** - * Load all the BPD details data: - * - Activation Status - * - Abi list - * - Periods - * - Amount foreach period !== "Inactive" - * - Transactions foreach period !== "Inactive" - */ -export function* loadBpdData() { - // TODO: move from here - const abiList: ReturnType = yield* select(abiSelector); - - // The volatility of the bank list is extremely low. - // There is no need to refresh it every time. - // A further refinement could be to insert an expiring cache - if (!isReady(abiList)) { - yield* put(loadAbi.request()); - } - yield* call(checkPreviousFailures); - // First request the bpd activation status - yield* put(bpdLoadActivationStatus.request()); - - const activationStatus = yield* take< - ActionType< - | typeof bpdLoadActivationStatus.success - | typeof bpdLoadActivationStatus.failure - > - >([bpdLoadActivationStatus.success, bpdLoadActivationStatus.failure]); - - if (activationStatus.type === getType(bpdLoadActivationStatus.success)) { - // if the user is not registered with bpd, - // there is no need to request other data as it is never allowed to view closed periods - if (!activationStatus.payload.enabled) { - yield* put(bpdAllData.success()); - return; - } - - // In case of success, request the periods, amounts and ranking foreach required period - yield* put(bpdPeriodsAmountLoad.request()); - - const periods = yield* take< - ActionType< - | typeof bpdPeriodsAmountLoad.success - | typeof bpdPeriodsAmountLoad.failure - > - >([bpdPeriodsAmountLoad.success, bpdPeriodsAmountLoad.failure]); - - if (periods.type === getType(bpdPeriodsAmountLoad.success)) { - // The load of all the required data for bpd is now completed with success - yield* put(bpdAllData.success()); - } else { - // The load of all the required bpd data is failed - yield* put(bpdAllData.failure(periods.payload)); - } - } else { - // The load of all the required bpd data is failed - yield* put(bpdAllData.failure(activationStatus.payload)); - } -} - -// to keep solid code encapsulation -export const testableFunctions = { - checkPreviousFailures: isTestEnv ? checkPreviousFailures : undefined -}; diff --git a/ts/features/bonus/bpd/saga/networking/loadPeriodsWithInfo.ts b/ts/features/bonus/bpd/saga/networking/loadPeriodsWithInfo.ts deleted file mode 100644 index 62aa95d5945..00000000000 --- a/ts/features/bonus/bpd/saga/networking/loadPeriodsWithInfo.ts +++ /dev/null @@ -1,123 +0,0 @@ -import * as AR from "fp-ts/lib/Array"; -import * as E from "fp-ts/lib/Either"; -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import { all, call, put } from "typed-redux-saga/macro"; -import { SagaCallReturnType } from "../../../../../types/utils"; -import { BackendBpdClient } from "../../api/backendBpdClient"; -import { bpdPeriodsAmountLoad } from "../../store/actions/periods"; -import { - BpdPeriodWithInfo, - BpdRanking, - bpdRankingNotReady, - BpdRankingReady -} from "../../store/reducers/details/periods"; -import { BpdAmount, BpdAmountError, bpdLoadAmountSaga } from "./amount"; -import { bpdLoadPeriodsSaga } from "./periods"; -import { bpdLoadRakingV2 } from "./ranking"; - -/** - * Load the periods information list and adds the amount and ranking information - * foreach period (active or closed) - * @param bpdClient - */ -export function* loadPeriodsWithInfo( - bpdClient: Pick< - ReturnType, - "awardPeriods" | "totalCashback" | "getRankingV2" - > -) { - // Request the period list - const maybePeriods: SagaCallReturnType = - yield* call(bpdLoadPeriodsSaga, bpdClient.awardPeriods); - - if (E.isLeft(maybePeriods)) { - // Error while receiving the period list - yield* put(bpdPeriodsAmountLoad.failure(maybePeriods.left)); - } else { - const periods = maybePeriods.right; - - const rankings: E.Either< - Error, - ReadonlyArray - > = yield* call(bpdLoadRakingV2, bpdClient.getRankingV2); - - if (E.isLeft(rankings)) { - yield* put( - bpdPeriodsAmountLoad.failure(new Error("Error while loading rankings")) - ); - return; - } - - // request the amounts for all the required periods - const amounts: ReadonlyArray> = - yield* all( - periods - // no need to request the inactive period, the amount and transaction number is always 0 - .filter(p => p.status !== "Inactive") - .map(period => - call( - bpdLoadAmountSaga, - bpdClient.totalCashback, - period.awardPeriodId - ) - ) - ); - - // Check if the required period amount are without error - // With a single error, we can't display the periods list - if (amounts.some(E.isLeft)) { - yield* put( - bpdPeriodsAmountLoad.failure(new Error("Error while loading amounts")) - ); - return; - } - - // the transactionNumber and totalCashback for inactive (future) period is 0, no need to request - // creating the static response - const amountWithInactivePeriod = [ - ...periods - .filter(p => p.status === "Inactive") - .map>(p => - E.right({ - awardPeriodId: p.awardPeriodId, - transactionNumber: 0, - totalCashback: 0 - }) - ), - ...amounts - ]; - - // compose the period with the amount information - const periodsWithAmount = amountWithInactivePeriod - .filter(E.isRight) - .reduce>( - (acc, curr) => - pipe( - [...periods], - AR.findFirst(p => p.awardPeriodId === curr.right.awardPeriodId), - O.foldW( - () => acc, - period => [ - ...acc, - { - ...period, - amount: curr.right, - ranking: pipe( - [...rankings.right], - AR.findFirst( - r => r.awardPeriodId === curr.right.awardPeriodId - ), - O.getOrElseW(() => - bpdRankingNotReady(curr.right.awardPeriodId) - ) - ) - } - ] - ) - ), - [] - ); - yield* put(bpdPeriodsAmountLoad.success(periodsWithAmount)); - } -} diff --git a/ts/features/bonus/bpd/saga/networking/patchCitizenIban.ts b/ts/features/bonus/bpd/saga/networking/patchCitizenIban.ts deleted file mode 100644 index cb6c6884d7f..00000000000 --- a/ts/features/bonus/bpd/saga/networking/patchCitizenIban.ts +++ /dev/null @@ -1,103 +0,0 @@ -import * as O from "fp-ts/lib/Option"; -import * as E from "fp-ts/lib/Either"; -import { call, put, select } from "typed-redux-saga/macro"; -import { ActionType } from "typesafe-actions"; -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { pipe } from "fp-ts/lib/function"; -import { BackendBpdClient } from "../../api/backendBpdClient"; -import { SagaCallReturnType } from "../../../../../types/utils"; -import { Iban } from "../../../../../../definitions/backend/Iban"; -import { bpdUpsertIban, IbanUpsertResult } from "../../store/actions/iban"; -import { profileSelector } from "../../../../../store/reducers/profile"; -import { readablePrivacyReport } from "../../../../../utils/reporters"; -import { convertUnknownToError } from "../../../../../utils/errors"; -// representation of iban status -export enum IbanStatus { - "OK" = "OK", - "NOT_OWNED" = "NOT_OWNED", - "CANT_VERIFY" = "CANT_VERIFY", - "NOT_VALID" = "NOT_VALID", - "UNKNOWN" = "UNKNOWN" -} - -const ibanStatusMapping: Map = new Map([ - ["OK", IbanStatus.OK], - ["KO", IbanStatus.NOT_OWNED], - ["UNKNOWN_PSP", IbanStatus.CANT_VERIFY] -]); - -const successCases = new Set([ - IbanStatus.OK, - IbanStatus.CANT_VERIFY, - IbanStatus.NOT_OWNED -]); - -// convert the validation code coming from response to an internal logic status -const fromValidationToStatus = (ibanOutcome: string): IbanStatus => - pipe( - ibanStatusMapping.get(ibanOutcome), - O.fromNullable, - O.getOrElseW(() => IbanStatus.UNKNOWN) - ); - -const transformIbanOutCome = ( - ibanStatus: IbanStatus, - iban: Iban -): IbanUpsertResult => ({ - // don't store iban if the outcome is a failure - payoffInstr: successCases.has(ibanStatus) ? iban : undefined, - status: ibanStatus -}); - -/** - * upsert the citizen iban - */ -export function* patchCitizenIban( - updatePaymentMethod: ReturnType< - typeof BackendBpdClient - >["updatePaymentMethod"], - action: ActionType -) { - try { - const profileState: ReturnType = yield* select( - profileSelector - ); - if (pot.isNone(profileState)) { - // it should never happen - throw new Error(`profile is None`); - } - const iban: Iban = action.payload; - const updatePaymentMethodResult: SagaCallReturnType< - ReturnType - > = yield* call(updatePaymentMethod(iban, profileState.value), {}); - if (E.isRight(updatePaymentMethodResult)) { - const statusCode = updatePaymentMethodResult.right.status; - if (statusCode === 200 && updatePaymentMethodResult.right.value) { - const validationStatus = - updatePaymentMethodResult.right.value.validationStatus; - yield* put( - bpdUpsertIban.success( - transformIbanOutCome(fromValidationToStatus(validationStatus), iban) - ) - ); - return; - } - // iban not valid - else if (statusCode === 400) { - yield* put( - bpdUpsertIban.success( - transformIbanOutCome(IbanStatus.NOT_VALID, iban) - ) - ); - return; - } - throw new Error( - `response status ${updatePaymentMethodResult.right.status}` - ); - } else { - throw new Error(readablePrivacyReport(updatePaymentMethodResult.left)); - } - } catch (e) { - yield* put(bpdUpsertIban.failure(convertUnknownToError(e))); - } -} diff --git a/ts/features/bonus/bpd/saga/networking/paymentMethod.ts b/ts/features/bonus/bpd/saga/networking/paymentMethod.ts deleted file mode 100644 index 36c0be12c73..00000000000 --- a/ts/features/bonus/bpd/saga/networking/paymentMethod.ts +++ /dev/null @@ -1,194 +0,0 @@ -import { call, put } from "typed-redux-saga/macro"; -import { ActionType } from "typesafe-actions"; -import { readableReport } from "@pagopa/ts-commons/lib/reporters"; -import * as O from "fp-ts/lib/Option"; -import * as E from "fp-ts/lib/Either"; -import { pipe } from "fp-ts/lib/function"; -import { - BpdPaymentMethodActivation, - bpdPaymentMethodActivation, - BpdPmActivationStatus, - bpdUpdatePaymentMethodActivation, - HPan -} from "../../store/actions/paymentMethods"; -import { SagaCallReturnType } from "../../../../../types/utils"; -import { BackendBpdClient } from "../../api/backendBpdClient"; -import { getError } from "../../../../../utils/errors"; -import { - PaymentInstrumentResource, - StatusEnum -} from "../../../../../../definitions/bpd/payment/PaymentInstrumentResource"; -import { getPaymentStatus } from "../../store/reducers/details/paymentMethods"; - -const mapStatus: Map = new Map< - StatusEnum, - BpdPmActivationStatus ->([ - [StatusEnum.ACTIVE, "active"], - [StatusEnum.INACTIVE, "inactive"] -]); - -// convert the network payload to the logical app representation of it -const convertNetworkPayload = ( - networkPayload: PaymentInstrumentResource -): BpdPaymentMethodActivation => ({ - hPan: networkPayload.hpan as HPan, - activationStatus: pipe( - mapStatus.get(networkPayload.Status), - O.fromNullable, - O.getOrElseW(() => "notActivable" as const) - ), - activationDate: networkPayload.activationDate, - deactivationDate: networkPayload.deactivationDate -}); - -/** - * return {@link BpdPaymentMethodActivation} when network response is conflict (409) - */ -const whenConflict = (hPan: HPan): BpdPaymentMethodActivation => ({ - hPan, - activationStatus: "notActivable" -}); - -/** - * Request the actual state of bpd on a payment method - */ -export function* bpdLoadPaymentMethodActivationSaga( - findPaymentMethod: ReturnType["findPayment"], - action: ActionType -) { - try { - const findPaymentMethodResult: SagaCallReturnType< - typeof findPaymentMethod - > = yield* call(findPaymentMethod, { id: action.payload } as any); - if (E.isRight(findPaymentMethodResult)) { - if (findPaymentMethodResult.right.status === 200) { - yield* put( - bpdPaymentMethodActivation.success( - convertNetworkPayload(findPaymentMethodResult.right.value) - ) - ); - return; - } else if (findPaymentMethodResult.right.status === 409) { - // conflict means not activable - yield* put( - bpdPaymentMethodActivation.success(whenConflict(action.payload)) - ); - return; - } else if (findPaymentMethodResult.right.status === 404) { - yield* put( - bpdPaymentMethodActivation.success({ - hPan: action.payload, - activationStatus: "inactive" - }) - ); - return; - } - throw new Error( - `response status ${findPaymentMethodResult.right.status}` - ); - } else { - throw new Error(readableReport(findPaymentMethodResult.left)); - } - } catch (e) { - yield* put( - bpdPaymentMethodActivation.failure({ - hPan: action.payload, - error: getError(e) - }) - ); - } -} - -/** - * Change the activation state on a payment method - * action.payload.value: if true it enrolls the method otherwise it deletes it - */ -export function* bpdUpdatePaymentMethodActivationSaga( - enrollPayment: ReturnType["enrollPayment"], - deletePayment: ReturnType["deletePayment"], - action: ActionType -) { - if (action.payload.value) { - yield* call(enrollPaymentMethod, enrollPayment, action); - } else { - yield* call(deletePaymentMethod, deletePayment, action); - } -} - -function* enrollPaymentMethod( - enrollPaymentMethod: ReturnType["enrollPayment"], - action: ActionType -) { - try { - const enrollPaymentMethodResult: SagaCallReturnType< - typeof enrollPaymentMethod - > = yield* call(enrollPaymentMethod, { id: action.payload.hPan } as any); - if (E.isRight(enrollPaymentMethodResult)) { - if (enrollPaymentMethodResult.right.status === 200) { - const responsePayload = enrollPaymentMethodResult.right.value; - yield* put( - bpdUpdatePaymentMethodActivation.success({ - hPan: action.payload.hPan, - activationDate: responsePayload.activationDate, - activationStatus: getPaymentStatus(action.payload.value) - }) - ); - return; - } else if (enrollPaymentMethodResult.right.status === 409) { - yield* put( - bpdUpdatePaymentMethodActivation.success( - whenConflict(action.payload.hPan) - ) - ); - return; - } - throw new Error( - `response status ${enrollPaymentMethodResult.right.status}` - ); - } else { - throw new Error(readableReport(enrollPaymentMethodResult.left)); - } - } catch (e) { - yield* put( - bpdUpdatePaymentMethodActivation.failure({ - hPan: action.payload.hPan, - error: getError(e) - }) - ); - } -} - -function* deletePaymentMethod( - deletePaymentMethod: ReturnType["deletePayment"], - action: ActionType -) { - try { - const deletePaymentMethodResult: SagaCallReturnType< - typeof deletePaymentMethod - > = yield* call(deletePaymentMethod, { id: action.payload.hPan } as any); - if (E.isRight(deletePaymentMethodResult)) { - if (deletePaymentMethodResult.right.status === 204) { - yield* put( - bpdUpdatePaymentMethodActivation.success({ - hPan: action.payload.hPan, - activationStatus: getPaymentStatus(action.payload.value) - }) - ); - return; - } - throw new Error( - `response status ${deletePaymentMethodResult.right.status}` - ); - } else { - throw new Error(readableReport(deletePaymentMethodResult.left)); - } - } catch (e) { - yield* put( - bpdUpdatePaymentMethodActivation.failure({ - hPan: action.payload.hPan, - error: getError(e) - }) - ); - } -} diff --git a/ts/features/bonus/bpd/saga/networking/periods.ts b/ts/features/bonus/bpd/saga/networking/periods.ts deleted file mode 100644 index 8ec66a13d36..00000000000 --- a/ts/features/bonus/bpd/saga/networking/periods.ts +++ /dev/null @@ -1,86 +0,0 @@ -import * as E from "fp-ts/lib/Either"; -import * as O from "fp-ts/lib/Option"; -import { readableReport } from "@pagopa/ts-commons/lib/reporters"; -import { call } from "typed-redux-saga/macro"; -import { pipe } from "fp-ts/lib/function"; -import { AwardPeriodResource } from "../../../../../../definitions/bpd/award_periods/AwardPeriodResource"; -import { mixpanelTrack } from "../../../../../mixpanel"; -import { - ReduxSagaEffect, - SagaCallReturnType -} from "../../../../../types/utils"; -import { convertUnknownToError, getError } from "../../../../../utils/errors"; -import { BackendBpdClient } from "../../api/backendBpdClient"; -import { - AwardPeriodId, - BpdPeriod, - BpdPeriodStatus -} from "../../store/actions/periods"; - -// mapping between network payload status and app domain model status -const periodStatusMap: Map = new Map< - string, - BpdPeriodStatus ->([ - ["ACTIVE", "Active"], - ["INACTIVE", "Inactive"], - ["CLOSED", "Closed"] -]); - -// convert a network payload award period into the relative app domain model -const convertPeriod = ( - networkPeriod: AwardPeriodResource, - statusFallback: BpdPeriodStatus = "Inactive" -): BpdPeriod => ({ - ...networkPeriod, - awardPeriodId: networkPeriod.awardPeriodId as AwardPeriodId, - superCashbackAmount: networkPeriod.maxAmount, - status: pipe( - periodStatusMap.get(networkPeriod.status), - O.fromNullable, - O.getOrElse(() => statusFallback) - ) -}); - -const mixpanelActionRequest = "BPD_PERIODS_REQUEST"; -const mixpanelActionSuccess = "BPD_PERIODS_SUCCESS"; -const mixpanelActionFailure = "BPD_PERIODS_FAILURE"; - -/** - * Networking logic to request the periods list - * @param awardPeriods - */ -export function* bpdLoadPeriodsSaga( - awardPeriods: ReturnType["awardPeriods"] -): Generator< - ReduxSagaEffect, - E.Either>, - SagaCallReturnType -> { - void mixpanelTrack(mixpanelActionRequest); - try { - const awardPeriodsResult: SagaCallReturnType = - yield* call(awardPeriods, {} as any); - if (E.isRight(awardPeriodsResult)) { - if (awardPeriodsResult.right.status === 200) { - const periods = awardPeriodsResult.right.value; - void mixpanelTrack(mixpanelActionSuccess, { - count: periods.length - }); - // convert data into app domain model - return E.right>( - periods.map(p => convertPeriod(p)) - ); - } else { - throw new Error(`response status ${awardPeriodsResult.right.status}`); - } - } else { - throw new Error(readableReport(awardPeriodsResult.left)); - } - } catch (e) { - void mixpanelTrack(mixpanelActionFailure, { - reason: convertUnknownToError(e).message - }); - return E.left>(getError(e)); - } -} diff --git a/ts/features/bonus/bpd/saga/networking/ranking.ts b/ts/features/bonus/bpd/saga/networking/ranking.ts deleted file mode 100644 index 372deb4e8a1..00000000000 --- a/ts/features/bonus/bpd/saga/networking/ranking.ts +++ /dev/null @@ -1,151 +0,0 @@ -import { readableReport } from "@pagopa/ts-commons/lib/reporters"; -import * as E from "fp-ts/lib/Either"; -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import * as RA from "fp-ts/lib/ReadonlyArray"; -import { call, put } from "typed-redux-saga/macro"; -import { ActionType } from "typesafe-actions"; -import { CitizenRankingMilestoneResourceArray } from "../../../../../../definitions/bpd/citizen_v2/CitizenRankingMilestoneResourceArray"; -import { MilestoneResource } from "../../../../../../definitions/bpd/citizen_v2/MilestoneResource"; -import { mixpanelTrack } from "../../../../../mixpanel"; -import { - ReduxSagaEffect, - SagaCallReturnType -} from "../../../../../types/utils"; -import { getError } from "../../../../../utils/errors"; -import { BackendBpdClient } from "../../api/backendBpdClient"; -import { AwardPeriodId } from "../../store/actions/periods"; -import { - BpdTransactionId, - bpdTransactionsLoadMilestone -} from "../../store/actions/transactions"; -import { BpdRankingReady } from "../../store/reducers/details/periods"; -import { BpdPivotTransaction } from "../../store/reducers/details/transactionsv2/entities"; - -const version = "_V2"; - -const mixpanelActionRequest = `BPD_RANKING${version}_REQUEST`; -const mixpanelActionSuccess = `BPD_RANKING${version}_SUCCESS`; -const mixpanelActionFailure = `BPD_RANKING${version}_FAILURE`; - -// convert the network payload ranking into the relative app domain model -const convertRankingArrayV2 = ( - rankings: CitizenRankingMilestoneResourceArray -): ReadonlyArray => - rankings.map(rr => ({ - ...rr, - awardPeriodId: rr.awardPeriodId as AwardPeriodId, - pivot: extractPivot(rr.milestoneResource), - kind: "ready" - })); - -const extractPivot = ( - mr: MilestoneResource | undefined -): BpdPivotTransaction | undefined => { - if (mr?.idTrxPivot !== undefined && mr?.cashbackNorm !== undefined) { - return { - amount: mr.cashbackNorm, - idTrx: mr.idTrxPivot as BpdTransactionId - }; - } - return undefined; -}; - -/** - * Load the ranking for all the periods - * @param getRanking - */ -export function* bpdLoadRakingV2( - getRanking: ReturnType["getRankingV2"] -): Generator< - ReduxSagaEffect, - E.Either>, - any -> { - try { - void mixpanelTrack(mixpanelActionRequest); - const getRankingResult: SagaCallReturnType = yield* call( - getRanking, - {} as any - ); - if (E.isRight(getRankingResult)) { - if (getRankingResult.right.status === 200) { - void mixpanelTrack(mixpanelActionSuccess, { - count: getRankingResult.right.value?.length - }); - return E.right>( - convertRankingArrayV2(getRankingResult.right.value) - ); - } else if (getRankingResult.right.status === 404) { - void mixpanelTrack(mixpanelActionSuccess, { - count: 0 - }); - return E.right>([]); - } else { - return E.left>( - new Error(`response status ${getRankingResult.right.status}`) - ); - } - } else { - return E.left>( - new Error(readableReport(getRankingResult.left)) - ); - } - } catch (e) { - void mixpanelTrack(mixpanelActionFailure, { - reason: getError(e).message - }); - return E.left>(getError(e)); - } -} - -/** - * Handle the action bpdTransactionsLoadMilestone.request - * @param getRanking - * @param action - */ -export function* handleLoadMilestone( - getRanking: ReturnType["getRankingV2"], - action: ActionType -) { - // get the results - const result: SagaCallReturnType = yield* call( - bpdLoadRakingV2, - getRanking - ); - - const extractMilestonePivot = pipe( - result, - E.chain(x => - E.fromOption( - () => - new Error( - `Period ${action.payload} not found in the ranking response` - ) - )( - pipe( - x, - RA.findFirst(el => el.awardPeriodId === action.payload), - O.map(el => el.pivot) - ) - ) - ) - ); - - // dispatch the related action - if (E.isRight(extractMilestonePivot)) { - yield* put( - bpdTransactionsLoadMilestone.success({ - awardPeriodId: action.payload, - result: extractMilestonePivot.right - }) - ); - } else { - yield* put( - bpdTransactionsLoadMilestone.failure({ - awardPeriodId: action.payload, - error: extractMilestonePivot.left - }) - ); - } -} diff --git a/ts/features/bonus/bpd/saga/networking/transactions.ts b/ts/features/bonus/bpd/saga/networking/transactions.ts deleted file mode 100644 index 540d4f64fe2..00000000000 --- a/ts/features/bonus/bpd/saga/networking/transactions.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import { CircuitType } from "../../store/actions/transactions"; - -/** - * convert a network circuit into the relative app domain model - * TODO we don't know which values circuit type could have - * see https://docs.google.com/document/d/1LNbuPEeqyyt9fsJ8yAOOLQG1xLERO6nOdtlnG95cj74/edit#bookmark=id.nzho5vra0820 - */ -const mapNetworkCircuitType: Map = new Map< - string, - CircuitType ->([ - ["00", "PagoBancomat"], - ["01", "Visa"], - ["02", "Mastercard / Maestro"], - ["03", "Amex"], - ["04", "JCB"], - ["05", "UnionPay"], - ["06", "Diners"], - ["07", "PostePay"], - ["08", "BancomatPay"], - ["10", "Private"] -]); - -export const convertCircuitTypeCode = (code: string): CircuitType => - pipe( - mapNetworkCircuitType.get(code), - O.fromNullable, - O.getOrElse(() => "Unknown" as CircuitType) - ); diff --git a/ts/features/bonus/bpd/saga/networking/winning-transactions/countByDay.ts b/ts/features/bonus/bpd/saga/networking/winning-transactions/countByDay.ts deleted file mode 100644 index 7927f59ab57..00000000000 --- a/ts/features/bonus/bpd/saga/networking/winning-transactions/countByDay.ts +++ /dev/null @@ -1,100 +0,0 @@ -import * as E from "fp-ts/lib/Either"; -import { readableReport } from "@pagopa/ts-commons/lib/reporters"; -import { call, put } from "typed-redux-saga/macro"; -import { ActionType } from "typesafe-actions"; -import { mixpanelTrack } from "../../../../../../mixpanel"; -import { - ReduxSagaEffect, - SagaCallReturnType -} from "../../../../../../types/utils"; -import { getError } from "../../../../../../utils/errors"; -import { BackendBpdClient } from "../../../api/backendBpdClient"; -import { AwardPeriodId } from "../../../store/actions/periods"; -import { - bpdTransactionsLoadCountByDay, - TrxCountByDayResource -} from "../../../store/actions/transactions"; - -const mixpanelActionRequest = `BPD_COUNT_BY_DAY_REQUEST`; -const mixpanelActionSuccess = `BPD_COUNT_BY_DAY_SUCCESS`; -const mixpanelActionFailure = `BPD_COUNT_BY_DAY_FAILURE`; - -/** - * Load the countByDay for a period - * @param getCountByDay - * @param awardPeriodId - */ -export function* bpdLoadCountByDay( - getCountByDay: ReturnType< - typeof BackendBpdClient - >["winningTransactionsV2CountByDay"], - awardPeriodId: AwardPeriodId -): Generator< - ReduxSagaEffect, - E.Either, - SagaCallReturnType -> { - try { - void mixpanelTrack(mixpanelActionRequest, { awardPeriodId }); - const getCountByDayResults = yield* call(getCountByDay, { - awardPeriodId - } as any); - if (E.isRight(getCountByDayResults)) { - if (getCountByDayResults.right.status === 200) { - void mixpanelTrack(mixpanelActionSuccess, { - awardPeriodId, - count: getCountByDayResults.right.value?.length - }); - return E.right({ - awardPeriodId, - results: getCountByDayResults.right.value - }); - } else { - return E.left( - new Error(`response status ${getCountByDayResults.right.status}`) - ); - } - } else { - return E.left( - new Error(readableReport(getCountByDayResults.left)) - ); - } - } catch (e) { - void mixpanelTrack(mixpanelActionFailure, { - awardPeriodId, - reason: getError(e).message - }); - return E.left(getError(e)); - } -} - -/** - * handle the action bpdTransactionsLoadCountByDay.request - * @param getCountByDay - * @param action - */ -export function* handleCountByDay( - getCountByDay: ReturnType< - typeof BackendBpdClient - >["winningTransactionsV2CountByDay"], - action: ActionType -) { - // get the results - const result: SagaCallReturnType = yield* call( - bpdLoadCountByDay, - getCountByDay, - action.payload - ); - - // dispatch the related action - if (E.isRight(result)) { - yield* put(bpdTransactionsLoadCountByDay.success(result.right)); - } else { - yield* put( - bpdTransactionsLoadCountByDay.failure({ - awardPeriodId: action.payload, - error: result.left - }) - ); - } -} diff --git a/ts/features/bonus/bpd/saga/networking/winning-transactions/loadTransactionsRequiredData.ts b/ts/features/bonus/bpd/saga/networking/winning-transactions/loadTransactionsRequiredData.ts deleted file mode 100644 index 25f370d8e46..00000000000 --- a/ts/features/bonus/bpd/saga/networking/winning-transactions/loadTransactionsRequiredData.ts +++ /dev/null @@ -1,109 +0,0 @@ -import * as E from "fp-ts/lib/Either"; -import { call, put, take } from "typed-redux-saga/macro"; -import { ActionType, getType } from "typesafe-actions"; -import { - ReduxSagaEffect, - SagaCallReturnType -} from "../../../../../../types/utils"; -import { waitBackoffError } from "../../../../../../utils/backoffError"; -import { AwardPeriodId } from "../../../store/actions/periods"; -import { - BpdTransactionsError, - bpdTransactionsLoadCountByDay, - bpdTransactionsLoadMilestone, - bpdTransactionsLoadPage, - bpdTransactionsLoadRequiredData -} from "../../../store/actions/transactions"; - -/** - * Load all the information required to render the transactions list : - * - Milestone Pivot - * - CountByDay - * - First transaction page - * These are the required information in order to do the first render for the transaction page - */ -export function* loadTransactionsRequiredData( - periodId: AwardPeriodId -): Generator, any> { - // We check if there is a failure on the whole loadTransactionsRequiredData block - yield* call(waitBackoffError, bpdTransactionsLoadRequiredData.failure); - - // First request the Milestone Pivot - yield* put(bpdTransactionsLoadMilestone.request(periodId)); - - const milestoneResponse = yield* take< - ActionType< - | typeof bpdTransactionsLoadMilestone.success - | typeof bpdTransactionsLoadMilestone.failure - > - >([ - bpdTransactionsLoadMilestone.success, - bpdTransactionsLoadMilestone.failure - ]); - - if ( - milestoneResponse.type === getType(bpdTransactionsLoadMilestone.failure) - ) { - return E.left({ - awardPeriodId: periodId, - error: new Error("Failed to load bpd transactions milestone") - }); - } - - // Request CountByDay - yield* put(bpdTransactionsLoadCountByDay.request(periodId)); - - const countByDayResponse = yield* take< - ActionType< - | typeof bpdTransactionsLoadCountByDay.success - | typeof bpdTransactionsLoadCountByDay.failure - > - >([ - bpdTransactionsLoadCountByDay.success, - bpdTransactionsLoadCountByDay.failure - ]); - - if ( - countByDayResponse.type === getType(bpdTransactionsLoadCountByDay.failure) - ) { - return E.left({ - awardPeriodId: periodId, - error: new Error("Failed to load bpd transactions countByDay") - }); - } - - // Request first transaction page for the period - yield* put(bpdTransactionsLoadPage.request({ awardPeriodId: periodId })); - - const firstPageResponse = yield* take< - ActionType< - | typeof bpdTransactionsLoadPage.success - | typeof bpdTransactionsLoadPage.failure - > - >([bpdTransactionsLoadPage.success, bpdTransactionsLoadPage.failure]); - if (firstPageResponse.type === getType(bpdTransactionsLoadPage.failure)) { - return E.left({ - awardPeriodId: periodId, - error: new Error("Failed to load the first transactions page") - }); - } - return E.right(true); -} - -/** - * Handle the trigger action bpdTransactionsLoadRequiredData.request - * @param action - */ -export function* handleTransactionsLoadRequiredData( - action: ActionType -) { - // get the results - const result: SagaCallReturnType = - yield* call(loadTransactionsRequiredData, action.payload); - - if (E.isRight(result)) { - yield* put(bpdTransactionsLoadRequiredData.success(action.payload)); - } else { - yield* put(bpdTransactionsLoadRequiredData.failure(result.left)); - } -} diff --git a/ts/features/bonus/bpd/saga/networking/winning-transactions/transactionsPage.ts b/ts/features/bonus/bpd/saga/networking/winning-transactions/transactionsPage.ts deleted file mode 100644 index 54cbcdfa6d1..00000000000 --- a/ts/features/bonus/bpd/saga/networking/winning-transactions/transactionsPage.ts +++ /dev/null @@ -1,111 +0,0 @@ -import * as E from "fp-ts/lib/Either"; -import { readableReport } from "@pagopa/ts-commons/lib/reporters"; -import { call, put } from "typed-redux-saga/macro"; -import { ActionType } from "typesafe-actions"; -import { mixpanelTrack } from "../../../../../../mixpanel"; -import { - ReduxSagaEffect, - SagaCallReturnType -} from "../../../../../../types/utils"; -import { waitBackoffError } from "../../../../../../utils/backoffError"; -import { getError } from "../../../../../../utils/errors"; -import { BackendBpdClient } from "../../../api/backendBpdClient"; -import { AwardPeriodId } from "../../../store/actions/periods"; -import { - BpdTransactionPageSuccessPayload, - bpdTransactionsLoadPage -} from "../../../store/actions/transactions"; - -const mixpanelActionRequest = `BPD_TRANSACTIONS_PAGE_REQUEST`; -const mixpanelActionSuccess = `BPD_TRANSACTIONS_PAGE_SUCCESS`; -const mixpanelActionFailure = `BPD_TRANSACTIONS_PAGE_FAILURE`; - -/** - * Load a page of transactions for a period - * @param getTransactionPage - * @param awardPeriodId - * @param cursor - */ -export function* bpdLoadTransactionsPage( - getTransactionPage: ReturnType< - typeof BackendBpdClient - >["winningTransactionsV2"], - awardPeriodId: AwardPeriodId, - cursor?: number -): Generator< - ReduxSagaEffect, - E.Either, - SagaCallReturnType -> { - try { - void mixpanelTrack(mixpanelActionRequest, { awardPeriodId, cursor }); - const getTransactionsPageResults = yield* call(getTransactionPage, { - awardPeriodId, - nextCursor: cursor - } as any); - if (E.isRight(getTransactionsPageResults)) { - if (getTransactionsPageResults.right.status === 200) { - void mixpanelTrack(mixpanelActionSuccess, { - awardPeriodId, - cursor, - count: getTransactionsPageResults.right.value?.transactions.length - }); - return E.right({ - awardPeriodId, - results: getTransactionsPageResults.right.value - }); - } else { - return E.left( - new Error( - `response status ${getTransactionsPageResults.right.status}` - ) - ); - } - } else { - return E.left( - new Error(readableReport(getTransactionsPageResults.left)) - ); - } - } catch (e) { - void mixpanelTrack(mixpanelActionFailure, { - awardPeriodId, - cursor, - reason: getError(e).message - }); - return E.left(getError(e)); - } -} - -/** - * handle the action bpdTransactionsLoadCountByDay.request - * @param getTransactionsPage - * @param action - */ -export function* handleTransactionsPage( - getTransactionsPage: ReturnType< - typeof BackendBpdClient - >["winningTransactionsV2"], - action: ActionType -) { - yield* call(waitBackoffError, bpdTransactionsLoadPage.failure); - // get the results - const result: SagaCallReturnType = - yield* call( - bpdLoadTransactionsPage, - getTransactionsPage, - action.payload.awardPeriodId, - action.payload.nextCursor - ); - - // dispatch the related action - if (E.isRight(result)) { - yield* put(bpdTransactionsLoadPage.success(result.right)); - } else { - yield* put( - bpdTransactionsLoadPage.failure({ - awardPeriodId: action.payload.awardPeriodId, - error: result.left - }) - ); - } -} diff --git a/ts/features/bonus/bpd/saga/orchestration/activateBpdOnNewAddedPaymentMethods.ts b/ts/features/bonus/bpd/saga/orchestration/activateBpdOnNewAddedPaymentMethods.ts deleted file mode 100644 index 8c2b6aa8aba..00000000000 --- a/ts/features/bonus/bpd/saga/orchestration/activateBpdOnNewAddedPaymentMethods.ts +++ /dev/null @@ -1,37 +0,0 @@ -import * as E from "fp-ts/lib/Either"; -import { call } from "typed-redux-saga/macro"; -import { EnableableFunctionsEnum } from "../../../../../../definitions/pagopa/EnableableFunctions"; -import { navigateToWalletHome } from "../../../../../store/actions/navigation"; -import { PaymentMethod } from "../../../../../types/pagopa"; -import { SagaCallReturnType } from "../../../../../types/utils"; -import { hasFunctionEnabled } from "../../../../../utils/walletv2"; -import { isBpdEnabled } from "./onboarding/startOnboarding"; - -/** - * Allows the user to activate bpd on a set of new added payment methods - */ -export function* activateBpdOnNewPaymentMethods( - paymentMethods: ReadonlyArray, - navigateToActivateNewMethods: () => void -) { - const atLeastOnePaymentMethodWithBpdCapability = paymentMethods.some(b => - hasFunctionEnabled(b, EnableableFunctionsEnum.BPD) - ); - - // No payment method with bpd capability added in the current workflow, return to wallet home - if (!atLeastOnePaymentMethodWithBpdCapability) { - return yield* call(navigateToWalletHome); - } - const isBpdEnabledResponse: SagaCallReturnType = - yield* call(isBpdEnabled); - - // Error while reading the bpdEnabled, return to wallet - if (E.isLeft(isBpdEnabledResponse)) { - yield* call(navigateToWalletHome); - } else { - if (isBpdEnabledResponse.right) { - // navigate to activate cashback on new payment methods if the user is onboarded to the program and is active - yield* call(navigateToActivateNewMethods); - } - } -} diff --git a/ts/features/bonus/bpd/saga/orchestration/insertIban.ts b/ts/features/bonus/bpd/saga/orchestration/insertIban.ts deleted file mode 100644 index e5e723e1bdf..00000000000 --- a/ts/features/bonus/bpd/saga/orchestration/insertIban.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { SagaIterator } from "redux-saga"; -import { call, put, select, take } from "typed-redux-saga/macro"; -import { ActionType, isActionOf } from "typesafe-actions"; -import { - navigateBack, - navigateToWalletHome -} from "../../../../../store/actions/navigation"; -import { paymentMethodsSelector } from "../../../../../store/reducers/wallet/wallets"; -import { - bpdIbanInsertionCancel, - bpdIbanInsertionContinue -} from "../../store/actions/iban"; -import { bpdOnboardingCompleted } from "../../store/actions/onboarding"; -import { isBpdOnboardingOngoing } from "../../store/reducers/onboarding/ongoing"; - -// TODO: if isOnboarding===true, change with an action that triggers a saga that choose -// which screen to display, (the user already have payment methods or not) - -/** - * Old style orchestrator, please don't use this as reference for future development - * @deprecated - */ -export function* bpdIbanInsertionWorker() { - const onboardingOngoing: ReturnType = - yield* select(isBpdOnboardingOngoing); - // ensure the first screen of the saga is the iban main screen. - - // wait for the user iban insertion o cancellation - const nextAction = yield* take< - ActionType - >([bpdIbanInsertionCancel, bpdIbanInsertionContinue]); - if (isActionOf(bpdIbanInsertionCancel, nextAction)) { - yield* call(onboardingOngoing ? navigateToWalletHome : navigateBack); - } else { - if (onboardingOngoing) { - const paymentMethods: ReturnType = - yield* select(paymentMethodsSelector); - - // Error while loading the wallet, display a message that informs the user about the error - if (paymentMethods.kind === "PotNoneError") { - yield* put(bpdOnboardingCompleted()); - return; - } - - yield* put(bpdOnboardingCompleted()); - } else { - yield* call(navigateBack); - } - } -} - -/** - * This saga start the workflow that allows the user to insert / modify the IBAN associated to bpd. - * In this first phase subject to changes, the call to the bpdIbanInsertionWorker is preserved, - * instead of removing the call. - */ -export function* handleBpdIbanInsertion(): SagaIterator { - yield* call(bpdIbanInsertionWorker); -} diff --git a/ts/features/bonus/bpd/saga/orchestration/onboarding/enrollToBpd.ts b/ts/features/bonus/bpd/saga/orchestration/onboarding/enrollToBpd.ts deleted file mode 100644 index cd34de7c194..00000000000 --- a/ts/features/bonus/bpd/saga/orchestration/onboarding/enrollToBpd.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { CommonActions } from "@react-navigation/native"; -import { call, put, race, take } from "typed-redux-saga/macro"; -import { ActionType } from "typesafe-actions"; -import NavigationService from "../../../../../../navigation/NavigationService"; -import { bpdAllData } from "../../../store/actions/details"; -import { bpdIbanInsertionStart } from "../../../store/actions/iban"; -import { - bpdEnrollUserToProgram, - bpdOnboardingCancel -} from "../../../store/actions/onboarding"; - -export const isLoadingScreen = () => true; - -/** - * Old style orchestrator, please don't use this as reference for future development - * @deprecated - */ -function* enrollToBpdWorker() { - const currentRoute: ReturnType = - yield* call(NavigationService.getCurrentRouteName); - - if (currentRoute !== undefined && !isLoadingScreen()) { - // show the loading page while communicate with the server for the activation - throw new Error("Not in the loading screen"); - } - - // enroll the user and wait for the result - yield* put(bpdEnrollUserToProgram.request()); - - const enrollResult: ActionType = - yield* take(bpdEnrollUserToProgram.success); - - if (enrollResult.payload.enabled) { - yield* put(bpdAllData.request()); - yield* put(bpdIbanInsertionStart()); - } - // TODO: handle false case to avoid making the user remain blocked in case of malfunction -} - -/** - * This saga enroll the user to the bpd - */ -export function* handleBpdEnroll() { - const { cancelAction } = yield* race({ - enroll: call(enrollToBpdWorker), - cancelAction: take(bpdOnboardingCancel) - }); - - if (cancelAction) { - yield* call( - NavigationService.dispatchNavigationAction, - CommonActions.goBack() - ); - } -} diff --git a/ts/features/bonus/bpd/saga/orchestration/onboarding/startOnboarding.ts b/ts/features/bonus/bpd/saga/orchestration/onboarding/startOnboarding.ts deleted file mode 100644 index 78012d3cbb6..00000000000 --- a/ts/features/bonus/bpd/saga/orchestration/onboarding/startOnboarding.ts +++ /dev/null @@ -1,97 +0,0 @@ -import { StackActions } from "@react-navigation/native"; -import * as E from "fp-ts/lib/Either"; -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { call, put, select, take, race } from "typed-redux-saga/macro"; -import { ActionType } from "typesafe-actions"; -import { pipe } from "fp-ts/lib/function"; -import NavigationService from "../../../../../../navigation/NavigationService"; -import { navigateBack } from "../../../../../../store/actions/navigation"; -import { fetchWalletsRequest } from "../../../../../../store/actions/wallet/wallets"; -import { - ReduxSagaEffect, - SagaCallReturnType -} from "../../../../../../types/utils"; -import { getAsyncResult } from "../../../../../../utils/saga"; -import { bpdLoadActivationStatus } from "../../../store/actions/details"; -import { - bpdOnboardingAcceptDeclaration, - bpdOnboardingCancel, - bpdUserActivate -} from "../../../store/actions/onboarding"; -import { bpdEnabledSelector } from "../../../store/reducers/details/activation"; - -export const isLoadingScreen = () => true; - -export function* getActivationStatus() { - return yield* call(() => getAsyncResult(bpdLoadActivationStatus, undefined)); -} - -export function* isBpdEnabled(): Generator< - ReduxSagaEffect, - E.Either, - any -> { - const remoteActive: ReturnType = yield* select( - bpdEnabledSelector - ); - if (pot.isSome(remoteActive)) { - return E.right(remoteActive.value); - } else { - const activationStatus = yield* call(getActivationStatus); - return pipe( - activationStatus, - E.map(citizen => citizen.enabled) - ); - } -} - -/** - * Old style orchestrator, please don't use this as reference for future development - * @deprecated - */ -export function* bpdStartOnboardingWorker() { - const currentRoute: ReturnType = - yield* call(NavigationService.getCurrentRouteName); - - // go to the loading page (if I'm not on that screen) - if (currentRoute !== undefined && !isLoadingScreen()) { - throw new Error("Not in the loading screen"); - } - - // read if the bpd is active for the user - const isBpdActive: SagaCallReturnType = yield* call( - isBpdEnabled - ); - - if (E.isRight(isBpdActive)) { - // Refresh the wallets to prevent that added cards are not visible - yield* put(fetchWalletsRequest()); - - // wait for the user that choose to continue - yield* take(bpdUserActivate); - - // Navigate to the Onboarding Declaration and wait for the action that complete the saga - } - - // The saga ends when the user accepts the declaration - yield* take(bpdOnboardingAcceptDeclaration); -} - -/** - * This saga check if the bpd is active for the user and choose if start the onboarding or go directly to the bpd details - */ -export function* handleBpdStartOnboardingSaga() { - const { cancelAction } = yield* race({ - onboarding: call(bpdStartOnboardingWorker), - cancelAction: - take>(bpdOnboardingCancel) - }); - - if (cancelAction) { - yield* call( - NavigationService.dispatchNavigationAction, - StackActions.popToTop() - ); - yield* call(navigateBack); - } -} diff --git a/ts/features/bonus/bpd/saga/orchestration/optInPaymentMethods/__tests__/optInDeletionChoiceHandler.test.ts b/ts/features/bonus/bpd/saga/orchestration/optInPaymentMethods/__tests__/optInDeletionChoiceHandler.test.ts deleted file mode 100644 index 2cb01d01737..00000000000 --- a/ts/features/bonus/bpd/saga/orchestration/optInPaymentMethods/__tests__/optInDeletionChoiceHandler.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { testSaga } from "redux-saga-test-plan"; -import { optInDeletionChoiceHandler } from "../optInDeletionChoiceHandler"; -import { - DeleteAllByFunctionSuccess, - deleteAllPaymentMethodsByFunction -} from "../../../../../../../store/actions/wallet/delete"; -import { EnableableFunctionsEnum } from "../../../../../../../../definitions/pagopa/EnableableFunctions"; -import { bpdUpdateOptInStatusMethod } from "../../../../store/actions/onboarding"; -import { CitizenOptInStatusEnum } from "../../../../../../../../definitions/bpd/citizen_v2/CitizenOptInStatus"; - -describe("optInDeletionChoiceHandler saga", () => { - jest.useFakeTimers(); - - it("If deleteAllPaymentMethodsByFunction fails, should return", () => { - testSaga(optInDeletionChoiceHandler) - .next() - .put( - deleteAllPaymentMethodsByFunction.request(EnableableFunctionsEnum.BPD) - ) - .next() - .take([ - deleteAllPaymentMethodsByFunction.success, - deleteAllPaymentMethodsByFunction.failure - ]) - .next(deleteAllPaymentMethodsByFunction.failure({ error: new Error() })) - .isDone(); - }); - - it("If deleteAllPaymentMethodsByFunction succeed, should put the bpdUpdateOptInStatusMethod.request action", () => { - testSaga(optInDeletionChoiceHandler) - .next() - .put( - deleteAllPaymentMethodsByFunction.request(EnableableFunctionsEnum.BPD) - ) - .next() - .take([ - deleteAllPaymentMethodsByFunction.success, - deleteAllPaymentMethodsByFunction.failure - ]) - .next( - deleteAllPaymentMethodsByFunction.success( - {} as DeleteAllByFunctionSuccess - ) - ) - .put(bpdUpdateOptInStatusMethod.request(CitizenOptInStatusEnum.DENIED)) - .next() - .isDone(); - }); -}); diff --git a/ts/features/bonus/bpd/saga/orchestration/optInPaymentMethods/__tests__/optInShouldShowChoiceHandler.test.ts b/ts/features/bonus/bpd/saga/orchestration/optInPaymentMethods/__tests__/optInShouldShowChoiceHandler.test.ts deleted file mode 100644 index 31950c6dd24..00000000000 --- a/ts/features/bonus/bpd/saga/orchestration/optInPaymentMethods/__tests__/optInShouldShowChoiceHandler.test.ts +++ /dev/null @@ -1,199 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { testSaga } from "redux-saga-test-plan"; -import { getType } from "typesafe-actions"; -import { CitizenOptInStatusEnum } from "../../../../../../../../definitions/bpd/citizen_v2/CitizenOptInStatus"; -import { - fetchWalletsFailure, - fetchWalletsRequestWithExpBackoff, - fetchWalletsSuccess -} from "../../../../../../../store/actions/wallet/wallets"; -import { - getBPDMethodsVisibleInWalletSelector, - pagoPaCreditCardWalletV1Selector -} from "../../../../../../../store/reducers/wallet/wallets"; -import { - remoteReady, - remoteUndefined -} from "../../../../../../../common/model/RemoteValue"; -import { - BpdActivationPayload, - bpdLoadActivationStatus -} from "../../../../store/actions/details"; -import { optInPaymentMethodsShowChoice } from "../../../../store/actions/optInPaymentMethods"; -import { - activationStatusSelector, - optInStatusSelector -} from "../../../../store/reducers/details/activation"; -import { optInShouldShowChoiceHandler } from "../optInShouldShowChoiceHandler"; - -const mockActivationStatus: BpdActivationPayload = { - enabled: true, - activationStatus: "never", - payoffInstr: undefined, - optInStatus: CitizenOptInStatusEnum.NOREQ -}; - -describe("optInShouldShowChoiceHandler saga", () => { - jest.useFakeTimers(); - - it("If bpdAllData fails, should dispatch the optInPaymentMethodsShowChoice.failure action and return", () => { - testSaga(optInShouldShowChoiceHandler) - .next() - .select(activationStatusSelector) - .next(remoteUndefined) - .put(bpdLoadActivationStatus.request()) - .next() - .take([ - getType(bpdLoadActivationStatus.success), - getType(bpdLoadActivationStatus.failure) - ]) - .next(bpdLoadActivationStatus.failure(new Error())) - .put(optInPaymentMethodsShowChoice.failure(new Error())) - .next() - .isDone(); - }); - - it("If bpdEnabled is not potSome, should dispatch the optInPaymentMethodsShowChoice.failure action and return", () => { - testSaga(optInShouldShowChoiceHandler) - .next() - .select(activationStatusSelector) - .next(remoteUndefined) - .put(bpdLoadActivationStatus.request()) - .next() - .take([ - getType(bpdLoadActivationStatus.success), - getType(bpdLoadActivationStatus.failure) - ]) - .next(bpdLoadActivationStatus.success(mockActivationStatus)) - .select(activationStatusSelector) - .next(remoteUndefined) - .put( - optInPaymentMethodsShowChoice.failure( - new Error("The bpdEnabled value is not potSome") - ) - ) - .next() - .isDone(); - }); - - it("If bpdEnabled is potSome with the value false, should dispatch the optInPaymentMethodsShowChoice.success action with payload false and return", () => { - testSaga(optInShouldShowChoiceHandler) - .next() - .select(activationStatusSelector) - .next(remoteUndefined) - .put(bpdLoadActivationStatus.request()) - .next() - .take([ - getType(bpdLoadActivationStatus.success), - getType(bpdLoadActivationStatus.failure) - ]) - .next(bpdLoadActivationStatus.success(mockActivationStatus)) - .select(activationStatusSelector) - .next(remoteReady("never")) - .put(optInPaymentMethodsShowChoice.success(false)) - .next() - .isDone(); - }); - - it("If optInStatus is not potSome, should dispatch the optInPaymentMethodsShowChoice.failure action and return", () => { - testSaga(optInShouldShowChoiceHandler) - .next() - .select(activationStatusSelector) - .next(remoteUndefined) - .put(bpdLoadActivationStatus.request()) - .next() - .take([ - getType(bpdLoadActivationStatus.success), - getType(bpdLoadActivationStatus.failure) - ]) - .next(bpdLoadActivationStatus.success(mockActivationStatus)) - .select(activationStatusSelector) - .next(remoteReady("subscribed")) - .select(optInStatusSelector) - .next(pot.none) - .put( - optInPaymentMethodsShowChoice.failure( - new Error("The optInStatus value is not potSome") - ) - ) - .next() - .isDone(); - }); - - it("If optInStatus is potSome with value different from NOREQ, should dispatch the optInPaymentMethodsShowChoice.success action with payload false and return", () => { - testSaga(optInShouldShowChoiceHandler) - .next() - .select(activationStatusSelector) - .next(remoteUndefined) - .put(bpdLoadActivationStatus.request()) - .next() - .take([ - getType(bpdLoadActivationStatus.success), - getType(bpdLoadActivationStatus.failure) - ]) - .next(bpdLoadActivationStatus.success(mockActivationStatus)) - .select(activationStatusSelector) - .next(remoteReady("subscribed")) - .select(optInStatusSelector) - .next(pot.some(CitizenOptInStatusEnum.DENIED)) - .put(optInPaymentMethodsShowChoice.success(false)) - .next() - .isDone(); - }); - - it("If fetchWallets fails, should dispatch the optInPaymentMethodsShowChoice.failure action", () => { - testSaga(optInShouldShowChoiceHandler) - .next() - .select(activationStatusSelector) - .next(remoteUndefined) - .put(bpdLoadActivationStatus.request()) - .next() - .take([ - getType(bpdLoadActivationStatus.success), - getType(bpdLoadActivationStatus.failure) - ]) - .next(bpdLoadActivationStatus.success(mockActivationStatus)) - .select(activationStatusSelector) - .next(remoteReady("subscribed")) - .select(optInStatusSelector) - .next(pot.some(CitizenOptInStatusEnum.NOREQ)) - .select(pagoPaCreditCardWalletV1Selector) - .next(pot.none) - .put(fetchWalletsRequestWithExpBackoff()) - .next() - .take([getType(fetchWalletsSuccess), getType(fetchWalletsFailure)]) - .next(fetchWalletsFailure(new Error())) - .put(optInPaymentMethodsShowChoice.failure(new Error())) - .next() - .isDone(); - }); - - it("If fetchWallets succeed, should dispatch the optInPaymentMethodsShowChoice.success action", () => { - testSaga(optInShouldShowChoiceHandler) - .next() - .select(activationStatusSelector) - .next(remoteUndefined) - .put(bpdLoadActivationStatus.request()) - .next() - .take([ - getType(bpdLoadActivationStatus.success), - getType(bpdLoadActivationStatus.failure) - ]) - .next(bpdLoadActivationStatus.success(mockActivationStatus)) - .select(activationStatusSelector) - .next(remoteReady("subscribed")) - .select(optInStatusSelector) - .next(pot.some(CitizenOptInStatusEnum.NOREQ)) - .select(pagoPaCreditCardWalletV1Selector) - .next(pot.none) - .put(fetchWalletsRequestWithExpBackoff()) - .next() - .take([getType(fetchWalletsSuccess), getType(fetchWalletsFailure)]) - .next(fetchWalletsSuccess([])) - .select(getBPDMethodsVisibleInWalletSelector) - .next([]) - .put(optInPaymentMethodsShowChoice.success(false)) - .next() - .isDone(); - }); -}); diff --git a/ts/features/bonus/bpd/saga/orchestration/optInPaymentMethods/optInDeletionChoiceHandler.ts b/ts/features/bonus/bpd/saga/orchestration/optInPaymentMethods/optInDeletionChoiceHandler.ts deleted file mode 100644 index 05db6adfe00..00000000000 --- a/ts/features/bonus/bpd/saga/orchestration/optInPaymentMethods/optInDeletionChoiceHandler.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { put, take } from "typed-redux-saga/macro"; -import { ActionType, isActionOf } from "typesafe-actions"; -import { deleteAllPaymentMethodsByFunction } from "../../../../../../store/actions/wallet/delete"; -import { EnableableFunctionsEnum } from "../../../../../../../definitions/pagopa/EnableableFunctions"; -import { bpdUpdateOptInStatusMethod } from "../../../store/actions/onboarding"; -import { CitizenOptInStatusEnum } from "../../../../../../../definitions/bpd/citizen_v2/CitizenOptInStatus"; -import { ReduxSagaEffect } from "../../../../../../types/utils"; - -/** - * This saga orchestrate the choice of the user to delete the payment methods added during the cashback. - * This saga execute 2 actions: - * - delete all the payment methods with the BPD capability - * - store the user choice - */ - -export function* optInDeletionChoiceHandler(): Generator< - ReduxSagaEffect, - void, - any -> { - // Perform the payment methods deletion - yield* put( - deleteAllPaymentMethodsByFunction.request(EnableableFunctionsEnum.BPD) - ); - const deleteAllPaymentMethodsByFunctionStatus = yield* take< - ActionType< - | typeof deleteAllPaymentMethodsByFunction.success - | typeof deleteAllPaymentMethodsByFunction.failure - > - >([ - deleteAllPaymentMethodsByFunction.success, - deleteAllPaymentMethodsByFunction.failure - ]); - if ( - isActionOf( - deleteAllPaymentMethodsByFunction.success, - deleteAllPaymentMethodsByFunctionStatus - ) - ) { - // If the payment methods deletion succeeded, perform the opt-in update - yield* put( - bpdUpdateOptInStatusMethod.request(CitizenOptInStatusEnum.DENIED) - ); - } -} diff --git a/ts/features/bonus/bpd/saga/orchestration/optInPaymentMethods/optInShouldShowChoiceHandler.ts b/ts/features/bonus/bpd/saga/orchestration/optInPaymentMethods/optInShouldShowChoiceHandler.ts deleted file mode 100644 index 171c7b86ca8..00000000000 --- a/ts/features/bonus/bpd/saga/orchestration/optInPaymentMethods/optInShouldShowChoiceHandler.ts +++ /dev/null @@ -1,144 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { put, select, take } from "typed-redux-saga/macro"; -import { ActionType, getType, isActionOf } from "typesafe-actions"; -import { CitizenOptInStatusEnum } from "../../../../../../../definitions/bpd/citizen_v2/CitizenOptInStatus"; -import { - fetchWalletsFailure, - fetchWalletsRequestWithExpBackoff, - fetchWalletsSuccess -} from "../../../../../../store/actions/wallet/wallets"; -import { - getBPDMethodsVisibleInWalletSelector, - pagoPaCreditCardWalletV1Selector -} from "../../../../../../store/reducers/wallet/wallets"; -import { ReduxSagaEffect } from "../../../../../../types/utils"; -import { - isLoading, - isReady, - RemoteValue -} from "../../../../../../common/model/RemoteValue"; -import { - ActivationStatus, - bpdLoadActivationStatus -} from "../../../store/actions/details"; -import { optInPaymentMethodsShowChoice } from "../../../store/actions/optInPaymentMethods"; -import { - activationStatusSelector, - optInStatusSelector -} from "../../../store/reducers/details/activation"; - -/** - * This saga manage the flow that checks if a user has already take a choice about the opt-in of the payment methods. - * - * The saga follows this flow: - * - check if the user participate or not in the cashback program - * - check if the user has already taken the opt-in payment methods choice - * - request the user's payment methods - * - check if the loading of the payment method succeed - * - if succeed start the saga - */ -export function* optInShouldShowChoiceHandler(): Generator< - ReduxSagaEffect, - void, - any -> { - const bpdActivationInitialStatus: RemoteValue = - yield* select(activationStatusSelector); - - // Check is needed to avoid to spawn multiple request if the status - // is already loading - if (!isLoading(bpdActivationInitialStatus)) { - // Load the information about the participation of the user to the bpd program - yield* put(bpdLoadActivationStatus.request()); - } - const bpdLoadActivationStatusResponse = yield* take< - ActionType< - | typeof bpdLoadActivationStatus.success - | typeof bpdLoadActivationStatus.failure - > - >([ - getType(bpdLoadActivationStatus.success), - getType(bpdLoadActivationStatus.failure) - ]); - - // If the bpdAllData request fail report the error - if ( - isActionOf(bpdLoadActivationStatus.failure, bpdLoadActivationStatusResponse) - ) { - yield* put( - optInPaymentMethodsShowChoice.failure( - bpdLoadActivationStatusResponse.payload - ) - ); - return; - } - - const activationStatus: RemoteValue = yield* select( - activationStatusSelector - ); - - // Safety check on field returned in @link{activationStatus} and managed by @{activationStatusReducer} - if (!isReady(activationStatus)) { - yield* put( - optInPaymentMethodsShowChoice.failure( - new Error("The bpdEnabled value is not potSome") - ) - ); - return; - } - - // If the user is never been enrolled in the bpd program returns with value false - if (activationStatus.kind === "ready" && activationStatus.value === "never") { - yield* put(optInPaymentMethodsShowChoice.success(false)); - return; - } - - const optInStatus: pot.Pot = yield* select( - optInStatusSelector - ); - - // Safety check on field returned in @link{optInStatus} and managed by @{optInStatusReducer} - if (optInStatus.kind !== "PotSome") { - yield* put( - optInPaymentMethodsShowChoice.failure( - new Error("The optInStatus value is not potSome") - ) - ); - return; - } - - // If the user already take a choice returns with value false - if (optInStatus.value !== CitizenOptInStatusEnum.NOREQ) { - yield* put(optInPaymentMethodsShowChoice.success(false)); - return; - } - - // Check if wallets are already loaded - // this check is needed becaus the exponential backoff would raise an error cause of the spawning of multiple requests - const potWallets = yield* select(pagoPaCreditCardWalletV1Selector); - - if (!pot.isLoading(potWallets)) { - // Load the user payment methods - yield* put(fetchWalletsRequestWithExpBackoff()); - } - const fetchWalletsResultAction = yield* take< - ActionType - >([getType(fetchWalletsSuccess), getType(fetchWalletsFailure)]); - - // If the loading work successfully starts the OptInPaymentMethods saga - if (isActionOf(fetchWalletsSuccess, fetchWalletsResultAction)) { - const bpdPaymentMethods = yield* select( - getBPDMethodsVisibleInWalletSelector - ); - - if (bpdPaymentMethods.length > 0) { - yield* put(optInPaymentMethodsShowChoice.success(true)); - return; - } - yield* put(optInPaymentMethodsShowChoice.success(false)); - } else { - yield* put( - optInPaymentMethodsShowChoice.failure(fetchWalletsResultAction.payload) - ); - } -} diff --git a/ts/features/bonus/bpd/screens/details/BpdDetailsScreen.tsx b/ts/features/bonus/bpd/screens/details/BpdDetailsScreen.tsx deleted file mode 100644 index 12d88cd55ce..00000000000 --- a/ts/features/bonus/bpd/screens/details/BpdDetailsScreen.tsx +++ /dev/null @@ -1,167 +0,0 @@ -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import * as React from "react"; -import { useEffect } from "react"; -import { StyleSheet, View } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import LoadingSpinnerOverlay from "../../../../../components/LoadingSpinnerOverlay"; -import DarkLayout from "../../../../../components/screens/DarkLayout"; -import SectionStatusComponent from "../../../../../components/SectionStatus"; -import I18n from "../../../../../i18n"; -import { navigateBack } from "../../../../../store/actions/navigation"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp"; -import { showToast } from "../../../../../utils/showToast"; -import { useHardwareBackButton } from "../../../../../hooks/useHardwareBackButton"; -import BpdLastUpdateComponent from "../../components/BpdLastUpdateComponent"; -import { - isError, - isLoading, - isReady -} from "../../../../../common/model/RemoteValue"; -import { bpdAllData } from "../../store/actions/details"; -import { - bpdUnsubscribeCompleted, - bpdUnsubscribeFailure -} from "../../store/actions/onboarding"; -import { bpdUnsubscriptionSelector } from "../../store/reducers/details/activation"; -import { bpdSelectedPeriodSelector } from "../../store/reducers/details/selectedPeriod"; -import { bpdTransactionsForSelectedPeriod } from "../../store/reducers/details/transactions"; -import BpdPeriodSelector from "./BpdPeriodSelector"; -import BpdPeriodDetail from "./periods/BpdPeriodDetail"; -import GoToTransactions from "./transaction/GoToTransactions"; - -export type Props = ReturnType & - ReturnType; - -const styles = StyleSheet.create({ - headerSpacer: { - height: 172 - }, - selector: { - // TODO: temp, as placeholder from invision, waiting for the components - height: 192 + 10 + 16 + 16, - width: "100%", - position: "absolute", - top: 16, - zIndex: 7, - elevation: 7 - }, - selectorSpacer: { - height: 60 - } -}); - -/** - * The screen that allows the user to see all the details related to the bpd. - * @constructor - */ -const BpdDetailsScreen: React.FunctionComponent = props => { - const loading = isLoading(props.unsubscription); - const { - unsubscription, - completeUnsubscriptionSuccess, - completeUnsubscriptionFailure - } = props; - - useEffect(() => { - if (isError(unsubscription)) { - showToast(I18n.t("bonus.bpd.unsubscribe.failure"), "danger"); - completeUnsubscriptionFailure(); - } else if (isReady(unsubscription)) { - showToast(I18n.t("bonus.bpd.unsubscribe.success"), "success"); - completeUnsubscriptionSuccess(); - } - }, [ - unsubscription, - completeUnsubscriptionSuccess, - completeUnsubscriptionFailure - ]); - - useHardwareBackButton(() => { - props.goBack(); - return true; - }); - - /** - * Display the transactions button when: - * - Period is closed and transactions number is > 0 - * - Period is active - * never displays for inactive/incoming period - */ - const canRenderButton = pipe( - props.selectedPeriod, - O.fromNullable, - O.fold( - () => false, - sp => { - switch (sp.status) { - case "Closed": - return pipe( - props.selectedPeriod?.amount?.transactionNumber, - O.fromNullable, - O.map(trx => trx > 0), - O.getOrElse(() => false) - ); - case "Inactive": - return false; - default: - return true; - } - } - ) - ); - return ( - - } - gradientHeader={true} - hideHeader={true} - contextualHelp={emptyContextualHelp} - footerContent={ - canRenderButton ? ( - - ) : ( - // We need to render a footer element in order to have the right spacing when the device has the notch - - ) - } - footerFullWidth={} - > - - - - - - - - - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - completeUnsubscriptionSuccess: () => { - dispatch(bpdAllData.request()); - dispatch(bpdUnsubscribeCompleted()); - navigateBack(); - }, - goToTransactions: () => null, - goBack: () => navigateBack(), - completeUnsubscriptionFailure: () => dispatch(bpdUnsubscribeFailure()) -}); - -const mapStateToProps = (state: GlobalState) => ({ - unsubscription: bpdUnsubscriptionSelector(state), - transactions: bpdTransactionsForSelectedPeriod(state), - selectedPeriod: bpdSelectedPeriodSelector(state) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(BpdDetailsScreen); diff --git a/ts/features/bonus/bpd/screens/details/BpdPeriodSelector.tsx b/ts/features/bonus/bpd/screens/details/BpdPeriodSelector.tsx deleted file mode 100644 index d4770d5a015..00000000000 --- a/ts/features/bonus/bpd/screens/details/BpdPeriodSelector.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as AR from "fp-ts/lib/Array"; -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import * as React from "react"; -import { useEffect, useState } from "react"; -import { View, StyleSheet } from "react-native"; -import { widthPercentageToDP } from "react-native-responsive-screen"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { IOColors } from "@pagopa/io-app-design-system"; -import { IOStyles } from "../../../../../components/core/variables/IOStyles"; -import { HorizontalScroll } from "../../../../../components/HorizontalScroll"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { BpdCardComponent } from "../../components/bpdCardComponent/BpdCardComponent"; -import { bpdSelectPeriod } from "../../store/actions/selectedPeriod"; -import { bpdPeriodsAmountWalletVisibleSelector } from "../../store/reducers/details/combiner"; -import { BpdPeriodWithInfo } from "../../store/reducers/details/periods"; -import { bpdSelectedPeriodSelector } from "../../store/reducers/details/selectedPeriod"; - -export type Props = ReturnType & - ReturnType; - -const styles = StyleSheet.create({ - cardWrapper: { - width: widthPercentageToDP("100%"), - shadowColor: IOColors.black, - shadowOffset: { - width: 0, - height: 7 - }, - shadowOpacity: 0.29, - shadowRadius: 4.65, - height: 192 - } -}); - -/** - * An horizontal snap scroll view used to select a specific period of bpd. - * Each period is represented as a BpdPeriodCard. - * @constructor - */ -const BpdPeriodSelector: React.FunctionComponent = props => { - const periodWithAmountList = pot.getOrElse(props.periodsWithAmount, []); - const [initialPeriod, setInitialperiod] = useState(); - const constructPeriodList = () => - periodWithAmountList.map((periodWithAmount, i) => ( - - - - )); - - const selectPeriod = (index: number) => - pipe( - periodWithAmountList[index], - O.fromNullable, - O.map(currentItem => { - if (currentItem.awardPeriodId === props.selectedPeriod?.awardPeriodId) { - return; - } - props.changeSelectPeriod(currentItem); - }) - ); - - useEffect(() => { - if (initialPeriod === undefined) { - setInitialperiod( - pipe( - periodWithAmountList, - AR.findIndex( - elem => elem.awardPeriodId === props.selectedPeriod?.awardPeriodId - ), - O.getOrElse(() => 0) - ) - ); - } - }, [ - initialPeriod, - periodWithAmountList, - props.selectedPeriod?.awardPeriodId - ]); - - return ( - - {pot.isSome(props.periodsWithAmount) && - props.periodsWithAmount.value.length > 0 && ( - - )} - - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - changeSelectPeriod: (period: BpdPeriodWithInfo) => - dispatch(bpdSelectPeriod(period)) -}); - -const mapStateToProps = (state: GlobalState) => ({ - // ATM the rules of visualization of a period in the selector is the same of the wallet - periodsWithAmount: bpdPeriodsAmountWalletVisibleSelector(state), - selectedPeriod: bpdSelectedPeriodSelector(state) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(BpdPeriodSelector); diff --git a/ts/features/bonus/bpd/screens/details/components/bottomsheet/HowItWorks.tsx b/ts/features/bonus/bpd/screens/details/components/bottomsheet/HowItWorks.tsx deleted file mode 100644 index 4b970249384..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/bottomsheet/HowItWorks.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { View } from "react-native"; -import * as React from "react"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import Markdown from "../../../../../../../components/ui/Markdown"; -import I18n from "../../../../../../../i18n"; -import { localeDateFormat } from "../../../../../../../utils/locale"; -import { BpdPeriod } from "../../../../store/actions/periods"; - -type Props = { - period: BpdPeriod; -}; - -/** - * Display information about the current period - * @constructor - */ -export const HowItWorks: React.FunctionComponent = props => ( - - - - - {I18n.t("bonus.bpd.details.howItWorks.body", { - ...props.period, - startDate: localeDateFormat( - props.period.startDate, - I18n.t("global.dateFormats.fullFormatFullMonthLiteral") - ), - endDate: localeDateFormat( - props.period.endDate, - I18n.t("global.dateFormats.fullFormatFullMonthLiteral") - ) - })} - - - -); diff --git a/ts/features/bonus/bpd/screens/details/components/bottomsheet/WhyOtherCards.tsx b/ts/features/bonus/bpd/screens/details/components/bottomsheet/WhyOtherCards.tsx deleted file mode 100644 index 73cc3570d60..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/bottomsheet/WhyOtherCards.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import * as React from "react"; -import { View, StyleSheet } from "react-native"; -import { IOColors, VSpacer } from "@pagopa/io-app-design-system"; -import ButtonDefaultOpacity from "../../../../../../../components/ButtonDefaultOpacity"; -import { Body } from "../../../../../../../components/core/typography/Body"; -import { Link } from "../../../../../../../components/core/typography/Link"; -import I18n from "../../../../../../../i18n"; -import { useLegacyIOBottomSheetModal } from "../../../../../../../utils/hooks/bottomSheet"; -import { openWebUrl } from "../../../../../../../utils/url"; - -const styles = StyleSheet.create({ - link: { - backgroundColor: IOColors.white, - borderColor: IOColors.white, - paddingRight: 0, - paddingLeft: 0 - } -}); - -const findOutMore = "https://io.italia.it/cashback/faq/#n3_11"; - -/** - * Explains why there are other cards - * @constructor - */ -export const WhyOtherCards = () => ( - - - - - {I18n.t( - "bonus.bpd.details.paymentMethods.activateOnOthersChannel.whyOtherCards.body" - )} - - openWebUrl(findOutMore)} - onPressWithGestureHandler={true} - style={styles.link} - > - - {I18n.t( - "bonus.bpd.details.paymentMethods.activateOnOthersChannel.whyOtherCards.cta" - )} - - - - -); - -export const useWhyOtherCardsBottomSheet = () => - useLegacyIOBottomSheetModal( - , - I18n.t( - "bonus.bpd.details.paymentMethods.activateOnOthersChannel.whyOtherCards.title" - ), - 300 - ); diff --git a/ts/features/bonus/bpd/screens/details/components/iban/BaseIbanInformationComponent.tsx b/ts/features/bonus/bpd/screens/details/components/iban/BaseIbanInformationComponent.tsx deleted file mode 100644 index 5ec7171ae5b..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/iban/BaseIbanInformationComponent.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { Button } from "native-base"; -import * as React from "react"; -import { View, StyleSheet } from "react-native"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { InfoBox } from "../../../../../../../components/box/InfoBox"; -import { Body } from "../../../../../../../components/core/typography/Body"; -import { H4 } from "../../../../../../../components/core/typography/H4"; -import { Label } from "../../../../../../../components/core/typography/Label"; -import { Link } from "../../../../../../../components/core/typography/Link"; -import { Monospace } from "../../../../../../../components/core/typography/Monospace"; -import { IOStyles } from "../../../../../../../components/core/variables/IOStyles"; -import I18n from "../../../../../../../i18n"; -import { isStringNullyOrEmpty } from "../../../../../../../utils/strings"; -import { - isReady, - RemoteValue -} from "../../../../../../../common/model/RemoteValue"; - -export type BaseIbanProps = { - iban: string | undefined; - technicalAccount: RemoteValue; - onInsertIban: () => void; -}; - -const styles = StyleSheet.create({ - row: { flexDirection: "row", justifyContent: "space-between" }, - insertIbanButton: { width: "100%" } -}); - -/** - * Render a Infobox that warns the user that should insert the IBAN to receive - * the cashback amount - * @param props - * @constructor - */ -const NoIbanComponent = (props: { onPress: () => void }) => ( - <> - - {I18n.t("bonus.bpd.details.components.iban.noIbanBody")} - - - - -); - -/** - * Display the current IBAN - * @constructor - */ -const IbanComponent = (props: { iban: string }) => ( - {props.iban} -); - -/** - * Display the technical IBAN message - * @constructor - */ -const TechnicalIbanComponent = (props: { technicalIban: string }) => ( - {props.technicalIban} -); - -export const BaseIbanInformationComponent: React.FunctionComponent< - BaseIbanProps -> = props => ( - - -

{I18n.t("bonus.bpd.details.components.iban.title")}

- {!isStringNullyOrEmpty(props.iban) && ( - - {I18n.t("global.buttons.edit").toLowerCase()} - - )} -
- - {/* Also if it is a technical IBAN the field IBAN is filled (with a fake IBAN). */} - {props.iban ? ( - isReady(props.technicalAccount) && - props.technicalAccount.value !== undefined ? ( - - ) : ( - - ) - ) : ( - - )} - -
-); diff --git a/ts/features/bonus/bpd/screens/details/components/iban/IbanInformationComponent.tsx b/ts/features/bonus/bpd/screens/details/components/iban/IbanInformationComponent.tsx deleted file mode 100644 index 0ae8417a91f..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/iban/IbanInformationComponent.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import Placeholder from "rn-placeholder"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { - fold, - RemoteValue -} from "../../../../../../../common/model/RemoteValue"; -import { bpdIbanInsertionStart } from "../../../../store/actions/iban"; -import { bpdIbanSelector } from "../../../../store/reducers/details/activation"; -import { bpdTechnicalAccountSelector } from "../../../../store/reducers/details/activation/technicalAccount"; -import { - BaseIbanInformationComponent, - BaseIbanProps -} from "./BaseIbanInformationComponent"; - -export type Props = ReturnType & - ReturnType; - -const LoadingIban = () => ; - -/** - * Render the Iban based on the RemoteValue. - * For safeness, even undefined, remote and error cases are managed, although the iban detail - * screen is only accessible if bpd has been activated. - * @param props - * @constructor - */ -const RenderRemoteIban = ( - props: { - iban: RemoteValue; - } & Omit -) => - fold( - props.iban, - () => null, - () => , - value => ( - - ), - _ => null - ); - -/** - * Link {@link BaseIbanInformationComponent} to the business logic - * Read the iban RemoteValue from the store - * Dispatch bpdIbanInsertionStart() in case of new iban insertion (or modification - * @param props - * @constructor - */ -const IbanInformationComponent: React.FunctionComponent = props => ( - -); - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - startIbanOnboarding: () => dispatch(bpdIbanInsertionStart()) -}); - -const mapStateToProps = (state: GlobalState) => ({ - iban: bpdIbanSelector(state), - technicalAccount: bpdTechnicalAccountSelector(state) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(IbanInformationComponent); diff --git a/ts/features/bonus/bpd/screens/details/components/paymentMethod/WalletPaymentMethodBpdList.tsx b/ts/features/bonus/bpd/screens/details/components/paymentMethod/WalletPaymentMethodBpdList.tsx deleted file mode 100644 index e05b0760ad5..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/paymentMethod/WalletPaymentMethodBpdList.tsx +++ /dev/null @@ -1,207 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as O from "fp-ts/lib/Option"; -import * as React from "react"; -import { useEffect, useState } from "react"; -import { View, ActivityIndicator, Alert, StyleSheet } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { InfoBox } from "../../../../../../../components/box/InfoBox"; -import { Body } from "../../../../../../../components/core/typography/Body"; -import { H4 } from "../../../../../../../components/core/typography/H4"; -import { Link } from "../../../../../../../components/core/typography/Link"; -import I18n from "../../../../../../../i18n"; -import { navigateToWalletAddPaymentMethod } from "../../../../../../../store/actions/navigation"; -import { fetchWalletsRequestWithExpBackoff } from "../../../../../../../store/actions/wallet/wallets"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { showToast } from "../../../../../../../utils/showToast"; -import { PaymentMethodGroupedList } from "../../../../components/paymentMethodActivationToggle/list/PaymentMethodGroupedList"; -import { - atLeastOnePaymentMethodHasBpdEnabledSelector, - paymentMethodsWithActivationStatusSelector -} from "../../../../store/reducers/details/combiner"; - -type Props = ReturnType & - ReturnType; - -const styles = StyleSheet.create({ - row: { - flexDirection: "row", - justifyContent: "space-between" - } -}); - -const Spinner = () => ( - -); - -const UpdateLabel = (props: Props & { caption: string }) => - pot.isLoading(props.potWallets) ? ( - - ) : ( - {props.caption} - ); - -/** - * No payment methods are active - * @constructor - */ -const NoPaymentMethodAreActiveWarning = () => ( - - - {I18n.t("bonus.bpd.details.paymentMethods.noActiveMethod")} - - - -); - -/** - * No payment methods are found - * @constructor - */ -const NoPaymentMethodFound = () => ( - - - {I18n.t("bonus.bpd.details.paymentMethods.noPaymentMethods")} - - -); - -/** - * The wallet is none - * @param props - * @constructor - */ -const PaymentMethodNone = (props: Props) => ( - <> - -

{I18n.t("wallet.paymentMethods")}

- -
- -); - -/** - * The wallet is error - * @param props - * @constructor - */ -const PaymentMethodError = (props: Props) => ( - <> - -

{I18n.t("wallet.paymentMethods")}

- -
- - - {I18n.t("bonus.bpd.details.paymentMethods.error")} - - -); - -/** - * The wallet is some - * @param props - * @constructor - */ -const PaymentMethodSome = (props: Props) => - pot.isSome(props.potWallets) ? ( - - -

{I18n.t("wallet.paymentMethods")}

- -
- - {!props.atLeastOnePaymentMethodActive && - props.potWallets.value.length > 0 && ( - - )} - - {props.potWallets.value.length > 0 ? ( - - ) : ( - - )} -
- ) : null; - -const addPaymentMethod = (action: () => void) => - Alert.alert( - I18n.t("global.genericAlert"), - I18n.t("bonus.bpd.details.paymentMethods.add.alertBody"), - [ - { - text: I18n.t("global.buttons.continue"), - onPress: action - }, - { - text: I18n.t("global.buttons.cancel"), - style: "cancel" - } - ] - ); - -/** - * Render all the wallet v2 as bpd toggle - * @param props - * @constructor - */ -const WalletPaymentMethodBpdList: React.FunctionComponent = props => { - const [potState, setPotCurrentState] = useState(props.potWallets.kind); - const { potWallets } = props; - - useEffect(() => { - if (potWallets.kind !== potState) { - setPotCurrentState(potWallets.kind); - if (pot.isError(potWallets)) { - showToast(I18n.t("global.genericError"), "danger"); - } - } - }, [potWallets, potState]); - - return pot.fold( - props.potWallets, - () => , - () => , - _ => , - _ => , - _ => , - _ => , - _ => , - _ => - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - addPaymentMethod: () => { - addPaymentMethod(() => - navigateToWalletAddPaymentMethod({ inPayment: O.none }) - ); - }, - loadWallets: () => dispatch(fetchWalletsRequestWithExpBackoff()) -}); - -const mapStateToProps = (state: GlobalState) => ({ - potWallets: paymentMethodsWithActivationStatusSelector(state), - atLeastOnePaymentMethodActive: - atLeastOnePaymentMethodHasBpdEnabledSelector(state) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(WalletPaymentMethodBpdList); diff --git a/ts/features/bonus/bpd/screens/details/components/summary/BpdSummaryComponent.tsx b/ts/features/bonus/bpd/screens/details/components/summary/BpdSummaryComponent.tsx deleted file mode 100644 index 32c9f8e9760..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/summary/BpdSummaryComponent.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { View, StyleSheet } from "react-native"; -import { HSpacer, VSpacer } from "@pagopa/io-app-design-system"; -import { profileNameSelector } from "../../../../../../../store/reducers/profile"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { BpdPeriod } from "../../../../store/actions/periods"; -import { BpdPeriodWithInfo } from "../../../../store/reducers/details/periods"; -import { bpdSelectedPeriodSelector } from "../../../../store/reducers/details/selectedPeriod"; -import SuperCashbackRankingSummary from "./ranking/SuperCashbackRankingSummary"; -import { TextualSummary } from "./textualSummary/TextualSummary"; -import TransactionsGraphicalSummary from "./TransactionsGraphicalSummary"; - -type Props = ReturnType & - ReturnType; - -type SummaryData = { - period: BpdPeriodWithInfo; - name: string | undefined; -}; - -const styles = StyleSheet.create({ - row: { - flex: 1, - flexDirection: "row" - } -}); - -/** - * The graphical summary is visible only when the period is closed or when the period is Active - * and transactionNumber > 0 - * @param period - */ -const isGraphicalSummaryVisible = (period: BpdPeriodWithInfo) => - period.status === "Closed" || - (period.status === "Active" && period.amount.transactionNumber > 0); - -/** - * Return true if the SuperCashback is visible for the specified period - * @param period - */ -const isSuperCashbackVisible = (period: BpdPeriod) => period.minPosition > 0; - -const Content = (sd: SummaryData) => ( - - {isGraphicalSummaryVisible(sd.period) ? ( - - - {isSuperCashbackVisible(sd.period) ? ( - <> - - - - ) : null} - - ) : null} - - - -); - -/** - * Display a summary with a graphical and textual information about the minimum transaction - * and the amount earned for the period. - * @constructor - */ -const BpdSummaryComponent: React.FunctionComponent = props => - props.currentPeriod ? ( - - ) : null; - -const mapDispatchToProps = (_: Dispatch) => ({}); - -const mapStateToProps = (state: GlobalState) => ({ - currentPeriod: bpdSelectedPeriodSelector(state), - name: profileNameSelector(state) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(BpdSummaryComponent); diff --git a/ts/features/bonus/bpd/screens/details/components/summary/TransactionsGraphicalSummary.tsx b/ts/features/bonus/bpd/screens/details/components/summary/TransactionsGraphicalSummary.tsx deleted file mode 100644 index 1086496f9f4..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/summary/TransactionsGraphicalSummary.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import * as React from "react"; -import { StyleSheet, TouchableOpacity } from "react-native"; -import { connect } from "react-redux"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { H2 } from "../../../../../../../components/core/typography/H2"; -import { H5 } from "../../../../../../../components/core/typography/H5"; -import { IOStyles } from "../../../../../../../components/core/variables/IOStyles"; -import I18n from "../../../../../../../i18n"; -import { Dispatch } from "../../../../../../../store/actions/types"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { formatIntegerNumber } from "../../../../../../../utils/stringBuilder"; -import { BpdPeriod } from "../../../../store/actions/periods"; -import { BpdBaseShadowBoxLayout } from "./base/BpdBaseShadowBoxLayout"; -import { ProgressBar } from "./base/ProgressBar"; - -type Props = { - transactions: number; - minTransactions: number; - period: BpdPeriod; -} & ReturnType & - ReturnType; - -const styles = StyleSheet.create({ - title: { - textAlign: "center" - } -}); - -const loadLocales = () => ({ - title: I18n.t("bonus.bpd.details.components.transactionsCountOverview.title"), - of: I18n.t("bonus.bpd.details.components.transactionsCountOverview.of") -}); - -/** - * When transactions < minTransactions, display a progress bar with the related information - * @param props - * @deprecated not used anymore, it is kept for some time in case of second thoughts - */ -export const PercentageTransactionsSummary = (props: Props) => { - const { title, of } = loadLocales(); - return ( - - {title} - - } - row2={ -

-

- {formatIntegerNumber(props.transactions)} -

{" "} - {of} {formatIntegerNumber(props.minTransactions)} - - } - row3={ - <> - - - - } - /> - ); -}; - -/** - * When transactions >= minTransactions, display only a textual summary - * @param props - * @constructor - */ -const TextualTransactionsSummary = (props: Props) => { - const { title, of } = loadLocales(); - return ( - {title}} - row2={ -

- {formatIntegerNumber(props.transactions)} -

- } - row3={ -
- {of} {formatIntegerNumber(props.minTransactions)} -
- } - /> - ); -}; - -/** - * Displays to the user a summary of the transactions and how many are missing - * to reach the minimum necessary to receive the cashback. - * @param props - * @constructor - */ -const TransactionsGraphicalSummary = (props: Props) => ( - - - -); - -const mapStateToProps = (_: GlobalState) => ({}); - -const mapDispatchToProps = (_: Dispatch) => ({ - goToTransactions: () => null -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(TransactionsGraphicalSummary); diff --git a/ts/features/bonus/bpd/screens/details/components/summary/base/BpdBaseShadowBoxLayout.tsx b/ts/features/bonus/bpd/screens/details/components/summary/base/BpdBaseShadowBoxLayout.tsx deleted file mode 100644 index f3f0a76e69b..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/summary/base/BpdBaseShadowBoxLayout.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import * as React from "react"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { ShadowBox } from "./ShadowBox"; - -type Props = { - row1: React.ReactNode; - row2: React.ReactNode; - row3: React.ReactNode; -}; - -/** - * Define a base layout for a bpd infobox, using a {@link ShadowBox} - * @param props - * @constructor - */ -export const BpdBaseShadowBoxLayout: React.FunctionComponent = props => ( - - {props.row1} - - {props.row2} - {props.row3} - -); diff --git a/ts/features/bonus/bpd/screens/details/components/summary/base/ShadowBox.tsx b/ts/features/bonus/bpd/screens/details/components/summary/base/ShadowBox.tsx deleted file mode 100644 index d70870060db..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/summary/base/ShadowBox.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { IOColors } from "@pagopa/io-app-design-system"; -import * as React from "react"; -import { View, StyleSheet } from "react-native"; - -const styles = StyleSheet.create({ - body: { - borderRadius: 8, - backgroundColor: IOColors.white, - shadowColor: IOColors.bluegreyDark, - shadowOffset: { - width: 0, - height: 3 - }, - shadowOpacity: 0.2, - shadowRadius: 2.0, - elevation: 4, - flex: 1, - marginHorizontal: 2 - }, - container: { - paddingVertical: 12, - paddingHorizontal: 16 - } -}); - -/** - * A base shadowed box with a content - * @param props - * @constructor - */ -export const ShadowBox: React.FunctionComponent = props => ( - - {props.children} - -); diff --git a/ts/features/bonus/bpd/screens/details/components/summary/ranking/RankingNotReadyBottomSheet.tsx b/ts/features/bonus/bpd/screens/details/components/summary/ranking/RankingNotReadyBottomSheet.tsx deleted file mode 100644 index 1bc966cf071..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/summary/ranking/RankingNotReadyBottomSheet.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { View } from "react-native"; -import * as React from "react"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import I18n from "../../../../../../../../i18n"; -import Markdown from "../../../../../../../../components/ui/Markdown"; -import { useLegacyIOBottomSheetModal } from "../../../../../../../../utils/hooks/bottomSheet"; - -/** - * Display information about the current period - * @constructor - */ -const RankingNotReady = (): React.ReactElement => ( - - - - - {I18n.t("bonus.bpd.details.components.ranking.notReady.body")} - - - -); - -export const useRankingNotReadyBottomSheet = () => - useLegacyIOBottomSheetModal( - , - I18n.t("bonus.bpd.details.components.ranking.notReady.title"), - 450 - ); diff --git a/ts/features/bonus/bpd/screens/details/components/summary/ranking/SuperCashbackRankingSummary.tsx b/ts/features/bonus/bpd/screens/details/components/summary/ranking/SuperCashbackRankingSummary.tsx deleted file mode 100644 index 1d8e852dd8a..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/summary/ranking/SuperCashbackRankingSummary.tsx +++ /dev/null @@ -1,154 +0,0 @@ -import { Icon, VSpacer } from "@pagopa/io-app-design-system"; -import * as React from "react"; -import { StyleSheet, TouchableOpacity, View } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { H2 } from "../../../../../../../../components/core/typography/H2"; -import { H5 } from "../../../../../../../../components/core/typography/H5"; -import { IOStyles } from "../../../../../../../../components/core/variables/IOStyles"; -import I18n from "../../../../../../../../i18n"; -import { GlobalState } from "../../../../../../../../store/reducers/types"; -import { formatIntegerNumber } from "../../../../../../../../utils/stringBuilder"; -import { useSuperCashbackRankingBottomSheet } from "../../../../../components/superCashbackRanking/SuperCashbackRanking"; -import { - BpdPeriodWithInfo, - BpdRanking, - BpdRankingReady, - isBpdRankingReady -} from "../../../../../store/reducers/details/periods"; -import { BpdBaseShadowBoxLayout } from "../base/BpdBaseShadowBoxLayout"; -import { useRankingNotReadyBottomSheet } from "./RankingNotReadyBottomSheet"; - -const loadLocales = () => ({ - title: I18n.t("bonus.bpd.details.components.ranking.title"), - of: I18n.t("bonus.bpd.details.components.transactionsCountOverview.of"), - wip: I18n.t("profile.preferences.list.wip") -}); - -const styles = StyleSheet.create({ - title: { - textAlign: "center" - } -}); - -type OwnProps = { - period: BpdPeriodWithInfo; -}; - -type Props = ReturnType & - ReturnType & - OwnProps; - -const SuperCashbackRankingReady = (props: { - ranking: number; - minRanking: number; -}): React.ReactElement => { - const { title, of } = loadLocales(); - const { present, bottomSheet } = useSuperCashbackRankingBottomSheet(); - return ( - <> - {bottomSheet} - - - {title} - - } - row2={ -

- {formatIntegerNumber(props.ranking)}° -

- } - row3={ -
- {of} {formatIntegerNumber(props.minRanking)} -
- } - /> -
- - ); -}; - -const SuperCashbackRankingNotReady = (): React.ReactElement => { - const { title, wip } = loadLocales(); - const { present, bottomSheet } = useRankingNotReadyBottomSheet(); - return ( - <> - {bottomSheet} - - - {title} - - } - row2={ - <> - - - - - - - } - row3={ -
- {wip} -
- } - /> -
- - ); -}; - -/** - * The ranking should be visible only when the remoteRanking is enabled && isBpdRankingReady - * @param ranking - * @param remoteEnabled - */ -const shouldDisplayRankingReady = ( - ranking: BpdRanking, - remoteEnabled: boolean | undefined -): ranking is BpdRankingReady => - remoteEnabled === true && isBpdRankingReady(ranking); - -/** - * Choose the right super cashback ranking representation: - * 1) The ranking is ready: SuperCashbackRankingReady - * 2) The ranking is not ready: TBD - * TODO: the cashback ranking should also be remotely activable - * @param props - * @constructor - */ -const SuperCashbackRankingSummary = (props: Props): React.ReactElement => - shouldDisplayRankingReady(props.period.ranking, undefined) ? ( - - ) : ( - - ); - -const mapDispatchToProps = (_: Dispatch) => ({}); - -const mapStateToProps = (_: GlobalState) => ({}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(SuperCashbackRankingSummary); diff --git a/ts/features/bonus/bpd/screens/details/components/summary/textualSummary/ActiveTextualSummary.tsx b/ts/features/bonus/bpd/screens/details/components/summary/textualSummary/ActiveTextualSummary.tsx deleted file mode 100644 index c4ad08c57c5..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/summary/textualSummary/ActiveTextualSummary.tsx +++ /dev/null @@ -1,117 +0,0 @@ -import * as React from "react"; -import { InfoBox } from "../../../../../../../../components/box/InfoBox"; -import { Body } from "../../../../../../../../components/core/typography/Body"; -import { H4 } from "../../../../../../../../components/core/typography/H4"; -import I18n from "../../../../../../../../i18n"; -import { formatNumberAmount } from "../../../../../../../../utils/stringBuilder"; -import { BpdPeriod } from "../../../../../store/actions/periods"; -import { isBpdRankingReady } from "../../../../../store/reducers/details/periods"; -import { TextualSummary } from "./TextualSummary"; - -type Props = React.ComponentProps; - -/** - * Display a warning for the current period if transactions < minTransaction and status === "Active" - */ -const Warning = (props: { period: BpdPeriod }) => ( - - - {I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.currentPeriodKOBody.one" - )} -

- {I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.currentPeriodKOBody.two", - { - transactions: props.period.minTransactionNumber - } - )} -

- {I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.currentPeriodKOBody.three" - )} - -
-); - -/** - * Display a message informing the user that the cashback is unlocked for the current period - */ -const Unlock = (props: Props) => ( - - - {I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.currentPeriodUnlockBody", - { - transactions: props.period.minTransactionNumber, - name: props.name - } - )} - - -); - -/** - * Display a message informing the user that he reached the max cashback amount for the current period - */ -const MaxAmount = (props: { name: string | undefined }) => ( - - - {I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.currentPeriodMaxAmount", - { - name: props.name - } - )} - - -); - -/** - * Display a message informing the user that at the moment he may be eligible for supercashback - */ -const SuperCashback = (props: { superCashbackAmount: number }) => ( - - - {I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.currentPeriodSuperCashback", - { superCashbackAmount: formatNumberAmount(props.superCashbackAmount) } - )} - - -); - -export const ActiveTextualSummary = (props: Props) => { - // active period but still not enough transaction - if ( - props.period.amount.transactionNumber < props.period.minTransactionNumber && - props.period.amount.totalCashback > 0 - ) { - return ; - } - if ( - props.period.amount.transactionNumber >= props.period.minTransactionNumber - ) { - // The user is in the supercashback ranking atm - if ( - isBpdRankingReady(props.period.ranking) && - props.period.ranking.ranking <= props.period.minPosition - ) { - return ( - - ); - } - // The max cashback amount is reached - if (props.period.amount.totalCashback >= props.period.maxPeriodCashback) { - return ; - } - // Cashback unlocked! visible for the next 10 transaction only - if ( - props.period.amount.transactionNumber <= - props.period.minTransactionNumber + 10 - ) { - return ; - } - } - return null; -}; diff --git a/ts/features/bonus/bpd/screens/details/components/summary/textualSummary/ClosedTextualSummary.tsx b/ts/features/bonus/bpd/screens/details/components/summary/textualSummary/ClosedTextualSummary.tsx deleted file mode 100644 index 3948b728777..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/summary/textualSummary/ClosedTextualSummary.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import * as React from "react"; -import { InfoBox } from "../../../../../../../../components/box/InfoBox"; -import { Body } from "../../../../../../../../components/core/typography/Body"; -import I18n from "../../../../../../../../i18n"; -import { dateToAccessibilityReadableFormat } from "../../../../../../../../utils/accessibility"; -import { localeDateFormat } from "../../../../../../../../utils/locale"; -import { - formatIntegerNumber, - formatNumberAmount -} from "../../../../../../../../utils/stringBuilder"; -import { BpdPeriod } from "../../../../../store/actions/periods"; -import { isGracePeriod } from "../../../../../store/reducers/details/periods"; -import { TextualSummary } from "./TextualSummary"; - -type Props = React.ComponentProps; - -/** - * We await receipt of the latest transactions to consolidate the count - * @param props - * @constructor - */ -const GracePeriod = (props: { period: BpdPeriod }) => ( - - - {I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.gracePeriodBody", - { - date: dateToAccessibilityReadableFormat(props.period.endDate), - endGracePeriodDate: dateToAccessibilityReadableFormat( - endGracePeriod(props.period) - ) - } - )} - - -); - -/** - * The user doesn't receive the amount (not enough transactions for the closed period) - * @param props - * @constructor - */ -const KO = (props: Props) => ( - - - {I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.closedPeriodKOBody", - { - transactions: formatIntegerNumber(props.period.minTransactionNumber), - amount: formatNumberAmount(props.period.amount.totalCashback) - } - )} - - -); - -const transferDate = (period: BpdPeriod) => { - const endDate = new Date(period.endDate); - - // 60: max days to receive the money transfer - endDate.setDate(period.endDate.getDate() + 60); - return endDate; -}; - -const endGracePeriod = (period: BpdPeriod) => { - const endDate = new Date(period.endDate); - - endDate.setDate(period.endDate.getDate() + period.gracePeriod); - return endDate; -}; - -/** - * Enriches the text in case of Super Cashback or max amount - * @param props - */ -const enhanceOkText = (props: Props): O.Option => { - // the user earned the super cashback - if ( - props.period.superCashbackAmount > 0 && - props.period.amount.totalCashback >= props.period.superCashbackAmount - ) { - return O.some( - I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.closedPeriodSuperCashback", - { - amount: formatNumberAmount(props.period.superCashbackAmount) - } - ) - ); - } - // the user earned the max amount - else if ( - props.period.amount.totalCashback >= props.period.maxPeriodCashback - ) { - return O.some( - I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.closedPeriodMaxAmount" - ) - ); - } - return O.none; -}; - -/** - * The user will receive the refund! - * @param props - * @constructor - */ -const OK = (props: Props) => ( - - - {I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.closedPeriodOKBody", - { - name: props.name, - amount: formatNumberAmount(props.period.amount.totalCashback) - } - )} - {pipe( - enhanceOkText(props), - O.getOrElse(() => "") - ) + "!\n"} - {I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.moneyTransfer", - { - date: localeDateFormat( - transferDate(props.period), - I18n.t("global.dateFormats.fullFormatFullMonthLiteral") - ) - } - )} - - -); - -/** - * Return a textual summary for a closed period - * @param props - * @constructor - */ -export const ClosedTextualSummary = (props: Props) => { - // we are still in the grace period and warns the user that some transactions - // may still be pending - if (isGracePeriod(props.period)) { - return ; - } - // not enough transaction to receive the cashback - if ( - props.period.amount.transactionNumber < props.period.minTransactionNumber - ) { - return ; - } - // Congratulation! cashback received - return ; -}; diff --git a/ts/features/bonus/bpd/screens/details/components/summary/textualSummary/InactiveTextualSummary.tsx b/ts/features/bonus/bpd/screens/details/components/summary/textualSummary/InactiveTextualSummary.tsx deleted file mode 100644 index 5652f19b4d7..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/summary/textualSummary/InactiveTextualSummary.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import * as React from "react"; -import { InfoBox } from "../../../../../../../../components/box/InfoBox"; -import { Body } from "../../../../../../../../components/core/typography/Body"; -import I18n from "../../../../../../../../i18n"; -import { dateToAccessibilityReadableFormat } from "../../../../../../../../utils/accessibility"; -import { BpdPeriod } from "../../../../../store/actions/periods"; - -/** - * Inform the user about the start date of the next period - * @param props - * @constructor - */ -export const InactiveTextualSummary = (props: { - period: BpdPeriod; -}): React.ReactElement => ( - - - {I18n.t( - "bonus.bpd.details.components.transactionsCountOverview.inactivePeriodBody", - { - date: dateToAccessibilityReadableFormat(props.period.startDate) - } - )} - - -); diff --git a/ts/features/bonus/bpd/screens/details/components/summary/textualSummary/TextualSummary.tsx b/ts/features/bonus/bpd/screens/details/components/summary/textualSummary/TextualSummary.tsx deleted file mode 100644 index be6e7bde7d4..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/summary/textualSummary/TextualSummary.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as React from "react"; -import { BpdPeriodWithInfo } from "../../../../../store/reducers/details/periods"; -import { ActiveTextualSummary } from "./ActiveTextualSummary"; -import { ClosedTextualSummary } from "./ClosedTextualSummary"; -import { InactiveTextualSummary } from "./InactiveTextualSummary"; - -type Props = { - period: BpdPeriodWithInfo; - name: string | undefined; -}; - -/** - * Render additional text information for the user, related to the transactions and cashback amount - * Choose the textual infobox based on period and amount values - * @param props - * @constructor - */ -export const TextualSummary = (props: Props): React.ReactElement => { - switch (props.period.status) { - case "Inactive": - return ; - case "Closed": - return ; - case "Active": - return ; - } -}; diff --git a/ts/features/bonus/bpd/screens/details/components/unsubscribe/UnsubscribeComponent.tsx b/ts/features/bonus/bpd/screens/details/components/unsubscribe/UnsubscribeComponent.tsx deleted file mode 100644 index 92c50fe7bd7..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/unsubscribe/UnsubscribeComponent.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { View } from "react-native"; -import * as React from "react"; -import { IOIconSizeScale, Icon, VSpacer } from "@pagopa/io-app-design-system"; -import { H3 } from "../../../../../../../components/core/typography/H3"; -import Markdown from "../../../../../../../components/ui/Markdown"; -import I18n from "../../../../../../../i18n"; - -const iconSize: IOIconSizeScale = 48; - -/** - * Informs the user about the consequences of the cashback unsubscription - * @constructor - */ -export const UnsubscribeComponent = (): React.ReactElement => ( - - - - -

{I18n.t("bonus.bpd.unsubscribe.body1")}

- - {I18n.t("bonus.bpd.unsubscribe.body2")} -
-); diff --git a/ts/features/bonus/bpd/screens/details/components/unsubscribe/UnsubscribeToBpd.tsx b/ts/features/bonus/bpd/screens/details/components/unsubscribe/UnsubscribeToBpd.tsx deleted file mode 100644 index b0005d0bf2d..00000000000 --- a/ts/features/bonus/bpd/screens/details/components/unsubscribe/UnsubscribeToBpd.tsx +++ /dev/null @@ -1,91 +0,0 @@ -import * as React from "react"; -import { StyleSheet } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { IOColors } from "@pagopa/io-app-design-system"; -import ButtonDefaultOpacity from "../../../../../../../components/ButtonDefaultOpacity"; -import { Label } from "../../../../../../../components/core/typography/Label"; -import I18n from "../../../../../../../i18n"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { bpdDeleteUserFromProgram } from "../../../../store/actions/onboarding"; -import { identificationRequest } from "../../../../../../../store/actions/identification"; -import { shufflePinPadOnPayment } from "../../../../../../../config"; -import { useLegacyIOBottomSheetModal } from "../../../../../../../utils/hooks/bottomSheet"; -import { - cancelButtonProps, - errorButtonProps -} from "../../../../../../../components/buttons/ButtonConfigurations"; -import FooterWithButtons from "../../../../../../../components/ui/FooterWithButtons"; -import { UnsubscribeComponent } from "./UnsubscribeComponent"; - -type Props = ReturnType & - ReturnType; - -const styles = StyleSheet.create({ - button: { - width: "100%", - borderColor: IOColors.red, - borderWidth: 1, - backgroundColor: IOColors.white - } -}); - -/** - * Allow the user to unsubscribe from bpd - * @constructor - */ -const UnsubscribeToBpd: React.FunctionComponent = props => { - const { present, bottomSheet, dismiss } = useLegacyIOBottomSheetModal( - , - I18n.t("bonus.bpd.unsubscribe.title"), - 582, - dismiss()), - onPressWithGestureHandler: true - }} - rightButton={{ - ...errorButtonProps(() => { - dismiss(); - props.cancelBpd(); - }, I18n.t("bonus.bpd.unsubscribe.confirmCta")), - onPressWithGestureHandler: true - }} - /> - ); - - return ( - <> - - - - {bottomSheet} - - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - cancelBpd: () => { - const onSuccess = () => dispatch(bpdDeleteUserFromProgram.request()); - dispatch( - identificationRequest( - false, - true, - undefined, - { - label: I18n.t("global.buttons.cancel"), - onCancel: () => undefined - }, - { - onSuccess - }, - shufflePinPadOnPayment - ) - ); - } -}); - -const mapStateToProps = (_: GlobalState) => ({}); - -export default connect(mapStateToProps, mapDispatchToProps)(UnsubscribeToBpd); diff --git a/ts/features/bonus/bpd/screens/details/periods/BpdActivePeriod.tsx b/ts/features/bonus/bpd/screens/details/periods/BpdActivePeriod.tsx deleted file mode 100644 index bd8329460e6..00000000000 --- a/ts/features/bonus/bpd/screens/details/periods/BpdActivePeriod.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { View } from "react-native"; -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import IbanInformationComponent from "../components/iban/IbanInformationComponent"; -import BpdSummaryComponent from "../components/summary/BpdSummaryComponent"; -import UnsubscribeToBpd from "../components/unsubscribe/UnsubscribeToBpd"; -import WalletPaymentMethodBpdList from "../components/paymentMethod/WalletPaymentMethodBpdList"; - -export type Props = ReturnType & - ReturnType; - -/** - * Render the details for a current active cashback period - * @constructor - */ -const BpdActivePeriod: React.FunctionComponent = () => ( - - - - - - - - - - - -); - -const mapDispatchToProps = (_: Dispatch) => ({}); - -const mapStateToProps = (_: GlobalState) => ({}); - -export default connect(mapStateToProps, mapDispatchToProps)(BpdActivePeriod); diff --git a/ts/features/bonus/bpd/screens/details/periods/BpdClosedPeriod.tsx b/ts/features/bonus/bpd/screens/details/periods/BpdClosedPeriod.tsx deleted file mode 100644 index 1dddc2e798e..00000000000 --- a/ts/features/bonus/bpd/screens/details/periods/BpdClosedPeriod.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { View } from "react-native"; -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { - BpdPeriodWithInfo, - isGracePeriod -} from "../../../store/reducers/details/periods"; -import { bpdSelectedPeriodSelector } from "../../../store/reducers/details/selectedPeriod"; -import IbanInformationComponent from "../components/iban/IbanInformationComponent"; -import BpdSummaryComponent from "../components/summary/BpdSummaryComponent"; - -export type Props = ReturnType & - ReturnType; - -const shouldRenderIbanComponent = (period: BpdPeriodWithInfo) => - isGracePeriod(period) || - (period.status === "Closed" && - period.amount.transactionNumber >= period.minTransactionNumber); - -/** - * Render the details for a completed and closed cashback periods - * @constructor - */ -const BpdClosedPeriod = (props: Props): React.ReactElement => ( - - - - - {props.currentPeriod && shouldRenderIbanComponent(props.currentPeriod) && ( - <> - - - - )} - -); - -const mapDispatchToProps = (_: Dispatch) => ({}); - -const mapStateToProps = (state: GlobalState) => ({ - currentPeriod: bpdSelectedPeriodSelector(state) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(BpdClosedPeriod); diff --git a/ts/features/bonus/bpd/screens/details/periods/BpdInactivePeriod.tsx b/ts/features/bonus/bpd/screens/details/periods/BpdInactivePeriod.tsx deleted file mode 100644 index cd4a9300d54..00000000000 --- a/ts/features/bonus/bpd/screens/details/periods/BpdInactivePeriod.tsx +++ /dev/null @@ -1,38 +0,0 @@ -import { View } from "react-native"; -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import IbanInformationComponent from "../components/iban/IbanInformationComponent"; -import BpdSummaryComponent from "../components/summary/BpdSummaryComponent"; -import UnsubscribeToBpd from "../components/unsubscribe/UnsubscribeToBpd"; -import WalletPaymentMethodBpdList from "../components/paymentMethod/WalletPaymentMethodBpdList"; - -export type Props = ReturnType & - ReturnType; - -/** - * Render the details for a future cashback period - * @constructor - */ -const BpdInactivePeriod: React.FunctionComponent = () => ( - - - - - - - - - - - -); - -const mapDispatchToProps = (_: Dispatch) => ({}); - -const mapStateToProps = (_: GlobalState) => ({}); - -export default connect(mapStateToProps, mapDispatchToProps)(BpdInactivePeriod); diff --git a/ts/features/bonus/bpd/screens/details/periods/BpdPeriodDetail.tsx b/ts/features/bonus/bpd/screens/details/periods/BpdPeriodDetail.tsx deleted file mode 100644 index 4ab0821aeb7..00000000000 --- a/ts/features/bonus/bpd/screens/details/periods/BpdPeriodDetail.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { View } from "react-native"; -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { BpdPeriodWithInfo } from "../../../store/reducers/details/periods"; -import { bpdSelectedPeriodSelector } from "../../../store/reducers/details/selectedPeriod"; -import BpdActivePeriod from "./BpdActivePeriod"; -import BpdClosedPeriod from "./BpdClosedPeriod"; -import BpdInactivePeriod from "./BpdInactivePeriod"; - -export type Props = ReturnType & - ReturnType; - -const selectPeriodScreen = (period: BpdPeriodWithInfo) => { - switch (period.status) { - case "Active": - return ; - case "Closed": - return ; - case "Inactive": - return ; - } -}; - -/** - * The body and details for a specific cashback period. Will change if is Active, Inactive or Closed - * @constructor - */ -const BpdPeriodDetail: React.FunctionComponent = props => ( - - {props.selectedPeriod && selectPeriodScreen(props.selectedPeriod)} - -); - -const mapDispatchToProps = (_: Dispatch) => ({}); - -const mapStateToProps = (state: GlobalState) => ({ - selectedPeriod: bpdSelectedPeriodSelector(state) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(BpdPeriodDetail); diff --git a/ts/features/bonus/bpd/screens/details/transaction/BpdAvailableTransactionsScreen.tsx b/ts/features/bonus/bpd/screens/details/transaction/BpdAvailableTransactionsScreen.tsx deleted file mode 100644 index e7fb721c6f1..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/BpdAvailableTransactionsScreen.tsx +++ /dev/null @@ -1,339 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { compareDesc } from "date-fns"; -import * as AR from "fp-ts/lib/Array"; -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import * as React from "react"; -import { - View, - SafeAreaView, - ScrollView, - SectionList, - SectionListData, - SectionListRenderItem -} from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { InfoBox } from "../../../../../../components/box/InfoBox"; -import { H1 } from "../../../../../../components/core/typography/H1"; -import { H4 } from "../../../../../../components/core/typography/H4"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import BaseScreenComponent from "../../../../../../components/screens/BaseScreenComponent"; -import I18n from "../../../../../../i18n"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { emptyContextualHelp } from "../../../../../../utils/emptyContextualHelp"; -import { localeDateFormat } from "../../../../../../utils/locale"; -import BaseDailyTransactionHeader from "../../../components/BaseDailyTransactionHeader"; -import BpdTransactionSummaryComponent from "../../../components/BpdTransactionSummaryComponent"; -import { - BpdTransactionItem, - EnhancedBpdTransaction -} from "../../../components/transactionItem/BpdTransactionItem"; -import { - atLeastOnePaymentMethodHasBpdEnabledSelector, - bpdDisplayTransactionsSelector, - paymentMethodsWithActivationStatusSelector -} from "../../../store/reducers/details/combiner"; -import { bpdSelectedPeriodSelector } from "../../../store/reducers/details/selectedPeriod"; -import BpdCashbackMilestoneComponent from "./BpdCashbackMilestoneComponent"; -import BpdEmptyTransactionsList from "./BpdEmptyTransactionsList"; -import { BpdTransactionDetailRepresentation } from "./detail/BpdTransactionDetailComponent"; - -export type Props = ReturnType & - ReturnType; - -type TotalCashbackPerDate = { - trxDate: Date; - totalCashBack: number; -}; - -const dataForFlatList = ( - transactions: pot.Pot, Error> -) => pot.getOrElse(transactions, []); - -export const isTotalCashback = (item: any): item is TotalCashbackPerDate => - item.totalCashBack !== undefined; - -/** - * Builds the array of objects needed to show the sectionsList grouped by transaction day. - * - * We check the subtotal of TotalCashback earned on each transaction to check when the user reaches the cashback. - * - * When creating the final array if we reached the cashback amount we set all the following transaction cashback value to 0 - * - * If the sum of cashback comes over the award we remove the exceeding part on the transaction. - * @param transactions - * @param cashbackAward - */ -const getTransactionsByDaySections = ( - transactions: ReadonlyArray, - cashbackAward: number -): ReadonlyArray< - SectionListData -> => { - const dates = [ - ...new Set( - transactions.map(trx => - localeDateFormat(trx.trxDate, I18n.t("global.dateFormats.dayFullMonth")) - ) - ) - ]; - - const transactionsAsc = AR.reverse([...transactions]); - - // accumulator to define when the user reached the cashback award amount - // and tracing the sum of all the cashback value to check if any negative trx may cause a revoke of cashback award - const amountWinnerAccumulator = transactionsAsc.reduce( - ( - acc: { - winner?: { - amount: number; - index: number; - date: Date; - }; - sumAmount: number; - }, - t: EnhancedBpdTransaction, - currIndex: number - ) => { - const sum = acc.sumAmount + t.cashback; - if (sum >= cashbackAward && !acc.winner) { - return { - winner: { - amount: sum, - index: currIndex, - date: new Date(t.trxDate) - }, - sumAmount: sum - }; - } else if (sum < cashbackAward) { - return { - sumAmount: sum - }; - } - return { - ...acc, - sumAmount: sum - }; - }, - { - sumAmount: 0 - } - ); - - const maybeWinner = O.fromNullable(amountWinnerAccumulator.winner); - - // If the user reached the cashback amount within transactions we actualize all the cashback value starting from the index of winning transaction - // if the winning transaction makes cashback value exceed the limit we set the amount to the difference of transaction cashback value, total amout at winnign transaction and cashback award limit. - // all the following transactions will be set to 0 cashback value, since the limit has been reached (a dedicated item will be displayed) - const updatedTransactions = [...transactionsAsc].map( - (t, i): BpdTransactionDetailRepresentation => { - if (O.isSome(maybeWinner)) { - if ( - i === maybeWinner.value.index && - maybeWinner.value.amount > cashbackAward - ) { - return { - ...t, - cashback: t.cashback - (maybeWinner.value.amount - cashbackAward), - validForCashback: true - }; - } else if (i > maybeWinner.value.index) { - return { - ...t, - cashback: 0, - validForCashback: false - }; - } - } - return { ...t, validForCashback: true }; - } - ); - - return dates.map(d => ({ - title: d, - data: [ - ...updatedTransactions.filter( - t => - localeDateFormat( - t.trxDate, - I18n.t("global.dateFormats.dayFullMonth") - ) === d - ), - // we add the the data array an item to display the milestone reached - // in order to display the milestone after the latest transaction summed in the total we add 1 ms so that the ordering will set it correctly - ...pipe( - maybeWinner, - O.fold( - () => [], - w => { - if ( - localeDateFormat( - w.date, - I18n.t("global.dateFormats.dayFullMonth") - ) === d - ) { - return [ - { - totalCashBack: w.amount, - trxDate: new Date( - w.date.setMilliseconds(w.date.getMilliseconds() + 1) - ) - } - ]; - } - return []; - } - ) - ) - ].sort((trx1, trx2) => compareDesc(trx1.trxDate, trx2.trxDate)) - })); -}; - -const renderSectionHeader = (info: { - section: SectionListData< - BpdTransactionDetailRepresentation | TotalCashbackPerDate - >; -}): React.ReactNode => ( - !isTotalCashback(i)).length - } - /> -); - -export const NoPaymentMethodAreActiveWarning = () => ( - - -

- {I18n.t("bonus.bpd.details.transaction.noPaymentMethod.text1")} -

- {I18n.t("bonus.bpd.details.transaction.noPaymentMethod.text2")} -

- {I18n.t("bonus.bpd.details.transaction.noPaymentMethod.text3")} - -
- -
-); - -/** - * Display all the transactions for a specific period - * TODO: scroll to refresh, display error, display loading - * @constructor - */ -const BpdAvailableTransactionsScreen: React.FunctionComponent< - Props -> = props => { - const transactions = dataForFlatList(props.transactionForSelectedPeriod); - - const trxSortByDate = [...transactions].sort((trx1, trx2) => - compareDesc(trx1.trxDate, trx2.trxDate) - ); - - const maybeLastUpdateDate = pipe( - [...trxSortByDate].map(t => t.trxDate), - AR.lookup(0) - ); - - const renderTransactionItem: SectionListRenderItem< - BpdTransactionDetailRepresentation | TotalCashbackPerDate - > = info => { - if (isTotalCashback(info.item)) { - return ( - 0, - p => p.maxPeriodCashback - ) - )} - /> - ); - } - return ; - }; - - return ( - - - - -

{I18n.t("bonus.bpd.details.transaction.title")}

-
- - - - {props.selectedPeriod && O.isSome(maybeLastUpdateDate) && ( - <> - - - - )} - - {props.selectedPeriod && - (transactions.length > 0 ? ( - - isTotalCashback(t) - ? `awarded_cashback_item${t.totalCashBack}` - : t.keyId - } - /> - ) : !props.atLeastOnePaymentMethodActive && - pot.isSome(props.potWallets) && - props.potWallets.value.length > 0 ? ( - - - - ) : ( - - - - ))} - -
-
- ); -}; - -const mapDispatchToProps = (_: Dispatch) => ({}); - -const mapStateToProps = (state: GlobalState) => ({ - transactionForSelectedPeriod: bpdDisplayTransactionsSelector(state), - selectedPeriod: bpdSelectedPeriodSelector(state), - potWallets: paymentMethodsWithActivationStatusSelector(state), - atLeastOnePaymentMethodActive: - atLeastOnePaymentMethodHasBpdEnabledSelector(state) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(BpdAvailableTransactionsScreen); diff --git a/ts/features/bonus/bpd/screens/details/transaction/BpdCashbackMilestoneComponent.tsx b/ts/features/bonus/bpd/screens/details/transaction/BpdCashbackMilestoneComponent.tsx deleted file mode 100644 index 0c1986c28ed..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/BpdCashbackMilestoneComponent.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import * as React from "react"; -import { View, Image, StyleSheet } from "react-native"; -import { IOColors } from "@pagopa/io-app-design-system"; -import fireworksIcon from "../../../../../../../img/bonus/bpd/fireworks.png"; -import { formatNumberAmount } from "../../../../../../utils/stringBuilder"; -import { H4 } from "../../../../../../components/core/typography/H4"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import I18n from "../../../../../../i18n"; - -type Props = { - cashbackValue: number; -}; - -const styles = StyleSheet.create({ - container: { - flexDirection: "row", - alignItems: "center", - justifyContent: "space-between", - backgroundColor: IOColors.blue, - paddingVertical: 16 - }, - image: { width: 32, height: 32, resizeMode: "cover" }, - textPadding: { - paddingHorizontal: 16 - } -}); -const BpdCashbackMilestoneComponent: React.FunctionComponent = ( - props: Props -) => ( - - - -

- {I18n.t( - "bonus.bpd.details.transaction.detail.summary.milestone.title", - { cashbackValue: formatNumberAmount(props.cashbackValue, true) } - )} -

-

- {I18n.t("bonus.bpd.details.transaction.detail.summary.milestone.body")} -

-
-
-); - -export default BpdCashbackMilestoneComponent; diff --git a/ts/features/bonus/bpd/screens/details/transaction/BpdEmptyTransactionsList.tsx b/ts/features/bonus/bpd/screens/details/transaction/BpdEmptyTransactionsList.tsx deleted file mode 100644 index b0d44b9bcc8..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/BpdEmptyTransactionsList.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import { View } from "react-native"; -import * as React from "react"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { InfoBox } from "../../../../../../components/box/InfoBox"; -import { Body } from "../../../../../../components/core/typography/Body"; -import { H4 } from "../../../../../../components/core/typography/H4"; -import I18n from "../../../../../../i18n"; -import { BottomSheetBpdTransactionsBody } from "../../../components/BpdTransactionSummaryComponent"; - -const BpdEmptyTransactionsList: React.FunctionComponent = () => ( - <> - -

{I18n.t("bonus.bpd.details.transaction.detail.empty.text1")}

- {I18n.t("bonus.bpd.details.transaction.detail.empty.text2")} -
- - - - -

- {I18n.t( - "bonus.bpd.details.transaction.detail.summary.calendarBlock.text1" - )} -

- {" "} - {I18n.t( - "bonus.bpd.details.transaction.detail.summary.calendarBlock.text2" - )} -

- {I18n.t( - "bonus.bpd.details.transaction.detail.summary.calendarBlock.text3" - )} - -
- - - -); - -export default BpdEmptyTransactionsList; diff --git a/ts/features/bonus/bpd/screens/details/transaction/BpdTransactionsScreen.tsx b/ts/features/bonus/bpd/screens/details/transaction/BpdTransactionsScreen.tsx deleted file mode 100644 index c037245eac2..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/BpdTransactionsScreen.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { EnhancedBpdTransaction } from "../../../components/transactionItem/BpdTransactionItem"; -import { bpdAllData } from "../../../store/actions/details"; -import { bpdDisplayTransactionsSelector } from "../../../store/reducers/details/combiner"; -import { bpdLastUpdateSelector } from "../../../store/reducers/details/lastUpdate"; -import BpdAvailableTransactionsScreen from "./BpdAvailableTransactionsScreen"; -import LoadTransactions from "./LoadTransactions"; -import TransactionsUnavailable from "./TransactionsUnavailable"; - -export type Props = ReturnType & - ReturnType; - -/** - * Associate at every state of the pot transactions status the right screen to show - * @param transactions - */ -const handleTransactionsStatus = ( - transactions: pot.Pot, Error> -) => - pot.fold( - transactions, - () => , - () => , - _ => , - _ => , - _ => , - _ => , - _ => , - _ => - ); - -/** - * Display all the transactions for a specific period if available, in other case show a loading or an error screen. - * First check the whole bpd status than if is some check the transactions status. - * TODO: Delete, replaced by BpdTransactionsRouterScreen - * @deprecated - * @constructor - */ -const BpdTransactionsScreen: React.FC = (props: Props) => { - const { bpdLastUpdate, transactionForSelectedPeriod, loadTransactions } = - props; - React.useEffect(() => { - if ( - pot.isError(bpdLastUpdate) || - pot.isError(transactionForSelectedPeriod) - ) { - loadTransactions(); - } - }, [bpdLastUpdate, transactionForSelectedPeriod, loadTransactions]); - return pot.fold( - props.bpdLastUpdate, - () => , - () => , - _ => , - _ => , - _ => handleTransactionsStatus(props.transactionForSelectedPeriod), - _ => , - _ => , - _ => - ); -}; -const mapDispatchToProps = (dispatch: Dispatch) => ({ - loadTransactions: () => dispatch(bpdAllData.request()) -}); - -const mapStateToProps = (state: GlobalState) => ({ - transactionForSelectedPeriod: bpdDisplayTransactionsSelector(state), - bpdLastUpdate: bpdLastUpdateSelector(state) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(BpdTransactionsScreen); diff --git a/ts/features/bonus/bpd/screens/details/transaction/GoToTransactions.tsx b/ts/features/bonus/bpd/screens/details/transaction/GoToTransactions.tsx deleted file mode 100644 index 3eb39bf2c62..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/GoToTransactions.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import * as React from "react"; -import { getBottomSpace, isIphoneX } from "react-native-iphone-x-helper"; -import DeviceInfo from "react-native-device-info"; -import { Icon, HSpacer } from "@pagopa/io-app-design-system"; -import ButtonDefaultOpacity from "../../../../../../components/ButtonDefaultOpacity"; -import { Label } from "../../../../../../components/core/typography/Label"; -import I18n from "../../../../../../i18n"; - -type Props = { goToTransactions: () => void }; - -/** - * Display the transactions button when: - * - Period is closed and transactions number is > 0 - * - Period is active - * never displays for inactive/incoming period - * @param props - * @constructor - */ -const GoToTransactions: React.FunctionComponent = props => ( - - - - - -); - -export default GoToTransactions; diff --git a/ts/features/bonus/bpd/screens/details/transaction/LoadTransactions.tsx b/ts/features/bonus/bpd/screens/details/transaction/LoadTransactions.tsx deleted file mode 100644 index 1fbadfad0a6..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/LoadTransactions.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import * as React from "react"; -import { View, ActivityIndicator } from "react-native"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import { InfoScreenComponent } from "../../../../../../components/infoScreen/InfoScreenComponent"; -import I18n from "../../../../../../i18n"; - -/** - * This screen is displayed when loading the list of transactions - * @constructor - */ -const LoadTransactions: React.FunctionComponent = () => ( - - - } - title={I18n.t("bonus.bpd.details.transaction.loading")} - /> - -); - -export default LoadTransactions; diff --git a/ts/features/bonus/bpd/screens/details/transaction/TransactionsUnavailable.tsx b/ts/features/bonus/bpd/screens/details/transaction/TransactionsUnavailable.tsx deleted file mode 100644 index 6b4a36ee800..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/TransactionsUnavailable.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import * as React from "react"; -import { SafeAreaView } from "react-native"; -import image from "../../../../../../../img/wallet/errors/payment-unavailable-icon.png"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import { renderInfoRasterImage } from "../../../../../../components/infoScreen/imageRendering"; -import { InfoScreenComponent } from "../../../../../../components/infoScreen/InfoScreenComponent"; -import BaseScreenComponent from "../../../../../../components/screens/BaseScreenComponent"; -import I18n from "../../../../../../i18n"; - -export type Props = Pick< - React.ComponentProps, - "contextualHelp" ->; - -const loadLocales = () => ({ - headerTitle: I18n.t("bonus.bpd.details.transaction.goToButton"), - title: I18n.t("bonus.bpd.details.transaction.error.title"), - body: I18n.t("bonus.bpd.details.transaction.error.body") -}); - -/** - * This screen informs the user that there are problems retrieving the transactions list. - * @deprecated - * @constructor - */ -const TransactionsUnavailable: React.FunctionComponent = props => { - const { headerTitle, title, body } = loadLocales(); - - return ( - - - - - - ); -}; - -export default TransactionsUnavailable; diff --git a/ts/features/bonus/bpd/screens/details/transaction/detail/BpdTransactionDetailComponent.tsx b/ts/features/bonus/bpd/screens/details/transaction/detail/BpdTransactionDetailComponent.tsx deleted file mode 100644 index 36da07696bc..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/detail/BpdTransactionDetailComponent.tsx +++ /dev/null @@ -1,183 +0,0 @@ -import * as React from "react"; -import { View, Image, StyleSheet } from "react-native"; -import { HSpacer, VSpacer } from "@pagopa/io-app-design-system"; -import CopyButtonComponent from "../../../../../../../components/CopyButtonComponent"; -import { IOBadge } from "../../../../../../../components/core/IOBadge"; -import { Body } from "../../../../../../../components/core/typography/Body"; -import { H4 } from "../../../../../../../components/core/typography/H4"; -import { H5 } from "../../../../../../../components/core/typography/H5"; -import { Monospace } from "../../../../../../../components/core/typography/Monospace"; -import { IOStyles } from "../../../../../../../components/core/variables/IOStyles"; -import I18n from "../../../../../../../i18n"; -import { localeDateFormat } from "../../../../../../../utils/locale"; -import { formatNumberAmount } from "../../../../../../../utils/stringBuilder"; -import { EnhancedBpdTransaction } from "../../../../components/transactionItem/BpdTransactionItem"; -import { BpdTransactionWarning } from "./BpdTransactionWarning"; - -/** - * @deprecated - */ -export type BpdTransactionDetailRepresentation = EnhancedBpdTransaction & { - // false if the transaction is not valid for the cashback (eg: the user has - // already reached the maximum cashback value for the period ) - validForCashback: boolean; -}; - -export type BpdTransactionDetailRepresentationV2 = - BpdTransactionDetailRepresentation & { - isPivot: boolean; - }; - -type Props = { transaction: BpdTransactionDetailRepresentation }; - -const styles = StyleSheet.create({ - image: { width: 48, height: 30 }, - rowId: { - flexDirection: "row", - justifyContent: "space-between" - }, - copyText: { - flex: 1, - paddingRight: 16 - } -}); - -const loadLocales = () => ({ - paymentMethod: I18n.t("wallet.paymentMethod"), - acquirerId: I18n.t("bonus.bpd.details.transaction.detail.acquirerId"), - issuerId: I18n.t("bonus.bpd.details.transaction.detail.issuerId") -}); - -type IdBlockProps = { - title: string; - value: string; -}; - -const CancelBadge = () => ( - -); - -const Table = (props: Props) => { - const isMidNight = - props.transaction.trxDate.getHours() + - props.transaction.trxDate.getMinutes() + - props.transaction.trxDate.getSeconds() === - 0; - return ( - - -
- {isMidNight - ? I18n.t("payment.details.info.onlyDate") - : I18n.t("payment.details.info.dateAndTime")} -
-

- {isMidNight - ? localeDateFormat( - props.transaction.trxDate, - I18n.t( - "global.dateFormats.fullFormatShortMonthLiteralWithoutTime" - ) - ) - : localeDateFormat( - props.transaction.trxDate, - I18n.t("global.dateFormats.fullFormatShortMonthLiteralWithTime") - )} -

-
- - -
- {I18n.t("bonus.bpd.details.transaction.detail.paymentCircuit")} -
-

- {props.transaction.circuitType === "Unknown" - ? I18n.t("global.unknown") - : props.transaction.circuitType} -

-
- - -
- {I18n.t("bonus.bpd.details.transaction.detail.transactionAmount")} -
- - {props.transaction.amount < 0 && ( - <> - - - - )} -

- {formatNumberAmount(props.transaction.amount, true)} -

-
-
- - -
- {I18n.t("bonus.bpd.details.transaction.detail.cashbackAmount")} -
-

- {formatNumberAmount(props.transaction.cashback, true)} -

-
-
- ); -}; - -const IdBlock = (props: IdBlockProps) => ( - -
{props.title}
- - - {props.value} - - - -
-); - -/** - * This screen shows the details about a single transaction. - * https://pagopa.invisionapp.com/console/IO---Cashback---Dettaglio-e-transazioni-ckg6bcpcr0ryb016nas1h4nbf/ckg6cwqfc03uf012khxbr6mun/play - * @param props - * @constructor - */ -export const BpdTransactionDetailComponent: React.FunctionComponent< - Props -> = props => { - const { paymentMethod, acquirerId, issuerId } = loadLocales(); - - return ( - - - {paymentMethod} - - - - -

{props.transaction.title}

-
- - {/* using the keyvaluetable with custom style in order to be quick */} - - - - - - - - ); -}; diff --git a/ts/features/bonus/bpd/screens/details/transaction/detail/BpdTransactionWarning.tsx b/ts/features/bonus/bpd/screens/details/transaction/detail/BpdTransactionWarning.tsx deleted file mode 100644 index f966dafe117..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/detail/BpdTransactionWarning.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import * as React from "react"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { InfoBox } from "../../../../../../../components/box/InfoBox"; -import { Body } from "../../../../../../../components/core/typography/Body"; -import I18n from "../../../../../../../i18n"; -import { BpdTransactionDetailRepresentation } from "./BpdTransactionDetailComponent"; - -type Props = { transaction: BpdTransactionDetailRepresentation }; - -const TransactionWarning = (props: { text: string }) => ( - <> - - - {props.text} - - -); - -/** - * Displays a warning when certain conditions occur - * @param props - * @constructor - */ -export const BpdTransactionWarning: React.FunctionComponent = props => { - // transaction not valid for cashback (eg: the user has already reached - // the maximum cashback value for the period - if (!props.transaction.validForCashback) { - return ( - - ); - } - // max cashback for a single transaction reached - if ( - props.transaction.maxCashbackForTransactionAmount === - props.transaction.cashback - ) { - return ( - - ); - } - // transaction canceled (negative amount) - if (props.transaction.cashback < 0) { - return ( - - ); - } - return null; -}; diff --git a/ts/features/bonus/bpd/screens/details/transaction/detail/__test__/BdpTransactionDetailComponent.test.tsx b/ts/features/bonus/bpd/screens/details/transaction/detail/__test__/BdpTransactionDetailComponent.test.tsx deleted file mode 100644 index 2b26abeebc5..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/detail/__test__/BdpTransactionDetailComponent.test.tsx +++ /dev/null @@ -1,263 +0,0 @@ -// import { debug } from "console"; -import { render } from "@testing-library/react-native"; -import * as React from "react"; -import I18n from "../../../../../../../../i18n"; -import { HPan } from "../../../../../store/actions/paymentMethods"; -import { AwardPeriodId } from "../../../../../store/actions/periods"; -import { - BpdTransactionDetailComponent, - BpdTransactionDetailRepresentation -} from "../BpdTransactionDetailComponent"; - -const baseTransaction: BpdTransactionDetailRepresentation = { - hashPan: - "0d4194712c5d820fcbbb2e7ba199e15f73cfd43f8fe49f0aa62e7901253506df" as HPan, - idTrxAcquirer: "10126487773E", - idTrxIssuer: "R64692", - amount: 87.79, - awardPeriodId: 2 as AwardPeriodId, - image: 29, - maxCashbackForTransactionAmount: 15, - title: "xxxxxxx", - trxDate: new Date("2100-12-17T00:00"), - keyId: "xxxxxxxxx", - cashback: 8.779, - circuitType: "Mastercard / Maestro", - validForCashback: true -}; - -describe("Test Transaction Timestamp", () => { - it("It should render label 'Date' and a value without hours and minutes when the transaction has a timestamp 00:00", () => { - const myTransaction: BpdTransactionDetailRepresentation = { - hashPan: - "0d4194712c5d820fcbbb2e7ba199e15f73cfd43f8fe49f0aa62e7901253506df" as HPan, - idTrxAcquirer: "10126487773E", - idTrxIssuer: "R64692", - amount: 87.79, - awardPeriodId: 2 as AwardPeriodId, - image: 29, - maxCashbackForTransactionAmount: 15, - title: "xxxxxxx", - trxDate: new Date("2100-12-17T00:00"), - keyId: "xxxxxxxxx", - cashback: 8.779, - circuitType: "Mastercard / Maestro", - validForCashback: true - }; - - // cut : Component Under Test - const cut = render( - - ); - - const dateLabel = cut.getByTestId("dateLabel"); - const dateValue = cut.getByTestId("dateValue"); - - expect(dateLabel.children[0]).toMatch(/Date/); - expect(dateValue.children[0]).toMatch(/17 Dec 2100/); - }); - - it("It should render label 'Date and time' and a value with hours and minutes when the transaction has a timestamp different from 00:00 ", () => { - const myTransaction: BpdTransactionDetailRepresentation = { - hashPan: - "0d4194712c5d820fcbbb2e7ba199e15f73cfd43f8fe49f0aa62e7901253506df" as HPan, - idTrxAcquirer: "10126487773E", - idTrxIssuer: "R64692", - amount: 87.79, - awardPeriodId: 2 as AwardPeriodId, - image: 29, - maxCashbackForTransactionAmount: 0, - title: "xxxxxxx", - trxDate: new Date("2100-12-17T01:00"), - keyId: "xxxxxxxxx", - cashback: 8.779, - circuitType: "Mastercard / Maestro", - validForCashback: true - }; - - // cut : Component Under Test - const cut = render( - - ); - const dateLabel = cut.getByTestId("dateLabel"); - const dateValue = cut.getByTestId("dateValue"); - - expect(dateLabel.children[0]).toMatch(/Date and time/); - expect(dateValue.children[0]).toMatch(/17 Dec 2100, 01:00/); - }); -}); - -describe("Test BpdTransactionDetailComponent warnings", () => { - describe("It should render warning 'max cashback for transaction' when maxCashbackForTransactionAmount === cashback", () => { - const maxAmount = 15.01; - const testCases: ReadonlyArray = [ - { - ...baseTransaction, - cashback: maxAmount, - maxCashbackForTransactionAmount: maxAmount, - validForCashback: true - } - ]; - - test.each(testCases)("Test case: %p", transaction => { - const component = render( - - ); - - const warningTest = component.queryByText( - I18n.t("bonus.bpd.details.transaction.detail.maxCashbackWarning", { - amount: maxAmount - }) - ); - expect(warningTest).not.toBeNull(); - }); - }); - - describe("It shouldn't render warning 'max cashback for transaction' when maxCashbackForTransactionAmount !== cashback", () => { - const maxAmount = 15.01; - const testCases: ReadonlyArray = [ - { - ...baseTransaction, - cashback: maxAmount, - maxCashbackForTransactionAmount: maxAmount - 0.01, - validForCashback: true - }, - { - ...baseTransaction, - cashback: maxAmount, - maxCashbackForTransactionAmount: 0, - validForCashback: false - }, - { - ...baseTransaction, - cashback: maxAmount, - maxCashbackForTransactionAmount: maxAmount, - validForCashback: false - } - ]; - - test.each(testCases)("Test case: %p", transaction => { - const component = render( - - ); - - const warningTest = component.queryByText( - I18n.t("bonus.bpd.details.transaction.detail.maxCashbackWarning", { - amount: maxAmount - }) - ); - expect(warningTest).toBeNull(); - }); - }); - - describe("It should render warning 'canceledOperationWarning' when cashback value < 0", () => { - const testCases: ReadonlyArray = [ - { - ...baseTransaction, - cashback: -0.01, - validForCashback: true - } - ]; - - test.each(testCases)("Test case: %p", transaction => { - const component = render( - - ); - - const warningTest = component.queryByText( - I18n.t("bonus.bpd.details.transaction.detail.canceledOperationWarning") - ); - expect(warningTest).not.toBeNull(); - }); - }); - describe("It shouldn't render warning 'canceledOperationWarning' when cashback value >= 0", () => { - const testCases: ReadonlyArray = [ - { - ...baseTransaction, - cashback: 0, - validForCashback: true - }, - { - ...baseTransaction, - cashback: 5, - validForCashback: false - }, - { - ...baseTransaction, - cashback: -5, - validForCashback: false - } - ]; - - test.each(testCases)("Test case: %p", transaction => { - const component = render( - - ); - - const warningTest = component.queryByText( - I18n.t("bonus.bpd.details.transaction.detail.canceledOperationWarning") - ); - expect(warningTest).toBeNull(); - }); - }); - describe("It should render warning 'maxCashbackForPeriodWarning' when validForCashback === false", () => { - const testCases: ReadonlyArray = [ - { - ...baseTransaction, - cashback: 0, - validForCashback: false - }, - { - ...baseTransaction, - cashback: 10.5, - validForCashback: false - }, - { - ...baseTransaction, - cashback: -10.5, - validForCashback: false - }, - { - ...baseTransaction, - cashback: 15, - maxCashbackForTransactionAmount: 15, - validForCashback: false - } - ]; - - test.each(testCases)("Test case: %p", transaction => { - const component = render( - - ); - - const warningTest = component.queryByText( - I18n.t( - "bonus.bpd.details.transaction.detail.maxCashbackForPeriodWarning" - ) - ); - expect(warningTest).not.toBeNull(); - }); - }); - describe("It shouldn't render warning 'maxCashbackForPeriodWarning'", () => { - const testCases: ReadonlyArray = [ - { - ...baseTransaction, - cashback: 0, - validForCashback: true - } - ]; - - test.each(testCases)("Test case: %p", transaction => { - const component = render( - - ); - - const warningTest = component.queryByText( - I18n.t( - "bonus.bpd.details.transaction.detail.maxCashbackForPeriodWarning" - ) - ); - expect(warningTest).toBeNull(); - }); - }); -}); diff --git a/ts/features/bonus/bpd/screens/details/transaction/v2/BpdAvailableTransactionsScreenV2.tsx b/ts/features/bonus/bpd/screens/details/transaction/v2/BpdAvailableTransactionsScreenV2.tsx deleted file mode 100644 index e0f64466103..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/v2/BpdAvailableTransactionsScreenV2.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { View, SafeAreaView } from "react-native"; -import * as React from "react"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { H1 } from "../../../../../../../components/core/typography/H1"; -import { IOStyles } from "../../../../../../../components/core/variables/IOStyles"; -import BaseScreenComponent from "../../../../../../../components/screens/BaseScreenComponent"; -import I18n from "../../../../../../../i18n"; -import { emptyContextualHelp } from "../../../../../../../utils/emptyContextualHelp"; -import TransactionsSectionList from "./TransactionsSectionList"; - -export const BpdAvailableTransactionsScreenV2 = (): React.ReactElement => ( - - - - -

{I18n.t("bonus.bpd.details.transaction.title")}

-
- -
-
-); diff --git a/ts/features/bonus/bpd/screens/details/transaction/v2/BpdTransactionsRouterScreen.tsx b/ts/features/bonus/bpd/screens/details/transaction/v2/BpdTransactionsRouterScreen.tsx deleted file mode 100644 index 3ca6f6db0c3..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/v2/BpdTransactionsRouterScreen.tsx +++ /dev/null @@ -1,81 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as React from "react"; -import { useEffect, useState } from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import WorkunitGenericFailure from "../../../../../../../components/error/WorkunitGenericFailure"; -import { mixpanelTrack } from "../../../../../../../mixpanel"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { AwardPeriodId } from "../../../../store/actions/periods"; -import { bpdTransactionsLoadRequiredData } from "../../../../store/actions/transactions"; -import { bpdSelectedPeriodSelector } from "../../../../store/reducers/details/selectedPeriod"; -import { bpdTransactionsRequiredDataLoadStateSelector } from "../../../../store/reducers/details/transactionsv2/ui"; -import LoadTransactions from "../LoadTransactions"; -import { BpdAvailableTransactionsScreenV2 } from "./BpdAvailableTransactionsScreenV2"; -import TransactionsUnavailableV2 from "./TransactionsUnavailableV2"; - -type Props = ReturnType & - ReturnType; - -/** - * V2 version of the screen, makes sure that all essential data for viewing transactions is loaded - * @param props - * @constructor - */ -const BpdTransactionsRouterScreen = ( - props: Props -): React.ReactElement | null => { - const [firstLoadingRequest, setFirstLoadingRequest] = useState(false); - const [unexpectedError, setUnexpectedError] = useState(false); - - const { selectedPeriod, loadRequiredData } = props; - - // Refresh the transactions required data when the screen is open - useEffect(() => { - if (selectedPeriod) { - setFirstLoadingRequest(true); - loadRequiredData(selectedPeriod.awardPeriodId); - } else { - // This should never happens - setUnexpectedError(true); - } - }, [selectedPeriod, loadRequiredData]); - - // Handling unexpected error - if (unexpectedError) { - void mixpanelTrack("BPD_TRANSACTIONS_SCREEN_UNEXPECTED_ERROR"); - return ; - } - - // We want to be sure that before rendering the pot state, a first load request has been sent - if (!firstLoadingRequest) { - return ; - } - - return pot.fold( - props.transactionsRequiredData, - () => , - () => , - _ => , - _ => , - _ => , - _ => , - (_, __) => , - _ => - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - loadRequiredData: (periodId: AwardPeriodId) => - dispatch(bpdTransactionsLoadRequiredData.request(periodId)) -}); - -const mapStateToProps = (state: GlobalState) => ({ - transactionsRequiredData: bpdTransactionsRequiredDataLoadStateSelector(state), - selectedPeriod: bpdSelectedPeriodSelector(state) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(BpdTransactionsRouterScreen); diff --git a/ts/features/bonus/bpd/screens/details/transaction/v2/TransactionsSectionList.tsx b/ts/features/bonus/bpd/screens/details/transaction/v2/TransactionsSectionList.tsx deleted file mode 100644 index 24e49a64579..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/v2/TransactionsSectionList.tsx +++ /dev/null @@ -1,251 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import * as React from "react"; -import { useEffect } from "react"; -import { - View, - ActivityIndicator, - SectionList, - SectionListData, - SectionListRenderItemInfo -} from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { IOStyles } from "../../../../../../../components/core/variables/IOStyles"; -import I18n from "../../../../../../../i18n"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { localeDateFormat } from "../../../../../../../utils/locale"; -import { showToast } from "../../../../../../../utils/showToast"; -import BaseDailyTransactionHeader from "../../../../components/BaseDailyTransactionHeader"; -import BpdTransactionSummaryComponent from "../../../../components/BpdTransactionSummaryComponent"; -import { BpdTransactionItem } from "../../../../components/transactionItem/BpdTransactionItem"; -import { AwardPeriodId } from "../../../../store/actions/periods"; -import { - BpdTransactionId, - bpdTransactionsLoadPage -} from "../../../../store/actions/transactions"; -import { - atLeastOnePaymentMethodHasBpdEnabledSelector, - paymentMethodsWithActivationStatusSelector -} from "../../../../store/reducers/details/combiner"; -import { bpdLastUpdateSelector } from "../../../../store/reducers/details/lastUpdate"; -import { bpdSelectedPeriodSelector } from "../../../../store/reducers/details/selectedPeriod"; -import { bpdDaysInfoByIdSelector } from "../../../../store/reducers/details/transactionsv2/daysInfo"; -import { - bpdTransactionByIdSelector, - bpdTransactionsGetNextCursor, - BpdTransactionsSectionItem, - bpdTransactionsSelector -} from "../../../../store/reducers/details/transactionsv2/ui"; -import { NoPaymentMethodAreActiveWarning } from "../BpdAvailableTransactionsScreen"; -import BpdCashbackMilestoneComponent from "../BpdCashbackMilestoneComponent"; -import BpdEmptyTransactionsList from "../BpdEmptyTransactionsList"; - -type Props = ReturnType & - ReturnType; - -type HeaderProps = Props & { - info: { - section: SectionListData; - }; -}; - -const RenderSectionHeaderBase = ( - // we need to pass props as argument because with the current react-redux version useSelect cannot be used - props: HeaderProps -) => - pipe( - props.bpdDaysInfoByIdSelector(props.info.section.dayInfoId), - O.fold( - () => null, - daysInfo => ( - - ) - ) - ); - -/** - * In order to optimize the rendering of the item, we use the dayInfo as unique identifier to avoid to redraw the component. - * The dayInfo data cannot change while consulting the list and we use this information to avoid a deep comparison - */ -export const RenderSectionHeader = React.memo( - RenderSectionHeaderBase, - (prev: HeaderProps, curr: HeaderProps) => - prev.info.section.dayInfoId === curr.info.section.dayInfoId -); - -type ItemProps = Props & { - trxId: SectionListRenderItemInfo; -}; - -const RenderItemBase = ( - // we need to pass props as argument because with the current react-redux version useSelect cannot be used - props: ItemProps -): React.ReactElement | null => - pipe( - props.bpdTransactionByIdSelector(props.trxId.item), - O.fold( - () => null, - trx => ( - <> - {trx.isPivot && ( - 0, - p => p.maxPeriodCashback - ) - )} - /> - )} - - - ) - ) - ); - -/** - * In order to optimize the rendering of the item, we use the keyId as unique identifier to avoid to redraw the component. - * The trx data cannot change while consulting the list and we use this information to avoid a deep comparison - */ -export const RenderItem = React.memo( - RenderItemBase, - (prev: ItemProps, curr: ItemProps) => prev.trxId.item === curr.trxId.item -); - -/** - * The header of the transactions list - * @param props - * @constructor - */ -const TransactionsHeader = ( - props: Pick -) => ( - - - {props.selectedPeriod && - pot.isSome(props.maybeLastUpdateDate) && - props.selectedPeriod.amount.transactionNumber > 0 && ( - <> - - - - )} - -); - -/** - * This component is rendered when no transactions are available - * @param props - * @constructor - */ -const TransactionsEmpty = ( - props: Pick -) => ( - - {!props.atLeastOnePaymentMethodActive && - pot.isSome(props.potWallets) && - props.potWallets.value.length > 0 ? ( - - ) : ( - - )} - -); - -/** - * Loading item, placed in the footer during the loading of the next page - * @constructor - */ -const FooterLoading = () => ( - <> - - - -); - -const TransactionsSectionList = (props: Props): React.ReactElement => { - const isError = pot.isError(props.potTransactions); - const isLoading = pot.isLoading(props.potTransactions); - const transactions = pot.getOrElse(props.potTransactions, []); - - useEffect(() => { - if (isError) { - showToast( - I18n.t("bonus.bpd.details.transaction.error.pageLoading"), - "danger" - ); - } - }, [isError]); - - return ( - ( - - )} - ListHeaderComponent={} - ListEmptyComponent={} - ListFooterComponent={isLoading && } - onEndReached={() => { - if (props.selectedPeriod && props.nextCursor && !isLoading) { - props.loadNextPage( - props.selectedPeriod.awardPeriodId, - props.nextCursor - ); - } - }} - scrollEnabled={true} - stickySectionHeadersEnabled={true} - sections={transactions} - renderItem={ri => } - keyExtractor={t => t} - /> - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - loadNextPage: (awardPeriodId: AwardPeriodId, nextCursor: number) => - dispatch(bpdTransactionsLoadPage.request({ awardPeriodId, nextCursor })) -}); - -const mapStateToProps = (state: GlobalState) => ({ - selectedPeriod: bpdSelectedPeriodSelector(state), - maybeLastUpdateDate: bpdLastUpdateSelector(state), - potWallets: paymentMethodsWithActivationStatusSelector(state), - atLeastOnePaymentMethodActive: - atLeastOnePaymentMethodHasBpdEnabledSelector(state), - potTransactions: bpdTransactionsSelector(state), - nextCursor: bpdTransactionsGetNextCursor(state), - bpdTransactionByIdSelector: (trxId: BpdTransactionId) => - bpdTransactionByIdSelector(state, trxId), - bpdDaysInfoByIdSelector: (id: string) => bpdDaysInfoByIdSelector(state, id) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(TransactionsSectionList); diff --git a/ts/features/bonus/bpd/screens/details/transaction/v2/TransactionsUnavailableV2.tsx b/ts/features/bonus/bpd/screens/details/transaction/v2/TransactionsUnavailableV2.tsx deleted file mode 100644 index 3bb18877df4..00000000000 --- a/ts/features/bonus/bpd/screens/details/transaction/v2/TransactionsUnavailableV2.tsx +++ /dev/null @@ -1,84 +0,0 @@ -import * as React from "react"; -import { SafeAreaView } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import image from "../../../../../../../../img/wallet/errors/payment-unavailable-icon.png"; -import { IOStyles } from "../../../../../../../components/core/variables/IOStyles"; -import { renderInfoRasterImage } from "../../../../../../../components/infoScreen/imageRendering"; -import { InfoScreenComponent } from "../../../../../../../components/infoScreen/InfoScreenComponent"; -import BaseScreenComponent from "../../../../../../../components/screens/BaseScreenComponent"; -import FooterWithButtons from "../../../../../../../components/ui/FooterWithButtons"; -import I18n from "../../../../../../../i18n"; -import { navigateBack } from "../../../../../../../store/actions/navigation"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { - cancelButtonProps, - confirmButtonProps -} from "../../../../../../../components/buttons/ButtonConfigurations"; -import { AwardPeriodId } from "../../../../store/actions/periods"; -import { bpdTransactionsLoadRequiredData } from "../../../../store/actions/transactions"; -import { bpdSelectedPeriodSelector } from "../../../../store/reducers/details/selectedPeriod"; - -export type Props = Pick< - React.ComponentProps, - "contextualHelp" -> & - ReturnType & - ReturnType; - -const loadLocales = () => ({ - headerTitle: I18n.t("bonus.bpd.details.transaction.goToButton"), - title: I18n.t("bonus.bpd.details.transaction.error.title"), - body: I18n.t("bonus.bpd.details.transaction.error.body") -}); - -/** - * This screen informs the user that there are problems retrieving the transactions list. - * Replace TransactionsUnavailable, adding also the retry button - * @constructor - */ -const TransactionsUnavailableV2Base: React.FunctionComponent = props => { - const { headerTitle, title, body } = loadLocales(); - - return ( - - - - - props.selectedPeriod - ? props.retry(props.selectedPeriod.awardPeriodId) - : props.cancel(), - I18n.t("global.buttons.retry") - )} - /> - - - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - cancel: () => navigateBack(), - retry: (periodId: AwardPeriodId) => - dispatch(bpdTransactionsLoadRequiredData.request(periodId)) -}); - -const mapStateToProps = (state: GlobalState) => ({ - selectedPeriod: bpdSelectedPeriodSelector(state) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(TransactionsUnavailableV2Base); diff --git a/ts/features/bonus/bpd/screens/iban/IbanCTAEditScreen.tsx b/ts/features/bonus/bpd/screens/iban/IbanCTAEditScreen.tsx deleted file mode 100644 index 21f138ca4ae..00000000000 --- a/ts/features/bonus/bpd/screens/iban/IbanCTAEditScreen.tsx +++ /dev/null @@ -1,135 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as React from "react"; -import { Alert } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { LoadingErrorComponent } from "../../../../../components/LoadingErrorComponent"; -import BaseScreenComponent from "../../../../../components/screens/BaseScreenComponent"; -import I18n from "../../../../../i18n"; -import { navigateBack } from "../../../../../store/actions/navigation"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { useActionOnFocus } from "../../../../../utils/hooks/useOnFocus"; -import { isStrictSome } from "../../../../../utils/pot"; -import { bpdAllData } from "../../store/actions/details"; -import { bpdSelectPeriod } from "../../store/actions/selectedPeriod"; -import { bpdEnabledSelector } from "../../store/reducers/details/activation"; -import { bpdLastUpdateSelector } from "../../store/reducers/details/lastUpdate"; -import { - bpdPeriodsSelector, - BpdPeriodWithInfo -} from "../../store/reducers/details/periods"; - -type Props = ReturnType & - ReturnType; - -const loadLocales = () => ({ - loadingCaption: I18n.t("global.remoteStates.loading"), - title: I18n.t("bonus.bpd.iban.verify"), - alertNotActiveTitle: I18n.t("bonus.bpd.iban.cta.bpdNotActiveTitle"), - alertNotActiveMessage: I18n.t("bonus.bpd.iban.cta.bpdNotActiveMessage"), - alertNotPeriodActiveTitle: I18n.t("bonus.bpd.iban.cta.noActivePeriodTitle"), - alertNotPeriodActiveMessage: I18n.t( - "bonus.bpd.iban.cta.noActivePeriodMessage" - ) -}); - -const InnerIbanCTAEditScreen = (props: Props) => { - useActionOnFocus(props.load); - // keep track if loading has been completed or not - // to avoid to handle not update data coming from the store - const [isLoadingComplete, setLoadingComplete] = - React.useState(false); - const { title, loadingCaption } = loadLocales(); - const { - bpdLoadState, - bpdEnabled, - goBack, - navigateToBPDPeriodDetails, - bpdPeriods - } = props; - React.useEffect(() => { - const { - alertNotActiveTitle, - alertNotActiveMessage, - alertNotPeriodActiveTitle, - alertNotPeriodActiveMessage - } = loadLocales(); - if (!pot.isNone(bpdLoadState) && !pot.isNone(bpdEnabled)) { - setLoadingComplete(true); - } - if ( - isLoadingComplete && - isStrictSome(bpdLoadState) && - pot.isSome(bpdEnabled) - ) { - // citizen not active to BPD - if (!bpdEnabled.value) { - Alert.alert(alertNotActiveTitle, alertNotActiveMessage, [ - { - onPress: goBack - } - ]); - return; - } - const activePeriod = pot - .getOrElse(bpdPeriods, []) - .find(p => p.status === "Active"); - if (activePeriod) { - goBack(); - navigateToBPDPeriodDetails(activePeriod); - } - // no active period - else { - Alert.alert(alertNotPeriodActiveTitle, alertNotPeriodActiveMessage, [ - { - onPress: goBack - } - ]); - } - } - }, [ - bpdLoadState, - bpdEnabled, - isLoadingComplete, - bpdPeriods, - goBack, - navigateToBPDPeriodDetails - ]); - - const hasErrors = pot.isError(props.bpdLoadState); - return ( - - - - ); -}; - -/** - * Landing screen from the CTA message that asks to review user's IBAN insertion - */ -const IbanCTAEditScreen: React.FC = (props: Props) => ( - -); - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - load: () => { - dispatch(bpdAllData.request()); - }, - goBack: () => navigateBack(), - navigateToBPDPeriodDetails: (bpdPeriod: BpdPeriodWithInfo) => { - dispatch(bpdSelectPeriod(bpdPeriod)); - } -}); - -const mapStateToProps = (state: GlobalState) => ({ - bpdLoadState: bpdLastUpdateSelector(state), - bpdPeriods: bpdPeriodsSelector(state), - bpdEnabled: bpdEnabledSelector(state), - bpdRemoteConfig: {} -}); - -export default connect(mapStateToProps, mapDispatchToProps)(IbanCTAEditScreen); diff --git a/ts/features/bonus/bpd/screens/iban/IbanLoadingUpsert.tsx b/ts/features/bonus/bpd/screens/iban/IbanLoadingUpsert.tsx deleted file mode 100644 index 48be3b97c49..00000000000 --- a/ts/features/bonus/bpd/screens/iban/IbanLoadingUpsert.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { Iban } from "../../../../../../definitions/backend/Iban"; -import I18n from "../../../../../i18n"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { useHardwareBackButton } from "../../../../../hooks/useHardwareBackButton"; -import { LoadingErrorComponent } from "../../../../../components/LoadingErrorComponent"; -import { - bpdIbanInsertionCancel, - bpdUpsertIban -} from "../../store/actions/iban"; -import { - bpdUpsertIbanIsError, - bpdUpsertIbanSelector -} from "../../store/reducers/details/activation/payoffInstrument"; - -export type Props = ReturnType & - ReturnType; - -/** - * displays to the user a loading screen while sending the new iban or - * possibly an error screen in case of unmanaged errors. - * @param props - * @constructor - */ -const IbanLoadingUpsert: React.FunctionComponent = props => { - const loading = I18n.t("bonus.bpd.iban.loading"); - - useHardwareBackButton(() => { - if (!props.isLoading) { - props.onAbort(); - } - return true; - }); - return ( - - pipe(props.ibanValue.value, O.fromNullable, O.map(props.onRetry)) - } - /> - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - onAbort: () => dispatch(bpdIbanInsertionCancel()), - onRetry: (iban: Iban) => dispatch(bpdUpsertIban.request(iban)) -}); - -const mapStateToProps = (state: GlobalState) => ({ - isLoading: !bpdUpsertIbanIsError(state), - ibanValue: bpdUpsertIbanSelector(state) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(IbanLoadingUpsert); diff --git a/ts/features/bonus/bpd/screens/iban/MainIbanScreen.tsx b/ts/features/bonus/bpd/screens/iban/MainIbanScreen.tsx deleted file mode 100644 index 96ac8356324..00000000000 --- a/ts/features/bonus/bpd/screens/iban/MainIbanScreen.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import * as React from "react"; -import { useEffect } from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { showToast } from "../../../../../utils/showToast"; -import { - isError, - isLoading, - isReady -} from "../../../../../common/model/RemoteValue"; -import { IbanStatus } from "../../saga/networking/patchCitizenIban"; -import { - bpdIbanInsertionContinue, - bpdIbanInsertionResetScreen -} from "../../store/actions/iban"; -import { bpdUpsertIbanSelector } from "../../store/reducers/details/activation/payoffInstrument"; -import { isBpdOnboardingOngoing } from "../../store/reducers/onboarding/ongoing"; -import I18n from "../../../../../i18n"; -import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp"; -import IbanLoadingUpsert from "./IbanLoadingUpsert"; -import IbanInsertionScreen from "./insertion/IbanInsertionScreen"; -import IbanKoNotOwned from "./ko/IbanKONotOwned"; -import IbanKOWrong from "./ko/IbanKOWrong"; - -export type Props = ReturnType & - ReturnType; - -const chooseRenderScreen = (props: Props) => { - const ibanStatus = props.upsertValue.outcome; - if (isLoading(ibanStatus) || isError(ibanStatus)) { - return ; - } else if (isReady(ibanStatus)) { - switch (ibanStatus.value) { - case IbanStatus.NOT_OWNED: - return ; - case IbanStatus.NOT_VALID: - return ; - case IbanStatus.OK: - case IbanStatus.CANT_VERIFY: - props.reset(); - props.completed(); - if (!props.onboardingOngoing) { - showToast(I18n.t("bonus.bpd.iban.success"), "success"); - } - } - } - return ; -}; - -const MainIbanScreen: React.FunctionComponent = props => { - const { reset } = props; - useEffect(() => { - reset(); - }, [reset]); - return chooseRenderScreen(props); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - reset: () => dispatch(bpdIbanInsertionResetScreen()), - completed: () => dispatch(bpdIbanInsertionContinue()) -}); - -const mapStateToProps = (state: GlobalState) => ({ - upsertValue: bpdUpsertIbanSelector(state), - onboardingOngoing: isBpdOnboardingOngoing(state) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(MainIbanScreen); diff --git a/ts/features/bonus/bpd/screens/iban/insertion/IbanInsertionComponent.tsx b/ts/features/bonus/bpd/screens/iban/insertion/IbanInsertionComponent.tsx deleted file mode 100644 index 23de7bcbbb5..00000000000 --- a/ts/features/bonus/bpd/screens/iban/insertion/IbanInsertionComponent.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import * as E from "fp-ts/lib/Either"; -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import * as React from "react"; -import { useState } from "react"; -import { View, Platform, SafeAreaView, ScrollView } from "react-native"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { Iban } from "../../../../../../../definitions/backend/Iban"; -import { makeFontStyleObject } from "../../../../../../components/core/fonts"; -import { Body } from "../../../../../../components/core/typography/Body"; -import { H1 } from "../../../../../../components/core/typography/H1"; -import { H4 } from "../../../../../../components/core/typography/H4"; -import { H5 } from "../../../../../../components/core/typography/H5"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import { LabelledItem } from "../../../../../../components/LabelledItem"; -import BaseScreenComponent from "../../../../../../components/screens/BaseScreenComponent"; -import I18n from "../../../../../../i18n"; -import { maybeNotNullyString } from "../../../../../../utils/strings"; -import { FooterTwoButtons } from "../../../../../../components/markdown/FooterTwoButtons"; - -type OwnProps = { - onBack: () => void; - onIbanConfirm: (iban: Iban) => void; - onContinue: () => void; - startIban?: string; - cancelText: string; -}; - -type Props = OwnProps & - Pick, "contextualHelp">; - -// https://en.wikipedia.org/wiki/International_Bank_Account_Number -// Italian Iban max length: 27 (sample: IT60X0542811101000000123456) -const IbanMaxLength = 34; - -const loadLocales = () => ({ - headerTitle: I18n.t("bonus.bpd.title"), - title: I18n.t("bonus.bpd.iban.insertion.title"), - body1: I18n.t("bonus.bpd.iban.insertion.body1"), - body1Bold: I18n.t("bonus.bpd.iban.insertion.body1Bold"), - body2: I18n.t("bonus.bpd.iban.insertion.body2"), - ibanDescription: I18n.t("bonus.bpd.iban.iban") -}); - -const IBANInputStyle = makeFontStyleObject("Regular", false, "RobotoMono"); - -const upperCaseAndNoBlanks = (text: string) => - text.replace(/\s/g, "").toUpperCase(); - -export const IbanInsertionComponent: React.FunctionComponent = props => { - const [iban, setIban] = useState(props.startIban); - const isValidIban = iban && E.isRight(Iban.decode(iban)); - const { headerTitle, title, body1, body1Bold, body2, ibanDescription } = - loadLocales(); - return ( - - - - - -

{title}

- - - {body1} -

{body1Bold}

- - -
{ibanDescription}
- undefined, - _ => E.isRight(Iban.decode(iban)) - ) - )} - inputProps={{ - value: iban, - autoCapitalize: "characters", - maxLength: IbanMaxLength, - onChangeText: text => { - // On Android we cannot modify the input text, or the text is duplicated - setIban( - text - ? Platform.select({ - android: text, - default: upperCaseAndNoBlanks(text) - }) - : undefined - ); - }, - style: IBANInputStyle - }} - /> - - {body2} -
-
- - pipe( - Iban.decode(upperCaseAndNoBlanks(iban as string)), - E.map(props.onIbanConfirm) - ) - } - onCancel={props.onContinue} - rightText={I18n.t("global.buttons.continue")} - leftText={props.cancelText} - /> -
-
- ); -}; diff --git a/ts/features/bonus/bpd/screens/iban/insertion/IbanInsertionScreen.tsx b/ts/features/bonus/bpd/screens/iban/insertion/IbanInsertionScreen.tsx deleted file mode 100644 index d9e0cb37594..00000000000 --- a/ts/features/bonus/bpd/screens/iban/insertion/IbanInsertionScreen.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { Iban } from "../../../../../../../definitions/backend/Iban"; -import I18n from "../../../../../../i18n"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { emptyContextualHelp } from "../../../../../../utils/emptyContextualHelp"; -import { - bpdIbanInsertionCancel, - bpdIbanInsertionContinue, - bpdUpsertIban -} from "../../../store/actions/iban"; -import { bpdIbanPrefillSelector } from "../../../store/reducers/details/activation"; -import { isBpdOnboardingOngoing } from "../../../store/reducers/onboarding/ongoing"; -import { IbanInsertionComponent } from "./IbanInsertionComponent"; - -export type Props = ReturnType & - ReturnType; - -/** - * This screen allows the user to add / modify an iban to be used to receive the refund provided by bpd - * @constructor - */ -const IbanInsertionScreen: React.FunctionComponent = props => ( - -); - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - cancel: () => dispatch(bpdIbanInsertionCancel()), - continue: () => dispatch(bpdIbanInsertionContinue()), - submitIban: (iban: Iban) => dispatch(bpdUpsertIban.request(iban)) -}); - -const mapStateToProps = (state: GlobalState) => ({ - prefillIban: bpdIbanPrefillSelector(state), - onboardingOngoing: isBpdOnboardingOngoing(state) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(IbanInsertionScreen); diff --git a/ts/features/bonus/bpd/screens/iban/ko/IbanKOCannotVerify.tsx b/ts/features/bonus/bpd/screens/iban/ko/IbanKOCannotVerify.tsx deleted file mode 100644 index 544ec726984..00000000000 --- a/ts/features/bonus/bpd/screens/iban/ko/IbanKOCannotVerify.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import * as React from "react"; -import { SafeAreaView } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import image from "../../../../../../../img/search/search-icon.png"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import { renderInfoRasterImage } from "../../../../../../components/infoScreen/imageRendering"; -import { InfoScreenComponent } from "../../../../../../components/infoScreen/InfoScreenComponent"; -import BaseScreenComponent from "../../../../../../components/screens/BaseScreenComponent"; -import I18n from "../../../../../../i18n"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { FooterTwoButtons } from "../../../../../../components/markdown/FooterTwoButtons"; -import { - bpdIbanInsertionContinue, - bpdIbanInsertionResetScreen -} from "../../../store/actions/iban"; -import IbanKoBody from "./IbanKoBody"; - -export type Props = ReturnType & - ReturnType; - -const loadLocales = () => ({ - headerTitle: I18n.t("bonus.bpd.title"), - edit: I18n.t("bonus.bpd.iban.edit"), - continueStr: I18n.t("global.buttons.continue"), - title: I18n.t("bonus.bpd.iban.koCannotVerify.title"), - text1: I18n.t("bonus.bpd.iban.koCannotVerify.text1"), - text2: I18n.t("bonus.bpd.iban.koCannotVerify.text2") -}); - -/** - * This screen warns the user that the provided iban cannot be verified. - * This is just a warning, the user can continue and the iban has been registered on the bpd remote system. - * @constructor - * @deprecated not used anymore, still here just in case of change of mind - */ -const IbanKoCannotVerify: React.FunctionComponent = props => { - const { headerTitle, continueStr, edit, title, text1, text2 } = loadLocales(); - return ( - - - } - /> - - - - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - modifyIban: () => dispatch(bpdIbanInsertionResetScreen()), - completeInsertion: () => dispatch(bpdIbanInsertionContinue()) -}); - -const mapStateToProps = (_: GlobalState) => ({}); - -export default connect(mapStateToProps, mapDispatchToProps)(IbanKoCannotVerify); diff --git a/ts/features/bonus/bpd/screens/iban/ko/IbanKONotOwned.tsx b/ts/features/bonus/bpd/screens/iban/ko/IbanKONotOwned.tsx deleted file mode 100644 index d5eec253e6e..00000000000 --- a/ts/features/bonus/bpd/screens/iban/ko/IbanKONotOwned.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as React from "react"; -import { View, SafeAreaView, StyleSheet } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import image from "../../../../../../../img/pictograms/doubt.png"; -import { Body } from "../../../../../../components/core/typography/Body"; -import { H4 } from "../../../../../../components/core/typography/H4"; -import { Monospace } from "../../../../../../components/core/typography/Monospace"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import { renderInfoRasterImage } from "../../../../../../components/infoScreen/imageRendering"; -import { InfoScreenComponent } from "../../../../../../components/infoScreen/InfoScreenComponent"; -import BaseScreenComponent from "../../../../../../components/screens/BaseScreenComponent"; -import I18n from "../../../../../../i18n"; -import { profileSelector } from "../../../../../../store/reducers/profile"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { FooterTwoButtons } from "../../../../../../components/markdown/FooterTwoButtons"; -import { - bpdIbanInsertionCancel, - bpdIbanInsertionResetScreen -} from "../../../store/actions/iban"; -import IbanKoBody from "./IbanKoBody"; - -export type Props = ReturnType & - ReturnType & - Pick, "contextualHelp">; - -const loadLocales = () => ({ - headerTitle: I18n.t("bonus.bpd.title"), - cancel: I18n.t("global.buttons.cancel"), - verify: I18n.t("bonus.bpd.iban.verify"), - title: I18n.t("bonus.bpd.iban.koNotOwned.title"), - text1: I18n.t("bonus.bpd.iban.koNotOwned.text1"), - text2: I18n.t("bonus.bpd.iban.koNotOwned.text2") -}); - -const styles = StyleSheet.create({ - profile: { - flex: 1, - alignContent: "flex-end" - }, - text: { - textAlign: "center" - } -}); - -/** - * Render the profile information (name, surname, fiscal code) - * @param props - * @constructor - */ -const ProfileInformation = (props: Props) => { - // undefined profile should never happens - const profile = pot.getOrElse(props.profile, undefined); - - return ( - -

- {profile?.name} {profile?.family_name}, -

- - {I18n.t("profile.fiscalCode.fiscalCode")}{" "} - {profile?.fiscal_code} - -
- ); -}; - -/** - * This screen warns the user that the provided iban does not belong to him. - * This is just a warning, the user can continue and iban has been registered on the bpd remote system. - * @constructor - */ -const IbanKoNotOwned: React.FunctionComponent = props => { - const { headerTitle, verify, cancel, title, text1, text2 } = loadLocales(); - return ( - - - - - - - } - /> - - - - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - modifyIban: () => dispatch(bpdIbanInsertionResetScreen()), - cancel: () => dispatch(bpdIbanInsertionCancel()) -}); - -const mapStateToProps = (state: GlobalState) => ({ - profile: profileSelector(state) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(IbanKoNotOwned); diff --git a/ts/features/bonus/bpd/screens/iban/ko/IbanKOWrong.tsx b/ts/features/bonus/bpd/screens/iban/ko/IbanKOWrong.tsx deleted file mode 100644 index faa06e7f4ae..00000000000 --- a/ts/features/bonus/bpd/screens/iban/ko/IbanKOWrong.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import * as React from "react"; -import { SafeAreaView } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import image from "../../../../../../../img/servicesStatus/error-detail-icon.png"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import { renderInfoRasterImage } from "../../../../../../components/infoScreen/imageRendering"; -import { InfoScreenComponent } from "../../../../../../components/infoScreen/InfoScreenComponent"; -import BaseScreenComponent from "../../../../../../components/screens/BaseScreenComponent"; -import I18n from "../../../../../../i18n"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { FooterTwoButtons } from "../../../../../../components/markdown/FooterTwoButtons"; -import { - bpdIbanInsertionCancel, - bpdIbanInsertionResetScreen -} from "../../../store/actions/iban"; -import IbanKoBody from "./IbanKoBody"; - -export type Props = ReturnType & - ReturnType & - Pick, "contextualHelp">; - -const loadLocales = () => ({ - headerTitle: I18n.t("bonus.bpd.title"), - edit: I18n.t("bonus.bpd.iban.edit"), - title: I18n.t("bonus.bpd.iban.koWrong.title"), - text1: I18n.t("bonus.bpd.iban.koWrong.text1"), - text2: I18n.t("bonus.bpd.iban.koWrong.text2") -}); - -/** - * This screen informs the user that the provided iban is not right and cannot continue. - * @constructor - */ -const IbanKoWrong: React.FunctionComponent = props => { - const { headerTitle, title, text1, text2, edit } = loadLocales(); - return ( - - - } - /> - - - - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - modifyIban: () => { - dispatch(bpdIbanInsertionResetScreen()); - }, - cancel: () => dispatch(bpdIbanInsertionCancel()) -}); - -const mapStateToProps = (_: GlobalState) => ({}); - -export default connect(mapStateToProps, mapDispatchToProps)(IbanKoWrong); diff --git a/ts/features/bonus/bpd/screens/iban/ko/IbanKoBody.tsx b/ts/features/bonus/bpd/screens/iban/ko/IbanKoBody.tsx deleted file mode 100644 index 8fda0497a35..00000000000 --- a/ts/features/bonus/bpd/screens/iban/ko/IbanKoBody.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import * as React from "react"; -import { View, StyleSheet } from "react-native"; -import { connect } from "react-redux"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { H4 } from "../../../../../../components/core/typography/H4"; -import { Monospace } from "../../../../../../components/core/typography/Monospace"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { bpdUpsertIbanSelector } from "../../../store/reducers/details/activation/payoffInstrument"; - -type OwnProps = { - text1: string; - text2: string; - isFlex?: boolean; -}; - -export type Props = OwnProps & ReturnType; - -const styles = StyleSheet.create({ - text: { - textAlign: "center" - } -}); - -/** - * Common rendering of the body for all the Iban KO screens - * @param props - * @constructor - */ -const IbanKoBody: React.FunctionComponent = props => { - const iban: string = props.candidateIban.value ?? ""; - const style = props.isFlex ?? true ? IOStyles.flex : {}; - - return ( - -

- {props.text1} -

- - - {iban} - - -

- {props.text2} -

-
- ); -}; - -const mapStateToProps = (state: GlobalState) => ({ - candidateIban: bpdUpsertIbanSelector(state) -}); - -export default connect(mapStateToProps)(IbanKoBody); diff --git a/ts/features/bonus/bpd/screens/onboarding/BpdInformationScreen.tsx b/ts/features/bonus/bpd/screens/onboarding/BpdInformationScreen.tsx deleted file mode 100644 index 8c397896958..00000000000 --- a/ts/features/bonus/bpd/screens/onboarding/BpdInformationScreen.tsx +++ /dev/null @@ -1,63 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as React from "react"; -import { connect } from "react-redux"; -import I18n from "../../../../../i18n"; -import { Dispatch } from "../../../../../store/actions/types"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp"; -import { actionWithAlert } from "../../../common/components/alert/ActionWithAlert"; -import BonusInformationComponent from "../../../common/components/BonusInformationComponent"; -import { - bpdOnboardingCancel, - bpdUserActivate -} from "../../store/actions/onboarding"; -import { bpdEnabledSelector } from "../../store/reducers/details/activation"; - -export type Props = ReturnType & - ReturnType; - -/** - * This Screen shows all the information about the bpd program, with the rules and t&c. - */ -const BpdInformationScreen: React.FunctionComponent = (props: Props) => { - const onConfirm = () => - pot.getOrElse(props.bpdActiveBonus, false) - ? actionWithAlert({ - title: I18n.t("bonus.bpd.onboarding.alert.title"), - body: I18n.t("bonus.bpd.onboarding.alert.body"), - cancelText: I18n.t("bonus.bpd.onboarding.alert.cancel"), - confirmText: I18n.t("bonus.bpd.onboarding.alert.confirm"), - onConfirmAction: props.onCancel - }) - : props.userActivateBpd(); - - return ( - <> - {props.bonus ? ( - - ) : null} - - ); -}; - -const mapStateToProps = (state: GlobalState) => ({ - bonus: undefined, - bpdActiveBonus: bpdEnabledSelector(state) -}); - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - userActivateBpd: () => dispatch(bpdUserActivate()), - onCancel: () => dispatch(bpdOnboardingCancel()) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(BpdInformationScreen); diff --git a/ts/features/bonus/bpd/screens/onboarding/EnrollPaymentMethodsScreen.tsx b/ts/features/bonus/bpd/screens/onboarding/EnrollPaymentMethodsScreen.tsx deleted file mode 100644 index bf3a6adc246..00000000000 --- a/ts/features/bonus/bpd/screens/onboarding/EnrollPaymentMethodsScreen.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as React from "react"; -import { View, SafeAreaView, ScrollView } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { Body } from "../../../../../components/core/typography/Body"; -import { H1 } from "../../../../../components/core/typography/H1"; -import { IOStyles } from "../../../../../components/core/variables/IOStyles"; -import BaseScreenComponent from "../../../../../components/screens/BaseScreenComponent"; -import FooterWithButtons from "../../../../../components/ui/FooterWithButtons"; -import I18n from "../../../../../i18n"; -import { navigateToWalletHome } from "../../../../../store/actions/navigation"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { PaymentMethod } from "../../../../../types/pagopa"; -import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp"; -import { PaymentMethodGroupedList } from "../../components/paymentMethodActivationToggle/list/PaymentMethodGroupedList"; -import { paymentMethodsWithActivationStatusSelector } from "../../store/reducers/details/combiner"; -import { areAnyPaymentMethodsActiveSelector } from "../../store/reducers/details/paymentMethods"; - -const loadLocales = () => ({ - headerTitle: I18n.t("bonus.bpd.title"), - continueStr: I18n.t("global.buttons.continue"), - skip: I18n.t("global.buttons.skip"), - title: I18n.t("bonus.bpd.onboarding.enrollPaymentMethod.title"), - body1: I18n.t("bonus.bpd.onboarding.enrollPaymentMethod.body1"), - body2: I18n.t("bonus.bpd.onboarding.enrollPaymentMethod.body2") -}); - -export type Props = ReturnType & - ReturnType; - -const renderPaymentMethod = ( - potWallets: pot.Pot, Error> -) => - pot.fold( - potWallets, - // TODO: handle error, loading with spinner if needed - () => null, - () => null, - _ => null, - _ => null, - value => , - value => , - value => , - value => - ); - -/** - * return a two button footer - * left button is enabled when no payment methods are BPD active - * right button button is enabled when at least one payment method is BPD active - * @param props - */ -const getFooter = (props: Props) => { - const { continueStr, skip } = loadLocales(); - const notNowButtonProps = { - primary: false, - bordered: true, - disabled: props.areAnyPaymentMethodsActive, - onPress: props.skip, - title: skip - }; - const continueButtonProps = { - block: true, - primary: true, - disabled: !props.areAnyPaymentMethodsActive, - onPress: props.skip, - title: continueStr - }; - return ( - - ); -}; - -/** - * This screen allows the user to activate bpd on the payment methods already in the wallet - */ -const EnrollPaymentMethodsScreen: React.FunctionComponent = props => { - const { headerTitle, title, body1, body2 } = loadLocales(); - return ( - - - - - -

{title}

- - {body1} - - {renderPaymentMethod(props.potWallets)} - - {body2} -
-
- {getFooter(props)} -
-
- ); -}; - -const mapDispatchToProps = (_: Dispatch) => ({ - skip: () => { - navigateToWalletHome(); - } -}); - -const mapStateToProps = (state: GlobalState) => { - const potWallets = paymentMethodsWithActivationStatusSelector(state); - return { - potWallets, - areAnyPaymentMethodsActive: areAnyPaymentMethodsActiveSelector( - pot.getOrElse(potWallets, []) - )(state) - }; -}; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(EnrollPaymentMethodsScreen); diff --git a/ts/features/bonus/bpd/screens/onboarding/ErrorPaymentMethodsScreen.tsx b/ts/features/bonus/bpd/screens/onboarding/ErrorPaymentMethodsScreen.tsx deleted file mode 100644 index e9fb04cc972..00000000000 --- a/ts/features/bonus/bpd/screens/onboarding/ErrorPaymentMethodsScreen.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import * as React from "react"; -import { View, SafeAreaView, ScrollView, StyleSheet } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { Icon, VSpacer } from "@pagopa/io-app-design-system"; -import { Body } from "../../../../../components/core/typography/Body"; -import { H2 } from "../../../../../components/core/typography/H2"; -import { IOStyles } from "../../../../../components/core/variables/IOStyles"; -import BaseScreenComponent from "../../../../../components/screens/BaseScreenComponent"; -import FooterWithButtons from "../../../../../components/ui/FooterWithButtons"; -import I18n from "../../../../../i18n"; -import { navigateToWalletHome } from "../../../../../store/actions/navigation"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp"; -import { confirmButtonProps } from "../../../../../components/buttons/ButtonConfigurations"; - -export type Props = ReturnType & - ReturnType; - -const styles = StyleSheet.create({ - center: { textAlign: "center" } -}); - -const loadLocales = () => ({ - headerTitle: I18n.t("bonus.bpd.title"), - title: I18n.t("bonus.bpd.onboarding.errorPaymentMethod.title"), - body: I18n.t("bonus.bpd.onboarding.errorPaymentMethod.body"), - cta: I18n.t("bonus.bpd.onboarding.errorPaymentMethod.cta") -}); - -const ErrorPaymentMethodsScreen: React.FunctionComponent = props => { - const { headerTitle, title, body, cta } = loadLocales(); - return ( - - - - {/* TODO: Refactor the following screen because you can't - use huge spacers to center the screen content. - You have to think about position logic. */} - - - - - -

{title}

- - {body} -
-
- -
-
- ); -}; - -const mapDispatchToProps = (_: Dispatch) => ({ - skip: () => { - navigateToWalletHome(); - } -}); - -const mapStateToProps = (_: GlobalState) => ({}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(ErrorPaymentMethodsScreen); diff --git a/ts/features/bonus/bpd/screens/onboarding/LoadActivateBpdScreen.tsx b/ts/features/bonus/bpd/screens/onboarding/LoadActivateBpdScreen.tsx deleted file mode 100644 index 343de78761f..00000000000 --- a/ts/features/bonus/bpd/screens/onboarding/LoadActivateBpdScreen.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import I18n from "../../../../../i18n"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { useHardwareBackButton } from "../../../../../hooks/useHardwareBackButton"; -import { LoadingErrorComponent } from "../../../../../components/LoadingErrorComponent"; -import { isError } from "../../../../../common/model/RemoteValue"; -import { - bpdOnboardingAcceptDeclaration, - bpdOnboardingCancel -} from "../../store/actions/onboarding"; -import { bpdEnrollSelector } from "../../store/reducers/onboarding/enroll"; - -type Props = ReturnType & - ReturnType; - -/** - * This screen is displayed during the activation of the bpd program for the user - */ -const LoadActivateBpdScreen: React.FunctionComponent = props => { - const loadingCaption = - I18n.t("bonus.bpd.onboarding.loadingActivateBpd.title") + - "\n" + - I18n.t("bonus.bpd.onboarding.loadingActivateBpd.body"); - - useHardwareBackButton(() => { - if (!props.isLoading) { - props.onAbort(); - } - return true; - }); - - return ( - - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - onAbort: () => dispatch(bpdOnboardingCancel()), - onRetry: () => { - dispatch(bpdOnboardingAcceptDeclaration()); - } -}); - -const mapStateToProps = (globalState: GlobalState) => ({ - // display the error with the retry only in case of networking errors - isLoading: !isError(bpdEnrollSelector(globalState)) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(LoadActivateBpdScreen); diff --git a/ts/features/bonus/bpd/screens/onboarding/LoadBpdActivationStatus.tsx b/ts/features/bonus/bpd/screens/onboarding/LoadBpdActivationStatus.tsx deleted file mode 100644 index 7c60be9d180..00000000000 --- a/ts/features/bonus/bpd/screens/onboarding/LoadBpdActivationStatus.tsx +++ /dev/null @@ -1,60 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import I18n from "../../../../../i18n"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { useHardwareBackButton } from "../../../../../hooks/useHardwareBackButton"; -import { LoadingErrorComponent } from "../../../../../components/LoadingErrorComponent"; -import { - bpdOnboardingCancel, - bpdOnboardingStart -} from "../../store/actions/onboarding"; -import { bpdEnabledSelector } from "../../store/reducers/details/activation"; - -type Props = ReturnType & - ReturnType; - -/** - * This screen is displayed when the user start the onboarding flow but the application does not know yet if the user - * is already registered to the bpd service - * @constructor - */ - -const LoadBpdActivationStatus: React.FunctionComponent = props => { - const loadingCaption = I18n.t( - "bonus.bpd.onboarding.loadingActivationStatus.title" - ); - - useHardwareBackButton(() => { - if (!props.isLoading) { - props.onAbort(); - } - return true; - }); - - return ( - - ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - onAbort: () => dispatch(bpdOnboardingCancel()), - onRetry: () => { - dispatch(bpdOnboardingStart()); - } -}); - -const mapStateToProps = (globalState: GlobalState) => ({ - // display the error with the retry only in case of networking errors - isLoading: !pot.isError(bpdEnabledSelector(globalState)) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(LoadBpdActivationStatus); diff --git a/ts/features/bonus/bpd/screens/onboarding/NoPaymentMethodsAvailableScreen.tsx b/ts/features/bonus/bpd/screens/onboarding/NoPaymentMethodsAvailableScreen.tsx deleted file mode 100644 index 7c0dc2be012..00000000000 --- a/ts/features/bonus/bpd/screens/onboarding/NoPaymentMethodsAvailableScreen.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import * as O from "fp-ts/lib/Option"; -import * as React from "react"; -import { View, SafeAreaView } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { Body } from "../../../../../components/core/typography/Body"; -import { H1 } from "../../../../../components/core/typography/H1"; -import { IOStyles } from "../../../../../components/core/variables/IOStyles"; -import BaseScreenComponent from "../../../../../components/screens/BaseScreenComponent"; -import I18n from "../../../../../i18n"; -import { - navigateToWalletAddPaymentMethod, - navigateToWalletHome -} from "../../../../../store/actions/navigation"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp"; -import { FooterTwoButtons } from "../../../../../components/markdown/FooterTwoButtons"; - -export type Props = ReturnType & - ReturnType; - -const loadLocales = () => ({ - headerTitle: I18n.t("bonus.bpd.title"), - skip: I18n.t("global.buttons.skip"), - addMethod: I18n.t("wallet.newPaymentMethod.addButton"), - title: I18n.t("bonus.bpd.onboarding.noPaymentMethod.title"), - body: I18n.t("bonus.bpd.onboarding.noPaymentMethod.body") -}); - -const NoPaymentMethodsAvailableScreen: React.FunctionComponent< - Props -> = props => { - const { headerTitle, skip, addMethod, title, body } = loadLocales(); - return ( - - - - -

{title}

- - {body} -
- -
-
- ); -}; - -const mapDispatchToProps = (_: Dispatch) => ({ - skip: () => { - navigateToWalletHome(); - }, - addPaymentMethod: () => { - navigateToWalletHome(); - navigateToWalletAddPaymentMethod({ inPayment: O.none }); - } -}); - -const mapStateToProps = (_: GlobalState) => ({}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(NoPaymentMethodsAvailableScreen); diff --git a/ts/features/bonus/bpd/screens/onboarding/declaration/DeclarationComponent.tsx b/ts/features/bonus/bpd/screens/onboarding/declaration/DeclarationComponent.tsx deleted file mode 100644 index a9c79a05606..00000000000 --- a/ts/features/bonus/bpd/screens/onboarding/declaration/DeclarationComponent.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import * as React from "react"; -import { useReducer } from "react"; -import { View, SafeAreaView, ScrollView } from "react-native"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { InfoBox } from "../../../../../../components/box/InfoBox"; -import { Body } from "../../../../../../components/core/typography/Body"; -import { H1 } from "../../../../../../components/core/typography/H1"; -import { Label } from "../../../../../../components/core/typography/Label"; -import { Link } from "../../../../../../components/core/typography/Link"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import BaseScreenComponent from "../../../../../../components/screens/BaseScreenComponent"; -import I18n from "../../../../../../i18n"; -import { openWebUrl } from "../../../../../../utils/url"; -import { FooterTwoButtons } from "../../../../../../components/markdown/FooterTwoButtons"; -import { dpr28Dec2000Url } from "../../../../../../urls"; -import { DeclarationEntry } from "./DeclarationEntry"; - -type OwnProps = { - onCancel: () => void; - onConfirm: () => void; -}; - -type Props = OwnProps & - Pick, "contextualHelp">; -type NormalBold = { - normal: string; - bold: string; -}; - -type InnerAction = "increment" | "decrement"; - -const loadLocales = () => ({ - title: I18n.t("bonus.bpd.title"), - header: I18n.t("bonus.bpd.onboarding.declaration.header"), - age: I18n.t("bonus.bpd.onboarding.declaration.conditions.age"), - owner: { - normal: I18n.t("bonus.bpd.onboarding.declaration.conditions.owner.normal"), - bold: I18n.t("bonus.bpd.onboarding.declaration.conditions.owner.bold") - }, - resident: I18n.t("bonus.bpd.onboarding.declaration.conditions.resident"), - personal_use: { - normal: I18n.t( - "bonus.bpd.onboarding.declaration.conditions.personal_use.normal" - ), - bold: I18n.t( - "bonus.bpd.onboarding.declaration.conditions.personal_use.bold" - ) - }, - disclaimer: { - normal1: I18n.t("bonus.bpd.onboarding.declaration.disclaimer.normal1"), - link: I18n.t("bonus.bpd.onboarding.declaration.disclaimer.link"), - normal2: I18n.t("bonus.bpd.onboarding.declaration.disclaimer.normal2") - }, - cta: I18n.t("bonus.bpd.onboarding.declaration.cta") -}); - -/** - * Need a specific rendering of this component because have some bold parts - * @param personalUse - */ -const normalBoldText = (personalUse: NormalBold) => ( - - {personalUse.normal} - - -); - -const generateRequiredConditions = ( - list: ReadonlyArray, - dispatch: React.Dispatch -) => - list.map((condition, index) => ( - dispatch(newValue ? "increment" : "decrement")} - /> - )); - -function reducer(state: number, action: InnerAction) { - switch (action) { - case "increment": - return state + 1; - case "decrement": - return state - 1; - default: - throw new Error(); - } -} - -/** - * This screen allows the user to declare the required conditions. - * When all the condition are accepted, the continue button will be enabled - */ -export const DeclarationComponent: React.FunctionComponent = props => { - const { title, header, age, owner, resident, personal_use, disclaimer, cta } = - loadLocales(); - - // tracks the condition accepted, used to enabled the "continue" button - const [state, dispatch] = useReducer(reducer, 0); - - // transform the required textual conditions to graphical objects with checkbox - const requiredConditions = [ - age, - resident, - normalBoldText(owner), - normalBoldText(personal_use) - ]; - - return ( - - - - - -

{header}

- - {generateRequiredConditions(requiredConditions, dispatch)} - - - - {disclaimer.normal1} - openWebUrl(dpr28Dec2000Url)}> - {disclaimer.link} - - {disclaimer.normal2} - - -
-
- -
-
- ); -}; diff --git a/ts/features/bonus/bpd/screens/onboarding/declaration/DeclarationScreen.tsx b/ts/features/bonus/bpd/screens/onboarding/declaration/DeclarationScreen.tsx deleted file mode 100644 index c359f17680e..00000000000 --- a/ts/features/bonus/bpd/screens/onboarding/declaration/DeclarationScreen.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import * as React from "react"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { emptyContextualHelp } from "../../../../../../utils/emptyContextualHelp"; -import { - bpdOnboardingAcceptDeclaration, - bpdOnboardingCancel -} from "../../../store/actions/onboarding"; -import { DeclarationComponent } from "./DeclarationComponent"; - -export type Props = ReturnType; - -/** - * This screen allows the user to declare the required conditions - */ - -const DeclarationScreen: React.FunctionComponent = props => ( - -); - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - onCancel: () => dispatch(bpdOnboardingCancel()), - userAcceptDeclaration: () => dispatch(bpdOnboardingAcceptDeclaration()) -}); - -export default connect(undefined, mapDispatchToProps)(DeclarationScreen); diff --git a/ts/features/bonus/bpd/screens/optInPaymentMethods/OptInPaymentMethodsCashbackUpdateScreen.tsx b/ts/features/bonus/bpd/screens/optInPaymentMethods/OptInPaymentMethodsCashbackUpdateScreen.tsx deleted file mode 100644 index e7ea628da1b..00000000000 --- a/ts/features/bonus/bpd/screens/optInPaymentMethods/OptInPaymentMethodsCashbackUpdateScreen.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import { VSpacer } from "@pagopa/io-app-design-system"; -import React from "react"; -import { SafeAreaView, ScrollView, StyleSheet, View } from "react-native"; -import ButtonDefaultOpacity from "../../../../../components/ButtonDefaultOpacity"; -import { confirmButtonProps } from "../../../../../components/buttons/ButtonConfigurations"; -import { IORenderHtml } from "../../../../../components/core/IORenderHtml"; -import { H1 } from "../../../../../components/core/typography/H1"; -import { IOStyles } from "../../../../../components/core/variables/IOStyles"; -import BaseScreenComponent from "../../../../../components/screens/BaseScreenComponent"; -import FooterWithButtons from "../../../../../components/ui/FooterWithButtons"; -import I18n from "../../../../../i18n"; -import { useIODispatch, useIOSelector } from "../../../../../store/hooks"; -import { - getBPDMethodsSelector, - paymentMethodsSelector -} from "../../../../../store/reducers/wallet/wallets"; -import { - optInPaymentMethodsCompleted, - optInPaymentMethodsFailure -} from "../../store/actions/optInPaymentMethods"; - -const styles = StyleSheet.create({ - headerContainer: { - ...IOStyles.row, - justifyContent: "space-between" - } -}); -const OptInPaymentMethodsCashbackUpdateScreen = () => { - const dispatch = useIODispatch(); - const bpdPaymentMethods = useIOSelector(getBPDMethodsSelector); - const paymentMethods = useIOSelector(paymentMethodsSelector); - - // This screen should be shown only if the payment method are correctly loaded - if (paymentMethods.kind !== "PotSome") { - dispatch(optInPaymentMethodsFailure("error in the payment method loading")); - } - - const handleOnContinuePress = () => { - if (bpdPaymentMethods.length > 0) { - throw new Error("bpdPaymentMethods should be empty"); - } else { - dispatch(optInPaymentMethodsCompleted()); - } - }; - - return ( - // The void customGoBack is needed to have a centered header title - true} transparent={true} /> - } - > - - - - -

- {I18n.t("bonus.bpd.optInPaymentMethods.cashbackUpdate.title")} -

-
-
- - -
- -
-
- ); -}; - -export default OptInPaymentMethodsCashbackUpdateScreen; diff --git a/ts/features/bonus/bpd/screens/optInPaymentMethods/OptInPaymentMethodsChoiceScreen.tsx b/ts/features/bonus/bpd/screens/optInPaymentMethods/OptInPaymentMethodsChoiceScreen.tsx deleted file mode 100644 index 1edc207743a..00000000000 --- a/ts/features/bonus/bpd/screens/optInPaymentMethods/OptInPaymentMethodsChoiceScreen.tsx +++ /dev/null @@ -1,206 +0,0 @@ -import { useNavigation } from "@react-navigation/native"; -import React, { useEffect, useMemo, useRef, useState } from "react"; -import { SafeAreaView, ScrollView, View } from "react-native"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import ButtonDefaultOpacity from "../../../../../components/ButtonDefaultOpacity"; -import { IOBadge } from "../../../../../components/core/IOBadge"; -import { - RadioButtonList, - RadioItem -} from "../../../../../components/core/selection/RadioButtonList"; -import { Body } from "../../../../../components/core/typography/Body"; -import { H1 } from "../../../../../components/core/typography/H1"; -import { H4 } from "../../../../../components/core/typography/H4"; -import { LabelSmall } from "../../../../../components/core/typography/LabelSmall"; -import { IOStyles } from "../../../../../components/core/variables/IOStyles"; -import LoadingSpinnerOverlay from "../../../../../components/LoadingSpinnerOverlay"; -import BaseScreenComponent from "../../../../../components/screens/BaseScreenComponent"; -import { BlockButtonProps } from "../../../../../components/ui/BlockButtons"; -import FooterWithButtons from "../../../../../components/ui/FooterWithButtons"; -import I18n from "../../../../../i18n"; -import { useIODispatch, useIOSelector } from "../../../../../store/hooks"; -import { showToast } from "../../../../../utils/showToast"; -import { - confirmButtonProps, - disablePrimaryButtonProps, - errorBorderedButtonProps -} from "../../../../../components/buttons/ButtonConfigurations"; -import { useBottomSheetMethodsToDelete } from "../../components/optInStatus/BottomSheetMethodsToDelete"; -import { - isError, - isReady, - isUndefined -} from "../../../../../common/model/RemoteValue"; -import { optInPaymentMethodsShowChoice } from "../../store/actions/optInPaymentMethods"; -import { showOptInChoiceSelector } from "../../store/reducers/details/activation/ui"; - -type PaymentMethodsChoiceOptions = - | "keepPaymentMethods" - | "deletePaymentMethods"; - -const generateOptionBody = ( - title: string, - body: string, - showBadge?: boolean -) => ( - <> - {showBadge && ( - - - - - )} -

{title}

- - {body} - - -); - -const radioButtonListItems: () => ReadonlyArray< - RadioItem -> = () => [ - { - id: "keepPaymentMethods", - body: generateOptionBody( - I18n.t("bonus.bpd.optInPaymentMethods.choice.options.keepAll.title"), - I18n.t("bonus.bpd.optInPaymentMethods.choice.options.keepAll.body"), - true - ) - }, - { - id: "deletePaymentMethods", - body: generateOptionBody( - I18n.t("bonus.bpd.optInPaymentMethods.choice.options.deleteAll.title"), - I18n.t("bonus.bpd.optInPaymentMethods.choice.options.deleteAll.body") - ) - } -]; - -const disabledButtonProps = () => - disablePrimaryButtonProps( - I18n.t("global.buttons.continue"), - undefined, - "disabledContinueButton" - ); - -const OptInPaymentMethodsChoiceScreen = () => { - const [selectedMethod, setSelectedMethod] = - useState(null); - const navigation = useNavigation(); - const dispatch = useIODispatch(); - const showOptInChoice = useIOSelector(showOptInChoiceSelector); - - const renderingRef = useRef(false); - - const { presentBottomSheet, bottomSheet } = useBottomSheetMethodsToDelete({ - onDeletePress: () => { - throw new Error(); - } - }); - - const bottomButtons: { - [key in PaymentMethodsChoiceOptions]: BlockButtonProps; - } = useMemo( - () => ({ - keepPaymentMethods: confirmButtonProps( - () => { - throw new Error(); - }, - I18n.t("global.buttons.continue"), - undefined, - "continueButton" - ), - - deletePaymentMethods: errorBorderedButtonProps( - presentBottomSheet, - I18n.t("bonus.bpd.optInPaymentMethods.choice.deleteAllButton"), - undefined - ) - }), - [presentBottomSheet] - ); - - const computedBottomButtonProps = useMemo( - () => - selectedMethod === null - ? disabledButtonProps() - : bottomButtons[selectedMethod], - [selectedMethod, bottomButtons] - ); - - useEffect(() => { - if (isUndefined(showOptInChoice)) { - dispatch(optInPaymentMethodsShowChoice.request()); - } - if ( - isError(showOptInChoice) || - (isReady(showOptInChoice) && !showOptInChoice.value) - ) { - navigation.goBack(); - if (isError(showOptInChoice)) { - showToast(I18n.t("global.genericError")); - } - if (!renderingRef.current) { - showToast( - I18n.t("bonus.bpd.optInPaymentMethods.choice.toast"), - "warning" - ); - } - } - - // eslint-disable-next-line functional/immutable-data - renderingRef.current = true; - }, [dispatch, showOptInChoice, navigation]); - - return ( - - {/* The void customGoBack are needed to have a centered header title */} - true} transparent={true} /> - } - > - - -

{I18n.t("bonus.bpd.optInPaymentMethods.choice.title")}

- - - {I18n.t("bonus.bpd.optInPaymentMethods.choice.subtitle")} - - - - - items={radioButtonListItems()} - selectedItem={selectedMethod || undefined} - onPress={setSelectedMethod} - rightSideSelection - /> -
- -
- {bottomSheet} -
-
- ); -}; - -export default OptInPaymentMethodsChoiceScreen; diff --git a/ts/features/bonus/bpd/screens/optInPaymentMethods/OptInPaymentMethodsThankYouDeleteMethodsScreen.tsx b/ts/features/bonus/bpd/screens/optInPaymentMethods/OptInPaymentMethodsThankYouDeleteMethodsScreen.tsx deleted file mode 100644 index fb4241a6ddf..00000000000 --- a/ts/features/bonus/bpd/screens/optInPaymentMethods/OptInPaymentMethodsThankYouDeleteMethodsScreen.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { useNavigation } from "@react-navigation/native"; -import React, { useEffect } from "react"; -import { useDispatch } from "react-redux"; -import I18n from "../../../../../i18n"; -import ROUTES from "../../../../../navigation/routes"; -import { useIOSelector } from "../../../../../store/hooks"; -import { deleteAllPaymentMethodsByFunctionSelector } from "../../../../../store/reducers/wallet/wallets"; -import { showToast } from "../../../../../utils/showToast"; -import { LoadingErrorComponent } from "../../../../../components/LoadingErrorComponent"; -import RetryAfterDeletionFailsComponent from "../../components/optInPaymentMethods/RetryAfterDeletionFailsComponent"; -import ThankYouSuccessComponent from "../../components/optInPaymentMethods/ThankYouSuccessComponent"; -import { - isError, - isLoading, - isReady, - isUndefined -} from "../../../../../common/model/RemoteValue"; -import { optInPaymentMethodsDeletionChoice } from "../../store/actions/optInPaymentMethods"; -import { optInStatusSelector } from "../../store/reducers/details/activation"; - -const OptInPaymentMethodsThankYouDeleteMethodsScreen = () => { - const dispatch = useDispatch(); - const navigation = useNavigation(); - const deleteAllPaymentMethodsByFunctionStatus = useIOSelector( - deleteAllPaymentMethodsByFunctionSelector - ); - const optInStatus = useIOSelector(optInStatusSelector); - - useEffect(() => { - // dispatch saga that delete payment methods and update opt-in field - dispatch(optInPaymentMethodsDeletionChoice()); - }, [dispatch]); - - useEffect(() => { - // if the opt-in choice fails complete the workunit and show an error toast to the user - if ( - pot.isError(optInStatus) && - isReady(deleteAllPaymentMethodsByFunctionStatus) - ) { - showToast(I18n.t("bonus.bpd.optInPaymentMethods.thankYouPage.toast")); - navigation.navigate(ROUTES.WALLET_HOME); - } - }, [ - optInStatus, - deleteAllPaymentMethodsByFunctionStatus, - dispatch, - navigation - ]); - - const isOptInStatusLoading = pot.fold( - optInStatus, - () => true, - () => true, - _ => true, - _ => false, - _ => false, - _ => true, - _ => true, - _ => false - ); - // if the payment methods deletion fails show the retry component - if (isError(deleteAllPaymentMethodsByFunctionStatus)) { - return ; - } - - // if one between deleteAllPaymentMethodsByFunctionStatus and optInStatus is on loading (or undefined) state show loading component - if ( - isUndefined(deleteAllPaymentMethodsByFunctionStatus) || - isLoading(deleteAllPaymentMethodsByFunctionStatus) || - isOptInStatusLoading - ) { - return ( - true} - testID={"loadingComponent"} - /> - ); - } - - if (pot.isError(optInStatus)) { - return null; - } - // both the payment methods deletion and the opt-in update succeed, show the thank you page - return ; -}; - -export default OptInPaymentMethodsThankYouDeleteMethodsScreen; diff --git a/ts/features/bonus/bpd/screens/optInPaymentMethods/OptInPaymentMethodsThankYouKeepMethodsScreen.tsx b/ts/features/bonus/bpd/screens/optInPaymentMethods/OptInPaymentMethodsThankYouKeepMethodsScreen.tsx deleted file mode 100644 index b4fcb82e6f4..00000000000 --- a/ts/features/bonus/bpd/screens/optInPaymentMethods/OptInPaymentMethodsThankYouKeepMethodsScreen.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { useNavigation } from "@react-navigation/native"; -import React, { useEffect } from "react"; -import { useDispatch } from "react-redux"; -import { CitizenOptInStatusEnum } from "../../../../../../definitions/bpd/citizen_v2/CitizenOptInStatus"; -import I18n from "../../../../../i18n"; -import ROUTES from "../../../../../navigation/routes"; -import { useIOSelector } from "../../../../../store/hooks"; -import { showToast } from "../../../../../utils/showToast"; -import { LoadingErrorComponent } from "../../../../../components/LoadingErrorComponent"; -import ThankYouSuccessComponent from "../../components/optInPaymentMethods/ThankYouSuccessComponent"; -import { bpdUpdateOptInStatusMethod } from "../../store/actions/onboarding"; -import { optInStatusSelector } from "../../store/reducers/details/activation"; - -const OptInPaymentMethodsThankYouKeepMethodsScreen = () => { - const dispatch = useDispatch(); - const navigation = useNavigation(); - const optInStatus = useIOSelector(optInStatusSelector); - - useEffect(() => { - // dispatch saga that delete payment methods and update opt-in field - dispatch( - bpdUpdateOptInStatusMethod.request(CitizenOptInStatusEnum.ACCEPTED) - ); - }, [dispatch]); - - useEffect(() => { - // if the opt-in choice fails complete the workunit and show an error toast to the user - if (pot.isError(optInStatus)) { - showToast(I18n.t("bonus.bpd.optInPaymentMethods.thankYouPage.toast")); - navigation.navigate(ROUTES.WALLET_HOME); - } - }, [optInStatus, dispatch, navigation]); - - const isOptInStatusLoading = pot.fold( - optInStatus, - () => true, - () => true, - _ => true, - _ => false, - _ => false, - _ => true, - _ => true, - _ => false - ); - - // if optInStatus is on loading (or undefined) state show loading component - if (isOptInStatusLoading) { - return ( - true} - testID={"loadingComponent"} - /> - ); - } - - if (pot.isError(optInStatus)) { - return null; - } - - // the opt-in update succeed, show the thank you page - return ; -}; - -export default OptInPaymentMethodsThankYouKeepMethodsScreen; diff --git a/ts/features/bonus/bpd/store/actions/details.ts b/ts/features/bonus/bpd/store/actions/details.ts deleted file mode 100644 index deed80aa2b0..00000000000 --- a/ts/features/bonus/bpd/store/actions/details.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { ActionType, createAsyncAction } from "typesafe-actions"; -import { CitizenOptInStatusEnum } from "../../../../../../definitions/bpd/citizen_v2/CitizenOptInStatus"; - -/** - * This file contains all the action related to the bpd details like the activation status, value, etc. - */ - -export type ActivationStatus = "never" | "unsubscribed" | "subscribed"; -// TODO change payload for loadBpdActivationStatus with this one -export type BpdActivationPayload = { - enabled: boolean; - activationStatus: ActivationStatus; - payoffInstr: string | undefined; - optInStatus?: CitizenOptInStatusEnum; - technicalAccount?: string; -}; - -/** - * Request the activation status for the user to the bpd program - */ -export const bpdLoadActivationStatus = createAsyncAction( - "BPD_LOAD_ACTIVATION_STATUS_REQUEST", - "BPD_LOAD_ACTIVATION_STATUS_SUCCESS", - "BPD_LOAD_ACTIVATION_STATUS_FAILURE" -)(); - -export const bpdAllData = createAsyncAction( - "BPD_ALL_DATA_REQUEST", - "BPD_ALL_DATA_SUCCESS", - "BPD_ALL_DATA_FAILURE" -)(); - -export type BpdDetailsActions = - | ActionType - | ActionType; diff --git a/ts/features/bonus/bpd/store/actions/iban.ts b/ts/features/bonus/bpd/store/actions/iban.ts deleted file mode 100644 index 28c08ec9377..00000000000 --- a/ts/features/bonus/bpd/store/actions/iban.ts +++ /dev/null @@ -1,54 +0,0 @@ -// represent the outcome of iban upsert -import { - ActionType, - createAsyncAction, - createStandardAction -} from "typesafe-actions"; -import { Iban } from "../../../../../../definitions/backend/Iban"; -import { IbanStatus } from "../../saga/networking/patchCitizenIban"; - -/** - * This file contains all the action related to the iban insertion / change workflow. - */ - -export type IbanUpsertResult = { payoffInstr?: Iban; status: IbanStatus }; -/** - * Request the upsert of the citizen iban - */ -export const bpdUpsertIban = createAsyncAction( - "BPD_UPSERT_IBAN_REQUEST", - "BPD_UPSERT_IBAN_SUCCESS", - "BPD_UPSERT_IBAN_FAILURE" -)(); - -/** - * The action that triggers the start of insertion / modification of the IBAN - */ -export const bpdIbanInsertionStart = createStandardAction( - "BPD_IBAN_INSERTION_START" -)(); - -/** - * The user choose to go forward and terminate the iban insertion - */ -export const bpdIbanInsertionContinue = createStandardAction( - "BPD_IBAN_INSERTION_CONTINUE" -)(); - -/** - * The user choose to cancel the iban insertion - */ -export const bpdIbanInsertionCancel = createStandardAction( - "BPD_IBAN_INSERTION_CANCEL" -)(); - -export const bpdIbanInsertionResetScreen = createStandardAction( - "BPD_IBAN_INSERTION_RESET_SCREEN" -)(); - -export type BpdIbanActions = - | ActionType - | ActionType - | ActionType - | ActionType - | ActionType; diff --git a/ts/features/bonus/bpd/store/actions/index.ts b/ts/features/bonus/bpd/store/actions/index.ts deleted file mode 100644 index 2276e91ae2a..00000000000 --- a/ts/features/bonus/bpd/store/actions/index.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { BpdDetailsActions } from "./details"; -import { BpdIbanActions } from "./iban"; -import { BpdOnboardingActions } from "./onboarding"; -import { BpdPaymentMethodActions } from "./paymentMethods"; -import { BpdPeriodsAction } from "./periods"; -import { BpdSelectPeriodAction } from "./selectedPeriod"; -import { BpdTransactionsAction } from "./transactions"; -import { OptInPaymentMethodsActions } from "./optInPaymentMethods"; - -export type BpdActions = - | BpdOnboardingActions - | BpdDetailsActions - | BpdIbanActions - | BpdPaymentMethodActions - | BpdPeriodsAction - | BpdTransactionsAction - | BpdSelectPeriodAction - | OptInPaymentMethodsActions; diff --git a/ts/features/bonus/bpd/store/actions/onboarding.ts b/ts/features/bonus/bpd/store/actions/onboarding.ts deleted file mode 100644 index 17f6e59e11b..00000000000 --- a/ts/features/bonus/bpd/store/actions/onboarding.ts +++ /dev/null @@ -1,99 +0,0 @@ -import { - ActionType, - createAsyncAction, - createStandardAction -} from "typesafe-actions"; -import { BpdActivationPayload } from "./details"; - -/** - * Enroll the user to the bpd program - */ -// TODO: change CitizenResource with boolean? -export const bpdEnrollUserToProgram = createAsyncAction( - "BPD_ENROLL_REQUEST", - "BPD_ENROLL_SUCCESS", - "BPD_ENROLL_FAILURE" -)(); - -/** - * update the optInStatus of an enrolled citizen - */ -export const bpdUpdateOptInStatusMethod = createAsyncAction( - "BPD_UPDATE_OPT_IN_STATUS_REQUEST", - "BPD_UPDATE_OPT_IN_STATUS_SUCCESS", - "BPD_UPDATE_OPT_IN_STATUS_FAILURE" -)< - NonNullable, - NonNullable, - Error ->(); - -/** - * Delete user from bpd program - */ -export const bpdDeleteUserFromProgram = createAsyncAction( - "BPD_DELETE_REQUEST", - "BPD_DELETE_SUCCESS", - "BPD_DELETE_FAILURE" -)(); - -/** - * The user ends the unsubscribe workflow - */ -export const bpdUnsubscribeCompleted = createStandardAction( - "BPD_UNSUBSCRIBE_COMPLETED" -)(); - -/** - * The user complete the unsubscribe workflow with a failure - */ -export const bpdUnsubscribeFailure = createStandardAction( - "BPD_UNSUBSCRIBE_COMPLETED_WITH_FAILURE" -)(); - -/** - * Start the onboarding workflow - */ -export const bpdOnboardingStart = createStandardAction( - "BPD_ONBOARDING_START" -)(); - -/** - * The user choose to activate the bpd - */ -export const bpdUserActivate = createStandardAction( - "BPD_ONBOARDING_USER_ACTIVATE" -)(); - -/** - * Cancel the onboarding workflow - */ -export const bpdOnboardingCancel = createStandardAction( - "BPD_ONBOARDING_CANCEL" -)(); - -/** - * The user completed the final step of the onboarding (choose to add iban) - */ -export const bpdOnboardingCompleted = createStandardAction( - "BPD_ONBOARDING_COMPLETED" -)(); - -/** - * The user accepts and confirms the declaration - */ -export const bpdOnboardingAcceptDeclaration = createStandardAction( - "BPD_ONBOARDING_ACCEPT_DECLARATION" -)(); - -export type BpdOnboardingActions = - | ActionType - | ActionType - | ActionType - | ActionType - | ActionType - | ActionType - | ActionType - | ActionType - | ActionType - | ActionType; diff --git a/ts/features/bonus/bpd/store/actions/optInPaymentMethods.ts b/ts/features/bonus/bpd/store/actions/optInPaymentMethods.ts deleted file mode 100644 index 0a7bdb5d761..00000000000 --- a/ts/features/bonus/bpd/store/actions/optInPaymentMethods.ts +++ /dev/null @@ -1,61 +0,0 @@ -import { - ActionType, - createAsyncAction, - createStandardAction -} from "typesafe-actions"; - -/** - * The user starts the workflow for making a decision regarding the opt-in of payment methods - */ -export const optInPaymentMethodsStart = createStandardAction( - "OPT_IN_PAYMENT_METHODS_START" -)(); -/** - * The user completes the workflow for making a decision regarding the opt-in of payment methods - */ -export const optInPaymentMethodsCompleted = createStandardAction( - "OPT_IN_PAYMENT_METHODS_COMPLETED" -)(); - -/** - * The user can't choose to cancel the opt-in choice, this action is needed for the workunit - */ -export const optInPaymentMethodsCancel = createStandardAction( - "OPT_IN_PAYMENT_METHODS_CANCEL" -)(); - -/** - * The user can't choose to press the back button, this action is needed for the workunit - */ -export const optInPaymentMethodsBack = createStandardAction( - "OPT_IN_PAYMENT_METHODS_BACK" -)(); - -/** - * The workflow fails - */ -export const optInPaymentMethodsFailure = createStandardAction( - "OPT_IN_PAYMENT_METHODS_FAILURE" -)(); - -/** - * Triggers the saga that deletes the user's payment methods and update the opt-in choice - */ -export const optInPaymentMethodsDeletionChoice = createStandardAction( - "OPT_IN_PAYMENT_METHODS_DELETION_CHOICE" -)(); - -export const optInPaymentMethodsShowChoice = createAsyncAction( - "OPT_IN_PAYMENT_METHODS_SHOW_CHOICE_REQUEST", - "OPT_IN_PAYMENT_METHODS_SHOW_CHOICE_SUCCESS", - "OPT_IN_PAYMENT_METHODS_SHOW_CHOICE_FAILURE" -)(); - -export type OptInPaymentMethodsActions = - | ActionType - | ActionType - | ActionType - | ActionType - | ActionType - | ActionType - | ActionType; diff --git a/ts/features/bonus/bpd/store/actions/paymentMethods.ts b/ts/features/bonus/bpd/store/actions/paymentMethods.ts deleted file mode 100644 index 862132f1102..00000000000 --- a/ts/features/bonus/bpd/store/actions/paymentMethods.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { IUnitTag } from "@pagopa/ts-commons/lib/units"; -import { ActionType, createAsyncAction } from "typesafe-actions"; - -// Temp type to ensure that only HPan from walled are used to query the bpd pm activation -export type HPan = string & IUnitTag<"HPan">; - -/** - * The possible bpd activation status on a payment method - * - active: bpd is active on the payment method - * - inactive: bpd is not active on the payment method - * - notActivable: bpd activate on the payment method by someone else, cannot be activated - */ -export type BpdPmActivationStatus = "active" | "inactive" | "notActivable"; - -/** - * The response payload that represents the actual state of bpd on a payment method - */ -export type BpdPaymentMethodActivation = { - hPan: HPan; - activationStatus: BpdPmActivationStatus; - activationDate?: string; - deactivationDate?: string; -}; - -/** - * The failure payload when trying to know the actual state or update bpd on a payment method - */ -type BpdPaymentMethodFailure = { - hPan: HPan; - error: Error; -}; - -/** - * The request payload to update the activation status on a payment method - */ -type BpdUpdatePaymentMethodActivationPayload = { - hPan: HPan; - value: boolean; -}; - -/** - * The remote request to find out the current activation status of a payment method. - */ -export const bpdPaymentMethodActivation = createAsyncAction( - "BPD_PAYMENT_METHOD_ACTIVATION_REQUEST", - "BPD_PAYMENT_METHOD_ACTIVATION_SUCCESS", - "BPD_PAYMENT_METHOD_ACTIVATION_FAILURE" -)(); - -/** - * The remote request to change the bpd activation on a payment method - */ -export const bpdUpdatePaymentMethodActivation = createAsyncAction( - "BPD_PAYMENT_METHOD_ACTIVATION_UPDATE_REQUEST", - "BPD_PAYMENT_METHOD_ACTIVATION_UPDATE_SUCCESS", - "BPD_PAYMENT_METHOD_ACTIVATION_UPDATE_FAILURE" -)< - BpdUpdatePaymentMethodActivationPayload, - BpdPaymentMethodActivation, - BpdPaymentMethodFailure ->(); - -export type BpdPaymentMethodActions = - | ActionType - | ActionType; diff --git a/ts/features/bonus/bpd/store/actions/periods.ts b/ts/features/bonus/bpd/store/actions/periods.ts deleted file mode 100644 index ee9b978442b..00000000000 --- a/ts/features/bonus/bpd/store/actions/periods.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { IUnitTag } from "@pagopa/ts-commons/lib/units"; -import { ActionType, createAsyncAction } from "typesafe-actions"; -import { BpdPeriodWithInfo } from "../reducers/details/periods"; - -export type AwardPeriodId = number & IUnitTag<"AwardPeriodId">; - -/** - * The possible state for a period: - * - Active: the current period, can be only one at time - * - Closed: a past period that has ended - * - Inactive: a future period not yet active - */ -export type BpdPeriodStatus = "Active" | "Inactive" | "Closed"; - -/** - * This type contains all the information related to a specific cashback period. - * TODO: use this or the remote data? - */ -export type BpdPeriod = WithAwardPeriodId & { - startDate: Date; - endDate: Date; - status: BpdPeriodStatus; - // minimum transaction number required to be eligible for the cashback - minTransactionNumber: number; - // maxAmount in the API, the max super cashback amount - superCashbackAmount: number; - // last valid position in the ranking to win the super cashback - minPosition: number; - // the max amount of cashback that can be accumulated for the single transaction - maxTransactionCashback: number; - // the max amount of cashback that can be accumulated in the period - maxPeriodCashback: number; - // the cashback % applied foreach transaction - cashbackPercentage: number; - // number of days required from the end of a period to consider the ranking confirmed - gracePeriod: number; -}; - -export type WithAwardPeriodId = { - awardPeriodId: AwardPeriodId; -}; - -/** - * Request the period list & amount - */ -export const bpdPeriodsAmountLoad = createAsyncAction( - "BPD_PERIODS_AMOUNT_REQUEST", - "BPD_PERIODS_AMOUNT_SUCCESS", - "BPD_PERIODS_AMOUNT_FAILURE" -), Error>(); - -export type BpdPeriodsAction = ActionType; diff --git a/ts/features/bonus/bpd/store/actions/selectedPeriod.ts b/ts/features/bonus/bpd/store/actions/selectedPeriod.ts deleted file mode 100644 index c9729e6fa73..00000000000 --- a/ts/features/bonus/bpd/store/actions/selectedPeriod.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ActionType, createStandardAction } from "typesafe-actions"; -import { BpdPeriodWithInfo } from "../reducers/details/periods"; - -/** - * Request the period list - */ -export const bpdSelectPeriod = - createStandardAction("BPD_SELECT_PERIOD")(); - -export type BpdSelectPeriodAction = ActionType; diff --git a/ts/features/bonus/bpd/store/actions/transactions.ts b/ts/features/bonus/bpd/store/actions/transactions.ts deleted file mode 100644 index 6c49ed179cc..00000000000 --- a/ts/features/bonus/bpd/store/actions/transactions.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { IUnitTag } from "@pagopa/ts-commons/lib/units"; -import { ActionType, createAsyncAction } from "typesafe-actions"; -import { TrxCountByDayResourceArray } from "../../../../../../definitions/bpd/winning_transactions_v2/TrxCountByDayResourceArray"; -import { WinningTransactionPageResource } from "../../../../../../definitions/bpd/winning_transactions_v2/WinningTransactionPageResource"; -import { BpdPivotTransaction } from "../reducers/details/transactionsv2/entities"; -import { HPan } from "./paymentMethods"; -import { AwardPeriodId, WithAwardPeriodId } from "./periods"; - -// TODO: placeholder, TBD how to map the circuit Type -export type CircuitType = - | "PagoBancomat" - | "Visa" - | "Mastercard / Maestro" - | "Amex" - | "JCB" - | "UnionPay" - | "Diners" - | "PostePay" - | "BancomatPay" - | "Private" - | "Unknown"; - -/** - * The single transaction acquired in a cashback period - */ -export type BpdTransaction = WithAwardPeriodId & { - // The hashPan of the payment method used for the transaction - hashPan: HPan; - // id acquirer - idTrxAcquirer: string; - // id issuer - idTrxIssuer: string; - // total amount of the transaction, if negative the operation has been canceled - amount: number; - trxDate: Date; - // cashback received from the transaction, if negative the operation has been canceled and also the cashback - cashback: number; - circuitType: CircuitType; -}; - -/** - * Unique Id for BpdTransactions - */ -export type BpdTransactionId = string & IUnitTag<"BpdTransactionId">; - -// TODO: integrate in BpdTransaction after removing the feature flag -export type BpdTransactionV2 = BpdTransaction & { - idTrx: BpdTransactionId; - validForCashback: boolean; - isPivot: boolean; -}; - -export type BpdTransactions = WithAwardPeriodId & { - results: ReadonlyArray; -}; - -export type BpdTransactionsError = WithAwardPeriodId & { - error: Error; -}; - -/** - * Request all the transactions for a specific period - */ -export const bpdTransactionsLoad = createAsyncAction( - "BPD_TRANSACTIONS_REQUEST", - "BPD_TRANSACTIONS_SUCCESS", - "BPD_TRANSACTIONS_FAILURE" -)(); - -type BpdTransactionPageRequestPayload = WithAwardPeriodId & { - nextCursor?: number; -}; - -export type BpdTransactionPageSuccessPayload = WithAwardPeriodId & { - results: WinningTransactionPageResource; -}; - -/** - * Load a page of transactions for a specific period - */ -export const bpdTransactionsLoadPage = createAsyncAction( - "BPD_TRANSACTIONS_PAGE_REQUEST", - "BPD_TRANSACTIONS_PAGE_SUCCESS", - "BPD_TRANSACTIONS_PAGE_FAILURE" -)< - BpdTransactionPageRequestPayload, - BpdTransactionPageSuccessPayload, - BpdTransactionsError ->(); - -export type TrxCountByDayResource = WithAwardPeriodId & { - results: TrxCountByDayResourceArray; -}; - -/** - * Load the countByDay stats for a specific period - */ -export const bpdTransactionsLoadCountByDay = createAsyncAction( - "BPD_TRANSACTIONS_COUNT_BY_DAY_REQUEST", - "BPD_TRANSACTIONS_COUNT_BY_DAY_SUCCESS", - "BPD_TRANSACTIONS_COUNT_BY_DAY_FAILURE" -)(); - -export type TrxMilestonePayload = WithAwardPeriodId & { - result: BpdPivotTransaction | undefined; -}; - -/** - * Load the milestone pivot for a specific period - */ -export const bpdTransactionsLoadMilestone = createAsyncAction( - "BPD_TRANSACTIONS_MILESTONE_REQUEST", - "BPD_TRANSACTIONS_MILESTONE_SUCCESS", - "BPD_TRANSACTIONS_MILESTONE_FAILURE" -)(); - -/** - * Load the required data to render the transaction screen - */ -export const bpdTransactionsLoadRequiredData = createAsyncAction( - "BPD_TRANSACTIONS_LOAD_REQUIRED_DATA_REQUEST", - "BPD_TRANSACTIONS_LOAD_REQUIRED_DATA_SUCCESS", - "BPD_TRANSACTIONS_LOAD_REQUIRED_DATA_FAILURE" -)(); - -export type BpdTransactionsAction = - | ActionType - | ActionType - | ActionType - | ActionType - | ActionType; diff --git a/ts/features/bonus/bpd/store/reducers/__mock__/amount.ts b/ts/features/bonus/bpd/store/reducers/__mock__/amount.ts deleted file mode 100644 index bf903e9d48f..00000000000 --- a/ts/features/bonus/bpd/store/reducers/__mock__/amount.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { BpdAmount } from "../../../saga/networking/amount"; -import { AwardPeriodId } from "../../actions/periods"; - -export const zeroAmount: BpdAmount = { - awardPeriodId: 2 as AwardPeriodId, - totalCashback: 0, - transactionNumber: 0 -}; - -export const notEligibleAmount: BpdAmount = { - awardPeriodId: 2 as AwardPeriodId, - totalCashback: 10.25, - transactionNumber: 3 -}; - -export const eligibleAmount: BpdAmount = { - awardPeriodId: 2 as AwardPeriodId, - totalCashback: 83.52, - transactionNumber: 50 -}; - -export const eligibleMaxAmount: BpdAmount = { - awardPeriodId: 2 as AwardPeriodId, - totalCashback: 150, - transactionNumber: 50 -}; diff --git a/ts/features/bonus/bpd/store/reducers/__mock__/bancomat.ts b/ts/features/bonus/bpd/store/reducers/__mock__/bancomat.ts deleted file mode 100644 index 19d42551128..00000000000 --- a/ts/features/bonus/bpd/store/reducers/__mock__/bancomat.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { WalletTypeEnum } from "../../../../../../../definitions/pagopa/WalletV2"; -import { PatchedWalletV2 } from "../../../../../../types/pagopa"; -import { EnableableFunctionsEnum } from "../../../../../../../definitions/pagopa/EnableableFunctions"; - -export const bancomat = { - walletType: WalletTypeEnum.Bancomat, - createDate: "2021-04-05", - enableableFunctions: [EnableableFunctionsEnum.BPD], - favourite: false, - idWallet: 24415, - info: { - blurredNumber: "0003", - brand: "MASTERCARD", - brandLogo: - "https://wisp2.pagopa.gov.it/wallet/assets/img/creditcard/carta_mc.png", - expireMonth: "4", - expireYear: "2021", - hashPan: "e105a87731025d54181d8e4c4c04ff344ce82e57d6a3d6c6911e8eadb0348d7b", - holder: "Maria Rossi", - htokenList: ["token1", "token2"], - issuerAbiCode: "00123", - type: "PP" - }, - onboardingChannel: "I", - pagoPA: true, - updateDate: "2021-04-05" -} as PatchedWalletV2; diff --git a/ts/features/bonus/bpd/store/reducers/__mock__/periods.ts b/ts/features/bonus/bpd/store/reducers/__mock__/periods.ts deleted file mode 100644 index 7814f38fdf5..00000000000 --- a/ts/features/bonus/bpd/store/reducers/__mock__/periods.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { - AwardPeriodId, - BpdPeriod, - WithAwardPeriodId -} from "../../actions/periods"; - -export const closedPeriod: BpdPeriod = { - awardPeriodId: 0 as AwardPeriodId, - minTransactionNumber: 50, - maxPeriodCashback: 150, - gracePeriod: 5, - startDate: new Date("2020-10-01"), - endDate: new Date("2020-10-31"), - cashbackPercentage: 0.1, - maxTransactionCashback: 10, - minPosition: 100000, - status: "Closed", - superCashbackAmount: 1500 -}; - -export const activePeriod: BpdPeriod = { - awardPeriodId: 1 as AwardPeriodId, - minTransactionNumber: 50, - maxPeriodCashback: 150, - gracePeriod: 5, - startDate: new Date("2020-11-01"), - endDate: new Date("2020-11-30"), - cashbackPercentage: 0.1, - maxTransactionCashback: 10, - minPosition: 100000, - status: "Active", - superCashbackAmount: 1500 -}; - -export const inactivePeriod: BpdPeriod = { - awardPeriodId: 2 as AwardPeriodId, - minTransactionNumber: 50, - maxPeriodCashback: 150, - gracePeriod: 5, - startDate: new Date("2021-01-01"), - endDate: new Date("2021-06-30"), - cashbackPercentage: 0.1, - maxTransactionCashback: 10, - minPosition: 100000, - status: "Inactive", - superCashbackAmount: 1500 -}; - -export const withAwardPeriodId = ( - value: T, - newId: AwardPeriodId -): T => ({ ...value, awardPeriodId: newId }); diff --git a/ts/features/bonus/bpd/store/reducers/__mock__/ranking.ts b/ts/features/bonus/bpd/store/reducers/__mock__/ranking.ts deleted file mode 100644 index 57ecedc2cb8..00000000000 --- a/ts/features/bonus/bpd/store/reducers/__mock__/ranking.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { AwardPeriodId } from "../../actions/periods"; -import { BpdRankingNotReady, BpdRankingReady } from "../details/periods"; - -export const notReadyRanking: BpdRankingNotReady = { - awardPeriodId: 0 as AwardPeriodId, - kind: "notReady" -}; - -export const readyRanking: BpdRankingReady = { - kind: "ready", - awardPeriodId: 1 as AwardPeriodId, - maxTransactionNumber: 150, - minTransactionNumber: 0, - ranking: 1500, - totalParticipants: 30000, - transactionNumber: 5 -}; diff --git a/ts/features/bonus/bpd/store/reducers/__test__/combiner.test.ts b/ts/features/bonus/bpd/store/reducers/__test__/combiner.test.ts deleted file mode 100644 index 2d2ff48da20..00000000000 --- a/ts/features/bonus/bpd/store/reducers/__test__/combiner.test.ts +++ /dev/null @@ -1,342 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { AwardPeriodId } from "../../actions/periods"; -import { eligibleAmount, zeroAmount } from "../__mock__/amount"; -import { - activePeriod, - closedPeriod, - inactivePeriod -} from "../__mock__/periods"; -import { readyRanking } from "../__mock__/ranking"; -import { bpdPeriodsAmountWalletVisibleSelector } from "../details/combiner"; -import { BpdPeriodWithInfo } from "../details/periods"; - -const inactivePeriodA: BpdPeriodWithInfo = { - amount: zeroAmount, - ...{ - ...inactivePeriod, - startDate: new Date("2025-01-01"), - awardPeriodId: 55 as AwardPeriodId - }, - ranking: readyRanking -}; -const inactivePeriodB: BpdPeriodWithInfo = { - amount: zeroAmount, - ...inactivePeriod, - ranking: readyRanking -}; -const inactivePeriodC: BpdPeriodWithInfo = { - amount: zeroAmount, - ...{ - ...inactivePeriod, - startDate: new Date("2022-01-01"), - awardPeriodId: 56 as AwardPeriodId - }, - ranking: readyRanking -}; - -const activePeriodAmount: BpdPeriodWithInfo = { - amount: zeroAmount, - ...activePeriod, - ranking: readyRanking -}; - -const closedPeriodZeroAmount: BpdPeriodWithInfo = { - amount: zeroAmount, - ...closedPeriod, - ranking: readyRanking -}; - -const closedPeriodWithAmount: BpdPeriodWithInfo = { - amount: eligibleAmount, - ...closedPeriod, - ranking: readyRanking -}; - -describe("test bpdPeriodsAmountWalletVisibleSelector when bpd is enabled", () => { - it("one inactive period should return one inactive period", () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([inactivePeriodB]), - pot.some(true) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(1); - expect(visiblePeriods.value[0].awardPeriodId).toBe( - inactivePeriod.awardPeriodId - ); - } - }); - it("with multiple inactive period should return the most recent inactive period", () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([inactivePeriodA, inactivePeriodB, inactivePeriodC]), - pot.some(true) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(1); - expect(visiblePeriods.value[0].awardPeriodId).toBe( - inactivePeriodB.awardPeriodId - ); - } - }); - - it( - "with multiple inactive period and an active period " + - "should return the active period", - () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([ - activePeriodAmount, - inactivePeriodA, - inactivePeriodB, - inactivePeriodC - ]), - pot.some(true) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(1); - expect(visiblePeriods.value[0].awardPeriodId).toBe( - activePeriodAmount.awardPeriodId - ); - } - } - ); - - it( - "with multiple inactive period, an active period and a closed period (with amount zero) " + - "should return the active period", - () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([ - closedPeriodZeroAmount, - activePeriodAmount, - inactivePeriodA, - inactivePeriodB, - inactivePeriodC - ]), - pot.some(true) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(1); - expect(visiblePeriods.value[0].awardPeriodId).toBe( - activePeriodAmount.awardPeriodId - ); - } - } - ); - - it( - "with multiple inactive period, an active period and a closed period (with amount > zero) " + - "should return the active period and the closed period", - () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([ - closedPeriodWithAmount, - activePeriodAmount, - inactivePeriodA, - inactivePeriodB, - inactivePeriodC - ]), - pot.some(true) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(2); - expect(visiblePeriods.value[0].awardPeriodId).toBe( - closedPeriodWithAmount.awardPeriodId - ); - expect(visiblePeriods.value[1].awardPeriodId).toBe( - activePeriodAmount.awardPeriodId - ); - } - } - ); - - it( - "with multiple inactive period and a closed period (with amount > zero) " + - "should return the closed period", - () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([ - closedPeriodWithAmount, - inactivePeriodA, - inactivePeriodB, - inactivePeriodC - ]), - pot.some(true) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(1); - expect(visiblePeriods.value[0].awardPeriodId).toBe( - closedPeriodWithAmount.awardPeriodId - ); - } - } - ); - it( - "with multiple inactive period and a closed period (with amount zero) " + - "should return no periods", - () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([ - closedPeriodZeroAmount, - inactivePeriodA, - inactivePeriodB, - inactivePeriodC - ]), - pot.some(true) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(0); - } - } - ); -}); - -describe("test bpdPeriodsAmountWalletVisibleSelector when bpd is disabled", () => { - it("one inactive period should return no periods", () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([inactivePeriodB]), - pot.some(false) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(0); - } - }); - it("with multiple inactive period should return no periods", () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([inactivePeriodA, inactivePeriodB, inactivePeriodC]), - pot.some(false) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(0); - } - }); - - it( - "with multiple inactive period and an active period " + - "should return no periods", - () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([ - activePeriodAmount, - inactivePeriodA, - inactivePeriodB, - inactivePeriodC - ]), - pot.some(false) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(0); - } - } - ); - - it( - "with multiple inactive period, an active period and a closed period (with amount zero) " + - "should return no periods", - () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([ - closedPeriodZeroAmount, - activePeriodAmount, - inactivePeriodA, - inactivePeriodB, - inactivePeriodC - ]), - pot.some(false) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(0); - } - } - ); - - it( - "with multiple inactive period, an active period and a closed period (with amount > zero) " + - "should return the closed period", - () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([ - closedPeriodWithAmount, - activePeriodAmount, - inactivePeriodA, - inactivePeriodB, - inactivePeriodC - ]), - pot.some(false) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(1); - expect(visiblePeriods.value[0].awardPeriodId).toBe( - closedPeriodWithAmount.awardPeriodId - ); - } - } - ); - - it( - "with multiple inactive period and a closed period (with amount > zero) " + - "should return the closed period", - () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([ - closedPeriodWithAmount, - inactivePeriodA, - inactivePeriodB, - inactivePeriodC - ]), - pot.some(false) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(1); - expect(visiblePeriods.value[0].awardPeriodId).toBe( - closedPeriodWithAmount.awardPeriodId - ); - } - } - ); - it( - "with multiple inactive period and a closed period (with amount zero) " + - "should return no periods", - () => { - const visiblePeriods = bpdPeriodsAmountWalletVisibleSelector.resultFunc( - pot.some([ - closedPeriodZeroAmount, - inactivePeriodA, - inactivePeriodB, - inactivePeriodC - ]), - pot.some(false) - ); - - expect(pot.isSome(visiblePeriods)).toBeTruthy(); - if (pot.isSome(visiblePeriods)) { - expect(visiblePeriods.value.length).toBe(0); - } - } - ); -}); diff --git a/ts/features/bonus/bpd/store/reducers/__test__/payoffInstruments.test.ts b/ts/features/bonus/bpd/store/reducers/__test__/payoffInstruments.test.ts deleted file mode 100644 index 765754400fd..00000000000 --- a/ts/features/bonus/bpd/store/reducers/__test__/payoffInstruments.test.ts +++ /dev/null @@ -1,140 +0,0 @@ -import { Iban } from "../../../../../../../definitions/backend/Iban"; -import { applicationChangeState } from "../../../../../../store/actions/application"; -import { appReducer } from "../../../../../../store/reducers"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { - remoteError, - remoteLoading, - remoteReady, - remoteUndefined -} from "../../../../../../common/model/RemoteValue"; -import { IbanStatus } from "../../../saga/networking/patchCitizenIban"; -import { bpdUpsertIban } from "../../actions/iban"; - -jest.mock("@react-native-async-storage/async-storage", () => ({ - AsyncStorage: jest.fn() -})); - -jest.mock("react-native-share", () => ({ - open: jest.fn() -})); - -describe("test state with action bpdUpsertIban", () => { - const globalState: GlobalState = appReducer( - undefined, - applicationChangeState("active") - ); - - expect( - globalState.bonus.bpd.details.activation.payoffInstr.enrolledValue - ).toBe(remoteUndefined); - - expect( - globalState.bonus.bpd.details.activation.payoffInstr.upsert.outcome - ).toBe(remoteUndefined); - - it("check the state after a bpdUpsertIban.request", () => { - const upsertResult = appReducer( - globalState, - bpdUpsertIban.request("IT12313" as Iban) - ); - expect( - upsertResult.bonus.bpd.details.activation.payoffInstr.enrolledValue - ).toBe(remoteUndefined); - expect( - upsertResult.bonus.bpd.details.activation.payoffInstr.upsert.outcome - ).toBe(remoteLoading); - }); - it("check the state after a bpdUpsertIban.success CANT_VERIFY", () => { - const newIban = "IT123" as Iban; - const result = appReducer( - globalState, - bpdUpsertIban.success({ - status: IbanStatus.CANT_VERIFY, - payoffInstr: newIban - }) - ); - - expect( - result.bonus.bpd.details.activation.payoffInstr.enrolledValue - ).toStrictEqual(remoteReady(newIban)); - expect( - result.bonus.bpd.details.activation.payoffInstr.upsert - ).toStrictEqual({ - outcome: remoteReady("CANT_VERIFY" as IbanStatus), - value: newIban - }); - }); - it("check the state after a bpdUpsertIban.success OK", () => { - const newIban = "IT123" as Iban; - const result = appReducer( - globalState, - bpdUpsertIban.success({ - status: IbanStatus.OK, - payoffInstr: newIban - }) - ); - - expect( - result.bonus.bpd.details.activation.payoffInstr.enrolledValue - ).toStrictEqual(remoteReady(newIban)); - expect( - result.bonus.bpd.details.activation.payoffInstr.upsert - ).toStrictEqual({ - outcome: remoteReady("OK" as IbanStatus), - value: newIban - }); - }); - it("check the state after a bpdUpsertIban.success NOT_VALID", () => { - const result = appReducer( - globalState, - bpdUpsertIban.success({ - status: IbanStatus.NOT_VALID, - payoffInstr: undefined - }) - ); - - expect( - result.bonus.bpd.details.activation.payoffInstr.enrolledValue - ).toStrictEqual(remoteUndefined); - expect( - result.bonus.bpd.details.activation.payoffInstr.upsert - ).toStrictEqual({ - outcome: remoteReady("NOT_VALID" as IbanStatus), - value: undefined - }); - }); - it("check the state after a bpdUpsertIban.success NOT_OWNED", () => { - const result = appReducer( - globalState, - bpdUpsertIban.success({ - status: IbanStatus.NOT_OWNED, - payoffInstr: undefined - }) - ); - - expect( - result.bonus.bpd.details.activation.payoffInstr.enrolledValue - ).toStrictEqual(remoteUndefined); - expect( - result.bonus.bpd.details.activation.payoffInstr.upsert - ).toStrictEqual({ - outcome: remoteReady("NOT_OWNED" as IbanStatus), - value: undefined - }); - }); - it("check the state after a bpdUpsertIban.failure", () => { - const error = new Error("Error!"); - const result = appReducer(globalState, bpdUpsertIban.failure(error)); - - expect( - result.bonus.bpd.details.activation.payoffInstr.enrolledValue - ).toStrictEqual(remoteUndefined); - expect( - result.bonus.bpd.details.activation.payoffInstr.upsert - ).toStrictEqual({ - outcome: remoteError(error), - value: undefined - }); - }); -}); diff --git a/ts/features/bonus/bpd/store/reducers/details/__test__/paymentMethods.test.ts b/ts/features/bonus/bpd/store/reducers/details/__test__/paymentMethods.test.ts deleted file mode 100644 index 15b63ee7429..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/__test__/paymentMethods.test.ts +++ /dev/null @@ -1,109 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { - areAnyPaymentMethodsActiveSelector, - BpdPotPaymentMethodActivation -} from "../paymentMethods"; -import { IndexedById } from "../../../../../../../store/helpers/indexer"; -import { HPan } from "../../../actions/paymentMethods"; -import { fromPatchedWalletV2ToRawPaymentMethod } from "../../../../../../../utils/walletv2"; -import { bancomat } from "../../__mock__/bancomat"; -import { BancomatPaymentMethod } from "../../../../../../../types/pagopa"; - -const indexedPaymentMethods: IndexedById = { - id1: pot.some({ - hPan: "hpan1" as HPan, - activationStatus: "active" - }), - id2: pot.some({ - hPan: "hpan2" as HPan, - activationStatus: "active" - }), - id3: pot.some({ - hPan: "hpan2" as HPan, - activationStatus: "active" - }) -}; - -const paymentMethod = fromPatchedWalletV2ToRawPaymentMethod( - bancomat -) as BancomatPaymentMethod; -const paymentMethodBancomat = { - ...paymentMethod, - info: { ...paymentMethod.info, hashPan: "id1" } -}; - -describe("payment methods reducer tests", () => { - it("should return false since no methods are provided", () => { - expect( - areAnyPaymentMethodsActiveSelector([]).resultFunc(indexedPaymentMethods) - ).toBeFalsy(); - }); - - it("should return true (id1 - active)", () => { - expect( - areAnyPaymentMethodsActiveSelector([paymentMethodBancomat]).resultFunc( - indexedPaymentMethods - ) - ).toBeTruthy(); - }); - - it("should return false (id1 - not active)", () => { - expect( - areAnyPaymentMethodsActiveSelector([paymentMethodBancomat]).resultFunc({ - ...indexedPaymentMethods, - id1: pot.some({ - hPan: "hpan1" as HPan, - activationStatus: "inactive" - }) - }) - ).toBeFalsy(); - }); - - it("should return true, even if in error (some error)", () => { - expect( - areAnyPaymentMethodsActiveSelector([paymentMethodBancomat]).resultFunc({ - ...indexedPaymentMethods, - id1: pot.toError( - pot.some({ - hPan: "hpan1" as HPan, - activationStatus: "active" - }), - new Error("some error") - ) - }) - ).toBeTruthy(); - }); - - it("should return false (none error)", () => { - expect( - areAnyPaymentMethodsActiveSelector([paymentMethodBancomat]).resultFunc({ - ...indexedPaymentMethods, - id1: pot.toError(pot.none, new Error("some error")) - }) - ).toBeFalsy(); - }); - - it("should return true (at least one active)", () => { - const indexedPaymentMethodsOneActive: IndexedById = - { - id1: pot.some({ - hPan: "hpan1" as HPan, - activationStatus: "active" - }), - id2: pot.some({ - hPan: "hpan2" as HPan, - activationStatus: "inactive" - }), - id3: pot.some({ - hPan: "hpan2" as HPan, - activationStatus: "notActivable" - }), - id4: pot.none - }; - expect( - areAnyPaymentMethodsActiveSelector([paymentMethodBancomat]).resultFunc( - indexedPaymentMethodsOneActive - ) - ).toBeTruthy(); - }); -}); diff --git a/ts/features/bonus/bpd/store/reducers/details/activation/index.ts b/ts/features/bonus/bpd/store/reducers/details/activation/index.ts deleted file mode 100644 index 016572cdb6e..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/activation/index.ts +++ /dev/null @@ -1,233 +0,0 @@ -import * as O from "fp-ts/lib/Option"; -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { combineReducers } from "redux"; -import { createSelector } from "reselect"; -import { getType } from "typesafe-actions"; -import { pipe } from "fp-ts/lib/function"; -import { Action } from "../../../../../../../store/actions/types"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { - getValue, - remoteError, - remoteLoading, - remoteReady, - remoteUndefined, - RemoteValue -} from "../../../../../../../common/model/RemoteValue"; -import { - ActivationStatus, - bpdLoadActivationStatus -} from "../../../actions/details"; -import { - bpdDeleteUserFromProgram, - bpdEnrollUserToProgram, - bpdUnsubscribeCompleted, - bpdUnsubscribeFailure, - bpdUpdateOptInStatusMethod -} from "../../../actions/onboarding"; -import { CitizenOptInStatusEnum } from "../../../../../../../../definitions/bpd/citizen_v2/CitizenOptInStatus"; -import { optInPaymentMethodsStart } from "../../../actions/optInPaymentMethods"; -import paymentInstrumentReducer, { - bpdUpsertIbanSelector, - PayoffInstrumentType -} from "./payoffInstrument"; -import technicalAccountReducer, { - bpdTechnicalAccountSelector -} from "./technicalAccount"; -import { bpdActivationUiReducer, BpdActivationUiState } from "./ui"; - -export type BpdActivation = { - enabled: pot.Pot; - activationStatus: RemoteValue; - payoffInstr: PayoffInstrumentType; - unsubscription: RemoteValue; - technicalAccount: RemoteValue; - optInStatus: pot.Pot; - ui: BpdActivationUiState; -}; - -/** - * This reducer keeps the enabled status sync with the remote endpoint. - * This value can change when: - * - The application loads the first time the value - * - The user chooses to activate the bpd program - * - The user chooses to unsubscribe from the bpd program - * @param state - * @param action - */ -// TODO: check if the logic is ok -const enabledReducer = ( - state: pot.Pot = pot.none, - action: Action -): pot.Pot => { - switch (action.type) { - case getType(bpdLoadActivationStatus.request): - case getType(bpdEnrollUserToProgram.request): - return pot.toLoading(state); - case getType(bpdLoadActivationStatus.success): - case getType(bpdEnrollUserToProgram.success): - return pot.some(action.payload.enabled); - case getType(bpdDeleteUserFromProgram.success): - return pot.none; - case getType(bpdLoadActivationStatus.failure): - case getType(bpdEnrollUserToProgram.failure): - return pot.toError(state, action.payload); - } - return state; -}; - -const OPT_IN_INITIAL_STATE = pot.none; -const optInStatusReducer = ( - state: pot.Pot = OPT_IN_INITIAL_STATE, - action: Action -): pot.Pot => { - switch (action.type) { - case getType(optInPaymentMethodsStart): - return OPT_IN_INITIAL_STATE; - case getType(bpdLoadActivationStatus.request): - return pot.toLoading(state); - case getType(bpdUpdateOptInStatusMethod.request): - return pot.toUpdating(state, action.payload); - case getType(bpdLoadActivationStatus.success): - return action.payload.optInStatus - ? pot.some(action.payload.optInStatus) - : pot.none; - case getType(bpdUpdateOptInStatusMethod.success): - return pot.some(action.payload); - case getType(bpdLoadActivationStatus.failure): - case getType(bpdEnrollUserToProgram.failure): - case getType(bpdUpdateOptInStatusMethod.failure): - return pot.toError(state, action.payload); - } - return state; -}; - -/** - * Keep the state of "unsubscribe" from bpd outcome - * @param state - * @param action - */ -const unsubscriptionReducer = ( - state: RemoteValue = remoteUndefined, - action: Action -): RemoteValue => { - switch (action.type) { - case getType(bpdDeleteUserFromProgram.request): - return remoteLoading; - case getType(bpdDeleteUserFromProgram.success): - return remoteReady(true); - case getType(bpdDeleteUserFromProgram.failure): - return remoteError(action.payload); - // reset the state when return to wallet - case getType(bpdUnsubscribeCompleted): - case getType(bpdUnsubscribeFailure): - return remoteUndefined; - } - return state; -}; - -const activationStatusReducer = ( - state: RemoteValue = remoteUndefined, - action: Action -): RemoteValue => { - switch (action.type) { - case getType(bpdLoadActivationStatus.request): - case getType(bpdEnrollUserToProgram.request): - return remoteLoading; - case getType(bpdLoadActivationStatus.success): - case getType(bpdEnrollUserToProgram.success): - return remoteReady(action.payload.activationStatus); - case getType(bpdDeleteUserFromProgram.success): - return remoteUndefined; - case getType(bpdLoadActivationStatus.failure): - case getType(bpdEnrollUserToProgram.failure): - return remoteError(action.payload); - } - return state; -}; - -const bpdActivationReducer = combineReducers({ - enabled: enabledReducer, - activationStatus: activationStatusReducer, - payoffInstr: paymentInstrumentReducer, - unsubscription: unsubscriptionReducer, - technicalAccount: technicalAccountReducer, - optInStatus: optInStatusReducer, - ui: bpdActivationUiReducer -}); - -/** - * Return the optInStatus value related to the bpd program - * @param state - */ -export const optInStatusSelector = ( - state: GlobalState -): pot.Pot => - state.bonus.bpd.details.activation.optInStatus; - -/** - * Return the enabled value related to the bpd program - * @param state - */ -export const bpdEnabledSelector = ( - state: GlobalState -): pot.Pot => state.bonus.bpd.details.activation.enabled; - -/** - * Return the enabled value related to the bpd activation status - * @param state - */ -export const activationStatusSelector = ( - state: GlobalState -): RemoteValue => - state.bonus.bpd.details.activation.activationStatus; - -/** - * Return the Iban that the user has entered to receive the cashback amount - * @return {RemoteValue} - */ -export const bpdIbanSelector = createSelector< - GlobalState, - RemoteValue, - RemoteValue ->( - [ - (state: GlobalState) => - state.bonus.bpd.details.activation.payoffInstr.enrolledValue - ], - iban => iban -); - -/** - * Return the unsubscription state, memoized - */ -export const bpdUnsubscriptionSelector = createSelector( - [(state: GlobalState) => state.bonus.bpd.details.activation.unsubscription], - unsubscription => unsubscription -); - -/** - * Return the prefill text based on the iban upsert or iban inserted by user - * if the user tried to add a new iban (upsertIban.value !== undefined) return that iban - * else try to return the actual iban getValue(iban) - * - */ -export const bpdIbanPrefillSelector = createSelector( - [bpdIbanSelector, bpdUpsertIbanSelector, bpdTechnicalAccountSelector], - (iban, upsertIban, technicalAccount): string => - pipe( - upsertIban.value as string, - O.fromNullable, - O.alt(() => - pipe( - getValue(technicalAccount), - O.fromNullable, - O.map(_ => "") - ) - ), - O.alt(() => O.fromNullable(getValue(iban))), - O.getOrElse(() => "") - ) -); - -export default bpdActivationReducer; diff --git a/ts/features/bonus/bpd/store/reducers/details/activation/payoffInstrument.ts b/ts/features/bonus/bpd/store/reducers/details/activation/payoffInstrument.ts deleted file mode 100644 index a194e4cd0ba..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/activation/payoffInstrument.ts +++ /dev/null @@ -1,130 +0,0 @@ -import { combineReducers } from "redux"; -import { getType } from "typesafe-actions"; -import { Iban } from "../../../../../../../../definitions/backend/Iban"; -import { Action } from "../../../../../../../store/actions/types"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { - isError, - remoteError, - remoteLoading, - remoteReady, - remoteUndefined, - RemoteValue -} from "../../../../../../../common/model/RemoteValue"; -import { IbanStatus } from "../../../../saga/networking/patchCitizenIban"; -import { bpdLoadActivationStatus } from "../../../actions/details"; -import { - bpdIbanInsertionResetScreen, - bpdIbanInsertionStart, - bpdUpsertIban -} from "../../../actions/iban"; -import { bpdDeleteUserFromProgram } from "../../../actions/onboarding"; - -export type UpsertIBAN = { - // the results of the upsert operation - outcome: RemoteValue; - // the value the user is trying to enter - value: Iban | undefined; -}; - -export type PayoffInstrumentType = { - enrolledValue: RemoteValue; - upsert: UpsertIBAN; -}; - -/** - * This reducer keeps the latest valid paymentInstrument (IBAN) for the user. - * This value can change when: - * - The application load the first time the value - * - The user choose to edit / add a new paymentInstrument and the operation is completed with success. - * @param state - * @param action - */ -const paymentInstrumentValueReducer = ( - state: RemoteValue = remoteUndefined, - action: Action -): RemoteValue => { - switch (action.type) { - case getType(bpdDeleteUserFromProgram.success): - return remoteUndefined; - case getType(bpdLoadActivationStatus.request): - return remoteLoading; - case getType(bpdLoadActivationStatus.success): - return remoteReady(action.payload.payoffInstr); - // Update the effective value only if the upsert is OK or CANT_VERIFY - case getType(bpdUpsertIban.success): - return action.payload.status === IbanStatus.OK || - action.payload.status === IbanStatus.CANT_VERIFY - ? remoteReady(action.payload.payoffInstr) - : state; - case getType(bpdLoadActivationStatus.failure): - return remoteError(action.payload); - } - return state; -}; - -const INITIAL_UPSERT: UpsertIBAN = { - outcome: remoteUndefined, - value: undefined -}; - -/** - * This reducer updates the state of the upsert operation. - * @param state - * @param action - */ -const paymentInstrumentUpsertReducer = ( - state: UpsertIBAN = INITIAL_UPSERT, - action: Action -): UpsertIBAN => { - switch (action.type) { - case getType(bpdUpsertIban.request): - return { - value: action.payload, - outcome: remoteLoading - }; - case getType(bpdUpsertIban.success): - return { - value: action.payload.payoffInstr ?? state.value, - outcome: remoteReady(action.payload.status) - }; - case getType(bpdUpsertIban.failure): - return { - ...state, - outcome: remoteError(action.payload) - }; - case getType(bpdIbanInsertionResetScreen): - return { - ...state, - outcome: remoteUndefined - }; - case getType(bpdIbanInsertionStart): - return INITIAL_UPSERT; - } - return state; -}; - -/** - * Return the Iban that is currently used for an upsert operation. - * This is not the current Iban associated to the bpd program, but only a candidate. - * @param state - */ -export const bpdUpsertIbanSelector = (state: GlobalState): UpsertIBAN => - state.bonus.bpd.details.activation.payoffInstr.upsert; - -/** - * Return true if the iban upsertion received errors - * @param state - */ -export const bpdUpsertIbanIsError = (state: GlobalState): boolean => - isError(state.bonus.bpd.details.activation.payoffInstr.upsert.outcome); - -const paymentInstrumentReducer = combineReducers({ - // value is the effective value of the iban enrolled to the bpd program. - // it's the remote saved value. - enrolledValue: paymentInstrumentValueReducer, - // all the information related to the try to upsert a new iban. - upsert: paymentInstrumentUpsertReducer -}); - -export default paymentInstrumentReducer; diff --git a/ts/features/bonus/bpd/store/reducers/details/activation/technicalAccount.ts b/ts/features/bonus/bpd/store/reducers/details/activation/technicalAccount.ts deleted file mode 100644 index f6045a18507..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/activation/technicalAccount.ts +++ /dev/null @@ -1,65 +0,0 @@ -import { getType } from "typesafe-actions"; -import { createSelector } from "reselect"; -import { - remoteError, - remoteLoading, - remoteReady, - remoteUndefined, - RemoteValue -} from "../../../../../../../common/model/RemoteValue"; -import { Action } from "../../../../../../../store/actions/types"; -import { bpdDeleteUserFromProgram } from "../../../actions/onboarding"; -import { bpdLoadActivationStatus } from "../../../actions/details"; -import { bpdUpsertIban } from "../../../actions/iban"; -import { IbanStatus } from "../../../../saga/networking/patchCitizenIban"; -import { GlobalState } from "../../../../../../../store/reducers/types"; - -/** - * This reducer keeps the latest valid technicalAccount (technical IBAN) for the user. - * This value can change when: - * - The application loads the first time the value - * - The user chooses to edit / add a new paymentInstrument and the operation is completed with success. - * - The user chooses to unsubscribe from the cashback - * - * If the action `bpdLoadActivationStatus.success` is dispatched after a call to citizen v1 API, so the - * the technical account data is not available, the state will be remoteUndefined. - * @param state - * @param action - */ -const technicalAccountReducer = ( - state: RemoteValue = remoteUndefined, - action: Action -): RemoteValue => { - switch (action.type) { - case getType(bpdDeleteUserFromProgram.success): - return remoteUndefined; - case getType(bpdLoadActivationStatus.request): - return remoteLoading; - case getType(bpdLoadActivationStatus.success): - return remoteReady(action.payload.technicalAccount); - // Update the effective value only if the upsert is OK or CANT_VERIFY - case getType(bpdUpsertIban.success): - return action.payload.status === IbanStatus.OK || - action.payload.status === IbanStatus.CANT_VERIFY - ? remoteReady(undefined) - : state; - case getType(bpdLoadActivationStatus.failure): - return remoteError(action.payload); - } - return state; -}; - -export default technicalAccountReducer; - -/** - * Returns the technical account string to show to the user if he has one - * @return {RemoteValue} - */ -export const bpdTechnicalAccountSelector = createSelector< - GlobalState, - RemoteValue, - RemoteValue ->( - [(state: GlobalState) => state.bonus.bpd.details.activation.technicalAccount], - technicalAccount => technicalAccount -); diff --git a/ts/features/bonus/bpd/store/reducers/details/activation/ui.ts b/ts/features/bonus/bpd/store/reducers/details/activation/ui.ts deleted file mode 100644 index 26ff69147a2..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/activation/ui.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { getType } from "typesafe-actions"; -import { combineReducers } from "redux"; -import { - remoteError, - remoteLoading, - remoteReady, - remoteUndefined, - RemoteValue -} from "../../../../../../../common/model/RemoteValue"; -import { Action } from "../../../../../../../store/actions/types"; -import { optInPaymentMethodsShowChoice } from "../../../actions/optInPaymentMethods"; -import { GlobalState } from "../../../../../../../store/reducers/types"; - -export type ShowOptInChoice = RemoteValue; -export type BpdActivationUiState = { - showOptInChoice: ShowOptInChoice; -}; - -const SHOW_OPT_IN_CHOICE_INITIAL_STATE: ShowOptInChoice = remoteUndefined; -const showOptInChoiceReducer = ( - state: ShowOptInChoice = SHOW_OPT_IN_CHOICE_INITIAL_STATE, - action: Action -): ShowOptInChoice => { - switch (action.type) { - case getType(optInPaymentMethodsShowChoice.request): - return remoteLoading; - case getType(optInPaymentMethodsShowChoice.success): - return remoteReady(action.payload); - case getType(optInPaymentMethodsShowChoice.failure): - return remoteError(action.payload); - } - return state; -}; - -export const bpdActivationUiReducer = combineReducers< - BpdActivationUiState, - Action ->({ - showOptInChoice: showOptInChoiceReducer -}); - -/** - * Return the optInStatus value related to the bpd program - * @param state - */ -export const showOptInChoiceSelector = (state: GlobalState): ShowOptInChoice => - state.bonus.bpd.details.activation.ui.showOptInChoice; diff --git a/ts/features/bonus/bpd/store/reducers/details/combiner.ts b/ts/features/bonus/bpd/store/reducers/details/combiner.ts deleted file mode 100644 index 424f3a7db63..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/combiner.ts +++ /dev/null @@ -1,211 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { pipe } from "fp-ts/lib/function"; -import * as O from "fp-ts/lib/Option"; -import { createSelector } from "reselect"; -import { cardIcons } from "../../../../../../components/wallet/card/Logo"; -import { paymentMethodsSelector } from "../../../../../../store/reducers/wallet/wallets"; -import { PaymentMethod } from "../../../../../../types/pagopa"; -import { getPaymentMethodHash } from "../../../../../../utils/paymentMethod"; -import { FOUR_UNICODE_CIRCLES } from "../../../../../../utils/wallet"; -import { EnhancedBpdTransaction } from "../../../components/transactionItem/BpdTransactionItem"; -import { BpdPaymentMethodActivation } from "../../actions/paymentMethods"; -import { BpdTransaction } from "../../actions/transactions"; -import { bpdEnabledSelector } from "./activation"; -import { bpdPaymentMethodActivationSelector } from "./paymentMethods"; -import { bpdPeriodsSelector, BpdPeriodWithInfo } from "./periods"; -import { bpdSelectedPeriodSelector } from "./selectedPeriod"; -import { bpdTransactionsForSelectedPeriod } from "./transactions"; - -/** - * A period is visible in the wallet if the bpd is enabled AND the period is active OR - * the period is closed and the transactionNumber > 0 OR - * bpd is enabled AND the period is inactive (future) AND there are no active or closed period - * @param periodList - * @param periodAmount should be a period in periodList - * @param bpdEnabled - */ -const isPeriodAmountWalletVisible = ( - periodList: ReadonlyArray, - periodAmount: BpdPeriodWithInfo, - bpdEnabled: pot.Pot -) => - pot.isSome(bpdEnabled) && - ((periodAmount.status === "Active" && bpdEnabled.value) || - (periodAmount.status === "Closed" && - periodAmount.amount.transactionNumber > 0) || - // All the periods are inactive - (periodList.every(p => p.status === "Inactive") && - // This is the first inactive period - periodList.indexOf(periodAmount) === 0 && - periodAmount.status === "Inactive" && - bpdEnabled.value)); - -/** - * Return the {@link BpdPeriodWithInfo} that can be visible in the wallet - */ -export const bpdPeriodsAmountWalletVisibleSelector = createSelector( - [bpdPeriodsSelector, bpdEnabledSelector], - (potPeriodsAmount, bpdEnabled) => - pot.map(potPeriodsAmount, periodsAmountList => { - const periodsOrderedByDate = periodsAmountList - // create a sorted copy of the array - .concat() - .sort((pa1, pa2) => - pa1.startDate < pa2.startDate - ? -1 - : pa1.startDate > pa2.startDate - ? 1 - : 0 - ); - return periodsOrderedByDate.filter(periodAmount => - isPeriodAmountWalletVisible( - periodsOrderedByDate, - periodAmount, - bpdEnabled - ) - ); - }) -); - -/** - * The period should be visible in the snapped list only if: - * state === Closed (a closed period is always visible) - * bpdEnabled === true (a inactive or current period is visible only if bpd is Enabled) - * @param periodAmount - * @param bpdEnabled - */ -const isPeriodAmountSnappedVisible = ( - periodAmount: BpdPeriodWithInfo, - bpdEnabled: pot.Pot -) => periodAmount.status === "Closed" || pot.getOrElse(bpdEnabled, false); - -/** - * Return the {@link BpdPeriodWithInfo} that should be visible in the snapped List selector - */ -export const bpdPeriodsAmountSnappedListSelector = createSelector( - [bpdPeriodsSelector, bpdEnabledSelector], - (potPeriodsAmount, bpdEnabled) => - pot.map(potPeriodsAmount, periodsAmountList => - periodsAmountList.filter(periodAmount => - isPeriodAmountSnappedVisible(periodAmount, bpdEnabled) - ) - ) -); - -/** - * Pick a payment instrument from the wallet, using the provided hashpan - * @param hashPan - * @param potPaymentMethods - */ -export const pickPaymentMethodFromHashpan = ( - hashPan: string, - potPaymentMethods: pot.Pot, Error> -): O.Option => - pot.getOrElse( - pot.map(potPaymentMethods, paymentMethods => - O.fromNullable( - paymentMethods.find(w => getPaymentMethodHash(w) === hashPan) - ) - ), - O.none - ); - -const getId = (transaction: BpdTransaction) => - `${transaction.awardPeriodId}${transaction.trxDate}${transaction.hashPan}${transaction.idTrxAcquirer}${transaction.idTrxIssuer}${transaction.amount}`; - -/** - * Enhance a {@link BpdTransaction}, trying to found the payment method in the wallet, - * in order to associate a caption and an icon - */ -export const bpdDisplayTransactionsSelector = createSelector( - [ - bpdTransactionsForSelectedPeriod, - paymentMethodsSelector, - bpdSelectedPeriodSelector - ], - ( - potTransactions, - paymentMethod, - period - ): pot.Pot, Error> => - pot.map(potTransactions, transactions => - transactions.map( - t => - ({ - ...t, - image: pipe( - pickPaymentMethodFromHashpan(t.hashPan, paymentMethod), - O.map(pm => pm.icon), - O.getOrElse(() => cardIcons.UNKNOWN) - ), - title: pipe( - pickPaymentMethodFromHashpan(t.hashPan, paymentMethod), - O.map(pm => pm.caption), - O.getOrElse(() => FOUR_UNICODE_CIRCLES) - ), - keyId: getId(t), - maxCashbackForTransactionAmount: period?.maxTransactionCashback - } as EnhancedBpdTransaction) - ) - ) -); - -/** - * There is at least one payment method with bpd enabled? - */ -export const atLeastOnePaymentMethodHasBpdEnabledSelector = createSelector( - [paymentMethodsSelector, bpdPaymentMethodActivationSelector], - (paymentMethodsPot, bpdActivations): boolean => - pot.getOrElse( - pot.map(paymentMethodsPot, paymentMethods => - paymentMethods.some(pm => - pipe( - getPaymentMethodHash(pm), - O.fromNullable, - O.map(hpan => bpdActivations[hpan]), - O.map( - potActivation => - potActivation && - pot.isSome(potActivation) && - potActivation.value.activationStatus === "active" - ), - O.getOrElseW(() => false) - ) - ) - ), - false - ) -); - -export type PaymentMethodWithActivation = PaymentMethod & - Partial>; - -/** - * Add the information of activationStatus to a PatchedWalletV2 - * in order to group the elements "notActivable" - */ -export const paymentMethodsWithActivationStatusSelector = createSelector( - [paymentMethodsSelector, bpdPaymentMethodActivationSelector], - (paymentMethodsPot, bpdActivations) => - pot.map(paymentMethodsPot, paymentMethods => - paymentMethods.map(pm => { - // try to extract the activation status to enhance the wallet - const activationStatus = pipe( - getPaymentMethodHash(pm), - O.fromNullable, - O.chain(hp => O.fromNullable(bpdActivations[hp])), - O.map(paymentMethodActivation => - pot.getOrElse( - pot.map( - paymentMethodActivation, - activationStatus => activationStatus.activationStatus - ), - undefined - ) - ), - O.toUndefined - ); - return { ...pm, activationStatus }; - }) - ) -); diff --git a/ts/features/bonus/bpd/store/reducers/details/index.ts b/ts/features/bonus/bpd/store/reducers/details/index.ts deleted file mode 100644 index ae35d8a0e55..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/index.ts +++ /dev/null @@ -1,45 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { Action, combineReducers } from "redux"; -import { IndexedById } from "../../../../../../store/helpers/indexer"; -import { BpdTransaction } from "../../actions/transactions"; -import bpdActivationReducer, { BpdActivation } from "./activation"; -import { bpdLastUpdateReducer, lastUpdate } from "./lastUpdate"; -import { - bpdPaymentMethodsReducer, - BpdPotPaymentMethodActivation -} from "./paymentMethods"; -import { bpdPeriodsReducer, BpdPeriodWithInfo } from "./periods"; -import { bpdSelectedPeriodsReducer } from "./selectedPeriod"; -import { bpdTransactionsReducer } from "./transactions"; -import { - bpdTransactionsV2Reducer, - BpdTransactionsV2State -} from "./transactionsv2"; - -export type BpdDetailsState = { - activation: BpdActivation; - paymentMethods: IndexedById; - periods: pot.Pot, Error>; - selectedPeriod: BpdPeriodWithInfo | null; - transactions: IndexedById, Error>>; - transactionsV2: BpdTransactionsV2State; - lastUpdate: lastUpdate; -}; - -const bpdDetailsReducer = combineReducers({ - // The information related to the activation (enabled / IBAN) - activation: bpdActivationReducer, - // The state of cashback on each payment method in the wallet - paymentMethods: bpdPaymentMethodsReducer, - // All the periods of the cashback - periods: bpdPeriodsReducer, - // the current period displayed, selected by the user - selectedPeriod: bpdSelectedPeriodsReducer, - transactions: bpdTransactionsReducer, - // TODO: replace with transactions when completed - transactionsV2: bpdTransactionsV2Reducer, - // the last time we received updated data - lastUpdate: bpdLastUpdateReducer -}); - -export default bpdDetailsReducer; diff --git a/ts/features/bonus/bpd/store/reducers/details/lastUpdate.ts b/ts/features/bonus/bpd/store/reducers/details/lastUpdate.ts deleted file mode 100644 index 0e80d7892c0..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/lastUpdate.ts +++ /dev/null @@ -1,27 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { getType } from "typesafe-actions"; -import { Action } from "../../../../../../store/actions/types"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { bpdAllData } from "../../actions/details"; - -export type lastUpdate = pot.Pot; - -export const bpdLastUpdateReducer = ( - state: lastUpdate = pot.none, - action: Action -): lastUpdate => { - switch (action.type) { - // If the bpd info load succeed set a new date - case getType(bpdAllData.success): - return pot.some(new Date()); - case getType(bpdAllData.request): - return pot.toLoading(state); - case getType(bpdAllData.failure): - return pot.toError(state, action.payload); - } - - return state; -}; - -export const bpdLastUpdateSelector = (state: GlobalState) => - state.bonus.bpd.details.lastUpdate; diff --git a/ts/features/bonus/bpd/store/reducers/details/paymentMethods.ts b/ts/features/bonus/bpd/store/reducers/details/paymentMethods.ts deleted file mode 100644 index 25ae05ea504..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/paymentMethods.ts +++ /dev/null @@ -1,155 +0,0 @@ -import * as O from "fp-ts/lib/Option"; -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { createSelector } from "reselect"; -import { getType } from "typesafe-actions"; -import { pipe } from "fp-ts/lib/function"; -import { Action } from "../../../../../../store/actions/types"; -import { deleteWalletSuccess } from "../../../../../../store/actions/wallet/wallets"; -import { IndexedById } from "../../../../../../store/helpers/indexer"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { bpdDeleteUserFromProgram } from "../../actions/onboarding"; -import { - BpdPaymentMethodActivation, - bpdPaymentMethodActivation, - BpdPmActivationStatus, - bpdUpdatePaymentMethodActivation, - HPan -} from "../../actions/paymentMethods"; -import { PaymentMethod } from "../../../../../../types/pagopa"; -import { getPaymentMethodHash } from "../../../../../../utils/paymentMethod"; - -export type BpdPotPaymentMethodActivation = pot.Pot< - BpdPaymentMethodActivation, - Error ->; - -const readPot = ( - hPan: HPan, - data: IndexedById -): BpdPotPaymentMethodActivation => - pipe( - data[hPan], - O.fromNullable, - O.getOrElseW(() => pot.none) - ); - -export const getPaymentStatus = (value: boolean): BpdPmActivationStatus => - value ? "active" : "inactive"; - -/** - * This reducer keep the activation state and the upsert request foreach payment method, - * grouped by hPan. - * Foreach hPan there is a {@link BpdPotPaymentMethodActivation} containing the related bpd activation information. - * TODO: refactor with the common function in IndexedByIdPot - * @param state - * @param action - */ -export const bpdPaymentMethodsReducer = ( - state: IndexedById = {}, - action: Action -): IndexedById => { - switch (action.type) { - case getType(bpdPaymentMethodActivation.request): - return { ...state, [action.payload]: pot.noneLoading }; - case getType(bpdPaymentMethodActivation.success): - return { - ...state, - [action.payload.hPan]: pot.some(action.payload) - }; - case getType(bpdPaymentMethodActivation.failure): - return { - ...state, - [action.payload.hPan]: pot.toError( - readPot(action.payload.hPan, state), - action.payload.error - ) - }; - case getType(bpdUpdatePaymentMethodActivation.request): - const updateRequest = readPot(action.payload.hPan, state); - // write the candidate activationStatus, preserving all the others fields - return { - ...state, - [action.payload.hPan]: pot.toUpdating(updateRequest, { - ...(pot.isSome(updateRequest) - ? updateRequest.value - : { hPan: action.payload.hPan }), - activationStatus: getPaymentStatus(action.payload.value) - }) - }; - case getType(bpdUpdatePaymentMethodActivation.success): - return { ...state, [action.payload.hPan]: pot.some(action.payload) }; - case getType(bpdUpdatePaymentMethodActivation.failure): - const updateFailure = readPot(action.payload.hPan, state); - return { - ...state, - [action.payload.hPan]: pot.toError(updateFailure, action.payload.error) - }; - case getType(bpdDeleteUserFromProgram.success): - case getType(deleteWalletSuccess): - // if the user remove a payment method, we need to invalidate all the store - // because deleteWalletSuccess have Walletv1 as payload (without hash) - return {}; - } - return state; -}; - -/** - * The raw selection of the bpd activation status for a payment method - * @param state - * @param hPan - */ -const bpdPaymentMethodActivationByHPanValue = ( - state: GlobalState, - hPan: HPan -): pot.Pot | undefined => - state.bonus.bpd.details.paymentMethods[hPan]; - -/** - * Return all the activation states for payment methods, memoized - */ -export const bpdPaymentMethodActivationSelector = createSelector< - GlobalState, - IndexedById, - IndexedById ->( - [(state: GlobalState) => state.bonus.bpd.details.paymentMethods], - paymentMethod => paymentMethod -); - -/** - * Return the pot representing the bpd activation status for a payment method. - * It's wrapped with createSelector in order to memoize the value and avoid recalculation - * when the state change. - */ -export const bpdPaymentMethodValueSelector = createSelector( - [bpdPaymentMethodActivationByHPanValue], - potValue => potValue ?? pot.none -); - -/** - * Return true if at least one method from the given ones is BPD active - * @param paymentMethods - */ -export const areAnyPaymentMethodsActiveSelector = ( - paymentMethods: ReadonlyArray -) => - createSelector( - [bpdPaymentMethodActivationSelector], - (bpdPaymentMethodsActivation): boolean => { - const paymentMethodsHash = paymentMethods.map(getPaymentMethodHash); - return paymentMethodsHash.some(pmh => - pipe( - pmh, - O.fromNullable, - O.chainNullableK(h => bpdPaymentMethodsActivation[h]), - O.map(potActivation => - pot.getOrElse( - pot.map(potActivation, p => p.activationStatus === "active"), - false - ) - ), - O.getOrElse(() => false) - ) - ); - } - ); diff --git a/ts/features/bonus/bpd/store/reducers/details/periods.ts b/ts/features/bonus/bpd/store/reducers/details/periods.ts deleted file mode 100644 index 8daa16f2e10..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/periods.ts +++ /dev/null @@ -1,137 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { createSelector } from "reselect"; -import { getType } from "typesafe-actions"; -import { Action } from "../../../../../../store/actions/types"; -import { IndexedById } from "../../../../../../store/helpers/indexer"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { isDefined } from "../../../../../../utils/guards"; -import { BpdAmount } from "../../../saga/networking/amount"; -import { bpdUnsubscribeCompleted } from "../../actions/onboarding"; -import { - AwardPeriodId, - BpdPeriod, - bpdPeriodsAmountLoad, - WithAwardPeriodId -} from "../../actions/periods"; -import { BpdPivotTransaction } from "./transactionsv2/entities"; - -// The ranking is ready for a specific period -export type BpdRankingReady = WithAwardPeriodId & { - kind: "ready"; - // total number of citizens enrolled in bpd - totalParticipants: number; - // ranking position of the citizen for the specified period - ranking: number; - // number of first-ranked transactions - maxTransactionNumber: number; - // number of last-ranked transactions - minTransactionNumber: number; - // number of transactions made by the citizen for the period - transactionNumber: number; - // new v2 field, containing the trx pivot information - pivot?: BpdPivotTransaction; -}; - -// The ranking is still not ready for a period (eg. a period is just started -// and no transaction has been recorded) -// TODO: we should move all the types definition in the /types/* instead that in the state /action -export type BpdRankingNotReady = WithAwardPeriodId & { kind: "notReady" }; - -export const bpdRankingNotReady = ( - awardPeriodId: AwardPeriodId -): BpdRankingNotReady => ({ - awardPeriodId, - kind: "notReady" -}); - -export type BpdRanking = BpdRankingReady | BpdRankingNotReady; - -export const isBpdRankingReady = (r: BpdRanking): r is BpdRankingReady => - r.kind === "ready"; - -export const isBpdRankingNotReady = (r: BpdRanking): r is BpdRankingNotReady => - r.kind === "notReady"; - -/** - * Combine the period & amount - */ -export type BpdPeriodWithInfo = BpdPeriod & { - amount: BpdAmount; - ranking: BpdRanking; -}; - -/** - * A temporary implementation, based on the actual date - * TODO: remove this method when the new state "Waiting" will be ready - * @param period - */ -export const isGracePeriod = (period: BpdPeriod) => { - if (period.status === "Active" || period.status === "Inactive") { - return false; - } - const today = new Date(); - const endGracePeriod = new Date(period.endDate); - endGracePeriod.setDate(period.endDate.getDate() + period.gracePeriod); - // we are still in the grace period and warns the user that some transactions - // may still be pending - return today <= endGracePeriod && today >= period.endDate; -}; - -/** - * Store all the cashback periods with amounts - * @param state - * @param action - */ -export const bpdPeriodsReducer = ( - state: pot.Pot, Error> = pot.none, - action: Action -): pot.Pot, Error> => { - switch (action.type) { - case getType(bpdPeriodsAmountLoad.request): - return pot.toLoading(state); - case getType(bpdPeriodsAmountLoad.success): - return pot.some( - action.payload.reduce( - (acc: IndexedById, curr: BpdPeriodWithInfo) => ({ - ...acc, - [curr.awardPeriodId]: curr - }), - {} - ) - ); - case getType(bpdPeriodsAmountLoad.failure): - return pot.toError(state, action.payload); - case getType(bpdUnsubscribeCompleted): - return pot.none; - } - - return state; -}; - -/** - * Return the pot.Pot for IndexedById, memoized to avoid recalculations - */ -export const bpdPeriodsSelector = createSelector( - [(state: GlobalState) => state.bonus.bpd.details.periods], - (potValue): pot.Pot, Error> => - pot.map(potValue, potValue => Object.values(potValue).filter(isDefined)) -); - -/** - * Raw selector for a specific period (if exists) - * @param state - * @param id - */ -const bpdPeriodByIdRawSelector = ( - state: GlobalState, - id: AwardPeriodId -): pot.Pot => - pot.map(state.bonus.bpd.details.periods, periodList => periodList[id]); - -/** - * Return a specific period (if exists) - */ -export const bpdPeriodByIdSelector = createSelector( - [bpdPeriodByIdRawSelector], - period => period -); diff --git a/ts/features/bonus/bpd/store/reducers/details/selectedPeriod.ts b/ts/features/bonus/bpd/store/reducers/details/selectedPeriod.ts deleted file mode 100644 index 2635700ee11..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/selectedPeriod.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { createSelector } from "reselect"; -import { getType } from "typesafe-actions"; -import { Action } from "../../../../../../store/actions/types"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { bpdSelectPeriod } from "../../actions/selectedPeriod"; -import { BpdPeriodWithInfo } from "./periods"; - -/** - * Store the current period selected by the user (current displayed) - * @param state - * @param action - */ -export const bpdSelectedPeriodsReducer = ( - state: BpdPeriodWithInfo | null = null, - action: Action -): BpdPeriodWithInfo | null => { - switch (action.type) { - // The user manually selected a specific period - case getType(bpdSelectPeriod): - return action.payload; - } - return state; -}; - -/** - * Return current period selected by the user (current displayed) - * TODO: if null(generic navigation to cashback), return the current period ( start_date < today < end_date ) - * if no period match the date interval, return the nearest - */ -export const bpdSelectedPeriodSelector = createSelector( - [(state: GlobalState) => state.bonus.bpd.details.selectedPeriod], - (period): BpdPeriodWithInfo | undefined => - period === null ? undefined : period -); diff --git a/ts/features/bonus/bpd/store/reducers/details/transactions.ts b/ts/features/bonus/bpd/store/reducers/details/transactions.ts deleted file mode 100644 index a86ffede937..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/transactions.ts +++ /dev/null @@ -1,83 +0,0 @@ -import * as O from "fp-ts/lib/Option"; -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { createSelector } from "reselect"; -import { getType } from "typesafe-actions"; -import { pipe } from "fp-ts/lib/function"; -import { Action } from "../../../../../../store/actions/types"; -import { IndexedById } from "../../../../../../store/helpers/indexer"; -import { - toError, - toLoading, - toSome -} from "../../../../../../store/reducers/IndexedByIdPot"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { bpdDeleteUserFromProgram } from "../../actions/onboarding"; -import { AwardPeriodId } from "../../actions/periods"; -import { - BpdTransaction, - bpdTransactionsLoad -} from "../../actions/transactions"; -import { bpdSelectedPeriodSelector } from "./selectedPeriod"; - -/** - * Store the transactions foreach period - * @param state - * @param action - */ -export const bpdTransactionsReducer = ( - state: IndexedById, Error>> = {}, - action: Action -): IndexedById, Error>> => { - switch (action.type) { - case getType(bpdTransactionsLoad.request): - return toLoading(action.payload, state); - case getType(bpdTransactionsLoad.success): - return toSome( - action.payload.awardPeriodId, - state, - action.payload.results - ); - case getType(bpdTransactionsLoad.failure): - return toError(action.payload.awardPeriodId, state, action.payload.error); - case getType(bpdDeleteUserFromProgram.success): - return {}; - } - - return state; -}; - -/** - * The raw selector to read a specific period of transactions - * @param state - * @param periodId - */ -const bpdTransactionsByPeriodSelector = ( - state: GlobalState, - periodId: AwardPeriodId -): pot.Pot, Error> | undefined => - state.bonus.bpd.details.transactions[periodId]; - -/** - * Return the pot.Pot for IndexedById>, memoized to avoid recalculations - */ -export const bpdTransactionsSelector = createSelector( - [bpdTransactionsByPeriodSelector], - maybePotTransactions => maybePotTransactions ?? pot.none -); - -/** - * Return the list of transactions for the selected period (current displayed) - */ -export const bpdTransactionsForSelectedPeriod = createSelector( - [ - (state: GlobalState) => state.bonus.bpd.details.transactions, - bpdSelectedPeriodSelector - ], - (transactions, period) => - pipe( - period, - O.fromNullable, - O.chain(p => O.fromNullable(transactions[p.awardPeriodId])), - O.getOrElseW(() => pot.none) - ) -); diff --git a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/__mock__/transactions.ts b/ts/features/bonus/bpd/store/reducers/details/transactionsv2/__mock__/transactions.ts deleted file mode 100644 index 64bd54b1a5f..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/__mock__/transactions.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { WinningTransactionMilestoneResource } from "../../../../../../../../../definitions/bpd/winning_transactions_v2/WinningTransactionMilestoneResource"; -import { AwardPeriodId, WithAwardPeriodId } from "../../../../actions/periods"; - -export const awardPeriodTemplate: WithAwardPeriodId = { - awardPeriodId: 1 as AwardPeriodId -}; - -export const transactionTemplate: WinningTransactionMilestoneResource = { - awardPeriodId: awardPeriodTemplate.awardPeriodId, - idTrx: "1", - idTrxIssuer: "idTrxIssuer", - idTrxAcquirer: "idTrxIssuer", - cashback: 15, - hashPan: "hPan", - circuitType: "01", - trxDate: new Date("2021-01-01"), - amount: 150 -}; diff --git a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/__test__/normalization.test.ts b/ts/features/bonus/bpd/store/reducers/details/transactionsv2/__test__/normalization.test.ts deleted file mode 100644 index 663a8a4ad88..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/__test__/normalization.test.ts +++ /dev/null @@ -1,375 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { createStore } from "redux"; -import { WinningTransactionsOfTheDayResource } from "../../../../../../../../../definitions/bpd/winning_transactions_v2/WinningTransactionsOfTheDayResource"; -import { applicationChangeState } from "../../../../../../../../store/actions/application"; -import { appReducer } from "../../../../../../../../store/reducers"; -import { - BpdTransactionId, - bpdTransactionsLoadMilestone, - bpdTransactionsLoadPage, - TrxMilestonePayload -} from "../../../../actions/transactions"; -import { - awardPeriodTemplate, - transactionTemplate -} from "../__mock__/transactions"; -import { BpdTransactionsEntityState } from "../entities"; - -const pageOne: WinningTransactionsOfTheDayResource = { - date: new Date("2021-01-01"), - transactions: [ - transactionTemplate, - { ...transactionTemplate, idTrx: "2", cashback: 0 }, - { ...transactionTemplate, idTrx: "3" } - ] -}; - -const pageTwo: WinningTransactionsOfTheDayResource = { - date: new Date("2021-01-01"), - transactions: [ - { ...transactionTemplate, idTrx: "A" }, - { ...transactionTemplate, idTrx: "B", cashback: 0 }, - { ...transactionTemplate, idTrx: "C" } - ] -}; - -const pageThree: WinningTransactionsOfTheDayResource = { - date: new Date("2021-01-02"), - transactions: [ - { ...transactionTemplate, idTrx: "D" }, - { ...transactionTemplate, idTrx: "E", cashback: 15 } - ] -}; - -const templateMilestone: TrxMilestonePayload = { - ...awardPeriodTemplate, - result: { amount: 0.5, idTrx: "A" as BpdTransactionId } -}; - -type TransactionTestCheck = { - idTrx: string; - cashback: number; - validForCashback: boolean; -}; - -describe("Test the paginated transaction normalization", () => { - it("When no pivot is present, the cashback amount should not be normalized", () => { - const globalState = appReducer(undefined, applicationChangeState("active")); - const store = createStore(appReducer, globalState as any); - store.dispatch(bpdTransactionsLoadPage.request(awardPeriodTemplate)); - store.dispatch( - bpdTransactionsLoadPage.success({ - ...awardPeriodTemplate, - results: { - transactions: [pageOne] - } - }) - ); - - const transactionsEntities = - store.getState().bonus.bpd.details.transactionsV2.entitiesByPeriod[ - awardPeriodTemplate.awardPeriodId - ]; - - const expectedResults = [ - { - idTrx: "1", - cashback: transactionTemplate.cashback, - validForCashback: true - }, - { - idTrx: "2", - cashback: 0, - validForCashback: true - }, - { - idTrx: "3", - cashback: transactionTemplate.cashback, - validForCashback: true - } - ]; - - expect(transactionsEntities).toBeDefined(); - expect(transactionsEntities?.pivot).toBe(pot.none); - if (transactionsEntities) { - verifyTransactions(expectedResults, transactionsEntities); - } - }); - it( - "When a pivot is present and the pivot transaction has not yet been found," + - " the cashback amount should be normalized to zero for all the transactions", - () => { - const globalState = appReducer( - undefined, - applicationChangeState("active") - ); - const store = createStore(appReducer, globalState as any); - - store.dispatch( - bpdTransactionsLoadMilestone.request(awardPeriodTemplate.awardPeriodId) - ); - store.dispatch(bpdTransactionsLoadMilestone.success(templateMilestone)); - - store.dispatch(bpdTransactionsLoadPage.request(awardPeriodTemplate)); - store.dispatch( - bpdTransactionsLoadPage.success({ - ...awardPeriodTemplate, - results: { - transactions: [pageOne] - } - }) - ); - - const transactionsEntities = - store.getState().bonus.bpd.details.transactionsV2.entitiesByPeriod[ - awardPeriodTemplate.awardPeriodId - ]; - - const expectedResults: ReadonlyArray = [ - { - idTrx: "1", - cashback: 0, - validForCashback: false - }, - { - idTrx: "2", - cashback: 0, - validForCashback: false - }, - { - idTrx: "3", - cashback: 0, - validForCashback: false - } - ]; - - expect(transactionsEntities).toBeDefined(); - expect(transactionsEntities?.pivot).toStrictEqual( - pot.some(templateMilestone.result) - ); - if (transactionsEntities) { - verifyTransactions(expectedResults, transactionsEntities); - } - } - ); - - it( - "When a pivot is present and the pivot transaction has been found," + - " the cashback amount should be must be normalized appropriately (same day transactions in multiple pages)", - () => { - const globalState = appReducer( - undefined, - applicationChangeState("active") - ); - const store = createStore(appReducer, globalState as any); - - store.dispatch( - bpdTransactionsLoadMilestone.request(awardPeriodTemplate.awardPeriodId) - ); - store.dispatch(bpdTransactionsLoadMilestone.success(templateMilestone)); - - store.dispatch(bpdTransactionsLoadPage.request(awardPeriodTemplate)); - store.dispatch( - bpdTransactionsLoadPage.success({ - ...awardPeriodTemplate, - results: { - transactions: [pageOne] - } - }) - ); - store.dispatch(bpdTransactionsLoadPage.request(awardPeriodTemplate)); - store.dispatch( - bpdTransactionsLoadPage.success({ - ...awardPeriodTemplate, - results: { - transactions: [pageTwo] - } - }) - ); - store.dispatch(bpdTransactionsLoadPage.request(awardPeriodTemplate)); - store.dispatch( - bpdTransactionsLoadPage.success({ - ...awardPeriodTemplate, - results: { - transactions: [pageThree] - } - }) - ); - - const transactionsEntities = - store.getState().bonus.bpd.details.transactionsV2.entitiesByPeriod[ - awardPeriodTemplate.awardPeriodId - ]; - - const expectedResults: ReadonlyArray = [ - { - idTrx: "1", - cashback: 0, - validForCashback: false - }, - { - idTrx: "2", - cashback: 0, - validForCashback: false - }, - { - idTrx: "3", - cashback: 0, - validForCashback: false - }, - { - idTrx: "A", - cashback: templateMilestone.result?.amount ?? 0, - validForCashback: true - }, - { - idTrx: "B", - cashback: 0, - validForCashback: true - }, - { - idTrx: "C", - cashback: transactionTemplate.cashback, - validForCashback: true - }, - { - idTrx: "D", - cashback: transactionTemplate.cashback, - validForCashback: true - }, - { - idTrx: "E", - cashback: 15, - validForCashback: true - } - ]; - - expect(transactionsEntities).toBeDefined(); - expect(transactionsEntities?.pivot).toStrictEqual( - pot.some(templateMilestone.result) - ); - if (transactionsEntities) { - verifyTransactions(expectedResults, transactionsEntities); - } - } - ); - it( - "When a pivot is present and the pivot transaction has been found," + - " the cashback amount should be must be normalized appropriately (pivot with multiple days in same page)", - () => { - const globalState = appReducer( - undefined, - applicationChangeState("active") - ); - const store = createStore(appReducer, globalState as any); - - store.dispatch( - bpdTransactionsLoadMilestone.request(awardPeriodTemplate.awardPeriodId) - ); - store.dispatch(bpdTransactionsLoadMilestone.success(templateMilestone)); - - store.dispatch(bpdTransactionsLoadPage.request(awardPeriodTemplate)); - store.dispatch( - bpdTransactionsLoadPage.success({ - ...awardPeriodTemplate, - results: { - transactions: [pageOne] - } - }) - ); - store.dispatch(bpdTransactionsLoadPage.request(awardPeriodTemplate)); - store.dispatch( - bpdTransactionsLoadPage.success({ - ...awardPeriodTemplate, - results: { - transactions: [pageTwo, pageThree] - } - }) - ); - - const transactionsEntities = - store.getState().bonus.bpd.details.transactionsV2.entitiesByPeriod[ - awardPeriodTemplate.awardPeriodId - ]; - - const expectedResults: ReadonlyArray = [ - { - idTrx: "1", - cashback: 0, - validForCashback: false - }, - { - idTrx: "2", - cashback: 0, - validForCashback: false - }, - { - idTrx: "3", - cashback: 0, - validForCashback: false - }, - { - idTrx: "A", - cashback: templateMilestone.result?.amount ?? 0, - validForCashback: true - }, - { - idTrx: "B", - cashback: 0, - validForCashback: true - }, - { - idTrx: "C", - cashback: transactionTemplate.cashback, - validForCashback: true - }, - { - idTrx: "D", - cashback: transactionTemplate.cashback, - validForCashback: true - }, - { - idTrx: "E", - cashback: 15, - validForCashback: true - } - ]; - - expect(transactionsEntities).toBeDefined(); - expect(transactionsEntities?.pivot).toStrictEqual( - pot.some(templateMilestone.result) - ); - if (transactionsEntities) { - verifyTransactions(expectedResults, transactionsEntities); - } - } - ); -}); - -expect.extend({ - toBeAsd(received, validator) { - if (validator(received)) { - return { - message: () => `Email ${received} should NOT be valid`, - pass: true - }; - } else { - return { - message: () => `Email ${received} should be valid`, - pass: false - }; - } - } -}); - -const verifyTransactions = ( - trxCheckList: ReadonlyArray, - state: BpdTransactionsEntityState -) => { - trxCheckList.map(x => { - expect(state.byId[x.idTrx]).toBeDefined(); - expect(state.byId[x.idTrx]?.idTrx.toString()).toBe(x.idTrx); - expect(state.byId[x.idTrx]?.cashback).toBe(x.cashback); - expect(state.byId[x.idTrx]?.validForCashback).toBe(x.validForCashback); - }); -}; diff --git a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/__test__/transactions.test.ts b/ts/features/bonus/bpd/store/reducers/details/transactionsv2/__test__/transactions.test.ts deleted file mode 100644 index c4f37db13c2..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/__test__/transactions.test.ts +++ /dev/null @@ -1,199 +0,0 @@ -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { createStore } from "redux"; -import { WinningTransactionsOfTheDayResource } from "../../../../../../../../../definitions/bpd/winning_transactions_v2/WinningTransactionsOfTheDayResource"; -import { applicationChangeState } from "../../../../../../../../store/actions/application"; -import { appReducer } from "../../../../../../../../store/reducers"; -import { bpdTransactionsLoadPage } from "../../../../actions/transactions"; -import { - awardPeriodTemplate, - transactionTemplate -} from "../__mock__/transactions"; - -const pageOne: WinningTransactionsOfTheDayResource = { - date: new Date("2021-01-01"), - transactions: [ - transactionTemplate, - { ...transactionTemplate, idTrx: "2" }, - { ...transactionTemplate, idTrx: "3" } - ] -}; - -const pageTwo: WinningTransactionsOfTheDayResource = { - date: new Date("2021-01-01"), - transactions: [ - { ...transactionTemplate, idTrx: "4" }, - { ...transactionTemplate, idTrx: "5" } - ] -}; - -const pageThree: WinningTransactionsOfTheDayResource = { - date: new Date("2021-01-02"), - transactions: [ - { ...transactionTemplate, idTrx: "6" }, - { ...transactionTemplate, idTrx: "7" } - ] -}; - -describe("Test BpdTransactionsV2State store", () => { - it("When the action bpdTransactionsLoadPage.request is dispatched, the store should have the right shape", () => { - const globalState = appReducer(undefined, applicationChangeState("active")); - const store = createStore(appReducer, globalState as any); - store.dispatch(bpdTransactionsLoadPage.request(awardPeriodTemplate)); - - const transactionsStore = store.getState().bonus.bpd.details.transactionsV2; - - expect(transactionsStore.ui.sectionItems).toStrictEqual(pot.noneLoading); - - expect(transactionsStore.ui.awardPeriodId).toStrictEqual( - awardPeriodTemplate.awardPeriodId - ); - }); - it("When the action bpdTransactionsLoadPage.failure is dispatched, the store should have the right shape", () => { - const globalState = appReducer(undefined, applicationChangeState("active")); - const store = createStore(appReducer, globalState as any); - const testError = new Error("Test Error"); - store.dispatch(bpdTransactionsLoadPage.request(awardPeriodTemplate)); - store.dispatch( - bpdTransactionsLoadPage.failure({ - ...awardPeriodTemplate, - error: new Error("Test Error") - }) - ); - - const transactionsStore = store.getState().bonus.bpd.details.transactionsV2; - - expect(transactionsStore.ui.sectionItems).toStrictEqual({ - error: testError, - kind: "PotNoneError" - }); - - expect(transactionsStore.ui.awardPeriodId).toStrictEqual( - awardPeriodTemplate.awardPeriodId - ); - }); - it("When the action bpdTransactionsLoadPage.success is dispatched, the store should have the right shape", () => { - const globalState = appReducer(undefined, applicationChangeState("active")); - const store = createStore(appReducer, globalState as any); - store.dispatch(bpdTransactionsLoadPage.request(awardPeriodTemplate)); - store.dispatch( - bpdTransactionsLoadPage.success({ - ...awardPeriodTemplate, - results: { - transactions: [pageOne] - } - }) - ); - - const expectedTrxId = ["1", "2", "3"]; - const transactionsStore = store.getState().bonus.bpd.details.transactionsV2; - - expect(transactionsStore.ui.sectionItems.kind).toStrictEqual("PotSome"); - - if (pot.isSome(transactionsStore.ui.sectionItems)) { - const dateId = new Date("2021-01-01").toISOString(); - expect(transactionsStore.ui.sectionItems.value[dateId]?.dayInfoId).toBe( - dateId - ); - - expect( - transactionsStore.ui.sectionItems.value[dateId]?.data - ).toStrictEqual(expectedTrxId); - } - expect(transactionsStore.ui.awardPeriodId).toStrictEqual( - awardPeriodTemplate.awardPeriodId - ); - expect(transactionsStore.ui.nextCursor).toBeNull(); - - const entities = - transactionsStore.entitiesByPeriod[awardPeriodTemplate.awardPeriodId]; - - expect(entities).toBeDefined(); - - if (entities) { - expect(entities.foundPivot).toBe(false); - - expect(Object.keys(entities.byId).length).toBe(expectedTrxId.length); - - expectedTrxId.forEach(x => { - expect(entities.byId[x]).toBeDefined(); - expect(entities.byId[x]?.idTrx.toString()).toBe(x); - }); - } - }); - - it("When multiple action bpdTransactionsLoadPage.success are dispatched, the store should have the right shape and add merge correctly the new data", () => { - const globalState = appReducer(undefined, applicationChangeState("active")); - const store = createStore(appReducer, globalState as any); - store.dispatch(bpdTransactionsLoadPage.request(awardPeriodTemplate)); - store.dispatch( - bpdTransactionsLoadPage.success({ - ...awardPeriodTemplate, - results: { - nextCursor: 1, - transactions: [pageOne] - } - }) - ); - store.dispatch( - bpdTransactionsLoadPage.request({ ...awardPeriodTemplate, nextCursor: 1 }) - ); - store.dispatch( - bpdTransactionsLoadPage.success({ - ...awardPeriodTemplate, - results: { - nextCursor: 2, - transactions: [pageTwo, pageThree] - } - }) - ); - - const expectedTrxDayOne = ["1", "2", "3", "4", "5"]; - const expectedTrxDayTwo = ["6", "7"]; - const transactionsStore = store.getState().bonus.bpd.details.transactionsV2; - - expect(transactionsStore.ui.sectionItems.kind).toStrictEqual("PotSome"); - - expect(transactionsStore.ui.awardPeriodId).toStrictEqual( - awardPeriodTemplate.awardPeriodId - ); - - if (pot.isSome(transactionsStore.ui.sectionItems)) { - const dateIdDayOne = new Date("2021-01-01").toISOString(); - const dateIdDayTwo = new Date("2021-01-02").toISOString(); - expect( - transactionsStore.ui.sectionItems.value[dateIdDayOne]?.dayInfoId - ).toBe(dateIdDayOne); - expect( - transactionsStore.ui.sectionItems.value[dateIdDayTwo]?.dayInfoId - ).toBe(dateIdDayTwo); - - expect( - transactionsStore.ui.sectionItems.value[dateIdDayOne]?.data - ).toStrictEqual(expectedTrxDayOne); - - expect( - transactionsStore.ui.sectionItems.value[dateIdDayTwo]?.data - ).toStrictEqual(expectedTrxDayTwo); - } - - expect(transactionsStore.ui.nextCursor).toBe(2); - - const entities = - transactionsStore.entitiesByPeriod[awardPeriodTemplate.awardPeriodId]; - - expect(entities).toBeDefined(); - - if (entities) { - const expectedEntitiesId = expectedTrxDayOne.concat(expectedTrxDayTwo); - - expect(entities.foundPivot).toBe(false); - - expect(Object.keys(entities.byId).length).toBe(expectedEntitiesId.length); - - expectedEntitiesId.forEach(x => { - expect(entities.byId[x]).toBeDefined(); - expect(entities.byId[x]?.idTrx.toString()).toBe(x); - }); - } - }); -}); diff --git a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/daysInfo.ts b/ts/features/bonus/bpd/store/reducers/details/transactionsv2/daysInfo.ts deleted file mode 100644 index 66cb350bb96..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/daysInfo.ts +++ /dev/null @@ -1,145 +0,0 @@ -import * as O from "fp-ts/lib/Option"; -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { createSelector } from "reselect"; -import { getType } from "typesafe-actions"; -import { pipe } from "fp-ts/lib/function"; -import { Action } from "../../../../../../../store/actions/types"; -import { - IndexedById, - toArray, - toIndexed -} from "../../../../../../../store/helpers/indexer"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { AwardPeriodId } from "../../../actions/periods"; -import { - bpdTransactionsLoadCountByDay, - bpdTransactionsLoadRequiredData -} from "../../../actions/transactions"; -import { bpdSelectedPeriodSelector } from "../selectedPeriod"; - -export type BpdTransactionsDayInfo = { - trxDate: Date; - count: number; -}; - -export type BpdTransactionsDaysInfoState = { - // the DaysInfo are requested in bulk for a specific period, for this reason all the IndexedById object is a pot - byId: pot.Pot, Error>; -}; - -const initState: BpdTransactionsDaysInfoState = { - byId: pot.none -}; - -/** - * Update the byId entry for the selected period - * @param input - * @param period - * @param newVal - */ -const updateById = ( - input: IndexedById, - period: AwardPeriodId, - newVal: pot.Pot, Error> -): IndexedById => ({ - ...input, - [period]: { - byId: newVal - } -}); - -/** - * Get the BpdTransactionsDaysInfoState for a specific period - * @param state - * @param id - */ -const getPeriodEntry = ( - state: IndexedById, - id: AwardPeriodId -): BpdTransactionsDaysInfoState => state[id] ?? initState; - -export const bpdTransactionsDaysInfoReducer = ( - state: IndexedById = {}, - action: Action -): IndexedById => { - switch (action.type) { - case getType(bpdTransactionsLoadRequiredData.request): - return {}; - case getType(bpdTransactionsLoadCountByDay.request): - return updateById( - state, - action.payload, - pot.toLoading(getPeriodEntry(state, action.payload).byId) - ); - case getType(bpdTransactionsLoadCountByDay.success): - return updateById( - state, - action.payload.awardPeriodId, - pot.some( - toIndexed(action.payload.results, x => x.trxDate.toISOString()) - ) - ); - case getType(bpdTransactionsLoadCountByDay.failure): - const periodIdError = action.payload.awardPeriodId; - return updateById( - state, - periodIdError, - pot.toError( - getPeriodEntry(state, periodIdError).byId, - action.payload.error - ) - ); - } - return state; -}; - -/** - * Return the pot.Pot, Error>, for the selected period - */ -export const bpdDaysInfoForSelectedPeriodSelector = createSelector( - [ - (state: GlobalState) => - state.bonus.bpd.details.transactionsV2.daysInfoByPeriod, - bpdSelectedPeriodSelector - ], - ( - daysInfoByPeriod, - selectedPeriod - ): pot.Pot, Error> => - pot.map( - pipe( - selectedPeriod, - O.fromNullable, - O.chain(periodId => - O.fromNullable(daysInfoByPeriod[periodId.awardPeriodId]?.byId) - ), - O.getOrElseW(() => pot.none) - ), - byId => toArray(byId) - ) -); - -/** - * From id to Option - */ -export const bpdDaysInfoByIdSelector = createSelector( - [ - (state: GlobalState) => - state.bonus.bpd.details.transactionsV2.ui.awardPeriodId, - (state: GlobalState) => - state.bonus.bpd.details.transactionsV2.daysInfoByPeriod, - (_: GlobalState, daysInfoId: string) => daysInfoId - ], - ( - awardPeriodId, - daysInfoByPeriod, - daysInfoId - ): O.Option => - pipe( - awardPeriodId, - O.fromNullable, - O.chain(periodId => O.fromNullable(daysInfoByPeriod[periodId]?.byId)), - O.chain(pot.toOption), - O.chain(daysInfoById => O.fromNullable(daysInfoById[daysInfoId])) - ) -); diff --git a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/entities.ts b/ts/features/bonus/bpd/store/reducers/details/transactionsv2/entities.ts deleted file mode 100644 index 265b6291bbd..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/entities.ts +++ /dev/null @@ -1,238 +0,0 @@ -import * as O from "fp-ts/lib/Option"; -import * as pot from "@pagopa/ts-commons/lib/pot"; -import { createSelector } from "reselect"; -import { getType } from "typesafe-actions"; -import { pipe } from "fp-ts/lib/function"; -import { WinningTransactionMilestoneResource } from "../../../../../../../../definitions/bpd/winning_transactions_v2/WinningTransactionMilestoneResource"; -import { WinningTransactionPageResource } from "../../../../../../../../definitions/bpd/winning_transactions_v2/WinningTransactionPageResource"; -import { Action } from "../../../../../../../store/actions/types"; -import { - IndexedById, - toArray, - toIndexed -} from "../../../../../../../store/helpers/indexer"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { convertCircuitTypeCode } from "../../../../saga/networking/transactions"; -import { HPan } from "../../../actions/paymentMethods"; -import { AwardPeriodId } from "../../../actions/periods"; -import { - BpdTransactionId, - bpdTransactionsLoadMilestone, - bpdTransactionsLoadPage, - bpdTransactionsLoadRequiredData, - BpdTransactionV2 -} from "../../../actions/transactions"; -import { bpdSelectedPeriodSelector } from "../selectedPeriod"; - -export type BpdPivotTransaction = { - idTrx: BpdTransactionId; - amount: number; -}; - -export type BpdTransactionsEntityState = { - pivot: pot.Pot; - byId: IndexedById; - foundPivot: boolean; -}; -type NormalizedTransactions = { - data: ReadonlyArray; - found: boolean; -}; - -const initState: BpdTransactionsEntityState = { - pivot: pot.none, - foundPivot: false, - byId: {} -}; - -const getPeriodEntry = ( - state: IndexedById, - id: AwardPeriodId -): BpdTransactionsEntityState => state[id] ?? initState; - -const updatePeriodEntry = ( - input: IndexedById, - period: AwardPeriodId, - newVal: BpdTransactionsEntityState -): IndexedById => ({ - ...input, - [period]: newVal -}); - -/** - * Normalize the cashback amount for the transactions, using as ref the pivot transaction - * - pivot === null, the user did not reach the max cashback amount and all the transactions are valid - * - pivot !== null && not found, the transactions have 0 value - * - pivot !== null && not found && idTrx === pivot.id, pivot transaction found, the pivot transaction have pivot.amount - * - pivot !== null && found, the cashback transaction has the complete value - * @param transactions - * @param pivot - * @param foundPivot - */ -const normalizeCashback = ( - transactions: ReadonlyArray, - pivot: BpdPivotTransaction | null, - foundPivot: boolean -): NormalizedTransactions => { - // eslint-disable-next-line functional/no-let - let found = foundPivot; - return { - data: transactions.map(x => { - // prepare the base BpdTransactionV2, with the right types - const trxV2WithCircuit: BpdTransactionV2 = { - ...x, - circuitType: convertCircuitTypeCode(x.circuitType), - awardPeriodId: x.awardPeriodId as AwardPeriodId, - hashPan: x.hashPan as HPan, - validForCashback: false, - idTrx: x.idTrx as BpdTransactionId, - isPivot: false - }; - - if (found || pivot === null) { - return { ...trxV2WithCircuit, validForCashback: true }; - } - if (x.idTrx === pivot.idTrx) { - found = true; - return { - ...trxV2WithCircuit, - cashback: pivot.amount, - validForCashback: true, - isPivot: true - }; - } - - return { ...trxV2WithCircuit, cashback: 0, validForCashback: false }; - }), - found - }; -}; - -/** - * Add a new page data to BpdTransactionsEntityState for a specific period, updating it - * @param state - * @param newPage - */ -const updateTransactions = ( - state: BpdTransactionsEntityState, - newPage: WinningTransactionPageResource -): BpdTransactionsEntityState => { - // eslint-disable-next-line functional/no-let - let foundPivot = state.foundPivot; - - const pivot = pot.getOrElse(state.pivot, null); - const flatTransactions = newPage.transactions.reduce< - IndexedById - >((acc, val) => { - // calculate the normalization for the new page - const transactionsNormalized = normalizeCashback( - val.transactions, - pivot, - foundPivot - ); - // update the foundPivot - if (!foundPivot && transactionsNormalized.found) { - foundPivot = true; - } - return { - ...acc, - ...toIndexed(transactionsNormalized.data, x => x.idTrx) - } as IndexedById; - }, {} as IndexedById); - - // append the new normalized transactions to the previously calculated and update the foundPivot - return { - ...state, - foundPivot, - byId: { ...state.byId, ...flatTransactions } - }; -}; - -export const bpdTransactionsEntityReducer = ( - state: IndexedById = {}, - action: Action -): IndexedById => { - switch (action.type) { - case getType(bpdTransactionsLoadRequiredData.request): - return {}; - case getType(bpdTransactionsLoadPage.success): - return updatePeriodEntry( - state, - action.payload.awardPeriodId, - updateTransactions( - getPeriodEntry(state, action.payload.awardPeriodId), - action.payload.results - ) - ); - - case getType(bpdTransactionsLoadMilestone.request): - const periodMilestoneRequest = getPeriodEntry(state, action.payload); - return { - ...state, - [action.payload]: { - ...periodMilestoneRequest, - pivot: pot.toLoading(periodMilestoneRequest.pivot) - } - }; - case getType(bpdTransactionsLoadMilestone.success): - // When receive a new pivot, should refresh any existing transaction - // This should never happens but is present in order to avoid inconsistent state - const periodMilestoneSuccess = getPeriodEntry( - state, - action.payload.awardPeriodId - ); - const { data, found } = normalizeCashback( - toArray(periodMilestoneSuccess.byId), - action.payload.result ?? null, - periodMilestoneSuccess.foundPivot - ); - return { - ...state, - [action.payload.awardPeriodId]: { - ...periodMilestoneSuccess, - pivot: pot.some(action.payload.result ?? null), - byId: toIndexed(data, t => t.idTrx), - foundPivot: found - } - }; - - case getType(bpdTransactionsLoadMilestone.failure): - const periodMilestoneFailure = getPeriodEntry( - state, - action.payload.awardPeriodId - ); - return { - ...state, - [action.payload.awardPeriodId]: { - ...periodMilestoneFailure, - pivot: pot.toError(periodMilestoneFailure.pivot, action.payload.error) - } - }; - } - - return state; -}; - -/** - * Return the pot.Pot, for the selected period - */ -export const bpdTransactionsPivotForSelectedPeriodSelector = createSelector( - [ - (state: GlobalState) => - state.bonus.bpd.details.transactionsV2.entitiesByPeriod, - bpdSelectedPeriodSelector - ], - ( - bpdTransactionsEntity, - maybeSelectedPeriod - ): pot.Pot => - pipe( - maybeSelectedPeriod, - O.fromNullable, - O.chain(selectedPeriod => - O.fromNullable(bpdTransactionsEntity[selectedPeriod.awardPeriodId]) - ), - O.map(x => x.pivot), - O.getOrElseW(() => pot.none) - ) -); diff --git a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/index.ts b/ts/features/bonus/bpd/store/reducers/details/transactionsv2/index.ts deleted file mode 100644 index 29c278b0f02..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/index.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { combineReducers } from "redux"; -import { Action } from "../../../../../../../store/actions/types"; -import { IndexedById } from "../../../../../../../store/helpers/indexer"; -import { - bpdTransactionsDaysInfoReducer, - BpdTransactionsDaysInfoState -} from "./daysInfo"; -import { - bpdTransactionsEntityReducer, - BpdTransactionsEntityState -} from "./entities"; -import { bpdTransactionsUiReducer, BpdTransactionsUiState } from "./ui"; - -export type BpdTransactionsV2State = { - // daysInfoState, groupBy period - daysInfoByPeriod: IndexedById; - // transactionsEntitiesState, groupBy period - entitiesByPeriod: IndexedById; - // Ui state for transactions details - ui: BpdTransactionsUiState; -}; - -export const bpdTransactionsV2Reducer = combineReducers< - BpdTransactionsV2State, - Action ->({ - daysInfoByPeriod: bpdTransactionsDaysInfoReducer, - entitiesByPeriod: bpdTransactionsEntityReducer, - ui: bpdTransactionsUiReducer -}); diff --git a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/ui.ts b/ts/features/bonus/bpd/store/reducers/details/transactionsv2/ui.ts deleted file mode 100644 index c00e6219644..00000000000 --- a/ts/features/bonus/bpd/store/reducers/details/transactionsv2/ui.ts +++ /dev/null @@ -1,268 +0,0 @@ -import * as AR from "fp-ts/lib/Array"; -import * as O from "fp-ts/lib/Option"; -import * as pot from "@pagopa/ts-commons/lib/pot"; -import _ from "lodash"; -import { createSelector } from "reselect"; -import { getType } from "typesafe-actions"; -import { pipe } from "fp-ts/lib/function"; -import { WinningTransactionPageResource } from "../../../../../../../../definitions/bpd/winning_transactions_v2/WinningTransactionPageResource"; -import { cardIcons } from "../../../../../../../components/wallet/card/Logo"; -import { Action } from "../../../../../../../store/actions/types"; -import { - IndexedById, - toArray -} from "../../../../../../../store/helpers/indexer"; -import { GlobalState } from "../../../../../../../store/reducers/types"; -import { paymentMethodsSelector } from "../../../../../../../store/reducers/wallet/wallets"; -import { FOUR_UNICODE_CIRCLES } from "../../../../../../../utils/wallet"; -import { BpdTransactionDetailRepresentationV2 } from "../../../../screens/details/transaction/detail/BpdTransactionDetailComponent"; -import { AwardPeriodId } from "../../../actions/periods"; -import { - BpdTransactionId, - bpdTransactionsLoadPage, - bpdTransactionsLoadRequiredData -} from "../../../actions/transactions"; -import { pickPaymentMethodFromHashpan } from "../combiner"; -import { bpdSelectedPeriodSelector } from "../selectedPeriod"; - -/** - * The type that represents a section item that will be rendered with a SectionList. - * Each section contains an header (dayInfoId) and a list of trxId (data) - */ -export type BpdTransactionsSectionItem = { - dayInfoId: string; - data: ReadonlyArray; -}; - -/** - * Describe this store section, dedicated to rendering paginated transactions. - */ -export type BpdTransactionsUiState = { - nextCursor: number | null; - awardPeriodId: AwardPeriodId | null; - requiredDataLoaded: pot.Pot; - sectionItems: pot.Pot, Error>; - // Is the date of the first transaction of the first page (aka: the most recent transaction date) - lastTransactionDate: Date | null; -}; - -/** - * Extract the ids from the received payload - * @param page - */ -const fromWinningTransactionPageResourceToBpdTransactionsSectionItem = ( - page: WinningTransactionPageResource -): IndexedById => - page.transactions.reduce>( - (acc, val) => ({ - ...acc, - [val.date.toISOString()]: { - dayInfoId: val.date.toISOString(), - data: val.transactions.map(trx => trx.idTrx as BpdTransactionId) - } - }), - {} - ); - -/** - * Helper function for mergeWith, in order to merge the inner list of transactions - * @param obj - * @param dst - */ -const customizer = ( - obj: BpdTransactionsSectionItem | undefined, - dst: BpdTransactionsSectionItem | undefined -): BpdTransactionsSectionItem | undefined => { - if (obj !== undefined && dst !== undefined) { - return { - dayInfoId: dst.dayInfoId, - data: obj.data.concat(dst.data) - }; - } - - if (obj === undefined && dst !== undefined) { - return dst; - } - return undefined; -}; - -const initState: BpdTransactionsUiState = { - awardPeriodId: null, - sectionItems: pot.none, - requiredDataLoaded: pot.none, - nextCursor: null, - lastTransactionDate: null -}; - -export const bpdTransactionsUiReducer = ( - state: BpdTransactionsUiState = initState, - action: Action -): BpdTransactionsUiState => { - switch (action.type) { - case getType(bpdTransactionsLoadPage.request): - return { - ...state, - awardPeriodId: action.payload.awardPeriodId, - sectionItems: - action.payload.awardPeriodId !== state.awardPeriodId - ? pot.noneLoading - : pot.toLoading(state.sectionItems) - }; - case getType(bpdTransactionsLoadPage.success): - const currentSectionItems = pot.getOrElse(state.sectionItems, {}); - const newSectionItems = - fromWinningTransactionPageResourceToBpdTransactionsSectionItem( - action.payload.results - ); - - return { - ...state, - awardPeriodId: action.payload.awardPeriodId, - // If lastTransactionDate is null, pick the first transaction date - lastTransactionDate: - state.lastTransactionDate ?? - pipe( - AR.head([...action.payload.results.transactions]), - O.chain(x => AR.head([...x.transactions])), - O.map(y => y.trxDate), - O.toNullable - ), - nextCursor: action.payload.results.nextCursor ?? null, - sectionItems: pot.some( - _.mergeWith(currentSectionItems, newSectionItems, customizer) - ) - }; - case getType(bpdTransactionsLoadPage.failure): - return { - ...state, - awardPeriodId: action.payload.awardPeriodId, - nextCursor: state.nextCursor, - sectionItems: pot.toError(state.sectionItems, action.payload.error) - }; - - case getType(bpdTransactionsLoadRequiredData.request): - return { ...initState, awardPeriodId: action.payload }; - case getType(bpdTransactionsLoadRequiredData.success): - return { - ...state, - requiredDataLoaded: pot.some(true) - }; - case getType(bpdTransactionsLoadRequiredData.failure): - return { - ...state, - requiredDataLoaded: pot.toError( - state.requiredDataLoaded, - action.payload.error - ) - }; - } - - return state; -}; - -/** - * Return the remote state for all the required data to load the transaction screen. - * Return always pot.none if the selectedPeriod is different from the local currentPeriod (a different period is chosen, need to reload) - */ -export const bpdTransactionsRequiredDataLoadStateSelector = createSelector( - [ - (state: GlobalState) => - state.bonus.bpd.details.transactionsV2.ui.requiredDataLoaded, - (state: GlobalState) => - state.bonus.bpd.details.transactionsV2.ui.awardPeriodId, - bpdSelectedPeriodSelector - ], - ( - potRequiredData, - currentPeriodId, - maybeSelectedPeriod - ): pot.Pot => - pipe( - maybeSelectedPeriod, - O.fromNullable, - O.chain(selectedPeriod => - selectedPeriod.awardPeriodId === currentPeriodId - ? O.some(potRequiredData) - : O.none - ), - O.getOrElseW(() => pot.none) - ) -); - -/** - * Return the {@link Date} of the most recent transaction - */ -export const bpdLastTransactionUpdateSelector = createSelector( - [ - (state: GlobalState) => - state.bonus.bpd.details.transactionsV2.ui.lastTransactionDate - ], - (lastTransactionDate): O.Option => O.fromNullable(lastTransactionDate) -); - -/** - * Return the Pot List of BpdTransactionsSectionItem - */ -export const bpdTransactionsSelector = createSelector( - [ - (state: GlobalState) => - state.bonus.bpd.details.transactionsV2.ui.sectionItems - ], - (sectionItems): pot.Pot, Error> => - pot.map(sectionItems, si => toArray(si)) -); - -export const bpdTransactionsGetNextCursor = createSelector( - [ - (state: GlobalState) => state.bonus.bpd.details.transactionsV2.ui.nextCursor - ], - cursor => cursor -); - -/** - * From BpdTransactionId to Option - */ -export const bpdTransactionByIdSelector = createSelector( - [ - (state: GlobalState) => - state.bonus.bpd.details.transactionsV2.ui.awardPeriodId, - (state: GlobalState) => - state.bonus.bpd.details.transactionsV2.entitiesByPeriod, - paymentMethodsSelector, - bpdSelectedPeriodSelector, - (_: GlobalState, trxId: BpdTransactionId) => trxId - ], - ( - awardPeriodId, - entitiesByPeriod, - paymentMethods, - selectedPeriod, - trxId - ): O.Option => - pipe( - awardPeriodId, - O.fromNullable, - O.chain(periodId => - pipe( - entitiesByPeriod[periodId]?.byId[trxId], - O.fromNullable, - O.map(trx => ({ - ...trx, - image: pipe( - pickPaymentMethodFromHashpan(trx.hashPan, paymentMethods), - O.map(pm => pm.icon), - O.getOrElse(() => cardIcons.UNKNOWN) - ), - title: pipe( - pickPaymentMethodFromHashpan(trx.hashPan, paymentMethods), - O.map(pm => pm.caption), - O.getOrElse(() => FOUR_UNICODE_CIRCLES) - ), - keyId: trx.idTrx, - maxCashbackForTransactionAmount: - selectedPeriod?.maxTransactionCashback - })) - ) - ) - ) -); diff --git a/ts/features/bonus/bpd/store/reducers/index.ts b/ts/features/bonus/bpd/store/reducers/index.ts deleted file mode 100644 index 869ac998fd9..00000000000 --- a/ts/features/bonus/bpd/store/reducers/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { Action, combineReducers } from "redux"; -import bpdDetailsReducer, { BpdDetailsState } from "./details"; -import bpdOnboardingReducer, { BpdOnboardingState } from "./onboarding"; - -export type BpdState = { - details: BpdDetailsState; - onboarding: BpdOnboardingState; -}; - -const bpdReducer = combineReducers({ - details: bpdDetailsReducer, - onboarding: bpdOnboardingReducer -}); - -export default bpdReducer; diff --git a/ts/features/bonus/bpd/store/reducers/onboarding/enroll.ts b/ts/features/bonus/bpd/store/reducers/onboarding/enroll.ts deleted file mode 100644 index bc86ae7a47d..00000000000 --- a/ts/features/bonus/bpd/store/reducers/onboarding/enroll.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { getType } from "typesafe-actions"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { - remoteError, - remoteLoading, - remoteReady, - remoteUndefined, - RemoteValue -} from "../../../../../../common/model/RemoteValue"; -import { bpdEnrollUserToProgram } from "../../actions/onboarding"; -import { Action } from "../../../../../../store/actions/types"; - -/** - * This reducers use the action {@link bpdEnrollUserToProgram} to save&update the result of the enrollment operation - * @param state - * @param action - */ -const bpdEnrollUserReducer = ( - state: RemoteValue = remoteUndefined, - action: Action -): RemoteValue => { - switch (action.type) { - case getType(bpdEnrollUserToProgram.request): - return remoteLoading; - case getType(bpdEnrollUserToProgram.success): - return remoteReady(action.payload.enabled); - case getType(bpdEnrollUserToProgram.failure): - return remoteError(action.payload); - } - return state; -}; - -export const bpdEnrollSelector = ( - state: GlobalState -): RemoteValue => state.bonus.bpd.onboarding.enrollment; - -export default bpdEnrollUserReducer; diff --git a/ts/features/bonus/bpd/store/reducers/onboarding/index.ts b/ts/features/bonus/bpd/store/reducers/onboarding/index.ts deleted file mode 100644 index 987899a2643..00000000000 --- a/ts/features/bonus/bpd/store/reducers/onboarding/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { Action, combineReducers } from "redux"; -import { RemoteValue } from "../../../../../../common/model/RemoteValue"; -import bpdEnrollUserReducer from "./enroll"; -import ongoingOnboardingReducer from "./ongoing"; - -export type BpdOnboardingState = { - enrollment: RemoteValue; - ongoing: boolean; -}; - -const bpdOnboardingReducer = combineReducers({ - enrollment: bpdEnrollUserReducer, - ongoing: ongoingOnboardingReducer -}); - -export default bpdOnboardingReducer; diff --git a/ts/features/bonus/bpd/store/reducers/onboarding/ongoing.ts b/ts/features/bonus/bpd/store/reducers/onboarding/ongoing.ts deleted file mode 100644 index 48f75af59e2..00000000000 --- a/ts/features/bonus/bpd/store/reducers/onboarding/ongoing.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { getType } from "typesafe-actions"; -import { Action } from "../../../../../../store/actions/types"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { bpdIbanInsertionCancel } from "../../actions/iban"; -import { - bpdOnboardingCancel, - bpdOnboardingCompleted, - bpdUserActivate -} from "../../actions/onboarding"; - -/** - * This reducers is used to know if a current onboarding is ongoing - * @param state - * @param action - */ -const ongoingOnboardingReducer = ( - state: boolean = false, - action: Action -): boolean => { - switch (action.type) { - case getType(bpdUserActivate): - return true; - case getType(bpdOnboardingCancel): - case getType(bpdOnboardingCompleted): - case getType(bpdIbanInsertionCancel): - return false; - } - return state; -}; - -/** - * Return true if the user is the ongoing workflow - * @param state - */ -export const isBpdOnboardingOngoing = (state: GlobalState) => - state.bonus.bpd.onboarding.ongoing; - -export default ongoingOnboardingReducer; diff --git a/ts/features/bonus/bpd/types/PatchedWinningTransactionResource.ts b/ts/features/bonus/bpd/types/PatchedWinningTransactionResource.ts deleted file mode 100644 index b9eaeee29b2..00000000000 --- a/ts/features/bonus/bpd/types/PatchedWinningTransactionResource.ts +++ /dev/null @@ -1,46 +0,0 @@ -import * as t from "io-ts"; -import { DateFromISOString } from "../../../../utils/dates"; - -/** - * Patched version of bpd/winning_transactions/WinningTransactionResource - * - trxDate uses DateFromISOStringType instead of UTCISODateFromString (can't decode datetime with offset, ex 2020-10-23T11:13:07.442+02:00) - */ - -// required attributes -const PatchedWinningTransactionResourceR = t.interface({ - amount: t.number, - - awardPeriodId: t.Integer, - - cashback: t.number, - - circuitType: t.string, - - hashPan: t.string, - - idTrxAcquirer: t.string, - - idTrxIssuer: t.string, - - trxDate: DateFromISOString -}); - -// optional attributes -const PatchedWinningTransactionResourceO = t.partial({}); - -export const PatchedWinningTransactionResource = t.intersection( - [PatchedWinningTransactionResourceR, PatchedWinningTransactionResourceO], - "PatchedWinningTransactionResource" -); - -export type PatchedWinningTransactionResource = t.TypeOf< - typeof PatchedWinningTransactionResource ->; - -export const PatchedBpdWinningTransactions = t.readonlyArray( - PatchedWinningTransactionResource -); - -export type PatchedBpdWinningTransactions = t.TypeOf< - typeof PatchedBpdWinningTransactions ->; diff --git a/ts/features/bonus/bpd/utils/__test__/dates.test.ts b/ts/features/bonus/bpd/utils/__test__/dates.test.ts deleted file mode 100644 index 5a63330563e..00000000000 --- a/ts/features/bonus/bpd/utils/__test__/dates.test.ts +++ /dev/null @@ -1,34 +0,0 @@ -import MockDate from "mockdate"; -import { isInGracePeriod } from "../dates"; - -describe("isInGracePeriod", () => { - it("should return true if actual date is between endDate and gracePeriod date", () => { - const endDate = new Date(2020, 11, 31); - MockDate.set("2021-01-08"); - expect(isInGracePeriod(endDate, 10)).toBeTruthy(); - }); - - it("should return false if actual date is before endDate", () => { - const endDate = new Date(2020, 11, 31); - MockDate.set("2020-12-29"); - expect(isInGracePeriod(endDate, 10)).toBeFalsy(); - }); - - it("should return false if actual date is after endDate + gracePeriod", () => { - const endDate = new Date(2020, 11, 31); - MockDate.set("2021-01-29"); - expect(isInGracePeriod(endDate, 10)).toBeFalsy(); - }); - - it("should return false if invalid endDate is passed", () => { - const endDate = new Date(NaN); - MockDate.set("2021-01-29"); - expect(isInGracePeriod(endDate, 10)).toBeFalsy(); - }); - - it("should return false if invalid gracePeriod value is passed", () => { - const endDate = new Date(2020, 11, 31); - MockDate.set("2021-01-29"); - expect(isInGracePeriod(endDate, NaN)).toBeFalsy(); - }); -}); diff --git a/ts/features/bonus/bpd/utils/dates.ts b/ts/features/bonus/bpd/utils/dates.ts deleted file mode 100644 index 4ef0d3a12a8..00000000000 --- a/ts/features/bonus/bpd/utils/dates.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** - * Utility function to determine if we are currently in the grace time of a cashback period - * @param endDate - * @param gracePeriod - */ -export const isInGracePeriod = (endDate: Date, gracePeriod: number) => { - if (isNaN(endDate.getTime()) || isNaN(gracePeriod)) { - return false; - } - - const actualDate = new Date(); - const gracePeriodDate = new Date(endDate.getTime()); - gracePeriodDate.setDate(endDate.getDate() + gracePeriod); - - return ( - actualDate.getTime() >= endDate.getTime() && - actualDate.getTime() <= gracePeriodDate.getTime() - ); -}; diff --git a/ts/features/bonus/cgn/store/reducers/unsubscribe.ts b/ts/features/bonus/cgn/store/reducers/unsubscribe.ts index dac49367fea..8e76d74766d 100644 --- a/ts/features/bonus/cgn/store/reducers/unsubscribe.ts +++ b/ts/features/bonus/cgn/store/reducers/unsubscribe.ts @@ -15,7 +15,7 @@ import { cgnActivationComplete } from "../actions/activation"; export type CgnUnsubscribeState = RemoteValue; /** - * Keep the state of "unsubscribe" from bpd outcome + * Keep the state of "unsubscribe" from bonus outcome * @param state * @param action */ diff --git a/ts/features/bonus/bpd/screens/onboarding/declaration/DeclarationEntry.tsx b/ts/features/bonus/common/components/DeclarationEntry.tsx similarity index 86% rename from ts/features/bonus/bpd/screens/onboarding/declaration/DeclarationEntry.tsx rename to ts/features/bonus/common/components/DeclarationEntry.tsx index 64055b62486..fea7b09b00d 100644 --- a/ts/features/bonus/bpd/screens/onboarding/declaration/DeclarationEntry.tsx +++ b/ts/features/bonus/common/components/DeclarationEntry.tsx @@ -1,9 +1,9 @@ import * as React from "react"; import { View, StyleSheet, TouchableWithoutFeedback } from "react-native"; import { HSpacer, VSpacer } from "@pagopa/io-app-design-system"; -import { Body } from "../../../../../../components/core/typography/Body"; -import { XOR } from "../../../../../../types/utils"; -import { RawCheckBox } from "../../../../../../components/core/selection/checkbox/RawCheckBox"; +import { Body } from "../../../../components/core/typography/Body"; +import { XOR } from "../../../../types/utils"; +import { RawCheckBox } from "../../../../components/core/selection/checkbox/RawCheckBox"; const styles = StyleSheet.create({ main: { flex: 1, flexDirection: "row", flexWrap: "nowrap" }, diff --git a/ts/features/bonus/bpd/screens/details/components/summary/base/ProgressBar.tsx b/ts/features/bonus/common/components/ProgressBar.tsx similarity index 100% rename from ts/features/bonus/bpd/screens/details/components/summary/base/ProgressBar.tsx rename to ts/features/bonus/common/components/ProgressBar.tsx diff --git a/ts/features/bonus/common/screens/AvailableBonusScreen.tsx b/ts/features/bonus/common/screens/AvailableBonusScreen.tsx index d4720d971d6..ec4b6bf18ef 100644 --- a/ts/features/bonus/common/screens/AvailableBonusScreen.tsx +++ b/ts/features/bonus/common/screens/AvailableBonusScreen.tsx @@ -43,7 +43,6 @@ import { GlobalState } from "../../../../store/reducers/types"; import variables from "../../../../theme/variables"; import { storeUrl } from "../../../../utils/appVersion"; import { showToast } from "../../../../utils/showToast"; -import { bpdOnboardingStart } from "../../bpd/store/actions/onboarding"; import { cgnActivationStart } from "../../cgn/store/actions/activation"; import { AvailableBonusItem, @@ -261,7 +260,6 @@ const mapStateToProps = (state: GlobalState) => ({ const mapDispatchToProps = (dispatch: Dispatch) => ({ navigateBack: () => navigateBack(), loadAvailableBonuses: () => dispatch(loadAvailableBonuses.request()), - startBpdOnboarding: () => dispatch(bpdOnboardingStart()), startCgnActivation: () => dispatch(cgnActivationStart()), navigateToServiceDetailsScreen: ( params: ServiceDetailsScreenNavigationParams diff --git a/ts/features/bonus/common/store/actions/index.ts b/ts/features/bonus/common/store/actions/index.ts index 2b1426148da..645de11800f 100644 --- a/ts/features/bonus/common/store/actions/index.ts +++ b/ts/features/bonus/common/store/actions/index.ts @@ -1,4 +1,3 @@ -import { BpdActions } from "../../../bpd/store/actions"; import { CdcActions } from "../../../cdc/store/actions"; import { CgnActions } from "../../../cgn/store/actions"; import { SvActions } from "../../../siciliaVola/store/actions"; @@ -6,7 +5,6 @@ import { AvailableBonusesActions } from "./availableBonusesTypes"; export type BonusActions = | AvailableBonusesActions - | BpdActions | CgnActions | SvActions | CdcActions; diff --git a/ts/features/bonus/common/store/reducers/index.ts b/ts/features/bonus/common/store/reducers/index.ts index 27615e8d347..31659451b06 100644 --- a/ts/features/bonus/common/store/reducers/index.ts +++ b/ts/features/bonus/common/store/reducers/index.ts @@ -1,6 +1,5 @@ import { combineReducers } from "redux"; import { Action } from "../../../../../store/actions/types"; -import bpdReducer, { BpdState } from "../../../bpd/store/reducers"; import cdcReducer, { CdcState } from "../../../cdc/store/reducers"; import cgnReducer, { CgnState } from "../../../cgn/store/reducers"; import svReducer, { SvState } from "../../../siciliaVola/store/reducers"; @@ -10,7 +9,6 @@ import availableBonusesReducer, { export type BonusState = Readonly<{ availableBonusTypes: AvailableBonusTypesState; - bpd: BpdState; cgn: CgnState; sv: SvState; cdc: CdcState; @@ -18,7 +16,6 @@ export type BonusState = Readonly<{ const bonusReducer = combineReducers({ availableBonusTypes: availableBonusesReducer, - bpd: bpdReducer, cgn: cgnReducer, sv: svReducer, cdc: cdcReducer diff --git a/ts/features/bonus/siciliaVola/screens/voucherGeneration/DisabledAdditionalInfoScreen.tsx b/ts/features/bonus/siciliaVola/screens/voucherGeneration/DisabledAdditionalInfoScreen.tsx index d384800bcce..9b5fb9226e7 100644 --- a/ts/features/bonus/siciliaVola/screens/voucherGeneration/DisabledAdditionalInfoScreen.tsx +++ b/ts/features/bonus/siciliaVola/screens/voucherGeneration/DisabledAdditionalInfoScreen.tsx @@ -15,7 +15,7 @@ import I18n from "../../../../../i18n"; import { GlobalState } from "../../../../../store/reducers/types"; import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp"; import { openWebUrl } from "../../../../../utils/url"; -import { DeclarationEntry } from "../../../bpd/screens/onboarding/declaration/DeclarationEntry"; +import { DeclarationEntry } from "../../../common/components/DeclarationEntry"; import SV_ROUTES from "../../navigation/routes"; import { svGenerateVoucherBack, diff --git a/ts/features/design-system/core/DSLegacyPictograms.tsx b/ts/features/design-system/core/DSLegacyPictograms.tsx index d06d9ff0764..dbeb2167b77 100644 --- a/ts/features/design-system/core/DSLegacyPictograms.tsx +++ b/ts/features/design-system/core/DSLegacyPictograms.tsx @@ -1,39 +1,38 @@ -import * as React from "react"; -import { View, StyleSheet } from "react-native"; import { IOColors, useIOTheme } from "@pagopa/io-app-design-system"; +import * as React from "react"; +import { StyleSheet, View } from "react-native"; +import { H2 } from "../../../components/core/typography/H2"; import { DSAssetViewerBox, assetItemGutter, renderRasterImage } from "../components/DSAssetViewerBox"; -import { H2 } from "../../../components/core/typography/H2"; /* PICTOGRAMS */ -import Fireworks from "../../../../img/pictograms/fireworks.png"; -import FireworksWhite from "../../../../img/bonus/bpd/fireworks.png"; -import Question from "../../../../img/pictograms/doubt.png"; -import Hourglass from "../../../../img/pictograms/hourglass.png"; import AirBaloon from "../../../../img/bonus/siciliaVola/emptyVoucherList.svg"; -import AirBaloonRaster from "../../../../img/landing/session_expired.png"; -import AirBaloonArrow from "../../../../img/messages/empty-message-list-icon.png"; import Timeout from "../../../../img/bonus/siciliaVola/generateVoucherTimeout.svg"; +import BrokenLink from "../../../../img/broken-link.png"; +import AirBaloonRaster from "../../../../img/landing/session_expired.png"; import Baloons from "../../../../img/messages/empty-due-date-list-icon.png"; +import AirBaloonArrow from "../../../../img/messages/empty-message-list-icon.png"; import PiggyBank from "../../../../img/messages/empty-transaction-list-icon.png"; import Error from "../../../../img/messages/error-message-detail-icon.png"; +import Question from "../../../../img/pictograms/doubt.png"; +import Fireworks from "../../../../img/pictograms/fireworks.png"; +import Hourglass from "../../../../img/pictograms/hourglass.png"; +import CompletedRaster from "../../../../img/pictograms/payment-completed.png"; +import Completed from "../../../../img/pictograms/payment-completed.svg"; import BeerMug from "../../../../img/search/beer-mug.png"; import Search from "../../../../img/search/search-icon.png"; import Puzzle from "../../../../img/services/icon-loading-services.png"; +import ABILogo from "../../../../img/wallet/cards-icons/abiLogoFallback.png"; import Castle from "../../../../img/wallet/errors/domain-unknown-icon.png"; +import Umbrella from "../../../../img/wallet/errors/generic-error-icon.png"; import Abacus from "../../../../img/wallet/errors/invalid-amount-icon.png"; +import InProgress from "../../../../img/wallet/errors/missing-payment-id-icon.png"; import Vespa from "../../../../img/wallet/errors/payment-ongoing-icon.png"; import NotAvailable from "../../../../img/wallet/errors/payment-unavailable-icon.png"; -import InProgress from "../../../../img/wallet/errors/missing-payment-id-icon.png"; import Unrecognized from "../../../../img/wallet/errors/payment-unknown-icon.png"; -import Umbrella from "../../../../img/wallet/errors/generic-error-icon.png"; -import ABILogo from "../../../../img/wallet/cards-icons/abiLogoFallback.png"; -import CompletedRaster from "../../../../img/pictograms/payment-completed.png"; -import Completed from "../../../../img/pictograms/payment-completed.svg"; -import BrokenLink from "../../../../img/broken-link.png"; /* EU Covid Certificate */ import CertificateExpired from "../../../../img/features/euCovidCert/certificate_expired.png"; import CertificateNotFound from "../../../../img/features/euCovidCert/certificate_not_found.png"; @@ -66,11 +65,6 @@ export const DSLegacyPictograms = () => { name={"Fireworks"} image={renderRasterImage(Fireworks)} /> - { const aBPay: BPayPaymentMethod = { walletType: "BPay", createDate: "2021-07-08", - enableableFunctions: ["FA", "pagoPA", "BPD"], + enableableFunctions: ["FA", "pagoPA"], favourite: false, idWallet: 25572, info: { diff --git a/ts/features/wallet/cobadge/component/__tests__/CobadgeWalletPreview.test.tsx b/ts/features/wallet/cobadge/component/__tests__/CobadgeWalletPreview.test.tsx index a7cfe7bffc7..207a4ea77c8 100644 --- a/ts/features/wallet/cobadge/component/__tests__/CobadgeWalletPreview.test.tsx +++ b/ts/features/wallet/cobadge/component/__tests__/CobadgeWalletPreview.test.tsx @@ -23,7 +23,7 @@ describe("CobadgeWalletPreview component", () => { const aCobadgeCard: CreditCardPaymentMethod = { walletType: "Card", createDate: "2021-07-08", - enableableFunctions: ["FA", "pagoPA", "BPD"], + enableableFunctions: ["FA", "pagoPA"], favourite: false, idWallet: 25572, info: { diff --git a/ts/features/wallet/component/__test__/PagoPaPaymentCapability.test.tsx b/ts/features/wallet/component/__test__/PagoPaPaymentCapability.test.tsx index 0bb40920dbd..f7f55983d65 100644 --- a/ts/features/wallet/component/__test__/PagoPaPaymentCapability.test.tsx +++ b/ts/features/wallet/component/__test__/PagoPaPaymentCapability.test.tsx @@ -61,7 +61,7 @@ describe("PagoPaPaymentCapability", () => { const aPaymentMethod = { ...aBancomat, kind: "Bancomat", - enableableFunctions: [EnableableFunctionsEnum.BPD] + enableableFunctions: [] } as PaymentMethod; const globalState = appReducer(undefined, applicationChangeState("active")); @@ -88,7 +88,7 @@ describe("PagoPaPaymentCapability", () => { ...aNonMaestroCreditCard, kind: "CreditCard", pagoPA: false, - enableableFunctions: [EnableableFunctionsEnum.BPD] + enableableFunctions: [] } as PaymentMethod; const globalState = appReducer(undefined, applicationChangeState("active")); diff --git a/ts/features/wallet/component/card/FeaturedCardCarousel.tsx b/ts/features/wallet/component/card/FeaturedCardCarousel.tsx index 2dbf494a850..d8ccc7c1fb4 100644 --- a/ts/features/wallet/component/card/FeaturedCardCarousel.tsx +++ b/ts/features/wallet/component/card/FeaturedCardCarousel.tsx @@ -8,7 +8,6 @@ import { useEffect } from "react"; import { ScrollView, StyleSheet, View } from "react-native"; import { connect } from "react-redux"; import { BonusAvailable } from "../../../../../definitions/content/BonusAvailable"; -import cashbackLogo from "../../../../../img/bonus/bpd/logo_cashback_blue.png"; import cgnLogo from "../../../../../img/bonus/cgn/cgn_logo.png"; import { H3 } from "../../../../components/core/typography/H3"; import { IOStyles } from "../../../../components/core/variables/IOStyles"; @@ -31,8 +30,6 @@ import { import { GlobalState } from "../../../../store/reducers/types"; import { showToast } from "../../../../utils/showToast"; import { ID_CDC_TYPE, ID_CGN_TYPE } from "../../../bonus/common/utils"; -import { bpdOnboardingStart } from "../../../bonus/bpd/store/actions/onboarding"; -import { bpdEnabledSelector } from "../../../bonus/bpd/store/reducers/details/activation"; import { cgnActivationStart } from "../../../bonus/cgn/store/actions/activation"; import { isCgnEnrolledSelector } from "../../../bonus/cgn/store/reducers/details"; import { @@ -47,7 +44,7 @@ type Props = ReturnType & ReturnType; type BonusUtils = { - logo?: typeof cashbackLogo; + logo?: typeof cgnLogo; handler: (bonus: BonusAvailable) => void; }; @@ -193,13 +190,11 @@ const FeaturedCardCarousel: React.FunctionComponent = (props: Props) => { }; const mapStateToProps = (state: GlobalState) => ({ - bpdActiveBonus: bpdEnabledSelector(state), cgnActiveBonus: isCgnEnrolledSelector(state), availableBonusesList: supportedAvailableBonusSelector(state) }); const mapDispatchToProps = (dispatch: Dispatch) => ({ - startBpdOnboarding: () => dispatch(bpdOnboardingStart()), startCgnActivation: () => dispatch(cgnActivationStart()) }); diff --git a/ts/features/wallet/creditCard/screen/__tests__/CreditCardDetailScreen.test.tsx b/ts/features/wallet/creditCard/screen/__tests__/CreditCardDetailScreen.test.tsx index a342cad4c30..58bfeeb6cb9 100644 --- a/ts/features/wallet/creditCard/screen/__tests__/CreditCardDetailScreen.test.tsx +++ b/ts/features/wallet/creditCard/screen/__tests__/CreditCardDetailScreen.test.tsx @@ -23,10 +23,7 @@ import CreditCardDetailScreen from "../CreditCardDetailScreen"; const creditCard: CreditCardPaymentMethod = { walletType: WalletTypeEnum.Card, createDate: "2021-07-08", - enableableFunctions: [ - EnableableFunctionsEnum.BPD, - EnableableFunctionsEnum.pagoPA - ], + enableableFunctions: [EnableableFunctionsEnum.pagoPA], favourite: false, idWallet: 23216, info: { @@ -50,9 +47,6 @@ const creditCard: CreditCardPaymentMethod = { icon: 37 }; -jest.mock("../../../../../config", () => ({ - bpdEnabled: true -})); const mockInitiative = { initiativeId: "idpay", initiativeName: "idpay", diff --git a/ts/features/wallet/onboarding/bancomatPay/analytics/index.ts b/ts/features/wallet/onboarding/bancomatPay/analytics/index.ts index 39b6cc7c62b..02d709bae49 100644 --- a/ts/features/wallet/onboarding/bancomatPay/analytics/index.ts +++ b/ts/features/wallet/onboarding/bancomatPay/analytics/index.ts @@ -6,7 +6,7 @@ import { isTimeoutError } from "../../../../../utils/errors"; import { - addBPayToWallet, + addBPayToWalletAction, searchUserBPay, walletAddBPayBack, walletAddBPayCancel, @@ -23,8 +23,8 @@ export const trackBPayAction = case getType(walletAddBPayCompleted): case getType(walletAddBPayCancel): case getType(walletAddBPayBack): - case getType(addBPayToWallet.request): - case getType(addBPayToWallet.success): + case getType(addBPayToWalletAction.request): + case getType(addBPayToWalletAction.success): return mp.track(action.type); case getType(searchUserBPay.request): return mp.track(action.type, { abi: action.payload ?? "all" }); @@ -36,7 +36,7 @@ export const trackBPayAction = ) }); - case getType(addBPayToWallet.failure): + case getType(addBPayToWalletAction.failure): return mp.track(action.type, { reason: getNetworkErrorMessage(action.payload) }); diff --git a/ts/features/wallet/onboarding/bancomatPay/navigation/action.ts b/ts/features/wallet/onboarding/bancomatPay/navigation/action.ts index ea272d96f02..20a7c40ef06 100644 --- a/ts/features/wallet/onboarding/bancomatPay/navigation/action.ts +++ b/ts/features/wallet/onboarding/bancomatPay/navigation/action.ts @@ -44,17 +44,3 @@ export const navigateToOnboardingBPaySearchAvailableUserAccount = () => } }) ); - -/** - * @deprecated Do not use this method when you have access to a navigation prop or useNavigation since it will behave differently, - * and many helper methods specific to screens won't be available. - */ -export const navigateToActivateBpdOnNewBPay = () => - NavigationService.dispatchNavigationAction( - CommonActions.navigate(ROUTES.WALLET_NAVIGATOR, { - screen: WALLET_ONBOARDING_BPAY_ROUTES.MAIN, - params: { - screen: WALLET_ONBOARDING_BPAY_ROUTES.ACTIVATE_BPD_NEW - } - }) - ); diff --git a/ts/features/wallet/onboarding/bancomatPay/navigation/routes.ts b/ts/features/wallet/onboarding/bancomatPay/navigation/routes.ts index 1a6a6ecf8fd..5ab61b1bbef 100644 --- a/ts/features/wallet/onboarding/bancomatPay/navigation/routes.ts +++ b/ts/features/wallet/onboarding/bancomatPay/navigation/routes.ts @@ -4,9 +4,7 @@ const WALLET_ONBOARDING_BPAY_ROUTES = { START: "WALLET_ONBOARDING_BPAY_START", CHOOSE_BANK: "WALLET_ONBOARDING_BPAY_CHOOSE_BANK_SCREEN", SEARCH_AVAILABLE_USER_ACCOUNT: - "WALLET_ONBOARDING_BPAY_SEARCH_AVAILABLE_USER_ACCOUNT", - - ACTIVATE_BPD_NEW: "WALLET_ONBOARDING_BPAY_ACTIVATE_BPD_NEW" + "WALLET_ONBOARDING_BPAY_SEARCH_AVAILABLE_USER_ACCOUNT" } as const; export default WALLET_ONBOARDING_BPAY_ROUTES; diff --git a/ts/features/wallet/onboarding/bancomatPay/saga/networking/index.ts b/ts/features/wallet/onboarding/bancomatPay/saga/networking/index.ts index 7941564b873..0d7ea80b960 100644 --- a/ts/features/wallet/onboarding/bancomatPay/saga/networking/index.ts +++ b/ts/features/wallet/onboarding/bancomatPay/saga/networking/index.ts @@ -18,7 +18,7 @@ import { readablePrivacyReport } from "../../../../../../utils/reporters"; import { SessionManager } from "../../../../../../utils/SessionManager"; import { fromPatchedWalletV2ToRawBPay } from "../../../../../../utils/walletv2"; import { - addBPayToWallet as addBpayToWalletAction, + addBPayToWalletAction as addBpayToWalletAction, searchUserBPay } from "../../store/actions"; diff --git a/ts/features/wallet/onboarding/bancomatPay/saga/orchestration/addBPayToWallet.ts b/ts/features/wallet/onboarding/bancomatPay/saga/orchestration/addBPayToWallet.ts index 4119d8361c9..09d507bb984 100644 --- a/ts/features/wallet/onboarding/bancomatPay/saga/orchestration/addBPayToWallet.ts +++ b/ts/features/wallet/onboarding/bancomatPay/saga/orchestration/addBPayToWallet.ts @@ -1,19 +1,15 @@ -import { call, put, select } from "typed-redux-saga/macro"; +import { call, put } from "typed-redux-saga/macro"; import NavigationService from "../../../../../../navigation/NavigationService"; import ROUTES from "../../../../../../navigation/routes"; import { + WorkUnitHandler, executeWorkUnit, - withResetNavigationStack, - WorkUnitHandler + withResetNavigationStack } from "../../../../../../sagas/workUnit"; import { navigateToWalletHome } from "../../../../../../store/actions/navigation"; import { fetchWalletsRequest } from "../../../../../../store/actions/wallet/wallets"; -import { activateBpdOnNewPaymentMethods } from "../../../../../bonus/bpd/saga/orchestration/activateBpdOnNewAddedPaymentMethods"; -import { - navigateToActivateBpdOnNewBPay, - navigateToOnboardingBPaySearchStartScreen -} from "../../navigation/action"; +import { navigateToOnboardingBPaySearchStartScreen } from "../../navigation/action"; import WALLET_ONBOARDING_BPAY_ROUTES from "../../navigation/routes"; import { walletAddBPayBack, @@ -21,7 +17,6 @@ import { walletAddBPayCompleted, walletAddBPayFailure } from "../../store/actions"; -import { onboardingBPayAddedAccountSelector } from "../../store/reducers/addedBPay"; /** * Define the workflow that allows the user to add BPay accounts to the wallet. @@ -42,9 +37,9 @@ function* bPayWorkUnit() { } /** - * Chain the add BPay to wallet with "activate bpd on the new BPay accounts" + * add Bpay to wallet saga */ -export function* addBPayToWalletAndActivateBpd() { +export function* addBPayToWalletSaga() { const initialScreenName: ReturnType< typeof NavigationService.getCurrentRouteName > = yield* call(NavigationService.getCurrentRouteName); @@ -67,14 +62,5 @@ export function* addBPayToWalletAndActivateBpd() { if (res === "completed") { // refresh wallets list yield* put(fetchWalletsRequest()); - // read the new added BPay - const bPayAdded: ReturnType = - yield* select(onboardingBPayAddedAccountSelector); - - yield* call( - activateBpdOnNewPaymentMethods, - bPayAdded, - navigateToActivateBpdOnNewBPay - ); } } diff --git a/ts/features/wallet/onboarding/bancomatPay/screens/ActivateBpdOnNewBPayScreen.tsx b/ts/features/wallet/onboarding/bancomatPay/screens/ActivateBpdOnNewBPayScreen.tsx deleted file mode 100644 index 601aeb51307..00000000000 --- a/ts/features/wallet/onboarding/bancomatPay/screens/ActivateBpdOnNewBPayScreen.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as React from "react"; -import { connect } from "react-redux"; -import I18n from "../../../../../i18n"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp"; -import ActivateBpdOnNewPaymentMethodScreen from "../../common/screens/bpd/ActivateBpdOnNewPaymentMethodScreen"; -import { onboardingBPayAddedAccountSelector } from "../store/reducers/addedBPay"; - -type Props = ReturnType; - -/** - * The user can activate the cashback on the new added BPay account - * @param props - * @constructor - */ -const ActivateBpdOnNewBPayScreen = (props: Props) => ( - -); - -const mapStateToProps = (state: GlobalState) => ({ - newBPay: onboardingBPayAddedAccountSelector(state) -}); -export default connect(mapStateToProps)(ActivateBpdOnNewBPayScreen); diff --git a/ts/features/wallet/onboarding/bancomatPay/screens/add-account/AddBPayScreen.tsx b/ts/features/wallet/onboarding/bancomatPay/screens/add-account/AddBPayScreen.tsx index d65956ca407..4afc7d0b5cf 100644 --- a/ts/features/wallet/onboarding/bancomatPay/screens/add-account/AddBPayScreen.tsx +++ b/ts/features/wallet/onboarding/bancomatPay/screens/add-account/AddBPayScreen.tsx @@ -16,7 +16,7 @@ import { isReady } from "../../../../../../common/model/RemoteValue"; import { - addBPayToWallet, + addBPayToWalletAction, walletAddBPayCancel, walletAddBPayCompleted } from "../../store/actions"; @@ -96,10 +96,10 @@ const AddBPayScreen = (props: Props): React.ReactElement | null => { }; const mapDispatchToProps = (dispatch: Dispatch) => ({ - addBPay: (bPay: BPay) => dispatch(addBPayToWallet.request(bPay)), + addBPay: (bPay: BPay) => dispatch(addBPayToWalletAction.request(bPay)), onCompleted: () => dispatch(walletAddBPayCompleted()), onCancel: () => dispatch(walletAddBPayCancel()), - onRetry: (bPay: BPay) => dispatch(addBPayToWallet.request(bPay)) + onRetry: (bPay: BPay) => dispatch(addBPayToWalletAction.request(bPay)) }); const mapStateToProps = (state: GlobalState) => { diff --git a/ts/features/wallet/onboarding/bancomatPay/store/actions/index.ts b/ts/features/wallet/onboarding/bancomatPay/store/actions/index.ts index 87f9d0cd00b..f6566a097f6 100644 --- a/ts/features/wallet/onboarding/bancomatPay/store/actions/index.ts +++ b/ts/features/wallet/onboarding/bancomatPay/store/actions/index.ts @@ -19,7 +19,7 @@ export const searchUserBPay = createAsyncAction( /** * The user add a specific BPay account to the wallet */ -export const addBPayToWallet = createAsyncAction( +export const addBPayToWalletAction = createAsyncAction( "WALLET_ONBOARDING_BPAY_ADD_REQUEST", "WALLET_ONBOARDING_BPAY_ADD_SUCCESS", "WALLET_ONBOARDING_BPAY_ADD_FAILURE" @@ -63,7 +63,7 @@ export const walletAddBPayBack = createStandardAction( export type BPayActions = | ActionType - | ActionType + | ActionType | ActionType | ActionType | ActionType diff --git a/ts/features/wallet/onboarding/bancomatPay/store/reducers/addedBPay.ts b/ts/features/wallet/onboarding/bancomatPay/store/reducers/addedBPay.ts index 8c08ff68374..b47f75c9b09 100644 --- a/ts/features/wallet/onboarding/bancomatPay/store/reducers/addedBPay.ts +++ b/ts/features/wallet/onboarding/bancomatPay/store/reducers/addedBPay.ts @@ -8,7 +8,7 @@ import { import { enhanceBPay } from "../../../../../../utils/paymentMethod"; import { getValueOrElse } from "../../../../../../common/model/RemoteValue"; import { abiSelector } from "../../../store/abi"; -import { addBPayToWallet, walletAddBPayStart } from "../actions"; +import { addBPayToWalletAction, walletAddBPayStart } from "../actions"; const addedBPayReducer = ( state: ReadonlyArray = [], @@ -16,7 +16,7 @@ const addedBPayReducer = ( ): ReadonlyArray => { switch (action.type) { // Register a new BPay account added in the current onboarding session - case getType(addBPayToWallet.success): + case getType(addBPayToWalletAction.success): return [...state, action.payload]; // Reset the state when starting a new BPay onboarding workflow case getType(walletAddBPayStart): diff --git a/ts/features/wallet/onboarding/bancomatPay/store/reducers/addingBPay.ts b/ts/features/wallet/onboarding/bancomatPay/store/reducers/addingBPay.ts index 1e71e059508..a55df64f42f 100644 --- a/ts/features/wallet/onboarding/bancomatPay/store/reducers/addingBPay.ts +++ b/ts/features/wallet/onboarding/bancomatPay/store/reducers/addingBPay.ts @@ -10,7 +10,7 @@ import { remoteUndefined, RemoteValue } from "../../../../../../common/model/RemoteValue"; -import { addBPayToWallet, walletAddBPayStart } from "../actions"; +import { addBPayToWalletAction, walletAddBPayStart } from "../actions"; import { NetworkError } from "../../../../../../utils/errors"; export type AddingBPayState = { @@ -27,17 +27,17 @@ const addingBPayReducer = ( action: Action ): AddingBPayState => { switch (action.type) { - case getType(addBPayToWallet.request): + case getType(addBPayToWalletAction.request): return { selectedBPay: action.payload, addingResult: remoteLoading }; - case getType(addBPayToWallet.success): + case getType(addBPayToWalletAction.success): return { ...state, addingResult: remoteReady(action.payload) }; - case getType(addBPayToWallet.failure): + case getType(addBPayToWalletAction.failure): return { ...state, addingResult: remoteError(action.payload) diff --git a/ts/features/wallet/onboarding/cobadge/navigation/action.ts b/ts/features/wallet/onboarding/cobadge/navigation/action.ts index b656a6aa6b6..6ffb0063c1d 100644 --- a/ts/features/wallet/onboarding/cobadge/navigation/action.ts +++ b/ts/features/wallet/onboarding/cobadge/navigation/action.ts @@ -48,17 +48,3 @@ export const navigateToOnboardingCoBadgeSearchAvailable = () => } }) ); - -/** - * @deprecated Do not use this method when you have access to a navigation prop or useNavigation since it will behave differently, - * and many helper methods specific to screens won't be available. - */ -export const navigateToActivateBpdOnNewCoBadge = () => - NavigationService.dispatchNavigationAction( - CommonActions.navigate(ROUTES.WALLET_NAVIGATOR, { - screen: WALLET_ONBOARDING_COBADGE_ROUTES.MAIN, - params: { - screen: WALLET_ONBOARDING_COBADGE_ROUTES.ACTIVATE_BPD_NEW - } - }) - ); diff --git a/ts/features/wallet/onboarding/cobadge/navigation/routes.ts b/ts/features/wallet/onboarding/cobadge/navigation/routes.ts index b695faa6402..ff162ba4325 100644 --- a/ts/features/wallet/onboarding/cobadge/navigation/routes.ts +++ b/ts/features/wallet/onboarding/cobadge/navigation/routes.ts @@ -4,9 +4,7 @@ const WALLET_ONBOARDING_COBADGE_ROUTES = { CHOOSE_TYPE: "WALLET_ONBOARDING_COBADGE_CHOOSE_TYPE", START: "WALLET_ONBOARDING_COBADGE_START", - SEARCH_AVAILABLE: "WALLET_ONBOARDING_COBADGE_SEARCH_AVAILABLE", - - ACTIVATE_BPD_NEW: "WALLET_ONBOARDING_COBADGE_ACTIVATE_BPD_NEW" + SEARCH_AVAILABLE: "WALLET_ONBOARDING_COBADGE_SEARCH_AVAILABLE" } as const; export default WALLET_ONBOARDING_COBADGE_ROUTES; diff --git a/ts/features/wallet/onboarding/cobadge/saga/orchestration/addCoBadgeToWallet.ts b/ts/features/wallet/onboarding/cobadge/saga/orchestration/addCoBadgeToWallet.ts index 4aab3d35b68..dfb48e4e668 100644 --- a/ts/features/wallet/onboarding/cobadge/saga/orchestration/addCoBadgeToWallet.ts +++ b/ts/features/wallet/onboarding/cobadge/saga/orchestration/addCoBadgeToWallet.ts @@ -1,4 +1,4 @@ -import { call, put, select } from "typed-redux-saga/macro"; +import { call, put } from "typed-redux-saga/macro"; import NavigationService from "../../../../../../navigation/NavigationService"; import ROUTES from "../../../../../../navigation/routes"; import { @@ -9,11 +9,7 @@ import { import { navigateToWalletHome } from "../../../../../../store/actions/navigation"; import { fetchWalletsRequest } from "../../../../../../store/actions/wallet/wallets"; import { SagaCallReturnType } from "../../../../../../types/utils"; -import { activateBpdOnNewPaymentMethods } from "../../../../../bonus/bpd/saga/orchestration/activateBpdOnNewAddedPaymentMethods"; -import { - navigateToActivateBpdOnNewCoBadge, - navigateToOnboardingCoBadgeSearchStartScreen -} from "../../navigation/action"; +import { navigateToOnboardingCoBadgeSearchStartScreen } from "../../navigation/action"; import WALLET_ONBOARDING_COBADGE_ROUTES from "../../navigation/routes"; import { walletAddCoBadgeBack, @@ -21,7 +17,6 @@ import { walletAddCoBadgeCompleted, walletAddCoBadgeFailure } from "../../store/actions"; -import { onboardingCoBadgeAddedSelector } from "../../store/reducers/addedCoBadge"; /** * Define the workflow that allows the user to add a co-badge card to the wallet. @@ -50,9 +45,9 @@ const returnToWalletRoutes = new Set([ ]); /** - * Chain the add co-badge to wallet with "activate bpd on the new co-badge cards" + * add co-badge to wallet saga */ -export function* addCoBadgeToWalletAndActivateBpd() { +export function* addCoBadgeToWalletSaga() { const initialScreenName: ReturnType< typeof NavigationService.getCurrentRouteName > = yield* call(NavigationService.getCurrentRouteName); @@ -78,14 +73,5 @@ export function* addCoBadgeToWalletAndActivateBpd() { if (res === "completed") { // refresh wallets list yield* put(fetchWalletsRequest()); - // read the new added co-badge cards - const coBadgeAdded: ReturnType = - yield* select(onboardingCoBadgeAddedSelector); - - yield* call( - activateBpdOnNewPaymentMethods, - coBadgeAdded, - navigateToActivateBpdOnNewCoBadge - ); } } diff --git a/ts/features/wallet/onboarding/cobadge/screens/ActivateBpdOnNewCoBadgeScreen.tsx b/ts/features/wallet/onboarding/cobadge/screens/ActivateBpdOnNewCoBadgeScreen.tsx deleted file mode 100644 index 2623e1118f8..00000000000 --- a/ts/features/wallet/onboarding/cobadge/screens/ActivateBpdOnNewCoBadgeScreen.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import * as React from "react"; -import { connect } from "react-redux"; -import I18n from "../../../../../i18n"; -import { GlobalState } from "../../../../../store/reducers/types"; -import { emptyContextualHelp } from "../../../../../utils/emptyContextualHelp"; -import ActivateBpdOnNewPaymentMethodScreen from "../../common/screens/bpd/ActivateBpdOnNewPaymentMethodScreen"; -import { onboardingCoBadgeAddedSelector } from "../store/reducers/addedCoBadge"; - -type Props = ReturnType; - -/** - * The user can activate the cashback on the new added coBadge card - * @param props - * @constructor - */ -const ActivateBpdOnNewCoBadgeScreen = (props: Props) => ( - -); - -const mapStateToProps = (state: GlobalState) => ({ - newCoBadge: onboardingCoBadgeAddedSelector(state) -}); -export default connect(mapStateToProps)(ActivateBpdOnNewCoBadgeScreen); diff --git a/ts/features/wallet/onboarding/common/screens/bpd/ActivateBpdOnNewPaymentMethodScreen.tsx b/ts/features/wallet/onboarding/common/screens/bpd/ActivateBpdOnNewPaymentMethodScreen.tsx deleted file mode 100644 index 5df3f19afd3..00000000000 --- a/ts/features/wallet/onboarding/common/screens/bpd/ActivateBpdOnNewPaymentMethodScreen.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import * as React from "react"; -import { View, SafeAreaView, ScrollView } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { Body } from "../../../../../../components/core/typography/Body"; -import { H1 } from "../../../../../../components/core/typography/H1"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import BaseScreenComponent from "../../../../../../components/screens/BaseScreenComponent"; -import FooterWithButtons from "../../../../../../components/ui/FooterWithButtons"; -import I18n from "../../../../../../i18n"; -import { navigateBack } from "../../../../../../store/actions/navigation"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { PaymentMethod } from "../../../../../../types/pagopa"; -import { PaymentMethodRawList } from "../../../../../bonus/bpd/components/paymentMethodActivationToggle/list/PaymentMethodRawList"; -import { areAnyPaymentMethodsActiveSelector } from "../../../../../bonus/bpd/store/reducers/details/paymentMethods"; - -type OwnProps = { - paymentMethods: ReadonlyArray; - title: string; -}; - -export type Props = ReturnType & - ReturnType & - OwnProps & - Pick, "contextualHelp">; - -const loadLocales = () => ({ - title: I18n.t("bonus.bpd.activateOnNewMethods.title"), - body1: I18n.t("bonus.bpd.activateOnNewMethods.body1"), - body2: I18n.t("bonus.bpd.activateOnNewMethods.body2"), - skip: I18n.t("bonus.bpd.activateOnNewMethods.skip"), - continueStr: I18n.t("global.buttons.continue") -}); - -/** - * return a two button footer - * left button is enabled when no payment methods are BPD active - * right button button is enabled when at least one payment method is BPD active - * @param props - */ -const getFooter = (props: Props) => { - const { continueStr, skip } = loadLocales(); - const notNowButtonProps = { - primary: false, - bordered: true, - disabled: props.areAnyPaymentMethodsActive, - onPress: props.skip, - title: skip - }; - const continueButtonProps = { - block: true, - primary: true, - disabled: !props.areAnyPaymentMethodsActive, - onPress: props.skip, - title: continueStr - }; - return ( - - ); -}; - -const ActivateBpdOnNewPaymentMethodScreen: React.FunctionComponent< - Props -> = props => { - const { title, body1, body2 } = loadLocales(); - - return ( - - - - - -

{title}

- - {body1} - - - - {body2} -
-
- {getFooter(props)} -
-
- ); -}; - -const mapDispatchToProps = (_: Dispatch) => ({ - skip: () => navigateBack() -}); - -const mapStateToProps = (state: GlobalState, props: OwnProps) => ({ - areAnyPaymentMethodsActive: areAnyPaymentMethodsActiveSelector( - props.paymentMethods - )(state) -}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(ActivateBpdOnNewPaymentMethodScreen); diff --git a/ts/features/wallet/onboarding/common/screens/bpd/SuggestBpdActivationScreen.tsx b/ts/features/wallet/onboarding/common/screens/bpd/SuggestBpdActivationScreen.tsx deleted file mode 100644 index b4ed53131c5..00000000000 --- a/ts/features/wallet/onboarding/common/screens/bpd/SuggestBpdActivationScreen.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import * as React from "react"; -import { View, SafeAreaView, ScrollView } from "react-native"; -import { connect } from "react-redux"; -import { Dispatch } from "redux"; -import { VSpacer } from "@pagopa/io-app-design-system"; -import { Body } from "../../../../../../components/core/typography/Body"; -import { H1 } from "../../../../../../components/core/typography/H1"; -import { IOStyles } from "../../../../../../components/core/variables/IOStyles"; -import BaseScreenComponent from "../../../../../../components/screens/BaseScreenComponent"; -import I18n from "../../../../../../i18n"; -import { navigateBack } from "../../../../../../store/actions/navigation"; -import { GlobalState } from "../../../../../../store/reducers/types"; -import { emptyContextualHelp } from "../../../../../../utils/emptyContextualHelp"; -import { FooterTwoButtons } from "../../../../../../components/markdown/FooterTwoButtons"; -import { bpdOnboardingStart } from "../../../../../bonus/bpd/store/actions/onboarding"; - -export type Props = ReturnType & - ReturnType; - -const loadLocales = () => ({ - headerTitle: I18n.t("bonus.bpd.title"), - title: I18n.t("bonus.bpd.suggestActivation.title"), - body: I18n.t("bonus.bpd.suggestActivation.body"), - skip: I18n.t("bonus.bpd.suggestActivation.skip"), - confirm: I18n.t("bonus.bpd.suggestActivation.confirm") -}); - -/** - * this screen prompts the user to activate bpd after inserting a new bancomat. - * @constructor - */ -const SuggestBpdActivationScreen: React.FunctionComponent = props => { - const { headerTitle, title, body, skip, confirm } = loadLocales(); - - return ( - - - - - -

{title}

- - {body} -
-
- -
-
- ); -}; - -const mapDispatchToProps = (dispatch: Dispatch) => ({ - skip: () => navigateBack(), - activateBpd: () => dispatch(bpdOnboardingStart()) -}); - -const mapStateToProps = (_: GlobalState) => ({}); - -export default connect( - mapStateToProps, - mapDispatchToProps -)(SuggestBpdActivationScreen); diff --git a/ts/navigation/params/WalletParamsList.ts b/ts/navigation/params/WalletParamsList.ts index 1aeea2ef5b0..fec5a2ce8a4 100644 --- a/ts/navigation/params/WalletParamsList.ts +++ b/ts/navigation/params/WalletParamsList.ts @@ -60,7 +60,4 @@ export type WalletParamsList = { [WALLET_ONBOARDING_COBADGE_ROUTES.MAIN]: NavigatorScreenParams; [PAYPAL_ROUTES.ONBOARDING .MAIN]: NavigatorScreenParams; - - [WALLET_ONBOARDING_BPAY_ROUTES.ACTIVATE_BPD_NEW]: undefined; - [WALLET_ONBOARDING_COBADGE_ROUTES.ACTIVATE_BPD_NEW]: undefined; }; diff --git a/ts/sagas/profile.ts b/ts/sagas/profile.ts index 17d89b38b20..26d88cfa615 100644 --- a/ts/sagas/profile.ts +++ b/ts/sagas/profile.ts @@ -16,8 +16,6 @@ import { UpdateProfile412ErrorTypesEnum } from "../../definitions/backend/Update import { UserDataProcessingChoiceEnum } from "../../definitions/backend/UserDataProcessingChoice"; import { BackendClient } from "../api/backend"; import { tosVersion } from "../config"; -import { bpdLoadActivationStatus } from "../features/bonus/bpd/store/actions/details"; -import { bpdEnabledSelector } from "../features/bonus/bpd/store/reducers/details/activation"; import { cgnDetails } from "../features/bonus/cgn/store/actions/details"; import { cgnDetailSelector } from "../features/bonus/cgn/store/reducers/details"; import { withRefreshApiCall } from "../features/fastLogin/saga/utils"; @@ -326,21 +324,6 @@ function* startEmailValidationProcessSaga( } function* handleLoadBonusBeforeRemoveAccount() { - const bpdActive: ReturnType = yield* select( - bpdEnabledSelector - ); - - // check if there are some bpd - if (pot.isNone(bpdActive)) { - // Load the bpd data and wait for a response - yield* put(bpdLoadActivationStatus.request()); - - yield* take([ - bpdLoadActivationStatus.success, - bpdLoadActivationStatus.failure - ]); - } - const cgnActive: ReturnType = yield* select( cgnDetailSelector ); diff --git a/ts/sagas/wallet.ts b/ts/sagas/wallet.ts index c117adb37a5..491dbdec41b 100644 --- a/ts/sagas/wallet.ts +++ b/ts/sagas/wallet.ts @@ -44,9 +44,9 @@ import { handleAddpayToWallet, handleSearchUserBPay } from "../features/wallet/onboarding/bancomatPay/saga/networking"; -import { addBPayToWalletAndActivateBpd } from "../features/wallet/onboarding/bancomatPay/saga/orchestration/addBPayToWallet"; +import { addBPayToWalletSaga } from "../features/wallet/onboarding/bancomatPay/saga/orchestration/addBPayToWallet"; import { - addBPayToWallet, + addBPayToWalletAction, searchUserBPay, walletAddBPayStart } from "../features/wallet/onboarding/bancomatPay/store/actions"; @@ -55,7 +55,7 @@ import { handleLoadCoBadgeConfiguration, handleSearchUserCoBadge } from "../features/wallet/onboarding/cobadge/saga/networking"; -import { addCoBadgeToWalletAndActivateBpd } from "../features/wallet/onboarding/cobadge/saga/orchestration/addCoBadgeToWallet"; +import { addCoBadgeToWalletSaga } from "../features/wallet/onboarding/cobadge/saga/orchestration/addCoBadgeToWallet"; import { addCoBadgeToWallet, loadCoBadgeAbiConfiguration, @@ -771,7 +771,7 @@ export function* watchWalletSaga( ); // watch for add BPay to Wallet workflow - yield* takeLatest(walletAddBPayStart, addBPayToWalletAndActivateBpd); + yield* takeLatest(walletAddBPayStart, addBPayToWalletSaga); // watch for BancomatPay search request yield* takeLatest( @@ -782,12 +782,33 @@ export function* watchWalletSaga( ); // watch for add BancomatPay to the user's wallet yield* takeLatest( - addBPayToWallet.request, + addBPayToWalletAction.request, handleAddpayToWallet, paymentManagerClient.addBPayToWallet, pmSessionManager ); + // watch for CoBadge search request + yield* takeLatest( + searchUserCoBadge.request, + handleSearchUserCoBadge, + paymentManagerClient.getCobadgePans, + paymentManagerClient.searchCobadgePans, + pmSessionManager + ); + // watch for add CoBadge to the user's wallet + yield* takeLatest( + addCoBadgeToWallet.request, + handleAddCoBadgeToWallet, + paymentManagerClient.addCobadgeToWallet, + pmSessionManager + ); + // watch for CoBadge configuration request + yield* takeLatest( + loadCoBadgeAbiConfiguration.request, + handleLoadCoBadgeConfiguration, + contentClient.getCobadgeServices + ); // watch for CoBadge search request yield* takeLatest( searchUserCoBadge.request, @@ -811,7 +832,7 @@ export function* watchWalletSaga( ); // watch for add co-badge to Wallet workflow - yield* takeLatest(walletAddCoBadgeStart, addCoBadgeToWalletAndActivateBpd); + yield* takeLatest(walletAddCoBadgeStart, addCoBadgeToWalletSaga); yield* fork( watchPaypalOnboardingSaga, diff --git a/ts/screens/profile/RemoveAccountDetailsScreen.tsx b/ts/screens/profile/RemoveAccountDetailsScreen.tsx index 5a27983e2c4..091d97de597 100644 --- a/ts/screens/profile/RemoveAccountDetailsScreen.tsx +++ b/ts/screens/profile/RemoveAccountDetailsScreen.tsx @@ -17,7 +17,6 @@ import BaseScreenComponent from "../../components/screens/BaseScreenComponent"; import FooterWithButtons from "../../components/ui/FooterWithButtons"; import { shufflePinPadOnPayment } from "../../config"; import { LoadingErrorComponent } from "../../components/LoadingErrorComponent"; -import { bpdEnabledSelector } from "../../features/bonus/bpd/store/reducers/details/activation"; import { isCgnEnrolledSelector } from "../../features/bonus/cgn/store/reducers/details"; import I18n from "../../i18n"; import NavigationService from "../../navigation/NavigationService"; @@ -71,8 +70,7 @@ const RemoveAccountDetails: React.FunctionComponent = (props: Props) => { const [otherMotivation, setOtherMotivation] = React.useState(""); const handleContinuePress = () => { - const hasActiveBonus = - pot.getOrElse(props.bpdActiveBonus, false) || props.cgnActiveBonus; + const hasActiveBonus = props.cgnActiveBonus; if (hasActiveBonus) { Alert.alert( @@ -226,7 +224,6 @@ const mapDispatchToProps = (dispatch: Dispatch) => { }; const mapStateToProps = (state: GlobalState) => { - const bpdActiveBonus = bpdEnabledSelector(state); const cgnActiveBonus = isCgnEnrolledSelector(state); const userDataProcessing = userDataProcessingSelector(state); const isLoading = @@ -234,7 +231,6 @@ const mapStateToProps = (state: GlobalState) => { pot.isUpdating(userDataProcessing.DELETE); const isError = pot.isError(userDataProcessing.DELETE); return { - bpdActiveBonus, cgnActiveBonus, userDataProcessing, isLoading, diff --git a/ts/screens/wallet/payment/__tests__/PickPaymentMethodScreen.test.tsx b/ts/screens/wallet/payment/__tests__/PickPaymentMethodScreen.test.tsx index 39c087eb703..a4e0ccc569a 100644 --- a/ts/screens/wallet/payment/__tests__/PickPaymentMethodScreen.test.tsx +++ b/ts/screens/wallet/payment/__tests__/PickPaymentMethodScreen.test.tsx @@ -35,10 +35,7 @@ const aCreditCard = { brand: "VISA", type: undefined }, - enableableFunctions: [ - EnableableFunctionsEnum.pagoPA, - EnableableFunctionsEnum.BPD - ], + enableableFunctions: [EnableableFunctionsEnum.pagoPA], caption: "", icon: "", pagoPA: true, diff --git a/ts/screens/wallet/payment/__tests__/TransactionErrorScreen.test.tsx b/ts/screens/wallet/payment/__tests__/TransactionErrorScreen.test.tsx index 79ebc8a70ad..d69839896ce 100644 --- a/ts/screens/wallet/payment/__tests__/TransactionErrorScreen.test.tsx +++ b/ts/screens/wallet/payment/__tests__/TransactionErrorScreen.test.tsx @@ -17,6 +17,10 @@ import { GlobalState } from "../../../../store/reducers/types"; import { PayloadForAction } from "../../../../types/utils"; import { renderScreenWithNavigationStoreContext } from "../../../../utils/testWrapper"; import TransactionErrorScreen from "../TransactionErrorScreen"; +// this is required for TSC, since apparently +// it does not update the typedef unless it is actually +// imported in a test (or a global.d) and not in the jest global config +import "@testing-library/jest-native/extend-expect"; const rptId = { organizationFiscalCode: "00000000005", diff --git a/ts/store/reducers/backoffErrorConfig.ts b/ts/store/reducers/backoffErrorConfig.ts index 00b63a1e565..a7232176516 100644 --- a/ts/store/reducers/backoffErrorConfig.ts +++ b/ts/store/reducers/backoffErrorConfig.ts @@ -1,10 +1,6 @@ import { getType } from "typesafe-actions"; import _ from "lodash"; import { PayloadAC } from "typesafe-actions/dist/type-helpers"; -import { - bpdTransactionsLoadPage, - bpdTransactionsLoadRequiredData -} from "../../features/bonus/bpd/store/actions/transactions"; import { fetchTransactionsFailure, fetchTransactionsSuccess @@ -15,8 +11,6 @@ import { fetchWalletsFailure, fetchWalletsSuccess } from "../actions/wallet/wallets"; -import { bpdLoadActivationStatus } from "../../features/bonus/bpd/store/actions/details"; -import { bpdPeriodsAmountLoad } from "../../features/bonus/bpd/store/actions/periods"; import { euCovidCertificateGet } from "../../features/euCovidCert/store/actions"; import { svPossibleVoucherStateGet, @@ -36,13 +30,6 @@ const monitoredActions: ReadonlyArray< [addWalletCreditCardFailure, addWalletCreditCardSuccess], [fetchTransactionsFailure, fetchTransactionsSuccess], [fetchWalletsFailure, fetchWalletsSuccess], - [bpdLoadActivationStatus.failure, bpdLoadActivationStatus.success], - [bpdPeriodsAmountLoad.failure, bpdPeriodsAmountLoad.success], - [bpdTransactionsLoadPage.failure, bpdTransactionsLoadPage.success], - [ - bpdTransactionsLoadRequiredData.failure, - bpdTransactionsLoadRequiredData.success - ], [euCovidCertificateGet.failure, euCovidCertificateGet.success], [svPossibleVoucherStateGet.failure, svPossibleVoucherStateGet.success], [svVoucherListGet.failure, svVoucherListGet.success], diff --git a/ts/store/reducers/wallet/__mocks__/wallets.ts b/ts/store/reducers/wallet/__mocks__/wallets.ts index 2f432e39395..80b19984978 100644 --- a/ts/store/reducers/wallet/__mocks__/wallets.ts +++ b/ts/store/reducers/wallet/__mocks__/wallets.ts @@ -11,8 +11,7 @@ export const walletsV2_1 = { createDate: "2021-08-28", enableableFunctions: [ EnableableFunctionsEnum.FA, - EnableableFunctionsEnum.pagoPA, - EnableableFunctionsEnum.BPD + EnableableFunctionsEnum.pagoPA ], favourite: false, idWallet: 23190, @@ -39,8 +38,7 @@ export const walletsV2_1 = { createDate: "2021-07-22", enableableFunctions: [ EnableableFunctionsEnum.FA, - EnableableFunctionsEnum.pagoPA, - EnableableFunctionsEnum.BPD + EnableableFunctionsEnum.pagoPA ], favourite: false, idWallet: 29371, @@ -67,8 +65,7 @@ export const walletsV2_1 = { createDate: "2020-12-28", enableableFunctions: [ EnableableFunctionsEnum.FA, - EnableableFunctionsEnum.pagoPA, - EnableableFunctionsEnum.BPD + EnableableFunctionsEnum.pagoPA ], favourite: false, idWallet: 23216, @@ -100,8 +97,7 @@ export const walletsV2_2 = { createDate: "2021-08-28", enableableFunctions: [ EnableableFunctionsEnum.FA, - EnableableFunctionsEnum.pagoPA, - EnableableFunctionsEnum.BPD + EnableableFunctionsEnum.pagoPA ], favourite: false, idWallet: 23190, @@ -128,8 +124,7 @@ export const walletsV2_2 = { createDate: "2020-12-28", enableableFunctions: [ EnableableFunctionsEnum.FA, - EnableableFunctionsEnum.pagoPA, - EnableableFunctionsEnum.BPD + EnableableFunctionsEnum.pagoPA ], favourite: false, idWallet: 23216, @@ -162,8 +157,7 @@ export const walletsV2_3 = { createDate: "2021-10-22", enableableFunctions: [ EnableableFunctionsEnum.FA, - EnableableFunctionsEnum.pagoPA, - EnableableFunctionsEnum.BPD + EnableableFunctionsEnum.pagoPA ], favourite: false, idWallet: 20341, @@ -190,8 +184,7 @@ export const walletsV2_3 = { createDate: "2021-04-15", enableableFunctions: [ EnableableFunctionsEnum.FA, - EnableableFunctionsEnum.pagoPA, - EnableableFunctionsEnum.BPD + EnableableFunctionsEnum.pagoPA ], favourite: false, idWallet: 21750, @@ -218,8 +211,7 @@ export const walletsV2_3 = { createDate: "2021-07-08", enableableFunctions: [ EnableableFunctionsEnum.FA, - EnableableFunctionsEnum.pagoPA, - EnableableFunctionsEnum.BPD + EnableableFunctionsEnum.pagoPA ], favourite: false, idWallet: 25572, @@ -243,8 +235,7 @@ export const rawBPay: RawBPayPaymentMethod = { createDate: "2021-07-08", enableableFunctions: [ EnableableFunctionsEnum.FA, - EnableableFunctionsEnum.pagoPA, - EnableableFunctionsEnum.BPD + EnableableFunctionsEnum.pagoPA ], favourite: false, idWallet: 1, @@ -267,8 +258,7 @@ export const mockCreditCardPaymentMethod: PaymentMethod = { createDate: "2021-07-08", enableableFunctions: [ EnableableFunctionsEnum.FA, - EnableableFunctionsEnum.pagoPA, - EnableableFunctionsEnum.BPD + EnableableFunctionsEnum.pagoPA ], favourite: false, idWallet: 25572, diff --git a/ts/store/reducers/wallet/__tests__/wallets.test.ts b/ts/store/reducers/wallet/__tests__/wallets.test.ts index fce0853eeb5..0c74d145dd1 100644 --- a/ts/store/reducers/wallet/__tests__/wallets.test.ts +++ b/ts/store/reducers/wallet/__tests__/wallets.test.ts @@ -410,23 +410,13 @@ describe("walletV2 reducer - deleteAllByFunction", () => { { ...aPaymentMethod, idWallet: 3, - enableableFunctions: [EnableableFunctionsEnum.BPD] + enableableFunctions: [] } ]; const maybeWalletsV2 = PatchedWalletV2ListResponse.decode({ data: wallet }); - const maybeWalletsExceptBPDV2 = PatchedWalletV2ListResponse.decode({ - data: wallet.filter(w => - w.enableableFunctions.includes(EnableableFunctionsEnum.BPD) - ) - }); const convertedWallets = ( E.getOrElseW(() => [])(maybeWalletsV2) as PatchedWalletV2ListResponse ).data!.map(convertWalletV2toWalletV1); - const convertedWalletsExceptBPD = ( - E.getOrElseW(() => [])( - maybeWalletsExceptBPDV2 - ) as PatchedWalletV2ListResponse - ).data!.map(convertWalletV2toWalletV1); const globalState: GlobalState = appReducer( undefined, @@ -442,7 +432,7 @@ describe("walletV2 reducer - deleteAllByFunction", () => { const updatedState: GlobalState = appReducer( globalState, deleteAllPaymentMethodsByFunction.success({ - wallets: convertedWalletsExceptBPD, + wallets: convertedWallets, deletedMethodsCount: 1 }) ); @@ -450,7 +440,7 @@ describe("walletV2 reducer - deleteAllByFunction", () => { expect(pot.isSome(walletUpdated)).toBeTruthy(); if (pot.isSome(walletUpdated)) { expect(Object.keys(walletUpdated.value).length).toEqual( - convertedWalletsExceptBPD.length + convertedWallets.length ); } }); diff --git a/ts/store/reducers/wallet/wallets.ts b/ts/store/reducers/wallet/wallets.ts index dcbc7c0d19d..81c04ec7a3a 100644 --- a/ts/store/reducers/wallet/wallets.ts +++ b/ts/store/reducers/wallet/wallets.ts @@ -8,39 +8,36 @@ import { createSelector } from "reselect"; import { getType, isOfType } from "typesafe-actions"; import { WalletTypeEnum } from "../../../../definitions/pagopa/WalletV2"; import { + RemoteValue, getValueOrElse, remoteError, remoteLoading, remoteReady, - remoteUndefined, - RemoteValue + remoteUndefined } from "../../../common/model/RemoteValue"; import { abiSelector } from "../../../features/wallet/onboarding/store/abi"; import { - BancomatPaymentMethod, BPayPaymentMethod, + BancomatPaymentMethod, CreditCardPaymentMethod, - isBancomat, - isBPay, - isCreditCard, - isPayPal, - isRawCreditCard, - PaymentMethod, PayPalPaymentMethod, + PaymentMethod, RawCreditCardPaymentMethod, RawPaymentMethod, - Wallet + Wallet, + isBPay, + isBancomat, + isCreditCard, + isPayPal, + isRawCreditCard } from "../../../types/pagopa"; -import { EnableableFunctionsEnum } from "../../../../definitions/pagopa/EnableableFunctions"; import { TypeEnum } from "../../../../definitions/pagopa/walletv2/CardInfo"; -import { optInPaymentMethodsStart } from "../../../features/bonus/bpd/store/actions/optInPaymentMethods"; import { PotFromActions } from "../../../types/utils"; import { getErrorFromNetworkError } from "../../../utils/errors"; import { isDefined } from "../../../utils/guards"; import { enhancePaymentMethod } from "../../../utils/paymentMethod"; import { hasPaymentFeature } from "../../../utils/paymentMethodCapabilities"; -import { hasFunctionEnabled } from "../../../utils/walletv2"; import { sessionExpired, sessionInvalid } from "../../actions/authentication"; import { clearCache } from "../../actions/profile"; import { Action } from "../../actions/types"; @@ -210,20 +207,6 @@ export const withPaymentFeatureSelector = createSelector( ) ); -// return those payment methods that have BPD as enabled function -export const getBPDMethodsSelector = createSelector( - paymentMethodsSelector, - ( - potPm: ReturnType - ): ReadonlyArray => - pot.getOrElse( - pot.map(potPm, pms => - pms.filter(pm => hasFunctionEnabled(pm, EnableableFunctionsEnum.BPD)) - ), - [] - ) -); - /** * return true if the payment method is visible in the wallet (the onboardingChannel * is IO or WISP) or if the onboardingChannel is undefined. @@ -241,12 +224,6 @@ export const isVisibleInWallet = (pm: PaymentMethod): boolean => ); // return those payment methods that have BPD as enabled function and are visible in Wallet -export const getBPDMethodsVisibleInWalletSelector = createSelector( - getBPDMethodsSelector, - ( - pms: ReturnType - ): ReadonlyArray => pms.filter(isVisibleInWallet) -); export const rawCreditCardListSelector = createSelector( [paymentMethodsSelector], @@ -439,12 +416,6 @@ const reducer = ( // // delete all payments method by function // - case getType(optInPaymentMethodsStart): - // Reset the state when the opt-in payment methods workunit starts - return { - ...state, - deleteAllByFunction: WALLETS_INITIAL_STATE.deleteAllByFunction - }; case getType(deleteAllPaymentMethodsByFunction.request): return { ...state, deleteAllByFunction: remoteLoading }; case getType(deleteAllPaymentMethodsByFunction.success): diff --git a/ts/utils/__tests__/paymentMethodCapabilities.test.ts b/ts/utils/__tests__/paymentMethodCapabilities.test.ts index c9c68eeb8af..330c9ca3c24 100644 --- a/ts/utils/__tests__/paymentMethodCapabilities.test.ts +++ b/ts/utils/__tests__/paymentMethodCapabilities.test.ts @@ -76,7 +76,7 @@ const testCases: ReadonlyArray< { pm: { ...paymentMethod, - enableableFunctions: [EnableableFunctionsEnum.BPD], + enableableFunctions: [], pagoPA: true }, expected: { diff --git a/ts/utils/testFaker.ts b/ts/utils/testFaker.ts index 877e19a369d..2d09bc7cd29 100644 --- a/ts/utils/testFaker.ts +++ b/ts/utils/testFaker.ts @@ -9,7 +9,6 @@ import { ImportoEuroCents } from "../../definitions/backend/ImportoEuroCents"; import { PaymentRequestsGetResponse } from "../../definitions/backend/PaymentRequestsGetResponse"; import { SpidLevelEnum } from "../../definitions/backend/SpidLevel"; import { SpidIdp } from "../../definitions/content/SpidIdp"; -import { EnableableFunctionsEnum } from "../../definitions/pagopa/EnableableFunctions"; import { TypeEnum } from "../../definitions/pagopa/Wallet"; import { WalletTypeEnum } from "../../definitions/pagopa/WalletV2"; import { @@ -202,7 +201,7 @@ export const myWalletNoCreditCard: { [key: string]: any } = { export const bancomat = { walletType: WalletTypeEnum.Bancomat, createDate: "2021-04-05", - enableableFunctions: [EnableableFunctionsEnum.BPD], + enableableFunctions: [], favourite: false, idWallet: 24415, info: {