teamdigitale/italia-app

View on GitHub
ts/features/itwallet/common/components/ItwSkeumorphicCard/CardData.tsx

Summary

Maintainability
D
2 days
Test Coverage
/* eslint-disable dot-notation */
/* eslint-disable @typescript-eslint/dot-notation */
import { parse } from "date-fns";
import { pipe } from "fp-ts/lib/function";
import * as O from "fp-ts/lib/Option";
import { Fragment, default as React } from "react";
import { StyleSheet, View } from "react-native";
import { QrCodeImage } from "../../../../../components/QrCodeImage";
import { localeDateFormat } from "../../../../../utils/locale";
import {
  DrivingPrivilegesClaim,
  StringClaim
} from "../../utils/itwClaimsUtils";
import { ParsedCredential, StoredCredential } from "../../utils/itwTypesUtils";
import { CardClaim, CardClaimContainer, CardClaimRenderer } from "./CardClaim";
import { ClaimLabel } from "./ClaimLabel";
import { CardSide } from "./types";

type DataComponentProps = {
  claims: ParsedCredential;
};

const MdlFrontData = ({ claims }: DataComponentProps) => {
  const row = 11.6; // Row padding, defines the first row position
  const rowStep = 6.9; // Row step, defines the space between each row
  const rows: ReadonlyArray<number> = Array.from(
    { length: 6 },
    (_, i) => row + rowStep * i
  );
  const cols: ReadonlyArray<number> = [34, 57.5];

  return (
    <View testID="mdlFrontDataTestID" style={styles.container}>
      <CardClaim
        claim={claims["portrait"]}
        position={{ left: "4%", top: "30%" }}
        dimensions={{
          width: "22.5%",
          aspectRatio: 77 / 93 // This aspect ration was extracted from the Figma design
        }}
      />
      <CardClaim
        claim={claims["family_name"]}
        position={{ left: `${cols[0]}%`, top: `${rows[0]}%` }}
      />
      <CardClaim
        claim={claims["given_name"]}
        position={{ left: `${cols[0]}%`, top: `${rows[1]}%` }}
      />
      <CardClaim
        claim={claims["birth_date"]}
        position={{ left: `${cols[0]}%`, top: `${rows[2]}%` }}
        dateFormat="%d/%m/%y"
      />
      <CardClaim
        claim={claims["place_of_birth"]}
        position={{ left: `${cols[0] + 17}%`, top: `${rows[2]}%` }}
      />
      <CardClaim
        claim={claims["issue_date"]}
        position={{ left: `${cols[0]}%`, top: `${rows[3]}%` }}
        fontWeight={"Bold"}
      />
      <CardClaim
        claim={claims["issuing_authority"]}
        position={{ left: `${cols[1]}%`, top: `${rows[3]}%` }}
      />
      <CardClaim
        claim={claims["expiry_date"]}
        position={{ left: `${cols[0]}%`, top: `${rows[4]}%` }}
        fontWeight={"Bold"}
      />
      <CardClaim
        claim={claims["document_number"]}
        position={{ left: `${cols[0]}%`, top: `${rows[5]}%` }}
      />
      <CardClaim
        claim={claims["driving_privileges_details"]}
        position={{ left: "8%", bottom: "17.9%" }}
      />
    </View>
  );
};

const MdlBackData = ({ claims }: DataComponentProps) => {
  // Driving privilges list with the same order as on the Driving License physical card
  const drivingPrivileges = [
    "AM",
    "A1",
    "A2",
    "A",
    "B1",
    "B",
    "C1",
    "C",
    "D1",
    "D",
    "BE",
    "C1E",
    "CE",
    "D1E",
    "DE"
  ] as const;

  const row = 6.8; // Row padding, defines the first row position
  const rowStep = 4.7; // Row step, defines the space between each row
  // This object definies the rows of the driving privileges table, specifing the "y" coordinate for each item
  const privilegesTableRows: Record<string, number> = drivingPrivileges.reduce(
    (acc, privilege, index) => ({
      ...acc,
      [privilege]: row + rowStep * index
    }),
    {} as Record<string, number>
  );

  return (
    <View testID="mdlBackDataTestID" style={styles.container}>
      <CardClaimRenderer
        claim={claims["driving_privileges_details"]}
        is={DrivingPrivilegesClaim.is}
        component={privileges =>
          privileges.map(
            ({
              driving_privilege,
              issue_date,
              expiry_date,
              restrictions_conditions
            }) => (
              <Fragment key={`driving_privilege_row_${driving_privilege}`}>
                <CardClaimContainer
                  position={{
                    left: `41.5%`,
                    top: `${privilegesTableRows[driving_privilege] || 0}%`
                  }}
                >
                  <ClaimLabel fontSize={9}>
                    {localeDateFormat(parse(issue_date), "%d/%m/%y")}
                  </ClaimLabel>
                </CardClaimContainer>
                <CardClaimContainer
                  key={`driving_privilege_${driving_privilege}`}
                  position={{
                    left: `55%`,
                    top: `${privilegesTableRows[driving_privilege] || 0}%`
                  }}
                >
                  <ClaimLabel fontSize={9}>
                    {localeDateFormat(parse(expiry_date), "%d/%m/%y")}
                  </ClaimLabel>
                </CardClaimContainer>
                {restrictions_conditions && (
                  <CardClaimContainer
                    key={`driving_privilege_restricted_conditions_${driving_privilege}`}
                    position={{
                      left: `68.5%`,
                      top: `${privilegesTableRows[driving_privilege] || 0}%`
                    }}
                  >
                    <ClaimLabel fontSize={9}>
                      {restrictions_conditions}
                    </ClaimLabel>
                  </CardClaimContainer>
                )}
              </Fragment>
            )
          )
        }
      />
      <CardClaim
        claim={claims["restrictions_conditions"]}
        position={{ left: "8%", bottom: "6.5%" }}
        fontSize={9}
      />
    </View>
  );
};

const DcFrontData = ({ claims }: DataComponentProps) => {
  const row = 44.5; // Row padding, defines the first row position
  const rowStep = 11.4; // Row step, defines the space between each row

  const rows: ReadonlyArray<number> = Array.from(
    { length: 5 },
    (_, i) => row + rowStep * i
  );

  return (
    <View testID="dcFrontDataTestID" style={styles.container}>
      <CardClaim
        claim={claims["portrait"]}
        position={{ left: "2.55%", bottom: "1.%" }}
        dimensions={{
          width: "24.7%",
          aspectRatio: 73 / 106 // This aspect ration was extracted from the Figma design
        }}
      />
      <CardClaim
        claim={claims["given_name"]}
        position={{ right: "3.5%", top: `${rows[0]}%` }}
      />
      <CardClaim
        claim={claims["family_name"]}
        position={{ right: "3.5%", top: `${rows[1]}%` }}
      />
      <CardClaim
        claim={claims["birth_date"]}
        position={{ right: "3.5%", top: `${rows[2]}%` }}
      />
      <CardClaim
        claim={claims["document_number"]}
        position={{ right: "3.5%", top: `${rows[3]}%` }}
      />
      <CardClaim
        claim={claims["expiry_date"]}
        position={{ right: "3.5%", top: `${rows[4]}%` }}
      />
    </View>
  );
};

const DcBackData = ({ claims }: DataComponentProps) => (
  <View testID="dcBackDataTestID" style={styles.container}>
    <CardClaimRenderer
      claim={claims["link_qr_code"]}
      is={StringClaim.is}
      component={qrCode => (
        <CardClaimContainer
          position={{
            right: `6%`,
            top: `10%`
          }}
        >
          <QrCodeImage value={qrCode} size={"28.5%"} />
        </CardClaimContainer>
      )}
    />
  </View>
);

const dataComponentMap: Record<
  string,
  Record<CardSide, React.ElementType<DataComponentProps>>
> = {
  MDL: { front: MdlFrontData, back: MdlBackData },
  EuropeanDisabilityCard: { front: DcFrontData, back: DcBackData }
};

type CardDataProps = {
  credential: StoredCredential;
  side: CardSide;
};

const CardData = ({ credential, side }: CardDataProps) =>
  pipe(
    O.fromNullable(dataComponentMap[credential.credentialType]),
    O.map(components => components[side]),
    O.map(DataComponent => (
      <DataComponent
        key={`credential_data_${credential.credentialType}_${side}`}
        claims={credential.parsedCredential}
      />
    )),
    O.toNullable
  );

const styles = StyleSheet.create({
  container: {
    position: "absolute",
    width: "100%",
    height: "100%"
  }
});

const MemoizeCardData = React.memo(CardData);

export { MemoizeCardData as CardData };