All files / src/componentsV2/AccountProfile/Security Security.tsx

100% Statements 13/13
54.54% Branches 6/11
100% Functions 3/3
100% Lines 13/13

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128                                1x                               3x 3x 3x 3x 3x     3x           3x 1x     3x       3x                                                                   15x         15x                                                                      
import { Stack } from "@mui/material";
import { CheckCircleIcon, MinusCircleIcon } from "@phosphor-icons/react";
import GiveText from "@shared/Text/GiveText";
import { useAppTheme } from "@theme/v2/Provider";
import moment from "moment";
import Header from "./Header";
import TrustedDevices from "./TrustedDevices";
import GivePasswordInput from "@shared/GiveInputs/GivePasswordInput";
import { useGetUser } from "@services/api/onboarding/user";
import { useFormContext } from "react-hook-form";
import { useEffect } from "react";
import { useListTrustedDevices } from "@components/ProfileMenu/modals/hooks/useListTrustedDevices";
import { getPasswordRequirementState } from "./utils";
import { PASSWORD_MIN_CHARS } from "@constants/constants";
import { PASSWORD_HAS_NUMBER_REGEX } from "@validation/regex";
 
const INDICATIONS = {
  title: "Your password must contain at least:",
  elements: [
    `${PASSWORD_MIN_CHARS} Characters`,
    "One uppercase character",
    "One lowercase character",
    "One number",
    "One special character",
  ],
};
 
type Props = {
  setDisabled: React.Dispatch<React.SetStateAction<boolean>>;
};
 
export default function Security({ setDisabled }: Props) {
  const { palette } = useAppTheme();
  const { data: userData } = useGetUser();
  const { watch } = useFormContext();
  const { data } = useListTrustedDevices();
  const values = watch();
 
  const isDisabledPassword =
    values.confirm_password !== values.new_password ||
    values.current_password === values.new_password ||
    values.new_password?.length < 8 ||
    !PASSWORD_HAS_NUMBER_REGEX.test(values.new_password) ||
    values.current_password?.length < 4;
 
  useEffect(() => {
    setDisabled(isDisabledPassword);
  }, [isDisabledPassword]);
 
  const days = userData?.passwordLastChangedAt
    ? moment().diff(moment.unix(userData?.passwordLastChangedAt), "days")
    : 0;
 
  return (
    <>
      <Stack
        sx={{
          borderBottom: `1px solid ${palette.border?.primary}`,
          pb: "24px",
        }}
        gap="24px"
      >
        <Header
          days={days}
          title="Password"
          subtitle="Passwords are the first line of defence against unauthorised access to
        your account. By refreshing your password every 90 days, you’re highly
        reducing the risk of potential threats."
        />
        <GivePasswordInput
          name="current_password"
          label="Current Password"
          placeholder="Current Password"
          hidePlaceholder
        />
        <GivePasswordInput
          name="new_password"
          label="New Password"
          placeholder="New Password"
          hidePlaceholder
        />
        <Stack gap="12px">
          <GiveText variant="bodyS" color="secondary">
            {INDICATIONS.title}
          </GiveText>
          <Stack gap="8px">
            {INDICATIONS.elements.map((indication) => {
              const state = getPasswordRequirementState(
                indication,
                values.new_password,
              );
 
              return (
                <Stack
                  key={indication}
                  direction="row"
                  alignItems="center"
                  gap="8px"
                >
                  {state === "success" ? (
                    <CheckCircleIcon
                      fill={palette.primitive?.success?.[50]}
                      weight="fill"
                    />
                  ) : (
                    <MinusCircleIcon fill={palette.text.secondary} />
                  )}
 
                  <GiveText variant="bodyS" color={state}>
                    {indication}
                  </GiveText>
                </Stack>
              );
            })}
          </Stack>
        </Stack>
        <GivePasswordInput
          name="confirm_password"
          label="Confirm New Password"
          placeholder="Confirm New Password"
          hidePlaceholder
        />
      </Stack>
      <TrustedDevices devicesData={data?.data || []} />
    </>
  );
}