From 2a6ae974d16195d103206f05130ba056a8c60427 Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Wed, 19 Jul 2023 09:34:18 -0400 Subject: [PATCH 01/27] MNTOR-1809 - Add chart into dashboard top banner --- locales-pending/premium.ftl | 1 + .../dashboard/DashboardTopBanner.module.scss | 50 ++++++++++++++----- .../user/dashboard/DashboardTopBanner.tsx | 26 ++++++---- src/app/components/client/Chart.module.scss | 40 ++++++++++++--- src/app/components/client/Chart.tsx | 5 +- src/app/tokens.scss | 3 +- 6 files changed, 90 insertions(+), 35 deletions(-) diff --git a/locales-pending/premium.ftl b/locales-pending/premium.ftl index f18c74e05a0..6dcf999abef 100644 --- a/locales-pending/premium.ftl +++ b/locales-pending/premium.ftl @@ -38,6 +38,7 @@ exposure-chart-legend-heading-nr = Number # Variables: # $nr (number) - Number of a particular type of exposure found for the user exposure-chart-legend-value-nr = { $nr }x +exposure-chart-caption = This chart shows how many times your info is actively exposed. # Here's What We Fixed Progress Card diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.module.scss b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.module.scss index 3cd2b5ea124..9f3e603999b 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.module.scss +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.module.scss @@ -2,30 +2,56 @@ .container { display: flex; - flex-direction: row; + flex-direction: column; border: 1px solid rgba($color-purple-50, 0.2); border-radius: $border-radius-md; - padding: $layout-lg $spacing-xl; + padding: $layout-xs; background-color: $color-white; background-image: url("./images/dashboard-top-banner-wave.svg"); background-size: cover; background-position: center; + gap: $layout-xs; + align-items: center; - .content { - display: flex; - flex-direction: column; - gap: $spacing-md; + @media screen and (min-width: $screen-md) { + padding: $layout-lg $layout-xs; + flex-direction: row; + } + + .explainerContentWrapper { + padding: $layout-xs; - h3 { - font: $text-title-2xs; + @media screen and (min-width: $screen-md) { + justify-content: center; + flex: 0.5 1 0%; + + @media screen and (min-width: $screen-xl) { + flex: 1 1 0%; + } } - .cta { - align-self: start; + .explainerContent { + display: flex; + gap: $spacing-md; + flex-direction: column; + + @media screen and (min-width: $screen-md) { + max-width: $content-sm; + } + + h3 { + font: $text-title-2xs; + } + + .cta { + align-self: start; + } } } - & > * { - flex: 1 1 0%; + .chart { + @media screen and (min-width: $screen-md) { + flex: 1 1 0%; + } } } diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx index 3ca489c4aef..9d619565014 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx @@ -6,7 +6,7 @@ import styles from "./DashboardTopBanner.module.scss"; import { ReactElement } from "react"; import { Button } from "../../../../../components/server/Button"; import { useL10n } from "../../../../../hooks/l10n"; -import { useRouter } from "next/navigation"; +import { DoughnutChart as Chart } from "../../../../../components/client/Chart"; export type DashboardTopBannerProps = { type: @@ -20,7 +20,15 @@ export type DashboardTopBannerProps = { export const DashboardTopBanner = (props: DashboardTopBannerProps) => { const l10n = useL10n(); - const router = useRouter(); + + // all values are mocked for now + const chartData: Array<[string, number]> = [ + ["Home address", 11], + ["Family members", 12], + ["Contact Info", 1], + ["Full name", 5], + ["Other", 2], + ]; const contentData = { LetsFixDataContent: { @@ -36,9 +44,7 @@ export const DashboardTopBanner = (props: DashboardTopBannerProps) => { cta: { content: l10n.getString("dashboard-top-banner-protect-your-data-cta"), onClick: () => { - router.push( - `/redesign/user/dashboard/fix/data-broker-profiles/view-data-brokers` - ); + window.location.href = "/redesign/user/dashboard/fix/data-broker-profiles/view-data-brokers"; }, }, }, @@ -120,9 +126,9 @@ export const DashboardTopBanner = (props: DashboardTopBannerProps) => { return (
-
+
{content && ( - <> +

{content.headline}

{content.description}

@@ -130,10 +136,12 @@ export const DashboardTopBanner = (props: DashboardTopBannerProps) => { {content.cta.content} - +
)}
-
Chart goes here
+
+ +
); }; diff --git a/src/app/components/client/Chart.module.scss b/src/app/components/client/Chart.module.scss index 936d9580ef1..d845be55cc9 100644 --- a/src/app/components/client/Chart.module.scss +++ b/src/app/components/client/Chart.module.scss @@ -1,12 +1,16 @@ @import "../../tokens"; .chartContainer { - width: $content-sm; text-align: center; display: flex; + gap: $spacing-md; flex-direction: column; align-items: center; + @media screen and (min-width: $screen-lg) { + align-items: flex-start; + } + figcaption { font: $text-body-xs; font-style: italic; @@ -19,12 +23,24 @@ .chartAndLegendWrapper { display: flex; - gap: $spacing-2xl; - min-width: $content-xs; + flex-direction: column; + gap: $spacing-md; + + @media screen and (min-width: $screen-lg) { + flex-direction: row; + gap: $spacing-xl; + } .chart { - flex: 1 0 $content-sm; + flex: 1; + width: 100%; + @media screen and (min-width: $screen-lg) { + flex: 1 0 $content-xs; + width: auto; + } + + // Font size set in Chart.tsx .headingNr { font-family: var(--font-inter); font-weight: 600; @@ -43,10 +59,18 @@ } .legend { - flex: 0 0 $content-xs; - height: auto; - display: flex; - align-items: center; + align-self: center; + + @media screen and (min-width: $screen-md) { + flex: 0 0 150px; + } + + @media screen and (min-width: $screen-lg) { + flex: 0 0 200px; + height: auto; + display: flex; + align-items: center; + } thead { // These styles are taken from diff --git a/src/app/components/client/Chart.tsx b/src/app/components/client/Chart.tsx index f4ba068473a..fe47903144a 100644 --- a/src/app/components/client/Chart.tsx +++ b/src/app/components/client/Chart.tsx @@ -140,10 +140,7 @@ export const DoughnutChart = (props: Props) => {
-
- This chart shows the total number of exposures that are fixed ( - {sumOfFixedExposures} out of {props.totalExposures}). -
+
{l10n.getString("exposure-chart-caption")}
); }; diff --git a/src/app/tokens.scss b/src/app/tokens.scss index 450c7193d4b..6cec48e42ae 100644 --- a/src/app/tokens.scss +++ b/src/app/tokens.scss @@ -235,8 +235,7 @@ $text-body-lg: 400 clamp(16px, 1.5svw, 18px) / 1.5 var(--font-inter), sans-serif $text-body-md: 400 clamp(14px, 1.3svw, 16px) / 1.5 var(--font-inter), sans-serif; $text-body-sm: 400 clamp(12px, 1.14svw, 14px) / 1.5 var(--font-inter), sans-serif; -$text-body-xs: 400 clamp(10px, 0.975svw, 12px) / 1.5 var(--font-inter), - sans-serif; +$text-body-xs: 400 12px / 1.5 var(--font-inter), sans-serif; $tab-bar-height: 100px; $width-first-column-filter-bar: 50px; From ee4c4462d39ede58504e91e2879d19a743e8bafe Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Wed, 19 Jul 2023 09:43:19 -0400 Subject: [PATCH 02/27] clean up flex values --- src/app/components/client/Chart.module.scss | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/app/components/client/Chart.module.scss b/src/app/components/client/Chart.module.scss index d845be55cc9..009d9e51e9a 100644 --- a/src/app/components/client/Chart.module.scss +++ b/src/app/components/client/Chart.module.scss @@ -3,15 +3,12 @@ .chartContainer { text-align: center; display: flex; - gap: $spacing-md; + gap: $spacing-lg; flex-direction: column; align-items: center; - @media screen and (min-width: $screen-lg) { - align-items: flex-start; - } - figcaption { + text-align: center; font: $text-body-xs; font-style: italic; } @@ -34,11 +31,11 @@ .chart { flex: 1; width: 100%; + max-width: 250px; - @media screen and (min-width: $screen-lg) { - flex: 1 0 $content-xs; - width: auto; - } + // @media screen and (min-width: $screen-lg) { + // width: 250px; + // } // Font size set in Chart.tsx .headingNr { @@ -61,12 +58,7 @@ .legend { align-self: center; - @media screen and (min-width: $screen-md) { - flex: 0 0 150px; - } - @media screen and (min-width: $screen-lg) { - flex: 0 0 200px; height: auto; display: flex; align-items: center; From 21157410d0c28e58051d563d238cd2bb428d8d46 Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Wed, 19 Jul 2023 10:18:01 -0400 Subject: [PATCH 03/27] add about exposure num modal --- locales-pending/premium.ftl | 5 + .../dashboard/DashboardTopBanner.module.scss | 5 +- src/app/components/client/Chart.module.scss | 26 +- src/app/components/client/Chart.tsx | 224 +++++++++++------- .../client/ExposuresFilter.module.scss | 7 +- .../client/ProgressCard.module.scss | 9 +- src/app/tokens.scss | 12 + 7 files changed, 189 insertions(+), 99 deletions(-) diff --git a/locales-pending/premium.ftl b/locales-pending/premium.ftl index 6dcf999abef..2eeb24f4a0f 100644 --- a/locales-pending/premium.ftl +++ b/locales-pending/premium.ftl @@ -40,6 +40,11 @@ exposure-chart-legend-heading-nr = Number exposure-chart-legend-value-nr = { $nr }x exposure-chart-caption = This chart shows how many times your info is actively exposed. +modal-active-number-of-exposures-title = About your number of active exposures +modal-active-number-of-exposures-part-one = This chart includes the total number of times we found each type of data exposed across all data broker profiles and all data breaches for up to 5 email addresses that you are currently monitoring. +modal-active-number-of-exposures-part-two = For example, if you have 10 exposures of your phone number, that might mean one phone number is exposed across 10 different sites, or it could mean 2 different phone numbers were exposed across 5 different sites. +modal-active-number-of-exposures-part-three = This chart does not include any exposures that are in-progress of being auto-removed. Once your exposures are fixed, they will be added to your total number of fixed exposures on the Fixed page. + # Here's What We Fixed Progress Card exposure-card-company-logo = Company logo diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.module.scss b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.module.scss index 9f3e603999b..34abb809f70 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.module.scss +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.module.scss @@ -19,9 +19,12 @@ } .explainerContentWrapper { - padding: $layout-xs; + padding: $spacing-sm; + display: flex; + align-items: center; @media screen and (min-width: $screen-md) { + padding: $spacing-md; justify-content: center; flex: 0.5 1 0%; diff --git a/src/app/components/client/Chart.module.scss b/src/app/components/client/Chart.module.scss index 009d9e51e9a..7bdbb9749bd 100644 --- a/src/app/components/client/Chart.module.scss +++ b/src/app/components/client/Chart.module.scss @@ -9,8 +9,29 @@ figcaption { text-align: center; + display: flex; + align-items: center; + gap: $spacing-xs; font: $text-body-xs; font-style: italic; + + button { + @include question-mark-circle-btn; + } + } +} + +.modalBodyContent { + display: flex; + flex-direction: column; + gap: $spacing-sm; + + .confirmButtonWrapper { + align-self: center; + display: flex; + flex-direction: column; + min-width: $content-xs; + padding-block-start: $spacing-md; } } @@ -33,10 +54,6 @@ width: 100%; max-width: 250px; - // @media screen and (min-width: $screen-lg) { - // width: 250px; - // } - // Font size set in Chart.tsx .headingNr { font-family: var(--font-inter); @@ -85,6 +102,7 @@ font: $text-body-xs; td { + padding-block: $spacing-xs; padding-inline: $spacing-xs; } diff --git a/src/app/components/client/Chart.tsx b/src/app/components/client/Chart.tsx index fe47903144a..f1a3c03ec01 100644 --- a/src/app/components/client/Chart.tsx +++ b/src/app/components/client/Chart.tsx @@ -7,6 +7,14 @@ import { CSSProperties } from "react"; import { useL10n } from "../../hooks/l10n"; import styles from "./Chart.module.scss"; +import { QuestionMarkCircle } from "../server/Icons"; +import { useOverlayTrigger } from "react-aria"; +import { useOverlayTriggerState } from "react-stately"; +import { Button } from "../server/Button"; +import { ModalOverlay } from "./dialog/ModalOverlay"; +import { Dialog } from "./dialog/Dialog"; +import ModalImage from "../client/assets/modal-default-img.svg"; +import Image from "next/image"; export type Props = { data: Array<[string, number]>; @@ -14,6 +22,13 @@ export type Props = { }; export const DoughnutChart = (props: Props) => { const l10n = useL10n(); + + const explainerDialogState = useOverlayTriggerState({}); + const explainerDialogTrigger = useOverlayTrigger( + { type: "dialog" }, + explainerDialogState + ); + const sumOfFixedExposures = props.data.reduce( (total, [_label, num]) => total + num, 0 @@ -59,88 +74,137 @@ export const DoughnutChart = (props: Props) => { ); }); - return ( -
-
- ", "") - .replace("", "") - .replace("", "")} - viewBox={`0 0 ${diameter} ${diameter}`} - className={styles.chart} + const modalContent = ( +
+

{l10n.getString("modal-active-number-of-exposures-part-one")}

+

{l10n.getString("modal-active-number-of-exposures-part-two")}

+

{l10n.getString("modal-active-number-of-exposures-part-three")}

+
+
-
-
{l10n.getString("exposure-chart-caption")}
-
+
+ {l10n.getString("exposure-chart-caption")} + +
+ + {explainerDialogState.isOpen && ( + + } + onDismiss={() => explainerDialogState.close()} + > + {modalContent} + + + )} + ); }; diff --git a/src/app/components/client/ExposuresFilter.module.scss b/src/app/components/client/ExposuresFilter.module.scss index e314a4cd783..e2fbcaf5243 100644 --- a/src/app/components/client/ExposuresFilter.module.scss +++ b/src/app/components/client/ExposuresFilter.module.scss @@ -20,12 +20,7 @@ } button { - all: unset; - cursor: pointer; - - &:hover { - color: $color-grey-30; - } + @include question-mark-circle-btn; } ul.filterHeaderList { diff --git a/src/app/components/client/ProgressCard.module.scss b/src/app/components/client/ProgressCard.module.scss index 010fd94d757..e2574d606c0 100644 --- a/src/app/components/client/ProgressCard.module.scss +++ b/src/app/components/client/ProgressCard.module.scss @@ -18,14 +18,7 @@ font-weight: 600; button { - all: unset; - cursor: pointer; - height: 25px; // height of the button - color: $color-grey-40; - - &:hover { - color: $color-grey-30; - } + @include question-mark-circle-btn; } } diff --git a/src/app/tokens.scss b/src/app/tokens.scss index 6cec48e42ae..4afc9b459de 100644 --- a/src/app/tokens.scss +++ b/src/app/tokens.scss @@ -240,6 +240,18 @@ $text-body-xs: 400 12px / 1.5 var(--font-inter), sans-serif; $tab-bar-height: 100px; $width-first-column-filter-bar: 50px; +// Question circle mark for explainer dialogs +@mixin question-mark-circle-btn { + all: unset; + cursor: pointer; + height: 25px; // height of the button + color: $color-grey-40; + + &:hover { + color: $color-grey-30; + } +} + @mixin visually-hidden { // These styles are taken from // https://react-spectrum.adobe.com/react-aria/VisuallyHidden.html From 9cd205f52997d458f14ad892c30119af9ac9a455 Mon Sep 17 00:00:00 2001 From: mansaj Date: Fri, 21 Jul 2023 20:52:00 -0700 Subject: [PATCH 04/27] first pass --- .../(authenticated)/user/dashboard/page.tsx | 3 +- src/app/functions/server/dashboard.ts | 67 +++++++++++++++++++ src/app/functions/universal/user.ts | 2 +- 3 files changed, 70 insertions(+), 2 deletions(-) create mode 100644 src/app/functions/server/dashboard.ts diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx index 5bcf377f4dd..312476807d6 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx @@ -13,7 +13,7 @@ import { getLocale } from "../../../../../functions/server/l10n"; import { getOnerepProfileId } from "../../../../../../db/tables/subscribers"; import { authOptions } from "../../../../../api/utils/auth"; import { getLatestOnerepScan } from "../../../../../../db/tables/onerep_scans"; - +import { dashboardSummary } from "../../../../../functions/server/dashboard"; export default async function DashboardPage() { const session = await getServerSession(authOptions); if (!session?.user?.subscriber?.id) { @@ -35,6 +35,7 @@ export default async function DashboardPage() { const scanResult = await getLatestOnerepScan(profileId); const scanResultItems = scanResult?.onerep_scan_results?.data ?? []; const breaches = await getUserBreaches({ user: session.user }); + const summary = await dashboardSummary(session.user); const locale = getLocale(); return ( diff --git a/src/app/functions/server/dashboard.ts b/src/app/functions/server/dashboard.ts new file mode 100644 index 00000000000..be1c1b56acc --- /dev/null +++ b/src/app/functions/server/dashboard.ts @@ -0,0 +1,67 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +import { Session } from "next-auth"; +import { BreachDataTypes } from "../../../utils/breachResolution"; +import { getSubscriberByEmail } from "../../../../src/db/tables/subscribers.js"; +import { getBreaches } from "./getBreaches"; +import { getAllEmailsAndBreaches } from "../../../../src/utils/breaches.js"; +import { getLatestOnerepScan } from "../../../db/tables/onerep_scans"; + +export interface DashboardSummary { + data_breach_total_num: number; + data_broker_total_num: number; + total_exposures: number; + exposures_types: { + // shared + email_addresses: number; + phone_numbers: number; + + // data brokers + address?: number; + family_members?: number; + full_names?: number; + + // data breaches + social_security_numbers?: number; + ip_addresses?: number; + passwords?: number; + credit_card_numbers?: number; + debit_card_numbers?: number; + pin_numbers?: number; + security_questions?: number; + }; +} + +export async function dashboardSummary( + user: Session["user"] +): Promise { + const summary: DashboardSummary = { + data_breach_total_num: 0, + data_broker_total_num: 0, + total_exposures: 0, + exposures_types: { + email_addresses: 0, + phone_numbers: 0, + }, + }; + + // get breaches data + const subscriber = await getSubscriberByEmail(user.email); + const allBreaches = await getBreaches(); + const breachesData = await getAllEmailsAndBreaches(subscriber, allBreaches); + console.debug(JSON.stringify(breachesData)); + + // get scanned results from data brokers + const scannedResults = await getLatestOnerepScan( + user.subscriber?.onerep_profile_id as number + ); + console.debug(JSON.stringify(scannedResults)); + + // calculate broker summary from scanned results + + // calculate breaches summary from breaches data + + return summary; +} diff --git a/src/app/functions/universal/user.ts b/src/app/functions/universal/user.ts index f72cc162b3d..7c605eb4f23 100644 --- a/src/app/functions/universal/user.ts +++ b/src/app/functions/universal/user.ts @@ -6,7 +6,7 @@ import { Session } from "next-auth"; import { ISO8601DateString } from "../../../utils/parse.js"; export function hasPremium(user?: Session["user"]): boolean { - return user?.fxa?.subscriptions.includes("monitor") ?? false; + return user?.fxa?.subscriptions?.includes("monitor") ?? false; } export function canSubscribeToPremium(params: { From 876bd1d364ae0b39e845d9869e48c799a9d6a88d Mon Sep 17 00:00:00 2001 From: Joey Zhou Date: Sat, 22 Jul 2023 23:23:48 -0700 Subject: [PATCH 05/27] add summary metrics for dashboard --- .../(authenticated)/user/dashboard/page.tsx | 3 +- src/app/functions/server/dashboard.ts | 168 +++++++++++++----- 2 files changed, 129 insertions(+), 42 deletions(-) diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx index 312476807d6..8edd7c2751e 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx @@ -35,7 +35,8 @@ export default async function DashboardPage() { const scanResult = await getLatestOnerepScan(profileId); const scanResultItems = scanResult?.onerep_scan_results?.data ?? []; const breaches = await getUserBreaches({ user: session.user }); - const summary = await dashboardSummary(session.user); + const summary = dashboardSummary(scanResultItems, breaches); + console.debug({ summary }); const locale = getLocale(); return ( diff --git a/src/app/functions/server/dashboard.ts b/src/app/functions/server/dashboard.ts index be1c1b56acc..7f2eb7b7ba4 100644 --- a/src/app/functions/server/dashboard.ts +++ b/src/app/functions/server/dashboard.ts @@ -2,66 +2,152 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -import { Session } from "next-auth"; import { BreachDataTypes } from "../../../utils/breachResolution"; -import { getSubscriberByEmail } from "../../../../src/db/tables/subscribers.js"; -import { getBreaches } from "./getBreaches"; -import { getAllEmailsAndBreaches } from "../../../../src/utils/breaches.js"; -import { getLatestOnerepScan } from "../../../db/tables/onerep_scans"; export interface DashboardSummary { - data_breach_total_num: number; - data_broker_total_num: number; - total_exposures: number; - exposures_types: { + dataBreachTotalNum: number; + dataBrokerTotalNum: number; + totalExposures: number; + allExposures: { // shared - email_addresses: number; - phone_numbers: number; + emailAddresses: number; + phoneNumbers: number; // data brokers - address?: number; - family_members?: number; - full_names?: number; + addresses: number; + familyMembers: number; + fullNames: number; // data breaches - social_security_numbers?: number; - ip_addresses?: number; - passwords?: number; - credit_card_numbers?: number; - debit_card_numbers?: number; - pin_numbers?: number; - security_questions?: number; + socialSecurityNumbers: number; + ipAddresses: number; + passwords: number; + creditCardNumbers: number; + pinNumbers: number; + securityQuestions: number; }; + sanitizedExposures: Array>; } -export async function dashboardSummary( - user: Session["user"] -): Promise { +export function dashboardSummary( + scannedResults, + { breachesData } +): DashboardSummary { const summary: DashboardSummary = { - data_breach_total_num: 0, - data_broker_total_num: 0, - total_exposures: 0, - exposures_types: { - email_addresses: 0, - phone_numbers: 0, + dataBreachTotalNum: 0, + dataBrokerTotalNum: 0, + totalExposures: 0, + allExposures: { + emailAddresses: 0, + phoneNumbers: 0, + addresses: 0, + familyMembers: 0, + fullNames: 0, + + // data breaches + socialSecurityNumbers: 0, + ipAddresses: 0, + passwords: 0, + creditCardNumbers: 0, + pinNumbers: 0, + securityQuestions: 0, }, + sanitizedExposures: [], }; - // get breaches data - const subscriber = await getSubscriberByEmail(user.email); - const allBreaches = await getBreaches(); - const breachesData = await getAllEmailsAndBreaches(subscriber, allBreaches); - console.debug(JSON.stringify(breachesData)); + // calculate broker summary from scanned results + if (scannedResults) { + scannedResults.forEach((r) => { + // count email + summary.totalExposures += r.emails.length; + summary.allExposures.emailAddresses += r.emails.length; - // get scanned results from data brokers - const scannedResults = await getLatestOnerepScan( - user.subscriber?.onerep_profile_id as number - ); - console.debug(JSON.stringify(scannedResults)); + // count phones + summary.totalExposures += r.phones.length; + summary.allExposures.phoneNumbers += r.phones.length; - // calculate broker summary from scanned results + // count physical addresses + summary.totalExposures += r.addresses.length; + summary.allExposures.addresses += r.addresses.length; + + // count relatives + summary.totalExposures += r.relatives.length; + summary.allExposures.familyMembers += r.relatives.length; + + // count full name + summary.totalExposures++; + summary.allExposures.fullNames++; + }); + } // calculate breaches summary from breaches data + if (breachesData.verifiedEmails) { + for (const emailBreaches of breachesData.verifiedEmails) { + const breaches = emailBreaches.breaches; + breaches.forEach((b) => { + const dataClasses = b.DataClasses; + + // count password + if (dataClasses?.includes(BreachDataTypes.Passwords)) { + summary.totalExposures++; + summary.allExposures.passwords++; + } + + // count ssn + if (dataClasses?.includes(BreachDataTypes.SSN)) { + summary.totalExposures++; + summary.allExposures.socialSecurityNumbers++; + } + + // count IP + if (dataClasses?.includes(BreachDataTypes.IP)) { + summary.totalExposures++; + summary.allExposures.ipAddresses++; + } + + // count credit card + if (dataClasses?.includes(BreachDataTypes.CreditCard)) { + summary.totalExposures++; + summary.allExposures.creditCardNumbers++; + } + + // count pin numbers + if (dataClasses?.includes(BreachDataTypes.PIN)) { + summary.totalExposures++; + summary.allExposures.pinNumbers++; + } + + // count security questions + if (dataClasses?.includes(BreachDataTypes.SecurityQuestions)) { + summary.totalExposures++; + summary.allExposures.securityQuestions++; + } + }); + } + } + + return sanitizeExposures(summary); +} + +function sanitizeExposures(summary: DashboardSummary): DashboardSummary { + const { allExposures } = summary; + const sortedExposures = Object.entries(allExposures).sort( + (a, b) => b[1] - a[1] + ); + + const other = + summary.totalExposures - + sortedExposures.slice(0, 4).reduce((acc, cur) => acc + cur[1], 0); + + const sanitizedExposures = sortedExposures + .map((e) => { + const key = e[0]; + return { [key]: e[1] }; + }) + .splice(0, 4); + sanitizedExposures.push({ other }); + summary.sanitizedExposures = sanitizedExposures; + console.debug({ sanitizedExposures }); return summary; } From 7c63c22899cc21c2edeaddf38860c9d1420a8a05 Mon Sep 17 00:00:00 2001 From: Joey Zhou Date: Mon, 24 Jul 2023 09:30:38 -0700 Subject: [PATCH 06/27] add types --- src/app/functions/server/dashboard.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/functions/server/dashboard.ts b/src/app/functions/server/dashboard.ts index 7f2eb7b7ba4..09d26a402b7 100644 --- a/src/app/functions/server/dashboard.ts +++ b/src/app/functions/server/dashboard.ts @@ -3,7 +3,8 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import { BreachDataTypes } from "../../../utils/breachResolution"; - +import type { UserBreaches } from "./getUserBreaches"; +import type { ScanResult } from "./onerep"; export interface DashboardSummary { dataBreachTotalNum: number; dataBrokerTotalNum: number; @@ -30,8 +31,8 @@ export interface DashboardSummary { } export function dashboardSummary( - scannedResults, - { breachesData } + scannedResults: ScanResult[], + { breachesData }: UserBreaches ): DashboardSummary { const summary: DashboardSummary = { dataBreachTotalNum: 0, From 2330cc53b28a8a07620526b74181f03c6f3dbd6e Mon Sep 17 00:00:00 2001 From: Joey Zhou Date: Mon, 24 Jul 2023 09:41:56 -0700 Subject: [PATCH 07/27] todo comment --- src/app/functions/server/dashboard.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/functions/server/dashboard.ts b/src/app/functions/server/dashboard.ts index 09d26a402b7..fadf7e60328 100644 --- a/src/app/functions/server/dashboard.ts +++ b/src/app/functions/server/dashboard.ts @@ -82,6 +82,7 @@ export function dashboardSummary( } // calculate breaches summary from breaches data + // TODO: Modify after MNTOR-1947: Refactor user breaches object if (breachesData.verifiedEmails) { for (const emailBreaches of breachesData.verifiedEmails) { const breaches = emailBreaches.breaches; From f6fadeab1b41d2f2814e1a0fa37f1b1cc6c3ae67 Mon Sep 17 00:00:00 2001 From: Joey Zhou Date: Mon, 24 Jul 2023 10:00:44 -0700 Subject: [PATCH 08/27] review comment --- src/app/functions/server/dashboard.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/functions/server/dashboard.ts b/src/app/functions/server/dashboard.ts index fadf7e60328..52096bd1009 100644 --- a/src/app/functions/server/dashboard.ts +++ b/src/app/functions/server/dashboard.ts @@ -87,40 +87,40 @@ export function dashboardSummary( for (const emailBreaches of breachesData.verifiedEmails) { const breaches = emailBreaches.breaches; breaches.forEach((b) => { - const dataClasses = b.DataClasses; + const dataClasses = b.DataClasses ?? []; // count password - if (dataClasses?.includes(BreachDataTypes.Passwords)) { + if (dataClasses.includes(BreachDataTypes.Passwords)) { summary.totalExposures++; summary.allExposures.passwords++; } // count ssn - if (dataClasses?.includes(BreachDataTypes.SSN)) { + if (dataClasses.includes(BreachDataTypes.SSN)) { summary.totalExposures++; summary.allExposures.socialSecurityNumbers++; } // count IP - if (dataClasses?.includes(BreachDataTypes.IP)) { + if (dataClasses.includes(BreachDataTypes.IP)) { summary.totalExposures++; summary.allExposures.ipAddresses++; } // count credit card - if (dataClasses?.includes(BreachDataTypes.CreditCard)) { + if (dataClasses.includes(BreachDataTypes.CreditCard)) { summary.totalExposures++; summary.allExposures.creditCardNumbers++; } // count pin numbers - if (dataClasses?.includes(BreachDataTypes.PIN)) { + if (dataClasses.includes(BreachDataTypes.PIN)) { summary.totalExposures++; summary.allExposures.pinNumbers++; } // count security questions - if (dataClasses?.includes(BreachDataTypes.SecurityQuestions)) { + if (dataClasses.includes(BreachDataTypes.SecurityQuestions)) { summary.totalExposures++; summary.allExposures.securityQuestions++; } From e8c6ca51ab69f4376527bc1baf6daa4d697b6caf Mon Sep 17 00:00:00 2001 From: Joey Zhou Date: Mon, 24 Jul 2023 10:29:34 -0700 Subject: [PATCH 09/27] review comment --- src/app/functions/server/dashboard.ts | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/app/functions/server/dashboard.ts b/src/app/functions/server/dashboard.ts index 52096bd1009..3aae60c044b 100644 --- a/src/app/functions/server/dashboard.ts +++ b/src/app/functions/server/dashboard.ts @@ -132,21 +132,19 @@ export function dashboardSummary( } function sanitizeExposures(summary: DashboardSummary): DashboardSummary { + const NUM_OF_TOP_EXPOSURES = 4; const { allExposures } = summary; - const sortedExposures = Object.entries(allExposures).sort( - (a, b) => b[1] - a[1] - ); - - const other = - summary.totalExposures - - sortedExposures.slice(0, 4).reduce((acc, cur) => acc + cur[1], 0); - - const sanitizedExposures = sortedExposures + const sanitizedExposures = Object.entries(allExposures) + .sort((a, b) => b[1] - a[1]) .map((e) => { const key = e[0]; return { [key]: e[1] }; }) - .splice(0, 4); + .splice(0, NUM_OF_TOP_EXPOSURES); + const other = sanitizedExposures.reduce( + (total, cur) => total - (Object.values(cur).pop() || 0), + summary.totalExposures + ); sanitizedExposures.push({ other }); summary.sanitizedExposures = sanitizedExposures; From b1438b5d23409726e54452247f3a72f26bd7553b Mon Sep 17 00:00:00 2001 From: mansaj Date: Mon, 24 Jul 2023 12:01:22 -0700 Subject: [PATCH 10/27] map for localized key --- src/app/functions/server/dashboard.ts | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/app/functions/server/dashboard.ts b/src/app/functions/server/dashboard.ts index 3aae60c044b..7b614f04762 100644 --- a/src/app/functions/server/dashboard.ts +++ b/src/app/functions/server/dashboard.ts @@ -30,6 +30,24 @@ export interface DashboardSummary { sanitizedExposures: Array>; } +const exposureKeyMap: Record = { + emailAddresses: "email-addresses", + phoneNumbers: "phone-numbers", + + // data brokers + addresses: "physical-addresses", + familyMembers: "family-members-names", + fullNames: "full-names", + + // data breaches + socialSecurityNumbers: "social-security-numbers", + ipAddresses: "ip-addresses", + passwords: "passwords", + creditCardNumbers: "credit-cards", + pinNumbers: "pins", + securityQuestions: "security-questions-and-answers", +}; + export function dashboardSummary( scannedResults: ScanResult[], { breachesData }: UserBreaches @@ -137,7 +155,7 @@ function sanitizeExposures(summary: DashboardSummary): DashboardSummary { const sanitizedExposures = Object.entries(allExposures) .sort((a, b) => b[1] - a[1]) .map((e) => { - const key = e[0]; + const key = exposureKeyMap[e[0]]; return { [key]: e[1] }; }) .splice(0, NUM_OF_TOP_EXPOSURES); From a90adc424c1ea47b7e99526c130c7cb592f6e8cc Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 10:57:33 -0400 Subject: [PATCH 11/27] inject chart data into chart component --- locales/en/data-classes.ftl | 1 + .../user/dashboard/DashboardTopBanner.tsx | 30 ++++++++++++++----- .../(authenticated)/user/dashboard/View.tsx | 8 ++++- .../(authenticated)/user/dashboard/page.tsx | 1 + src/app/components/client/Chart.tsx | 2 +- src/app/functions/server/dashboard.ts | 2 +- 6 files changed, 33 insertions(+), 11 deletions(-) diff --git a/locales/en/data-classes.ftl b/locales/en/data-classes.ftl index c9cd58ba846..a9ecae4717b 100644 --- a/locales/en/data-classes.ftl +++ b/locales/en/data-classes.ftl @@ -153,3 +153,4 @@ website-activity = Website activity work-habits = Work habits years-of-birth = Years of birth years-of-professional-experience = Years of professional experience +other-data-class = Other \ No newline at end of file diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx index 9d619565014..1d30c243faa 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx @@ -7,6 +7,7 @@ import { ReactElement } from "react"; import { Button } from "../../../../../components/server/Button"; import { useL10n } from "../../../../../hooks/l10n"; import { DoughnutChart as Chart } from "../../../../../components/client/Chart"; +import { DashboardSummary } from "../../../../../functions/server/dashboard"; export type DashboardTopBannerProps = { type: @@ -16,19 +17,32 @@ export type DashboardTopBannerProps = { | "ResumeBreachResolutionContent" | "YourDataIsProtected"; chart: ReactElement; + chartData: DashboardSummary; }; export const DashboardTopBanner = (props: DashboardTopBannerProps) => { const l10n = useL10n(); + // console.log(props.chartData.sanitizedExposures); + + const chartData: [string, number][] = props.chartData.sanitizedExposures.map( + (obj) => { + const [key, value] = Object.entries(obj)[0]; + return [l10n.getString(key), value]; + } + ); + + // console.log(arrayOfTuples); + + // console.log(transformedData); // all values are mocked for now - const chartData: Array<[string, number]> = [ - ["Home address", 11], - ["Family members", 12], - ["Contact Info", 1], - ["Full name", 5], - ["Other", 2], - ]; + // const chartData: Array<[string, number]> = [ + // ["Home address", 11], + // ["Family members", 12], + // ["Contact Info", 1], + // ["Full name", 5], + // ["Other", 2], + // ]; const contentData = { LetsFixDataContent: { @@ -140,7 +154,7 @@ export const DashboardTopBanner = (props: DashboardTopBannerProps) => { )}
- +
); diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx index 6f43c48b405..a2a1becdfd6 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx @@ -22,12 +22,14 @@ import { useState } from "react"; import { ScanResult } from "../../../../../functions/server/onerep"; import { HibpLikeDbBreach } from "../../../../../../utils/hibp"; import { BundledVerifiedEmails } from "../../../../../../utils/breaches"; +import { DashboardSummary } from "../../../../../functions/server/dashboard"; export type Props = { user: Session["user"]; userBreaches: UserBreaches; isUserScannedResults: boolean; userScannedResults: ScanResult[]; + chartData: DashboardSummary; locale: string; }; @@ -218,7 +220,11 @@ export const View = (props: Props) => {
- } /> + } + />

{l10n.getString("dashboard-exposures-area-headline")} diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx index 8edd7c2751e..9902440bd28 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx @@ -45,6 +45,7 @@ export default async function DashboardPage() { userScannedResults={scanResultItems} userBreaches={breaches} locale={locale} + chartData={summary} isUserScannedResults={!!scanResultItems} /> ); diff --git a/src/app/components/client/Chart.tsx b/src/app/components/client/Chart.tsx index f1a3c03ec01..593d4e8b5ab 100644 --- a/src/app/components/client/Chart.tsx +++ b/src/app/components/client/Chart.tsx @@ -18,8 +18,8 @@ import Image from "next/image"; export type Props = { data: Array<[string, number]>; - totalExposures: number; }; + export const DoughnutChart = (props: Props) => { const l10n = useL10n(); diff --git a/src/app/functions/server/dashboard.ts b/src/app/functions/server/dashboard.ts index 7b614f04762..14c47b8b64d 100644 --- a/src/app/functions/server/dashboard.ts +++ b/src/app/functions/server/dashboard.ts @@ -163,7 +163,7 @@ function sanitizeExposures(summary: DashboardSummary): DashboardSummary { (total, cur) => total - (Object.values(cur).pop() || 0), summary.totalExposures ); - sanitizedExposures.push({ other }); + sanitizedExposures.push({ ["other-data-class"]: other }); summary.sanitizedExposures = sanitizedExposures; console.debug({ sanitizedExposures }); From 4efb33c1d37ad85f43ccf5ac087bf01b197e9ead Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 11:06:06 -0400 Subject: [PATCH 12/27] use variable for num email addresses --- locales-pending/premium.ftl | 4 +++- src/app/components/client/Chart.tsx | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/locales-pending/premium.ftl b/locales-pending/premium.ftl index 2eeb24f4a0f..40ec25758ac 100644 --- a/locales-pending/premium.ftl +++ b/locales-pending/premium.ftl @@ -41,7 +41,9 @@ exposure-chart-legend-value-nr = { $nr }x exposure-chart-caption = This chart shows how many times your info is actively exposed. modal-active-number-of-exposures-title = About your number of active exposures -modal-active-number-of-exposures-part-one = This chart includes the total number of times we found each type of data exposed across all data broker profiles and all data breaches for up to 5 email addresses that you are currently monitoring. +# Variables: +# $limit (number) - Number of email addresses included in the plan +modal-active-number-of-exposures-part-one = This chart includes the total number of times we found each type of data exposed across all data broker profiles and all data breaches for up to { $limit } email addresses that you are currently monitoring. modal-active-number-of-exposures-part-two = For example, if you have 10 exposures of your phone number, that might mean one phone number is exposed across 10 different sites, or it could mean 2 different phone numbers were exposed across 5 different sites. modal-active-number-of-exposures-part-three = This chart does not include any exposures that are in-progress of being auto-removed. Once your exposures are fixed, they will be added to your total number of fixed exposures on the Fixed page. diff --git a/src/app/components/client/Chart.tsx b/src/app/components/client/Chart.tsx index 593d4e8b5ab..3ca0eb4ec0d 100644 --- a/src/app/components/client/Chart.tsx +++ b/src/app/components/client/Chart.tsx @@ -15,6 +15,7 @@ import { ModalOverlay } from "./dialog/ModalOverlay"; import { Dialog } from "./dialog/Dialog"; import ModalImage from "../client/assets/modal-default-img.svg"; import Image from "next/image"; +import AppConstants from "../../../../src/appConstants"; export type Props = { data: Array<[string, number]>; @@ -76,7 +77,11 @@ export const DoughnutChart = (props: Props) => { const modalContent = (
-

{l10n.getString("modal-active-number-of-exposures-part-one")}

+

+ {l10n.getString("modal-active-number-of-exposures-part-one", { + limit: 5, + })} +

{l10n.getString("modal-active-number-of-exposures-part-two")}

{l10n.getString("modal-active-number-of-exposures-part-three")}

From 3b27dcc44ef285b6ddba1b8f90b993e02e2cbe75 Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 13:06:19 -0400 Subject: [PATCH 13/27] fix storybook for dashboard --- locales/en/data-classes.ftl | 3 ++- .../user/dashboard/Dashboard.stories.tsx | 9 +++++++ .../user/dashboard/DashboardTopBanner.tsx | 26 ++++--------------- .../(authenticated)/user/dashboard/View.tsx | 4 +-- .../(authenticated)/user/dashboard/page.tsx | 3 +-- 5 files changed, 18 insertions(+), 27 deletions(-) diff --git a/locales/en/data-classes.ftl b/locales/en/data-classes.ftl index a9ecae4717b..68c43733ee7 100644 --- a/locales/en/data-classes.ftl +++ b/locales/en/data-classes.ftl @@ -153,4 +153,5 @@ website-activity = Website activity work-habits = Work habits years-of-birth = Years of birth years-of-professional-experience = Years of professional experience -other-data-class = Other \ No newline at end of file +other-data-class = Other +full-names = Full name diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx index 95184c5a8df..10e36d80ed1 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx @@ -138,6 +138,14 @@ const breachItemArraySample: HibpLikeDbBreach[] = [ BreachMockItem4, ]; +const dashboardSummary: Record[] = [ + { "physical-addresses": 90 }, + { "family-members-names": 29 }, + { "full-names": 98 }, + { "phone-numbers": 8 }, + { "other-data-class": 80 }, +]; + export const Dashboard: Story = { render: () => ( @@ -166,6 +174,7 @@ export const Dashboard: Story = { userScannedResults={scannedResultsArraySample} locale={"en"} isUserScannedResults={true} + chartData={dashboardSummary} /> ), diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx index 1d30c243faa..5a91decc461 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx @@ -17,32 +17,16 @@ export type DashboardTopBannerProps = { | "ResumeBreachResolutionContent" | "YourDataIsProtected"; chart: ReactElement; - chartData: DashboardSummary; + chartData: Record[]; }; export const DashboardTopBanner = (props: DashboardTopBannerProps) => { const l10n = useL10n(); - // console.log(props.chartData.sanitizedExposures); - - const chartData: [string, number][] = props.chartData.sanitizedExposures.map( - (obj) => { - const [key, value] = Object.entries(obj)[0]; - return [l10n.getString(key), value]; - } - ); - - // console.log(arrayOfTuples); - - // console.log(transformedData); - // all values are mocked for now - // const chartData: Array<[string, number]> = [ - // ["Home address", 11], - // ["Family members", 12], - // ["Contact Info", 1], - // ["Full name", 5], - // ["Other", 2], - // ]; + const chartData: [string, number][] = props.chartData.map((obj) => { + const [key, value] = Object.entries(obj)[0]; + return [l10n.getString(key), value]; + }); const contentData = { LetsFixDataContent: { diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx index a2a1becdfd6..525583a1546 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx @@ -22,20 +22,18 @@ import { useState } from "react"; import { ScanResult } from "../../../../../functions/server/onerep"; import { HibpLikeDbBreach } from "../../../../../../utils/hibp"; import { BundledVerifiedEmails } from "../../../../../../utils/breaches"; -import { DashboardSummary } from "../../../../../functions/server/dashboard"; export type Props = { user: Session["user"]; userBreaches: UserBreaches; isUserScannedResults: boolean; userScannedResults: ScanResult[]; - chartData: DashboardSummary; + chartData: Record[]; locale: string; }; export const View = (props: Props) => { const l10n = useL10n(); - const totalBreaches = props.userBreaches.breachesData.verifiedEmails.reduce( (count, emailData) => count + emailData.breaches.length, 0 diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx index 9902440bd28..f68327ef4c4 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx @@ -36,7 +36,6 @@ export default async function DashboardPage() { const scanResultItems = scanResult?.onerep_scan_results?.data ?? []; const breaches = await getUserBreaches({ user: session.user }); const summary = dashboardSummary(scanResultItems, breaches); - console.debug({ summary }); const locale = getLocale(); return ( @@ -45,7 +44,7 @@ export default async function DashboardPage() { userScannedResults={scanResultItems} userBreaches={breaches} locale={locale} - chartData={summary} + chartData={summary.sanitizedExposures} isUserScannedResults={!!scanResultItems} /> ); From 30862d5c4038e5b4fdd0dd4838cc103c34becebc Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 13:30:31 -0400 Subject: [PATCH 14/27] switch between pre-scan and post-scan views --- .../user/dashboard/Dashboard.stories.tsx | 36 +++++++++++++++++-- .../user/dashboard/DashboardTopBanner.tsx | 4 +-- .../(authenticated)/user/dashboard/View.tsx | 12 ++++--- .../(authenticated)/user/dashboard/page.tsx | 1 - 4 files changed, 44 insertions(+), 9 deletions(-) diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx index 10e36d80ed1..c490aec45fd 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx @@ -146,7 +146,7 @@ const dashboardSummary: Record[] = [ { "other-data-class": 80 }, ]; -export const Dashboard: Story = { +export const DashboardWithScan: Story = { render: () => ( + + ), +}; + +export const DashboardWithoutScan: Story = { + render: () => ( + + diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx index 5a91decc461..4b729811e22 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx @@ -15,7 +15,7 @@ export type DashboardTopBannerProps = { | "DataBrokerScanUpsellContent" | "NoExposuresFoundContent" | "ResumeBreachResolutionContent" - | "YourDataIsProtected"; + | "YourDataIsProtectedContent"; chart: ReactElement; chartData: Record[]; }; @@ -99,7 +99,7 @@ export const DashboardTopBanner = (props: DashboardTopBannerProps) => { }, }, }, - YourDataIsProtected: { + YourDataIsProtectedContent: { headline: l10n.getString( "dashboard-top-banner-your-data-is-protected-title" ), diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx index 525583a1546..d9afcd9dc38 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx @@ -209,6 +209,8 @@ export const View = (props: Props) => { } ); + const isScanResultItemsEmpty = props.userScannedResults.length === 0; + return (
@@ -220,7 +222,11 @@ export const View = (props: Props) => {
} />
@@ -239,9 +245,7 @@ export const View = (props: Props) => {
    - {props.isUserScannedResults - ? exposureCardElems - : breachExposureCards} + {isScanResultItemsEmpty ? breachExposureCards : exposureCardElems}

diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx index f68327ef4c4..4b88cff4226 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx @@ -45,7 +45,6 @@ export default async function DashboardPage() { userBreaches={breaches} locale={locale} chartData={summary.sanitizedExposures} - isUserScannedResults={!!scanResultItems} /> ); } From 1d593d6ad9aadd91d014735301750b4e4664eef2 Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 13:33:00 -0400 Subject: [PATCH 15/27] fix breaking test --- .../user/dashboard/Dashboard.test.tsx | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.test.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.test.tsx index 4518ec13e30..c8f4c4aa186 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.test.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.test.tsx @@ -6,7 +6,10 @@ import { it, expect } from "@jest/globals"; import { render } from "@testing-library/react"; import { composeStory } from "@storybook/react"; import { axe } from "jest-axe"; -import Meta, { Dashboard } from "./Dashboard.stories"; +import Meta, { + DashboardWithScan, + DashboardWithoutScan, +} from "./Dashboard.stories"; jest.mock("next/navigation", () => ({ useRouter: jest.fn(), @@ -14,7 +17,13 @@ jest.mock("next/navigation", () => ({ })); it("passes the axe accessibility test suite", async () => { - const ComposedDashboard = composeStory(Dashboard, Meta); + const ComposedDashboard = composeStory(DashboardWithScan, Meta); + const { container } = render(); + expect(await axe(container)).toHaveNoViolations(); +}); + +it("passes the axe accessibility test suite", async () => { + const ComposedDashboard = composeStory(DashboardWithoutScan, Meta); const { container } = render(); expect(await axe(container)).toHaveNoViolations(); }); From 12155013288b5bc3123f427dbed2f64c64e52e4b Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 13:35:53 -0400 Subject: [PATCH 16/27] rm unused vars --- .../(authenticated)/user/dashboard/DashboardTopBanner.tsx | 1 - src/app/components/client/Chart.tsx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx index 4b729811e22..99ea1a1ab08 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx @@ -7,7 +7,6 @@ import { ReactElement } from "react"; import { Button } from "../../../../../components/server/Button"; import { useL10n } from "../../../../../hooks/l10n"; import { DoughnutChart as Chart } from "../../../../../components/client/Chart"; -import { DashboardSummary } from "../../../../../functions/server/dashboard"; export type DashboardTopBannerProps = { type: diff --git a/src/app/components/client/Chart.tsx b/src/app/components/client/Chart.tsx index 3ca0eb4ec0d..aec4e46fabf 100644 --- a/src/app/components/client/Chart.tsx +++ b/src/app/components/client/Chart.tsx @@ -15,7 +15,6 @@ import { ModalOverlay } from "./dialog/ModalOverlay"; import { Dialog } from "./dialog/Dialog"; import ModalImage from "../client/assets/modal-default-img.svg"; import Image from "next/image"; -import AppConstants from "../../../../src/appConstants"; export type Props = { data: Array<[string, number]>; From 919f46b61524fc7ac62ad69367d57c8e8cd04ead Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 13:36:54 -0400 Subject: [PATCH 17/27] fix l10n lint error and alphatetically organize vars --- locales/en/data-classes.ftl | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/locales/en/data-classes.ftl b/locales/en/data-classes.ftl index 68c43733ee7..9df3b0dd9d5 100644 --- a/locales/en/data-classes.ftl +++ b/locales/en/data-classes.ftl @@ -59,6 +59,7 @@ financial-investments = Financial investments financial-transactions = Financial transactions fitness-levels = Fitness levels flights-taken = Flights taken +full-names = Full name genders = Genders geographic-locations = Geographic locations government-issued-ids = Government issued IDs @@ -153,5 +154,3 @@ website-activity = Website activity work-habits = Work habits years-of-birth = Years of birth years-of-professional-experience = Years of professional experience -other-data-class = Other -full-names = Full name From 014e5674b502b747c42aa5573e8d5db35d545b93 Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 14:27:07 -0400 Subject: [PATCH 18/27] add correct data in dashboard top banner desc --- .../user/dashboard/Dashboard.stories.tsx | 64 ++++++++----------- .../user/dashboard/DashboardTopBanner.tsx | 27 +++++--- .../(authenticated)/user/dashboard/View.tsx | 9 +-- .../(authenticated)/user/dashboard/page.tsx | 2 +- 4 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx index c490aec45fd..99b852f191f 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx @@ -11,6 +11,7 @@ import { Shell } from "../../../Shell"; import { StateAbbr } from "../../../../../../utils/states"; import { getEnL10nSync } from "../../../../../functions/server/mockL10n"; import { createRandomScan } from "../../../../../../apiMocks/mockData"; +import { DashboardSummary } from "../../../../../functions/server/dashboard"; const meta: Meta = { title: "Pages/Dashboard", @@ -19,33 +20,6 @@ const meta: Meta = { export default meta; type Story = StoryObj; -const _ScanResultMockItem: ScanResult = { - id: 1, - profile_id: 1, - first_name: "John", - last_name: "Doe", - middle_name: "string", - age: `${30}`, - addresses: [ - { - city: "123", - state: "State" as StateAbbr, - street: "Street", - zip: "123456", - }, - ], - phones: [""], - emails: [""], - data_broker: "Familytree.com", - created_at: "11/09/23", - updated_at: "11/09/23", - url: "", - link: "", - relatives: [], - status: "new", - data_broker_id: 0, -}; - const BreachMockItem1: HibpLikeDbBreach = { AddedDate: new Date("2018-11-07T14:48:00.000Z"), BreachDate: "11/09/23", @@ -138,13 +112,31 @@ const breachItemArraySample: HibpLikeDbBreach[] = [ BreachMockItem4, ]; -const dashboardSummary: Record[] = [ - { "physical-addresses": 90 }, - { "family-members-names": 29 }, - { "full-names": 98 }, - { "phone-numbers": 8 }, - { "other-data-class": 80 }, -]; +const dashboardSummary: DashboardSummary = { + dataBreachTotalNum: 0, + dataBrokerTotalNum: 0, + totalExposures: 0, + allExposures: { + emailAddresses: 0, + phoneNumbers: 0, + addresses: 0, + familyMembers: 0, + fullNames: 0, + socialSecurityNumbers: 0, + ipAddresses: 0, + passwords: 0, + creditCardNumbers: 0, + pinNumbers: 0, + securityQuestions: 0, + }, + sanitizedExposures: [ + { "physical-addresses": 90 }, + { "family-members-names": 29 }, + { "full-names": 98 }, + { "phone-numbers": 8 }, + { "other-data-class": 80 }, + ], +}; export const DashboardWithScan: Story = { render: () => ( @@ -173,7 +165,7 @@ export const DashboardWithScan: Story = { }} userScannedResults={scannedResultsArraySample} locale={"en"} - chartData={dashboardSummary} + bannerData={dashboardSummary} /> ), @@ -206,7 +198,7 @@ export const DashboardWithoutScan: Story = { }} userScannedResults={[]} locale={"en"} - chartData={dashboardSummary} + bannerData={dashboardSummary} /> ), diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx index 99ea1a1ab08..e3784d5c1e0 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx @@ -7,6 +7,10 @@ import { ReactElement } from "react"; import { Button } from "../../../../../components/server/Button"; import { useL10n } from "../../../../../hooks/l10n"; import { DoughnutChart as Chart } from "../../../../../components/client/Chart"; +import { + DashboardSummary, + dashboardSummary, +} from "../../../../../functions/server/dashboard"; export type DashboardTopBannerProps = { type: @@ -15,17 +19,18 @@ export type DashboardTopBannerProps = { | "NoExposuresFoundContent" | "ResumeBreachResolutionContent" | "YourDataIsProtectedContent"; - chart: ReactElement; - chartData: Record[]; + bannerData: DashboardSummary; }; export const DashboardTopBanner = (props: DashboardTopBannerProps) => { const l10n = useL10n(); - const chartData: [string, number][] = props.chartData.map((obj) => { - const [key, value] = Object.entries(obj)[0]; - return [l10n.getString(key), value]; - }); + const chartData: [string, number][] = props.bannerData.sanitizedExposures.map( + (obj) => { + const [key, value] = Object.entries(obj)[0]; + return [l10n.getString(key), value]; + } + ); const contentData = { LetsFixDataContent: { @@ -33,15 +38,15 @@ export const DashboardTopBanner = (props: DashboardTopBannerProps) => { description: l10n.getString( "dashboard-top-banner-protect-your-data-description", { - // TODO: Replace all mocked exposure data - data_breach_total_num: 95, - data_broker_total_num: 15, + data_breach_total_num: props.bannerData.dataBreachTotalNum, + data_broker_total_num: props.bannerData.dataBrokerTotalNum, } ), cta: { content: l10n.getString("dashboard-top-banner-protect-your-data-cta"), onClick: () => { - window.location.href = "/redesign/user/dashboard/fix/data-broker-profiles/view-data-brokers"; + window.location.href = + "/redesign/user/dashboard/fix/data-broker-profiles/view-data-brokers"; }, }, }, @@ -86,6 +91,7 @@ export const DashboardTopBanner = (props: DashboardTopBannerProps) => { description: l10n.getString( "dashboard-top-banner-lets-keep-protecting-description", { + //TODO: Add remaining total exposures remaining_exposures_total_num: 40, } ), @@ -105,6 +111,7 @@ export const DashboardTopBanner = (props: DashboardTopBannerProps) => { description: l10n.getString( "dashboard-top-banner-your-data-is-protected-description", { + //TODO: Add original count of exposures starting_exposure_total_num: 100, } ), diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx index d9afcd9dc38..0c255e4d5fe 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx @@ -22,13 +22,13 @@ import { useState } from "react"; import { ScanResult } from "../../../../../functions/server/onerep"; import { HibpLikeDbBreach } from "../../../../../../utils/hibp"; import { BundledVerifiedEmails } from "../../../../../../utils/breaches"; +import { DashboardSummary } from "../../../../../functions/server/dashboard"; export type Props = { user: Session["user"]; userBreaches: UserBreaches; - isUserScannedResults: boolean; userScannedResults: ScanResult[]; - chartData: Record[]; + bannerData: DashboardSummary; locale: string; }; @@ -209,6 +209,8 @@ export const View = (props: Props) => { } ); + console.log([props.bannerData]); + const isScanResultItemsEmpty = props.userScannedResults.length === 0; return ( @@ -221,13 +223,12 @@ export const View = (props: Props) => {
} />

diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx index 4b88cff4226..d4dd5c8f000 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/page.tsx @@ -44,7 +44,7 @@ export default async function DashboardPage() { userScannedResults={scanResultItems} userBreaches={breaches} locale={locale} - chartData={summary.sanitizedExposures} + bannerData={summary} /> ); } From f8afc6803ac30252acb76032816387ac73ffa5b8 Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 14:32:50 -0400 Subject: [PATCH 19/27] rm console log --- .../redesign/(authenticated)/user/dashboard/View.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx index 0c255e4d5fe..bb710114461 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx @@ -209,8 +209,6 @@ export const View = (props: Props) => { } ); - console.log([props.bannerData]); - const isScanResultItemsEmpty = props.userScannedResults.length === 0; return ( From 04dd9561a588742d775e5e6c7493748822ab0857 Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 14:33:40 -0400 Subject: [PATCH 20/27] lint --- .../(authenticated)/user/dashboard/Dashboard.stories.tsx | 1 - .../(authenticated)/user/dashboard/DashboardTopBanner.tsx | 6 +----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx index 99b852f191f..06143fe7c5c 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx @@ -8,7 +8,6 @@ import { View as DashboardEl } from "./View"; import { HibpLikeDbBreach } from "../../../../../../utils/hibp"; import { ScanResult } from "../../../../../functions/server/onerep"; import { Shell } from "../../../Shell"; -import { StateAbbr } from "../../../../../../utils/states"; import { getEnL10nSync } from "../../../../../functions/server/mockL10n"; import { createRandomScan } from "../../../../../../apiMocks/mockData"; import { DashboardSummary } from "../../../../../functions/server/dashboard"; diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx index e3784d5c1e0..cc2dffecf67 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/DashboardTopBanner.tsx @@ -3,14 +3,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ import styles from "./DashboardTopBanner.module.scss"; -import { ReactElement } from "react"; import { Button } from "../../../../../components/server/Button"; import { useL10n } from "../../../../../hooks/l10n"; import { DoughnutChart as Chart } from "../../../../../components/client/Chart"; -import { - DashboardSummary, - dashboardSummary, -} from "../../../../../functions/server/dashboard"; +import { DashboardSummary } from "../../../../../functions/server/dashboard"; export type DashboardTopBannerProps = { type: From f7aac09e8898e507c41aa946ba569ee71b91945c Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 14:34:54 -0400 Subject: [PATCH 21/27] lint --- .../redesign/(authenticated)/user/dashboard/View.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx index bb710114461..d6c1e2b9651 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/View.tsx @@ -208,7 +208,6 @@ export const View = (props: Props) => { ); } ); - const isScanResultItemsEmpty = props.userScannedResults.length === 0; return ( From d7844bd0b8557cc7f033ad554fe7680c037821f2 Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 14:40:06 -0400 Subject: [PATCH 22/27] pluralize string --- locales-pending/premium.ftl | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/locales-pending/premium.ftl b/locales-pending/premium.ftl index 40ec25758ac..ed99f2842c4 100644 --- a/locales-pending/premium.ftl +++ b/locales-pending/premium.ftl @@ -43,7 +43,11 @@ exposure-chart-caption = This chart shows how many times your info is actively e modal-active-number-of-exposures-title = About your number of active exposures # Variables: # $limit (number) - Number of email addresses included in the plan -modal-active-number-of-exposures-part-one = This chart includes the total number of times we found each type of data exposed across all data broker profiles and all data breaches for up to { $limit } email addresses that you are currently monitoring. +modal-active-number-of-exposures-part-one = + { $limit -> + [one] This chart includes the total number of times we found each type of data exposed across all data broker profiles and all data breaches for the { $limit } email address that you are currently monitoring. + *[other] This chart includes the total number of times we found each type of data exposed across all data broker profiles and all data breaches for up to { $limit } email addresses that you are currently monitoring. + } modal-active-number-of-exposures-part-two = For example, if you have 10 exposures of your phone number, that might mean one phone number is exposed across 10 different sites, or it could mean 2 different phone numbers were exposed across 5 different sites. modal-active-number-of-exposures-part-three = This chart does not include any exposures that are in-progress of being auto-removed. Once your exposures are fixed, they will be added to your total number of fixed exposures on the Fixed page. From 58d16f5a8d7057c385ab04acb7b8662dda5d6721 Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 14:44:49 -0400 Subject: [PATCH 23/27] fix stories --- .../user/dashboard/Dashboard.stories.tsx | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx index 06143fe7c5c..10455d751ad 100644 --- a/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx +++ b/src/app/(proper_react)/redesign/(authenticated)/user/dashboard/Dashboard.stories.tsx @@ -111,10 +111,34 @@ const breachItemArraySample: HibpLikeDbBreach[] = [ BreachMockItem4, ]; -const dashboardSummary: DashboardSummary = { +const dashboardSummaryNoScan: DashboardSummary = { dataBreachTotalNum: 0, - dataBrokerTotalNum: 0, - totalExposures: 0, + dataBrokerTotalNum: 51, + totalExposures: 51, + allExposures: { + emailAddresses: 0, + phoneNumbers: 0, + addresses: 0, + familyMembers: 0, + fullNames: 0, + socialSecurityNumbers: 0, + ipAddresses: 0, + passwords: 0, + creditCardNumbers: 0, + pinNumbers: 0, + securityQuestions: 0, + }, + sanitizedExposures: [ + { "email-addresses": 30 }, + { "phone-numbers": 19 }, + { "social-security-numbers": 2 }, + ], +}; + +const dashboardSummaryWithScan: DashboardSummary = { + dataBreachTotalNum: 88, + dataBrokerTotalNum: 217, + totalExposures: 305, allExposures: { emailAddresses: 0, phoneNumbers: 0, @@ -164,7 +188,7 @@ export const DashboardWithScan: Story = { }} userScannedResults={scannedResultsArraySample} locale={"en"} - bannerData={dashboardSummary} + bannerData={dashboardSummaryWithScan} /> ), @@ -197,7 +221,7 @@ export const DashboardWithoutScan: Story = { }} userScannedResults={[]} locale={"en"} - bannerData={dashboardSummary} + bannerData={dashboardSummaryNoScan} /> ), From d614a3186ed76ddc46bfb076254007abf28cab4a Mon Sep 17 00:00:00 2001 From: mansaj Date: Tue, 25 Jul 2023 13:42:28 -0700 Subject: [PATCH 24/27] add total nums (#3252) --- src/app/functions/server/dashboard.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/app/functions/server/dashboard.ts b/src/app/functions/server/dashboard.ts index 14c47b8b64d..76455bb686a 100644 --- a/src/app/functions/server/dashboard.ts +++ b/src/app/functions/server/dashboard.ts @@ -54,7 +54,7 @@ export function dashboardSummary( ): DashboardSummary { const summary: DashboardSummary = { dataBreachTotalNum: 0, - dataBrokerTotalNum: 0, + dataBrokerTotalNum: scannedResults.length, totalExposures: 0, allExposures: { emailAddresses: 0, @@ -99,12 +99,15 @@ export function dashboardSummary( }); } + const uniqueBreaches = new Set(); + // calculate breaches summary from breaches data // TODO: Modify after MNTOR-1947: Refactor user breaches object if (breachesData.verifiedEmails) { for (const emailBreaches of breachesData.verifiedEmails) { const breaches = emailBreaches.breaches; breaches.forEach((b) => { + uniqueBreaches.add(b.Name); const dataClasses = b.DataClasses ?? []; // count password @@ -146,6 +149,9 @@ export function dashboardSummary( } } + // count unique breaches + summary.dataBreachTotalNum = uniqueBreaches.size; + return sanitizeExposures(summary); } From c7c4355df8f53e21e1be05f418214743ce3a8a21 Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 16:54:02 -0400 Subject: [PATCH 25/27] =?UTF-8?q?add=20special=20character=20for=20=C3=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales-pending/premium.ftl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales-pending/premium.ftl b/locales-pending/premium.ftl index ed99f2842c4..62198bb7094 100644 --- a/locales-pending/premium.ftl +++ b/locales-pending/premium.ftl @@ -37,7 +37,7 @@ exposure-chart-legend-heading-type = Exposure exposure-chart-legend-heading-nr = Number # Variables: # $nr (number) - Number of a particular type of exposure found for the user -exposure-chart-legend-value-nr = { $nr }x +exposure-chart-legend-value-nr = { $nr }× exposure-chart-caption = This chart shows how many times your info is actively exposed. modal-active-number-of-exposures-title = About your number of active exposures From 2dce8fcb27ddfcd88fa2e9f4714b22f2c0e96ab3 Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Tue, 25 Jul 2023 17:06:59 -0400 Subject: [PATCH 26/27] arrange colours of chart by odd first, then even --- src/app/components/client/Chart.module.scss | 54 ++++++++++----------- src/app/components/client/Chart.tsx | 1 - 2 files changed, 27 insertions(+), 28 deletions(-) diff --git a/src/app/components/client/Chart.module.scss b/src/app/components/client/Chart.module.scss index 7bdbb9749bd..f91196c53b0 100644 --- a/src/app/components/client/Chart.module.scss +++ b/src/app/components/client/Chart.module.scss @@ -129,111 +129,111 @@ } } .slice:nth-child(3) { - stroke: $color-purple-80; + stroke: $color-purple-70; } .legend tbody tr:nth-child(2) { .chartAndLegendWrapper:has(.slice:nth-child(3):hover) & { - color: $color-purple-80; + color: $color-purple-70; } rect { - fill: $color-purple-80; + fill: $color-purple-70; } } .slice:nth-child(4) { - stroke: $color-purple-70; + stroke: $color-purple-50; } .legend tbody tr:nth-child(3) { .chartAndLegendWrapper:has(.slice:nth-child(4):hover) & { - color: $color-purple-70; + color: $color-purple-50; } rect { - fill: $color-purple-70; + fill: $color-purple-50; } } .slice:nth-child(5) { - stroke: $color-purple-60; + stroke: $color-purple-30; } .legend tbody tr:nth-child(4) { .chartAndLegendWrapper:has(.slice:nth-child(5):hover) & { - color: $color-purple-60; + color: $color-purple-30; } rect { - fill: $color-purple-60; + fill: $color-purple-30; } } .slice:nth-child(6) { - stroke: $color-purple-50; + stroke: $color-purple-10; } .legend tbody tr:nth-child(5) { .chartAndLegendWrapper:has(.slice:nth-child(6):hover) & { - color: $color-purple-50; + color: $color-purple-10; } rect { - fill: $color-purple-50; + fill: $color-purple-10; } } .slice:nth-child(7) { - stroke: $color-purple-40; + stroke: $color-purple-80; } .legend tbody tr:nth-child(6) { .chartAndLegendWrapper:has(.slice:nth-child(7):hover) & { - color: $color-purple-40; + color: $color-purple-80; } rect { - fill: $color-purple-40; + fill: $color-purple-80; } } .slice:nth-child(8) { - stroke: $color-purple-30; + stroke: $color-purple-60; } .legend tbody tr:nth-child(7) { .chartAndLegendWrapper:has(.slice:nth-child(8):hover) & { - color: $color-purple-30; + color: $color-purple-60; } rect { - fill: $color-purple-30; + fill: $color-purple-60; } } .slice:nth-child(9) { - stroke: $color-purple-20; + stroke: $color-purple-40; } .legend tbody tr:nth-child(8) { .chartAndLegendWrapper:has(.slice:nth-child(9):hover) & { - color: $color-purple-20; + color: $color-purple-40; } rect { - fill: $color-purple-20; + fill: $color-purple-40; } } .slice:nth-child(10) { - stroke: $color-purple-10; + stroke: $color-purple-20; } .legend tbody tr:nth-child(9) { .chartAndLegendWrapper:has(.slice:nth-child(10):hover) & { - color: $color-purple-10; + color: $color-purple-20; } rect { - fill: $color-purple-10; + fill: $color-purple-20; } } .slice:nth-child(11) { - stroke: $color-purple-05; + stroke: $color-violet-90; } .legend tbody tr:nth-child(10) { .chartAndLegendWrapper:has(.slice:nth-child(11):hover) & { - color: $color-purple-05; + color: $color-violet-90; } rect { - fill: $color-purple-05; + fill: $color-violet-90; } } /* stylelint-enable no-descending-specificity */ diff --git a/src/app/components/client/Chart.tsx b/src/app/components/client/Chart.tsx index aec4e46fabf..73cd9c884d5 100644 --- a/src/app/components/client/Chart.tsx +++ b/src/app/components/client/Chart.tsx @@ -73,7 +73,6 @@ export const DoughnutChart = (props: Props) => { /> ); }); - const modalContent = (

From ebfcd2c1f8aa9758c04da9e0e002b5630cdc9eaa Mon Sep 17 00:00:00 2001 From: Kaitlyn Date: Wed, 26 Jul 2023 12:07:02 -0400 Subject: [PATCH 27/27] remove unused var --- src/app/components/client/stories/Chart.stories.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/components/client/stories/Chart.stories.ts b/src/app/components/client/stories/Chart.stories.ts index b7dd0662cba..eb921c71884 100644 --- a/src/app/components/client/stories/Chart.stories.ts +++ b/src/app/components/client/stories/Chart.stories.ts @@ -26,6 +26,5 @@ const data: Array<[string, number]> = [ export const FixedExposures: Story = { args: { data: data, - totalExposures: 309, }, };