EscolaLMS/Front

View on GitHub
src/components/Onboarding/Step/index.tsx

Summary

Maintainability
B
6 hrs
Test Coverage
import {
  OnboardingOption,
  OnboardingStep,
  OnboardingStepType,
} from "@/components/Onboarding";
import SlideOption from "@/components/Onboarding/Step/slide";
import ResponsiveImage from "@escolalms/components/lib/components/organisms/ResponsiveImage/ResponsiveImage";
import { Radio, Text, Title } from "@escolalms/components/lib/index";
import { getStylesBasedOnTheme } from "@escolalms/components/lib/utils/utils";
import { EscolaLMSContext } from "@escolalms/sdk/lib/react";
import { useCallback, useContext } from "react";
import { useTranslation } from "react-i18next";
import styled from "styled-components";

const StyledStep = styled.div`
  .step-image {
    z-index: 0;
  }
  > h4 {
    margin-top: 20px;
  }
  .options {
    margin-top: 30px;
    margin-bottom: 160px;
    .option {
      &:not(:last-child) {
        margin-bottom: 26px;
        @media (max-width: 768px) {
          margin-bottom: 7px;
        }
      }
    }

    &.buttons {
      display: flex;
      flex-direction: row;
      flex-wrap: wrap;
      gap: 7px;
      .option {
        margin-bottom: 0px;
      }
    }
  }
`;

const ButtonOption = styled.button<{ $isActive: boolean }>`
  all: unset;
  border-radius: 20px;
  font-size: 14px;
  font-family: ${({ theme }) => theme.font};
  color: ${({ theme }) => theme.textColor};
  border: 1px solid
    ${({ theme, $isActive }) => ($isActive ? theme.primaryColor : "#E0E0E0")};
  padding: 11px 17px;
`;

const StyledHint = styled.div`
  position: relative;
  border-radius: ${({ theme }) => theme.cardRadius}px;
  background-color: ${({ theme }) =>
    getStylesBasedOnTheme(theme.mode, theme.white, theme.textColor)};
  margin-bottom: 35px;
  padding: 11px 55px;
  z-index: 10;
  h4,
  p {
    color: ${({ theme }) =>
      getStylesBasedOnTheme(theme.mode, theme.textColor, theme.white)};
  }
  &:before {
    content: "";
    position: absolute;
    width: 11vmin;
    height: 6vmin;
    box-shadow: rgb(51, 51, 51) 4vmin -0.5vmin;
    border-radius: 50%;
    top: -1vmin;
    transform: translate(-140%, -3vmin) rotate(165deg);
    left: 20vmin;
    z-index: -1;
    @media (max-width: 768px) {
      left: 25vmin;
      width: 12vmin;
      height: 9vmin;
      box-shadow: rgb(51, 51, 51) 5vmin 0.5vmin;
    }
  }
`;

type Props = {
  step: OnboardingStep;
  onAnswer: (answer: string) => void;
  answers?: {
    [key: string]: string;
  };
};

const Step: React.FC<Props> = ({ step, onAnswer, answers }) => {
  const { settings } = useContext(EscolaLMSContext);
  const { i18n } = useTranslation();

  const getImage = useCallback(() => {
    if (step.image) {
      return settings.value.onboarding[step.image];
    } else {
      return null;
    }
  }, [step, settings.value.onboarding]);

  const renderProperOptions = useCallback(
    (option: OnboardingOption) => {
      switch (step.type) {
        case OnboardingStepType.radio:
          return (
            <Radio
              value={option.value}
              label={option.label[i18n.language]}
              name={step.data}
              onChange={(e) => onAnswer(e.target.value)}
            />
          );

        case OnboardingStepType.options:
          return (
            <ButtonOption
              $isActive={
                (answers && answers[step.data] === option.value) || false
              }
              onClick={() => onAnswer(option.value)}
            >
              {option.label[i18n.language]}
            </ButtonOption>
          );

        default:
          return (
            <Radio
              value={option.value}
              label={option.label[i18n.language]}
              name={step.data}
              onChange={(e) => onAnswer(e.target.value)}
            />
          );
      }
    },
    [i18n.language, step.type, step.data, onAnswer, answers]
  );

  return (
    <StyledStep>
      {step.image && getImage() && (
        <div className="step-image">
          <ResponsiveImage path={getImage()} srcSizes={[500, 750, 1000]} />
        </div>
      )}
      {step.hint && (
        <StyledHint>
          <Title level={4}>{step.hint.title[i18n.language]}</Title>
          <Text size="13">{step.hint.text[i18n.language]}</Text>
        </StyledHint>
      )}
      <Title level={4}>{step.question[i18n.language]}</Title>
      <div
        className={`options ${
          step.type === OnboardingStepType.options ? "buttons" : ""
        }`}
      >
        {step.type === OnboardingStepType.slide && (
          <SlideOption options={step.options} onAnswer={onAnswer} />
        )}
        {step.type !== OnboardingStepType.slide &&
          step.options.map((option, index) => (
            <div key={option.value + index} className="option">
              {renderProperOptions(option)}
            </div>
          ))}
      </div>
    </StyledStep>
  );
};
export default Step;