export function SecurityRecommendationsLayout()

in src/app/(proper_react)/(redesign)/(authenticated)/user/(dashboard)/dashboard/fix/security-recommendations/SecurityRecommendationsLayout.tsx [40:194]


export function SecurityRecommendationsLayout(
  props: SecurityRecommendationsLayoutProps,
) {
  const l10n = useL10n();
  const router = useRouter();
  const [isResolving, setIsResolving] = useState(false);

  const stepMap: Record<SecurityRecommendationTypes, StepLink["id"]> = {
    email: "SecurityTipsEmail",
    phone: "SecurityTipsPhone",
    ip: "SecurityTipsIp",
    done: "SecurityTipsIp",
  };

  const isStepDone = props.type === "done";

  const guidedExperienceBreaches = getGuidedExperienceBreaches(
    props.data.subscriberBreaches,
    props.subscriberEmails,
  );

  const nextStep = getNextGuidedStep(
    props.data,
    props.enabledFeatureFlags,
    stepMap[props.type],
  );
  const pageData = getSecurityRecommendationsByType({
    dataType: props.type,
    breaches: guidedExperienceBreaches,
    l10n: l10n,
    nextStep,
    countryCode: props.data.countryCode,
  });

  // The non-null assertion here should be safe since we already did this check
  // in `./[type]/page.tsx`:
  const { title, illustration, content, exposedData } = pageData!;
  const hasExposedData = exposedData.length > 0;

  // If there are no unresolved security recommendations:
  // Go to the next step in the guided resolution or back to the dashboard.
  useEffect(() => {
    if (!hasExposedData && !isStepDone) {
      router.push(nextStep.href);
    }
  }, [nextStep.href, router, hasExposedData, isStepDone]);

  // TODO: Write unit tests MNTOR-2560
  /* c8 ignore start */
  const handlePrimaryButtonPress = async () => {
    const securityRecommendatioBreachClasses: Record<
      SecurityRecommendationTypes,
      | (typeof SecurityRecommendationDataTypes)[keyof typeof SecurityRecommendationDataTypes]
      | null
    > = {
      email: SecurityRecommendationDataTypes.Email,
      phone: SecurityRecommendationDataTypes.Phone,
      ip: SecurityRecommendationDataTypes.IP,
      done: null,
    };

    const dataType = securityRecommendatioBreachClasses[props.type];
    // Only attempt to resolve the breaches if the following conditions are true:
    // - There is a matching data class type in this step
    // - The current step has unresolved exposed data
    // - There is no pending breach resolution request
    if (!dataType || !hasExposedData || isResolving) {
      return;
    }

    setIsResolving(true);
    try {
      const body: BreachBulkResolutionRequest = { dataType };
      const response = await fetch("/api/v1/user/breaches/bulk-resolve", {
        method: "PUT",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(body),
      });

      const result = await response.json();
      if (!result?.success) {
        throw new Error(
          `Could not resolve breach data class of type: ${props.type}`,
        );
      }

      const isCurrentStepSection = Object.values(stepMap).includes(nextStep.id);
      const nextRoute = isCurrentStepSection
        ? nextStep.href
        : "/user/dashboard/fix/security-recommendations/done";
      router.push(nextRoute);

      // Make sure the dashboard re-fetches the breaches on the next visit,
      // in order to make resolved breaches move to the "Fixed" tab.
      // If we had used server actions, we could've called
      // `revalidatePath("/user/dashboard")` there, but the API doesn't appear
      // to necessarily share a cache with the client.
      router.refresh();
    } catch {
      // TODO: MNTOR-2563: Capture client error with @next/sentry
      setIsResolving(false);
    }
  };
  /* c8 ignore stop */

  return (
    <FixView
      subscriberEmails={props.subscriberEmails}
      data={props.data}
      nextStep={nextStep}
      currentSection="security-recommendations"
      hideProgressIndicator={isStepDone}
      showConfetti={isStepDone}
      enabledFeatureFlags={props.enabledFeatureFlags}
    >
      <ResolutionContainer
        label={
          !isStepDone
            ? l10n.getString("security-recommendation-steps-label")
            : ""
        }
        type="securityRecommendations"
        title={title}
        illustration={illustration}
        isPremiumUser={hasPremium(props.data.user)}
        enabledFeatureFlags={props.enabledFeatureFlags}
        cta={
          !isStepDone && (
            <Button
              variant="primary"
              small
              autoFocus={true}
              /* c8 ignore next */
              onPress={() => void handlePrimaryButtonPress()}
              disabled={isResolving}
            >
              {l10n.getString("security-recommendation-steps-cta-label")}
            </Button>
          )
        }
        isStepDone={isStepDone}
        data={props.data}
        isEligibleForPremium={props.isEligibleForPremium}
      >
        <ResolutionContent
          content={content}
          exposedData={exposedData}
          locale={getLocale(l10n)}
        />
      </ResolutionContainer>
    </FixView>
  );
}