EscolaLMS/Front

View on GitHub
src/components/Subscriptions/Box/index.tsx

Summary

Maintainability
B
6 hrs
Test Coverage
import { Purchases } from "@revenuecat/purchases-capacitor";
import { useCallback, useEffect, useMemo } from "react";
import { useTranslation } from "react-i18next";
import { useHistory } from "react-router-dom";
import { Capacitor } from "@capacitor/core";
import styled from "styled-components";
import routeRoutes from "@/components/Routes/routes";
import { VITE_APP_ANDROID_APIKEY, VITE_APP_IOS_APIKEY } from "@/config/index";
import usePayment from "@/hooks/usePayment";
import { StarIcon } from "@/icons/index";
import { formatPrice, isMobilePlatform } from "@/utils/index";
import {
  findProductByIdentifier,
  getRevenuecatIdForSubscription,
  revenuecatErrorHandler,
} from "@/utils/payment";
import { Button } from "@escolalms/components/lib/components/atoms/Button/Button";
import { Text } from "@escolalms/components/lib/components/atoms/Typography/Text";
import { Title } from "@escolalms/components/lib/components/atoms/Typography/Title";
import { getStylesBasedOnTheme } from "@escolalms/components/lib/utils/utils";
import { CapacitorPaymentError } from "@/types/index";

const StyledSubscription = styled.div<{ $isMobile: boolean }>`
  border-radius: ${({ theme }) => theme.cardRadius}px;
  border: 1px solid ${({ theme }) => theme.gray3};
  padding: ${({ $isMobile }) => ($isMobile ? "20px" : "23px 75px")};
  transition: border 0.3s ease-in-out;
  margin-bottom: ${({ $isMobile }) => ($isMobile ? "30px" : "30px")};
  background-color: ${({ theme }) =>
    getStylesBasedOnTheme(theme.mode, theme.black, theme.white, "black")};
  .content {
    text-align: center;
    max-width: 210px;
    position: relative;
    margin: 0 auto;

    p {
      margin-bottom: 0;
    }
    .information {
      display: flex;
      align-items: center;
      justify-content: center;
      gap: 2.5px;
      margin-top: 5px;
      background-color: ${({ theme }) => theme.secondaryColor};
    }
    .divider {
      width: 21px;
      height: 3px;
      border-radius: 18px;
      background-color: ${({ theme }) => theme.primaryColor};
      margin: 23px auto 16px auto;
    }
    .description {
      margin-bottom: 17px;
    }
    .price {
      margin-bottom: 30px;
    }
    button {
      width: 100%;
    }
    .tag {
      position: absolute;
      top: -37px;
      left: 0;
      right: 0;
      margin: auto;
      border-radius: 18px;
      width: fit-content;
      background-color: ${({ theme }) => theme.textColor};
      padding: 3px 14px;

      p {
        text-transform: uppercase;
        color: ${({ theme }) => theme.white};
        margin-bottom: 0;
      }
    }
  }

  &:hover {
    border: 1px solid #333333;
  }
`;

type Props = {
  // TODO: when model types will be updated change this
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  subscription: any;
};

const SubscriptionBox: React.FC<Props> = ({ subscription }) => {
  const { t } = useTranslation();
  const { buySubscriptionByP24, user } = usePayment();

  const history = useHistory();
  const showTag = useMemo(() => {
    return subscription.tags && subscription.tags.includes("best-deal");
  }, [subscription.tags]);

  const handleBuySubscription = useCallback(() => {
    if (user.value?.id) {
      buySubscriptionByP24(subscription.id);
    } else {
      history.push(routeRoutes.login);
    }
  }, [subscription.id, user.value?.id, buySubscriptionByP24, history]);

  useEffect(() => {
    (async function () {
      const id = user?.value?.id;

      if (Capacitor.getPlatform() === "ios") {
        await Purchases.configure({
          apiKey: VITE_APP_IOS_APIKEY,
          appUserID: `${id}`,
        });
      } else if (Capacitor.getPlatform() === "android") {
        await Purchases.configure({
          apiKey: VITE_APP_ANDROID_APIKEY,
          appUserID: `${id}`,
        });
      }
    })();
  }, [user?.value?.id]);

  const buyOnMobile = useCallback(async () => {
    if (!user.value?.id) {
      history.push(routeRoutes.login);
      return;
    }
    const id = getRevenuecatIdForSubscription(subscription);
    const offerings = await Purchases.getOfferings();
    const packages = offerings?.current?.availablePackages || [];

    const product = findProductByIdentifier(packages, id);

    if (product) {
      try {
        await Purchases.purchaseStoreProduct({
          product: product,
        });
        // Redirect to course page
        history.push(routeRoutes.home);
      } catch (error) {
        revenuecatErrorHandler(error as CapacitorPaymentError);
      }
    }
  }, [history, subscription, user.value?.id]);

  return (
    <StyledSubscription $isMobile={isMobilePlatform}>
      <div className="content">
        {showTag && (
          <div className="tag">
            <Text size="13">{t("Subscriptions.CheapestOffer")}</Text>
          </div>
        )}

        <Text>{t("Subscriptions.AccessVia")}</Text>
        <Title level={1} as={"h4"}>
          {subscription.subscription_duration}{" "}
          {t(`Subscriptions.Periods.${subscription.subscription_period}`)}
        </Title>
        <Text className="information" size="13">
          <StarIcon /> {subscription.trial_duration}-
          {t(`Subscriptions.Periods.${subscription.trial_period}`)}{" "}
          {t("Subscriptions.TrialText")}
        </Text>
        <div className="divider"></div>
        <Text size="13" className="description">
          {subscription.name}
        </Text>
        <Text size="24" className="price" bold>
          {formatPrice(subscription.gross_price)} zł
        </Text>
        {
          <Button
            mode="secondary"
            onClick={() => {
              if (isMobilePlatform) {
                buyOnMobile();
                return;
              }
              handleBuySubscription();
            }}
          >
            {t("Subscriptions.IPick")}
          </Button>
        }
      </div>
    </StyledSubscription>
  );
};

export default SubscriptionBox;