teamdigitale/italia-app

View on GitHub
ts/features/idpay/payment/screens/IDPayPaymentAuthorizationScreen.tsx

Summary

Maintainability
C
1 day
Test Coverage
import {
  ContentWrapper,
  Divider,
  FooterWithButtons,
  H6,
  HSpacer,
  Icon,
  ListItemInfo,
  VSpacer
} from "@pagopa/io-app-design-system";
import { RouteProp, useRoute } from "@react-navigation/native";
import * as O from "fp-ts/lib/Option";
import { pipe } from "fp-ts/lib/function";
import React from "react";
import { SafeAreaView, View } from "react-native";
import { AuthPaymentResponseDTO } from "../../../../../definitions/idpay/AuthPaymentResponseDTO";
import { H1 } from "../../../../components/core/typography/H1";
import { H3 } from "../../../../components/core/typography/H3";
import { IOStyles } from "../../../../components/core/variables/IOStyles";
import BaseScreenComponent from "../../../../components/screens/BaseScreenComponent";
import I18n from "../../../../i18n";
import { identificationRequest } from "../../../../store/actions/identification";
import { useIODispatch } from "../../../../store/hooks";
import { emptyContextualHelp } from "../../../../utils/emptyContextualHelp";
import { Skeleton } from "../../common/components/Skeleton";
import {
  formatDateOrDefault,
  formatNumberCurrencyCents,
  formatNumberCurrencyCentsOrDefault
} from "../../common/utils/strings";
import { IdPayPaymentMachineContext } from "../machine/provider";
import {
  isAuthorizingSelector,
  isCancellingSelector,
  transactionDataSelector
} from "../machine/selectors";
import { IdPayPaymentParamsList } from "../navigation/params";
import { isLoadingSelector } from "../../common/machine/selectors";

export type IDPayPaymentAuthorizationScreenRouteParams = {
  trxCode?: string;
};

type IDPayPaymentAuthorizationRouteProps = RouteProp<
  IdPayPaymentParamsList,
  "IDPAY_PAYMENT_AUTHORIZATION"
>;

const IDPayPaymentAuthorizationScreen = () => {
  const { useActorRef, useSelector } = IdPayPaymentMachineContext;
  const { params } = useRoute<IDPayPaymentAuthorizationRouteProps>();
  const machine = useActorRef();
  const dispatch = useIODispatch();

  React.useEffect(() => {
    pipe(
      params.trxCode,
      O.fromNullable,
      O.map(code => machine.send({ type: "authorize-payment", trxCode: code }))
    );
  }, [params, machine]);

  const transactionData = useSelector(transactionDataSelector);
  const isLoading = useSelector(isLoadingSelector);
  const isAuthorizing = useSelector(isAuthorizingSelector);
  const isCancelling = useSelector(isCancellingSelector);

  const handleCancel = () => {
    machine.send({ type: "close" });
  };

  const handleConfirm = () => {
    dispatch(
      identificationRequest(
        false,
        true,
        undefined,
        {
          label: I18n.t("global.buttons.cancel"),
          onCancel: () => undefined
        },
        {
          onSuccess: () => machine.send({ type: "next" })
        }
      )
    );
  };

  const renderContent = () => {
    if (!isLoading && O.isSome(transactionData)) {
      return <AuthorizationScreenContent data={transactionData.value} />;
    }
    return <AuthorizationScreenSkeleton />;
  };

  return (
    <BaseScreenComponent
      headerTitle="Autorizza operazione"
      contextualHelp={emptyContextualHelp}
      goBack={false}
    >
      <SafeAreaView style={IOStyles.flex}>
        <View style={IOStyles.flex}>
          <ContentWrapper>
            <H1>{I18n.t("idpay.payment.authorization.header")}</H1>
            <VSpacer size={24} />
            {renderContent()}
          </ContentWrapper>
        </View>
      </SafeAreaView>
      <FooterWithButtons
        type="TwoButtonsInlineHalf"
        primary={{
          type: "Outline",
          buttonProps: {
            label: isCancelling ? "" : I18n.t("global.buttons.deny"),
            onPress: handleCancel,
            disabled: isLoading
          }
        }}
        secondary={{
          type: "Solid",
          buttonProps: {
            label: I18n.t("global.buttons.confirm"),
            onPress: handleConfirm,
            loading: isAuthorizing,
            disabled: isLoading
          }
        }}
      />
    </BaseScreenComponent>
  );
};

const AuthorizationScreenContent = ({
  data
}: {
  data: AuthPaymentResponseDTO;
}) => (
  <>
    <Divider />
    <VSpacer size={16} />
    <View style={[IOStyles.rowSpaceBetween, IOStyles.alignCenter]}>
      <H6>{I18n.t("idpay.payment.authorization.toAuth")}</H6>
      <H1>{formatNumberCurrencyCentsOrDefault(data.rewardCents)}</H1>
    </View>
    <VSpacer size={16} />
    <Divider />
    <ListItemInfo
      label={I18n.t("idpay.payment.authorization.amount")}
      value={formatNumberCurrencyCents(data.amountCents)}
      accessibilityLabel={I18n.t("idpay.payment.authorization.amount")}
    />
    <Divider />
    <ListItemInfo
      label={I18n.t("idpay.payment.authorization.businessName")}
      value={data.businessName || "-"}
      accessibilityLabel={I18n.t("idpay.payment.authorization.businessName")}
    />
    <Divider />
    <ListItemInfo
      label={I18n.t("idpay.payment.authorization.dateTime")}
      value={formatDateOrDefault(data.trxDate)}
      accessibilityLabel={I18n.t("idpay.payment.authorization.dateTime")}
    />
    <VSpacer size={24} />
    {/* TODO:: will be removed in favor of LIST_GROUP_HEADING in future updates */}
    <View style={[IOStyles.row, IOStyles.alignCenter]}>
      <Icon name="initiatives" size={24} color="bluegrey" />
      <HSpacer size={16} />
      <H3
        color="bluegrey"
        style={{
          // this should not happen, but the current typography adds 4
          // to paddingBottom because of line height
          // so we add 4 to paddingTop to compensate, else the text would not be centered
          // (this was temporarily approved by @dmnplb)
          paddingTop: 4
        }}
      >
        {I18n.t("idpay.payment.authorization.infoDivider")}
      </H3>
    </View>
    <VSpacer size={16} />

    <ListItemInfo
      label={I18n.t("idpay.payment.authorization.initiativeName")}
      value={data.initiativeName || "-"}
      accessibilityLabel={I18n.t("idpay.payment.authorization.initiativeName")}
    />
    <Divider />
    <ListItemInfo
      label={I18n.t("idpay.payment.authorization.availableAmount")}
      value={formatNumberCurrencyCentsOrDefault(data.residualBudgetCents)}
      accessibilityLabel={I18n.t("idpay.payment.authorization.availableAmount")}
    />
  </>
);

const SmallSkeleton = () => <Skeleton width={178} height={24} radius={8} />;

const AuthorizationScreenSkeleton = () => (
  <>
    <VSpacer size={16} />
    <View style={[IOStyles.rowSpaceBetween, IOStyles.alignCenter]}>
      <Skeleton width={82} height={29} />
      <Skeleton width={130} height={29} />
    </View>
    <VSpacer size={16} />
    <Divider />
    <ListItemInfo
      label={I18n.t("idpay.payment.authorization.amount")}
      value={<SmallSkeleton />}
      accessibilityLabel={I18n.t("idpay.payment.authorization.amount")}
    />
    <Divider />
    <ListItemInfo
      label={I18n.t("idpay.payment.authorization.businessName")}
      value={<SmallSkeleton />}
      accessibilityLabel={I18n.t("idpay.payment.authorization.businessName")}
    />
    <Divider />
    <ListItemInfo
      label={I18n.t("idpay.payment.authorization.dateTime")}
      value={<SmallSkeleton />}
      accessibilityLabel={I18n.t("idpay.payment.authorization.dateTime")}
    />
    <VSpacer size={24} />
    <View style={[IOStyles.row, IOStyles.alignCenter]}>
      <Icon name="initiatives" size={24} color="bluegrey" />
      <HSpacer size={16} />
      <H3
        color="bluegrey"
        style={{
          // see previous comment
          paddingTop: 4
        }}
      >
        {I18n.t("idpay.payment.authorization.infoDivider")}
      </H3>
    </View>
    <VSpacer size={16} />
    <ListItemInfo
      label={I18n.t("idpay.payment.authorization.initiativeName")}
      value={<SmallSkeleton />}
      accessibilityLabel={I18n.t("idpay.payment.authorization.initiativeName")}
    />
    <Divider />
    <ListItemInfo
      label={I18n.t("idpay.payment.authorization.availableAmount")}
      value={<SmallSkeleton />}
      accessibilityLabel={I18n.t("idpay.payment.authorization.availableAmount")}
    />
  </>
);

export { IDPayPaymentAuthorizationScreen };