Skip to content

Commit

Permalink
feat(Bonus Pagamenti Digitali): [#176578322] Visualize iteratively co…
Browse files Browse the repository at this point in the history
…-badge card that can be added to the wallet (#2770)

* [#176578578] create cobadge list selector

* [#176578578] add optional abiInfo to creditCardPaymentMethod

* [#176578578] add cobadgeCard to walletPreview

* [#176578578] create cobadgePreview card

* [#176578578] add cobadge card

* [#176578578] wip enhance crediCard

* [#176578578] enhance creditCard with abiInfo

* [#176578578] renaming

* [#176578578] add void CobadgeDetailScreen

* [#176578578] add navigation to cobadgeDetailScreen action

* [#176578578] add testId to cardPreview

* [#176578578] add fake cobadgeDetailScreen

* [#176578578] add navigation to cobadgeWalletDetail

* [#176578578] add cobadgeWalletPreview tests

* [#176578587] modify paymentMethodCapabilities test description

* [#176578587] extract cobadge param from navigation

* [#176578587] add dark layout

* [#176578587] add paymentMethodCapabilities

* [#176578587] add delete button

* [#176578587] add cobadge card labels

* [#176578587] add cobadge card

* [#176578587] drill brandLogo from cobadgeWalletPreview to CobadgeCard component

* [#176578587] remove bankName as abiLogo fallback

* [#176578587] update CobadgeWalletPreview test

* [#176578587] add abiLogo fallback

* [#176578587] update expirationDate label

* [#176578587] add expirationDate testId

* [#176578587] add CobadgeCard tests

* [#176578587] update labels

* [#176578587] removed unused import

* [#176578578] remove bancomat or bpay reference

* [#176578578] use already existing getCardIconFromBrandLogo function instead of create a new one

* [#176578578] add issuerAbiCode condition on cobadgeListVisibleInWalletSelector

* [#176578578] fix

* [#176578587] fix merge

* [#176578587] fix merge

* [#176578587] fix typo in test name

* [#176578587] align brandLogo size with invision

* [#176578587] pass the whole cobadge card instead of separate prop

* [#176578587] add key to abiLogo in cobadge card

* [#176578587] remove unused import

* [#176578587] update test

* [#176578322] add AddCobadgeComponent

* [#176578322] rename CobadgeCard

* [#176578322] refactor: add BaseCobadgeCard

* [#176578322] refactor: use the BaseCobadgeCard in CobadgeCard

* [#176578322] remove reactotron

* [#176578322] update labels

* [#176578322] update tests

* [#176578322] add extract title for a payment method util

* [#176578322] add extract card icon from brand util

* [#176578322] insert AddCobadgeComponent in AddCobadgeScreen

* [#176578322] add PreviewCoBadgeCard

* [#176578322] insert PreviewCoBadgeCard into AddCobadgeComponent

* [#176578322] remove reactotron

* [#176578322] remove unused import

* [#176578322] update labels

* [#176578322] add loading caption

* [#176578322] remove wrong test file

* [#176578322] update CoBadgeCard component

* [#176578322] fix lint errors

* [#176578322] porting BrandEnum -> PaymentNetworkEnum

Co-authored-by: fabriziofff <[email protected]>
  • Loading branch information
debiff and fabriziofff authored Feb 4, 2021
1 parent 4b6611f commit 187b665
Show file tree
Hide file tree
Showing 12 changed files with 323 additions and 66 deletions.
8 changes: 6 additions & 2 deletions locales/en/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -863,7 +863,11 @@ wallet:
koNotFound:
title: "TMP KO Timeout add"
add:
title: "TMP Add title"
screenTitle: "Do you want to add this card?"
label: "International circuit card {{current}} of {{length}}"
blocked: "This card is blocked and can't be activated for Cashback. Please, contact the card issuer."
warning: "Check that the card number matches the one on your physical card."
loading: "We are adding your international circuit card\n\nPlease hold on..."
alert:
supportedCardPageLinkError: An error occurred while opening the supported cards page.
msgErrorUpdateApp: "An error occurred while opening the app store"
Expand Down Expand Up @@ -1191,7 +1195,7 @@ wallet:
cobadge:
details:
card:
validUntil: "Valid until {{expireMonth}}/{{expireYear}}"
validUntil: "Valid until {{expiryDate}}"
newRemove:
title: "Delete this payment method?"
body: "This payment method will be removed. Any related feature, such as Cashback, will be disabled within 24 hours."
Expand Down
8 changes: 6 additions & 2 deletions locales/it/index.yml
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,11 @@ wallet:
koNotFound:
title: "TMP KO Timeout add"
add:
title: "TMP Add title"
screenTitle: "Vuoi aggiungere questa carta?"
label: "Carta con circuito internazionale {{current}} di {{length}}"
blocked: "Questa carta risulta bloccata e pertanto non può essere attivata ai fini del Cashback. Contatta la tua banca."
warning: "Controlla che il numero della carta corrisponda a quello riportato sulla tua carta fisica."
loading: "Stiamo aggiungendo la tua carta con circuito internazionale\n\nAttendi qualche secondo..."
alert:
supportedCardPageLinkError: Si è verificato un errore durante l'apertura della pagina di riferimento per le carte supportate.
msgErrorUpdateApp: "Si è verificato un errore durante l'apertura dello store delle app"
Expand Down Expand Up @@ -1218,7 +1222,7 @@ wallet:
cobadge:
details:
card:
validUntil: "Valida fino al {{expireMonth}}/{{expireYear}}"
validUntil: "Valida fino al {{expiryDate}}"
newRemove:
title: "Vuoi eliminare questo metodo?"
body: "Questo metodo di pagamento verrà rimosso. Eventuali funzioni collegate, come il Cashback, verranno disattivate entro 24h."
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,23 @@ import {
StyleProp,
StyleSheet
} from "react-native";
import { View } from "native-base";
import { Badge, View } from "native-base";
import I18n from "../../../../i18n";
import BaseCardComponent from "../../component/BaseCardComponent";
import { useImageResize } from "../../onboarding/bancomat/screens/hooks/useImageResize";
import { H4 } from "../../../../components/core/typography/H4";
import { H5 } from "../../../../components/core/typography/H5";
import abiLogoFallback from "../../../../../img/wallet/cards-icons/abiLogoFallback.png";
import { Abi } from "../../../../../definitions/pagopa/walletv2/Abi";
import { localeDateFormat } from "../../../../utils/locale";
import { IOColors } from "../../../../components/core/variables/IOColors";

type Props = {
caption?: string;
expireMonth?: string;
expireYear?: string;
abiLogo?: string;
expiringDate?: Date;
abi: Abi;
brandLogo: ImageSourcePropType;
blocked?: boolean;
};

const styles = StyleSheet.create({
Expand All @@ -33,14 +36,28 @@ const styles = StyleSheet.create({
height: 31,
resizeMode: "contain"
},
bankName: { textTransform: "capitalize" }
bankName: { textTransform: "capitalize" },
badgeInfo: {
borderWidth: 1,
borderStyle: "solid",
height: 25,
flexDirection: "row"
},
badgeInfoExpired: {
backgroundColor: IOColors.white,
borderColor: IOColors.red
}
});

const BASE_IMG_W = 160;
const BASE_IMG_H = 40;

const CobadgeCard: React.FunctionComponent<Props> = (props: Props) => {
const imgDimensions = useImageResize(BASE_IMG_W, BASE_IMG_H, props.abiLogo);
const BaseCoBadgeCard: React.FunctionComponent<Props> = (props: Props) => {
const imgDimensions = useImageResize(
BASE_IMG_W,
BASE_IMG_H,
props.abi?.logoUrl
);

const imageStyle: StyleProp<ImageStyle> | undefined = imgDimensions.fold(
undefined,
Expand All @@ -54,22 +71,35 @@ const CobadgeCard: React.FunctionComponent<Props> = (props: Props) => {
<BaseCardComponent
topLeftCorner={
<>
{props.abiLogo && imageStyle ? (
<Image
source={{ uri: props.abiLogo }}
style={imageStyle}
key={"abiLogo"}
testID={"abiLogo"}
/>
) : (
<Image
source={abiLogoFallback}
style={styles.abiLogoFallback}
key={"abiLogoFallback"}
testID={"abiLogoFallback"}
/>
)}
{props.expireMonth && props.expireYear && (
<View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between"
}}
>
{props.abi.logoUrl && imageStyle ? (
<Image
source={{ uri: props.abi.logoUrl }}
style={imageStyle}
key={"abiLogo"}
testID={"abiLogo"}
/>
) : (
<Image
source={abiLogoFallback}
style={styles.abiLogoFallback}
key={"abiLogoFallback"}
testID={"abiLogoFallback"}
/>
)}
{props.blocked && (
<Badge style={[styles.badgeInfo, styles.badgeInfoExpired]}>
<H5 color="red">{I18n.t("global.badges.blocked")}</H5>
</Badge>
)}
</View>
{props.expiringDate && (
<>
<View spacer={true} />
<H5
Expand All @@ -78,8 +108,10 @@ const CobadgeCard: React.FunctionComponent<Props> = (props: Props) => {
testID={"expirationDate"}
>
{I18n.t("wallet.cobadge.details.card.validUntil", {
expireMonth: props.expireMonth,
expireYear: props.expireYear
expiryDate: localeDateFormat(
props.expiringDate,
I18n.t("global.dateFormats.numericMonthYear")
)
})}
</H5>
</>
Expand Down Expand Up @@ -109,4 +141,4 @@ const CobadgeCard: React.FunctionComponent<Props> = (props: Props) => {
);
};

export default CobadgeCard;
export default BaseCoBadgeCard;
39 changes: 39 additions & 0 deletions ts/features/wallet/cobadge/component/CoBadgeCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as React from "react";
import { getCardIconFromBrandLogo } from "../../../../components/wallet/card/Logo";
import { CreditCardPaymentMethod } from "../../../../types/pagopa";
import BaseCoBadgeCard from "./BaseCoBadgeCard";

type Props = { enhancedCoBadge: CreditCardPaymentMethod };

const getExpireDate = (fullYear?: string, month?: string): Date | undefined => {
if (!fullYear || !month) {
return undefined;
}
const year = parseInt(fullYear, 10);
const indexedMonth = parseInt(month, 10);
if (isNaN(year) || isNaN(indexedMonth)) {
return undefined;
}
return new Date(year, indexedMonth - 1);
};

/**
* Render a Co-badge card already added to the wallet
* @param props
* @constructor
*/
const CoBadgeCard: React.FunctionComponent<Props> = props => {
const brandLogo = getCardIconFromBrandLogo(props.enhancedCoBadge.info);
return (
<BaseCoBadgeCard
abi={props.enhancedCoBadge.abiInfo ?? {}}
expiringDate={getExpireDate(
props.enhancedCoBadge.info.expireYear,
props.enhancedCoBadge.info.expireMonth
)}
brandLogo={brandLogo}
caption={props.enhancedCoBadge.caption}
/>
);
};
export default CoBadgeCard;
29 changes: 29 additions & 0 deletions ts/features/wallet/cobadge/component/PreviewCoBadgeCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import * as React from "react";
import { Abi } from "../../../../../definitions/pagopa/walletv2/Abi";
import { PaymentInstrument } from "../../../../../definitions/pagopa/walletv2/PaymentInstrument";
import { getCardIconFromPaymentNetwork } from "../../../../utils/card";
import {
getTitleFromPaymentInstrument,
isCoBadgeBlocked
} from "../../../../utils/paymentMethod";
import BaseCoBadgeCard from "./BaseCoBadgeCard";

type Props = { coBadge: PaymentInstrument; abi: Abi };

/**
* Display a preview of a cobadge that the user could add to the wallet
* @constructor
*/
const PreviewCoBadgeCard: React.FunctionComponent<Props> = props => {
const brandLogo = getCardIconFromPaymentNetwork(props.coBadge.paymentNetwork);
return (
<BaseCoBadgeCard
abi={props.abi}
expiringDate={props.coBadge.expiringDate}
blocked={isCoBadgeBlocked(props.coBadge)}
brandLogo={brandLogo}
caption={getTitleFromPaymentInstrument(props.coBadge)}
/>
);
};
export default PreviewCoBadgeCard;
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import configureMockStore from "redux-mock-store";
// import { pot } from "italia-ts-commons";
import { none, some } from "fp-ts/lib/Option";
import * as hooks from "../../../onboarding/bancomat/screens/hooks/useImageResize";
import CobadgeCard from "../CobadgeCard";
import CobadgeCard from "../BaseCoBadgeCard";
import defaultCardIcon from "../../../../../../img/wallet/cards-icons/unknown.png";
import { Abi } from "../../../../../../definitions/pagopa/walletv2/Abi";

jest.mock("../../../onboarding/bancomat/screens/hooks/useImageResize");

const aCaption = "****1234";
const anAbiLogo = "http://127.0.0.1:3000/static_contents/logos/abi/03069.png";
const aBrandLogo = defaultCardIcon;
const anExpireMonth = "6";
const anExpireYear = "2021";
const anexpiringDateNotExpired = new Date("01/05/2023");
describe("CoBadgeWalletPreview component", () => {
const mockStore = configureMockStore();
// eslint-disable-next-line functional/no-let
Expand All @@ -27,7 +27,8 @@ describe("CoBadgeWalletPreview component", () => {
});
it("should show the abiLogoFallback if there isn't the abiLogo", () => {
jest.spyOn(hooks, "useImageResize").mockReturnValue(none);
const component = getComponent(store, aBrandLogo);
const anAbiWithoutAbilogo = {} as Abi;
const component = getComponent(store, aBrandLogo, anAbiWithoutAbilogo);
const abiLogoFallbackComponent = component.queryByTestId("abiLogoFallback");

expect(abiLogoFallbackComponent).not.toBeNull();
Expand All @@ -38,29 +39,27 @@ describe("CoBadgeWalletPreview component", () => {

it("should show the abiLogo if there is an abiLogo and useImageResize return some value", () => {
jest.spyOn(hooks, "useImageResize").mockReturnValue(some([15, 15]));
const component = getComponent(store, aBrandLogo, anAbiLogo);
const anAbiWithAbiLogo = { logoUrl: anAbiLogo } as Abi;
const component = getComponent(store, aBrandLogo, anAbiWithAbiLogo);
const abiLogo = component.queryByTestId("abiLogo");

expect(abiLogo).not.toBeNull();
expect(abiLogo).toHaveProp("source", { uri: anAbiLogo });
});

it("should show the expiration date if both expireMonth and expireYear are defined", () => {
it("should show the expiration date if expiringDate is defined", () => {
jest.spyOn(hooks, "useImageResize").mockReturnValue(none);
const component = getComponent(
store,
aBrandLogo,
anAbiLogo,
aCaption,
anExpireMonth,
anExpireYear
anexpiringDateNotExpired
);
const expirationDate = component.queryByTestId("expirationDate");

expect(expirationDate).not.toBeNull();
expect(expirationDate).toHaveTextContent(
`Valid until ${anExpireMonth}/${anExpireYear}`
);
expect(expirationDate).toHaveTextContent(`Valid until 01/2023`);
});

it("should show the caption if is defined", () => {
Expand All @@ -76,19 +75,17 @@ describe("CoBadgeWalletPreview component", () => {
const getComponent = (
store: Store<unknown>,
brandLogo: ImageSourcePropType,
abiLogo?: string,
abi: Abi,
caption?: string,
expireMonth?: string,
expireYear?: string
expiringDate?: Date
) =>
render(
<Provider store={store}>
<CobadgeCard
brandLogo={brandLogo}
caption={caption}
expireMonth={expireMonth}
expireYear={expireYear}
abiLogo={abiLogo}
expiringDate={expiringDate}
abi={abi}
/>
</Provider>
);
10 changes: 2 additions & 8 deletions ts/features/wallet/cobadge/screen/CobadgeDetailScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import PaymentMethodCapabilities from "../../component/PaymentMethodCapabilities
import { Label } from "../../../../components/core/typography/Label";
import { deleteWalletRequest } from "../../../../store/actions/wallet/wallets";
import { showToast } from "../../../../utils/showToast";
import CobadgeCard from "../component/CobadgeCard";
import CobadgeCard from "../component/CoBadgeCard";
import { getCardIconFromBrandLogo } from "../../../../components/wallet/card/Logo";

type NavigationParams = Readonly<{
Expand Down Expand Up @@ -76,13 +76,7 @@ const CobadgeDetailScreen: React.FunctionComponent<Props> = props => {
hideHeader={true}
>
<View style={styles.cardContainer}>
<CobadgeCard
expireMonth={cobadge.info.expireMonth}
expireYear={cobadge.info.expireYear}
caption={cobadge.caption}
abiLogo={cobadge.abiInfo?.logoUrl}
brandLogo={brandLogo}
/>
<CobadgeCard enhancedCoBadge={cobadge} />
</View>
<View spacer={true} extralarge={true} />
<View style={IOStyles.horizontalContentPadding}>
Expand Down
Loading

0 comments on commit 187b665

Please sign in to comment.