mirror of
https://github.com/bitwarden/browser.git
synced 2024-11-21 11:35:34 +01:00
[SM-108] Refactor Reports - Add storybook stories (#3204)
This commit is contained in:
parent
4398467368
commit
6b1652e34c
@ -6,7 +6,7 @@
|
||||
"no-restricted-imports": [
|
||||
"error",
|
||||
{
|
||||
"patterns": ["**/core/*"]
|
||||
"patterns": ["**/core/*", "**/reports/*"]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ import { EventResponse } from "@bitwarden/common/models/response/eventResponse";
|
||||
import { ListResponse } from "@bitwarden/common/models/response/listResponse";
|
||||
import { EventView } from "@bitwarden/common/models/view/eventView";
|
||||
|
||||
import { EventService } from "src/app/core";
|
||||
import { EventService } from "../core";
|
||||
|
||||
@Directive()
|
||||
export abstract class BaseEventsComponent {
|
||||
|
@ -79,15 +79,6 @@ import { AttachmentsComponent as OrgAttachmentsComponent } from "../organization
|
||||
import { CiphersComponent as OrgCiphersComponent } from "../organizations/vault/ciphers.component";
|
||||
import { CollectionsComponent as OrgCollectionsComponent } from "../organizations/vault/collections.component";
|
||||
import { ProvidersComponent } from "../providers/providers.component";
|
||||
import { BreachReportComponent } from "../reports/breach-report.component";
|
||||
import { ExposedPasswordsReportComponent } from "../reports/exposed-passwords-report.component";
|
||||
import { InactiveTwoFactorReportComponent } from "../reports/inactive-two-factor-report.component";
|
||||
import { ReportCardComponent } from "../reports/report-card.component";
|
||||
import { ReportListComponent } from "../reports/report-list.component";
|
||||
import { ReportsComponent } from "../reports/reports.component";
|
||||
import { ReusedPasswordsReportComponent } from "../reports/reused-passwords-report.component";
|
||||
import { UnsecuredWebsitesReportComponent } from "../reports/unsecured-websites-report.component";
|
||||
import { WeakPasswordsReportComponent } from "../reports/weak-passwords-report.component";
|
||||
import { AccessComponent } from "../send/access.component";
|
||||
import { AddEditComponent as SendAddEditComponent } from "../send/add-edit.component";
|
||||
import { EffluxDatesComponent as SendEffluxDatesComponent } from "../send/efflux-dates.component";
|
||||
@ -190,7 +181,6 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
|
||||
AttachmentsComponent,
|
||||
BillingSyncApiKeyComponent,
|
||||
BillingSyncKeyComponent,
|
||||
BreachReportComponent,
|
||||
BulkActionsComponent,
|
||||
BulkDeleteComponent,
|
||||
BulkMoveComponent,
|
||||
@ -216,13 +206,11 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
|
||||
EmergencyAccessTakeoverComponent,
|
||||
EmergencyAccessViewComponent,
|
||||
EmergencyAddEditComponent,
|
||||
ExposedPasswordsReportComponent,
|
||||
FamiliesForEnterpriseSetupComponent,
|
||||
FolderAddEditComponent,
|
||||
FooterComponent,
|
||||
FrontendLayoutComponent,
|
||||
HintComponent,
|
||||
InactiveTwoFactorReportComponent,
|
||||
LockComponent,
|
||||
LoginComponent,
|
||||
MasterPasswordPolicyComponent,
|
||||
@ -281,12 +269,8 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
|
||||
RecoverTwoFactorComponent,
|
||||
RegisterComponent,
|
||||
RemovePasswordComponent,
|
||||
ReportCardComponent,
|
||||
ReportListComponent,
|
||||
ReportsComponent,
|
||||
RequireSsoPolicyComponent,
|
||||
ResetPasswordPolicyComponent,
|
||||
ReusedPasswordsReportComponent,
|
||||
SecurityComponent,
|
||||
SecurityKeysComponent,
|
||||
SendAddEditComponent,
|
||||
@ -313,7 +297,6 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
|
||||
TwoFactorVerifyComponent,
|
||||
TwoFactorWebAuthnComponent,
|
||||
TwoFactorYubiKeyComponent,
|
||||
UnsecuredWebsitesReportComponent,
|
||||
UpdateKeyComponent,
|
||||
UpdateLicenseComponent,
|
||||
UpdatePasswordComponent,
|
||||
@ -326,7 +309,6 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
|
||||
VerifyEmailComponent,
|
||||
VerifyEmailTokenComponent,
|
||||
VerifyRecoverDeleteComponent,
|
||||
WeakPasswordsReportComponent,
|
||||
],
|
||||
exports: [
|
||||
PremiumBadgeComponent,
|
||||
@ -343,7 +325,6 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
|
||||
AdjustSubscription,
|
||||
ApiKeyComponent,
|
||||
AttachmentsComponent,
|
||||
BreachReportComponent,
|
||||
BulkActionsComponent,
|
||||
BulkDeleteComponent,
|
||||
BulkMoveComponent,
|
||||
@ -369,13 +350,11 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
|
||||
EmergencyAccessTakeoverComponent,
|
||||
EmergencyAccessViewComponent,
|
||||
EmergencyAddEditComponent,
|
||||
ExposedPasswordsReportComponent,
|
||||
FamiliesForEnterpriseSetupComponent,
|
||||
FolderAddEditComponent,
|
||||
FooterComponent,
|
||||
FrontendLayoutComponent,
|
||||
HintComponent,
|
||||
InactiveTwoFactorReportComponent,
|
||||
LockComponent,
|
||||
LoginComponent,
|
||||
MasterPasswordPolicyComponent,
|
||||
@ -433,12 +412,8 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
|
||||
RecoverTwoFactorComponent,
|
||||
RegisterComponent,
|
||||
RemovePasswordComponent,
|
||||
ReportCardComponent,
|
||||
ReportListComponent,
|
||||
ReportsComponent,
|
||||
RequireSsoPolicyComponent,
|
||||
ResetPasswordPolicyComponent,
|
||||
ReusedPasswordsReportComponent,
|
||||
SecurityComponent,
|
||||
SecurityKeysComponent,
|
||||
SendAddEditComponent,
|
||||
@ -465,7 +440,6 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
|
||||
TwoFactorVerifyComponent,
|
||||
TwoFactorWebAuthnComponent,
|
||||
TwoFactorYubiKeyComponent,
|
||||
UnsecuredWebsitesReportComponent,
|
||||
UpdateKeyComponent,
|
||||
UpdateLicenseComponent,
|
||||
UpdatePasswordComponent,
|
||||
@ -478,7 +452,6 @@ import { OrganizationBadgeModule } from "./vault/modules/organization-badge/orga
|
||||
VerifyEmailComponent,
|
||||
VerifyEmailTokenComponent,
|
||||
VerifyRecoverDeleteComponent,
|
||||
WeakPasswordsReportComponent,
|
||||
],
|
||||
})
|
||||
export class LooseComponentsModule {}
|
||||
|
@ -64,6 +64,7 @@ import {
|
||||
FormFieldModule,
|
||||
SubmitButtonModule,
|
||||
MenuModule,
|
||||
IconModule,
|
||||
} from "@bitwarden/components";
|
||||
|
||||
import { PaymentComponent } from "../settings/payment.component";
|
||||
@ -139,6 +140,7 @@ registerLocaleData(localeZhTw, "zh-TW");
|
||||
MenuModule,
|
||||
FormFieldModule,
|
||||
SubmitButtonModule,
|
||||
IconModule,
|
||||
],
|
||||
exports: [
|
||||
CommonModule,
|
||||
@ -159,6 +161,7 @@ registerLocaleData(localeZhTw, "zh-TW");
|
||||
SubmitButtonModule,
|
||||
PaymentComponent,
|
||||
TaxInfoComponent,
|
||||
IconModule,
|
||||
],
|
||||
providers: [DatePipe],
|
||||
bootstrap: [],
|
||||
|
@ -13,7 +13,7 @@ import { PolicyService } from "@bitwarden/common/abstractions/policy.service";
|
||||
import { SyncService } from "@bitwarden/common/abstractions/sync.service";
|
||||
import { ProductType } from "@bitwarden/common/enums/productType";
|
||||
|
||||
import { OrganizationPlansComponent } from "src/app/settings/organization-plans.component";
|
||||
import { OrganizationPlansComponent } from "../../settings/organization-plans.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-billing",
|
||||
|
@ -17,7 +17,7 @@ import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
import { OrganizationConnectionResponse } from "@bitwarden/common/models/response/organizationConnectionResponse";
|
||||
import { OrganizationSubscriptionResponse } from "@bitwarden/common/models/response/organizationSubscriptionResponse";
|
||||
|
||||
import { BillingSyncKeyComponent } from "src/app/settings/billing-sync-key.component";
|
||||
import { BillingSyncKeyComponent } from "../../settings/billing-sync-key.component";
|
||||
|
||||
import { BillingSyncApiKeyComponent } from "./billing-sync-api-key.component";
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Component } from "@angular/core";
|
||||
import { Params } from "@angular/router";
|
||||
|
||||
import { BaseAcceptComponent } from "src/app/common/base.accept.component";
|
||||
import { BaseAcceptComponent } from "../../common/base.accept.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-accept-family-sponsorship",
|
||||
|
@ -15,8 +15,8 @@ import { ProductType } from "@bitwarden/common/enums/productType";
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
import { OrganizationSponsorshipRedeemRequest } from "@bitwarden/common/models/request/organization/organizationSponsorshipRedeemRequest";
|
||||
|
||||
import { DeleteOrganizationComponent } from "src/app/organizations/settings/delete-organization.component";
|
||||
import { OrganizationPlansComponent } from "src/app/settings/organization-plans.component";
|
||||
import { OrganizationPlansComponent } from "../../settings/organization-plans.component";
|
||||
import { DeleteOrganizationComponent } from "../settings/delete-organization.component";
|
||||
|
||||
@Component({
|
||||
selector: "families-for-enterprise-setup",
|
||||
|
@ -11,11 +11,12 @@ import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { Cipher } from "@bitwarden/common/models/domain/cipher";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipherView";
|
||||
|
||||
import { ExposedPasswordsReportComponent as BaseExposedPasswordsReportComponent } from "../../reports/exposed-passwords-report.component";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { ExposedPasswordsReportComponent as BaseExposedPasswordsReportComponent } from "../../reports/pages/exposed-passwords-report.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-org-exposed-passwords-report",
|
||||
templateUrl: "../../reports/exposed-passwords-report.component.html",
|
||||
templateUrl: "../../reports/pages/exposed-passwords-report.component.html",
|
||||
})
|
||||
export class ExposedPasswordsReportComponent extends BaseExposedPasswordsReportComponent {
|
||||
manageableCiphers: Cipher[];
|
||||
|
@ -10,11 +10,12 @@ import { PasswordRepromptService } from "@bitwarden/common/abstractions/password
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipherView";
|
||||
|
||||
import { InactiveTwoFactorReportComponent as BaseInactiveTwoFactorReportComponent } from "../../reports/inactive-two-factor-report.component";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { InactiveTwoFactorReportComponent as BaseInactiveTwoFactorReportComponent } from "../../reports/pages/inactive-two-factor-report.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-inactive-two-factor-report",
|
||||
templateUrl: "../../reports/inactive-two-factor-report.component.html",
|
||||
templateUrl: "../../reports/pages/inactive-two-factor-report.component.html",
|
||||
})
|
||||
export class InactiveTwoFactorReportComponent extends BaseInactiveTwoFactorReportComponent {
|
||||
constructor(
|
||||
|
@ -10,11 +10,12 @@ import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { Cipher } from "@bitwarden/common/models/domain/cipher";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipherView";
|
||||
|
||||
import { ReusedPasswordsReportComponent as BaseReusedPasswordsReportComponent } from "../../reports/reused-passwords-report.component";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { ReusedPasswordsReportComponent as BaseReusedPasswordsReportComponent } from "../../reports/pages/reused-passwords-report.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-reused-passwords-report",
|
||||
templateUrl: "../../reports/reused-passwords-report.component.html",
|
||||
templateUrl: "../../reports/pages/reused-passwords-report.component.html",
|
||||
})
|
||||
export class ReusedPasswordsReportComponent extends BaseReusedPasswordsReportComponent {
|
||||
manageableCiphers: Cipher[];
|
||||
|
@ -9,11 +9,12 @@ import { PasswordRepromptService } from "@bitwarden/common/abstractions/password
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipherView";
|
||||
|
||||
import { UnsecuredWebsitesReportComponent as BaseUnsecuredWebsitesReportComponent } from "../../reports/unsecured-websites-report.component";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { UnsecuredWebsitesReportComponent as BaseUnsecuredWebsitesReportComponent } from "../../reports/pages/unsecured-websites-report.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-unsecured-websites-report",
|
||||
templateUrl: "../../reports/unsecured-websites-report.component.html",
|
||||
templateUrl: "../../reports/pages/unsecured-websites-report.component.html",
|
||||
})
|
||||
export class UnsecuredWebsitesReportComponent extends BaseUnsecuredWebsitesReportComponent {
|
||||
constructor(
|
||||
|
@ -11,11 +11,12 @@ import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
import { Cipher } from "@bitwarden/common/models/domain/cipher";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipherView";
|
||||
|
||||
import { WeakPasswordsReportComponent as BaseWeakPasswordsReportComponent } from "../../reports/weak-passwords-report.component";
|
||||
// eslint-disable-next-line no-restricted-imports
|
||||
import { WeakPasswordsReportComponent as BaseWeakPasswordsReportComponent } from "../../reports/pages/weak-passwords-report.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-weak-passwords-report",
|
||||
templateUrl: "../../reports/weak-passwords-report.component.html",
|
||||
templateUrl: "../../reports/pages/weak-passwords-report.component.html",
|
||||
})
|
||||
export class WeakPasswordsReportComponent extends BaseWeakPasswordsReportComponent {
|
||||
manageableCiphers: Cipher[];
|
||||
|
@ -31,7 +31,7 @@ import { IndividualVaultModule } from "./modules/vault/modules/individual-vault/
|
||||
import { OrganizationsRoutingModule } from "./organizations/organization-routing.module";
|
||||
import { AcceptFamilySponsorshipComponent } from "./organizations/sponsorships/accept-family-sponsorship.component";
|
||||
import { FamiliesForEnterpriseSetupComponent } from "./organizations/sponsorships/families-for-enterprise-setup.component";
|
||||
import { ReportsRoutingModule } from "./reports/reports-routing.module";
|
||||
import { ReportsModule } from "./reports";
|
||||
import { AccessComponent } from "./send/access.component";
|
||||
import { SendComponent } from "./send/send.component";
|
||||
import { AccountComponent } from "./settings/account.component";
|
||||
@ -238,7 +238,7 @@ const routes: Routes = [
|
||||
},
|
||||
{
|
||||
path: "reports",
|
||||
loadChildren: () => ReportsRoutingModule,
|
||||
loadChildren: () => ReportsModule,
|
||||
},
|
||||
{ path: "setup/families-for-enterprise", component: FamiliesForEnterpriseSetupComponent },
|
||||
],
|
||||
|
3
apps/web/src/app/reports/index.ts
Normal file
3
apps/web/src/app/reports/index.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export * from "./reports.module";
|
||||
export * from "./models/report-entry";
|
||||
export * from "./models/report-variant";
|
9
apps/web/src/app/reports/models/report-entry.ts
Normal file
9
apps/web/src/app/reports/models/report-entry.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { ReportVariant } from "./report-variant";
|
||||
|
||||
export type ReportEntry = {
|
||||
title: string;
|
||||
description: string;
|
||||
route: string;
|
||||
icon: string;
|
||||
variant: ReportVariant;
|
||||
};
|
5
apps/web/src/app/reports/models/report-variant.ts
Normal file
5
apps/web/src/app/reports/models/report-variant.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export enum ReportVariant {
|
||||
Enabled = "Enabled",
|
||||
RequiresPremium = "RequiresPremium",
|
||||
RequiresUpgrade = "RequiresUpgrade",
|
||||
}
|
@ -8,8 +8,8 @@ import { CipherRepromptType } from "@bitwarden/common/enums/cipherRepromptType";
|
||||
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||
import { CipherView } from "@bitwarden/common/models/view/cipherView";
|
||||
|
||||
import { AddEditComponent as OrgAddEditComponent } from "../organizations/vault/add-edit.component";
|
||||
import { AddEditComponent } from "../vault/add-edit.component";
|
||||
import { AddEditComponent as OrgAddEditComponent } from "../../organizations/vault/add-edit.component";
|
||||
import { AddEditComponent } from "../../vault/add-edit.component";
|
||||
|
||||
@Directive()
|
||||
export class CipherReportComponent {
|
||||
@ -79,13 +79,6 @@ export class CipherReportComponent {
|
||||
this.messagingService.send("upgradeOrganization", { organizationId: this.organization.id });
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
const accessPremium = await this.stateService.getCanAccessPremium();
|
||||
if (this.requiresPaid && !accessPremium) {
|
||||
this.messagingService.send("premiumRequired");
|
||||
this.loading = false;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<div class="page-header">
|
||||
<h1>{{ "reports" | i18n }}</h1>
|
||||
</div>
|
||||
|
||||
<p>{{ "reportsDesc" | i18n }}</p>
|
||||
|
||||
<app-report-list [reports]="reports"></app-report-list>
|
52
apps/web/src/app/reports/pages/reports-home.component.ts
Normal file
52
apps/web/src/app/reports/pages/reports-home.component.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import { Component, OnInit } from "@angular/core";
|
||||
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
|
||||
import { ReportEntry } from "../models/report-entry";
|
||||
import { ReportVariant } from "../models/report-variant";
|
||||
import { reports, ReportType } from "../reports";
|
||||
|
||||
@Component({
|
||||
selector: "app-reports-home",
|
||||
templateUrl: "reports-home.component.html",
|
||||
})
|
||||
export class ReportsHomeComponent implements OnInit {
|
||||
reports: ReportEntry[];
|
||||
|
||||
constructor(private stateService: StateService) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
const userHasPremium = await this.stateService.getCanAccessPremium();
|
||||
|
||||
const reportRequiresPremium = userHasPremium
|
||||
? ReportVariant.Enabled
|
||||
: ReportVariant.RequiresPremium;
|
||||
|
||||
this.reports = [
|
||||
{
|
||||
...reports[ReportType.ExposedPasswords],
|
||||
variant: reportRequiresPremium,
|
||||
},
|
||||
{
|
||||
...reports[ReportType.ReusedPasswords],
|
||||
variant: reportRequiresPremium,
|
||||
},
|
||||
{
|
||||
...reports[ReportType.WeakPasswords],
|
||||
variant: reportRequiresPremium,
|
||||
},
|
||||
{
|
||||
...reports[ReportType.UnsecuredWebsites],
|
||||
variant: reportRequiresPremium,
|
||||
},
|
||||
{
|
||||
...reports[ReportType.Inactive2fa],
|
||||
variant: reportRequiresPremium,
|
||||
},
|
||||
{
|
||||
...reports[ReportType.DataBreach],
|
||||
variant: ReportVariant.Enabled,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
@ -1,25 +1,26 @@
|
||||
<a
|
||||
class="tw-block tw-h-full tw-w-72 tw-overflow-hidden tw-rounded tw-border tw-border-solid tw-border-secondary-300 !tw-text-main tw-transition-all hover:tw-scale-105 hover:tw-no-underline focus:tw-outline-none focus:tw-ring focus:tw-ring-primary-700 focus:tw-ring-offset-2"
|
||||
[routerLink]="route"
|
||||
(click)="click()"
|
||||
>
|
||||
<div class="tw-relative">
|
||||
<div
|
||||
class="tw-flex tw-h-28 tw-bg-background-alt2 tw-text-center tw-text-primary-300"
|
||||
[ngClass]="{ 'tw-grayscale': premium }"
|
||||
[ngClass]="{ 'tw-grayscale': disabled }"
|
||||
>
|
||||
<div class="tw-m-auto" [innerHtml]="icon"></div>
|
||||
<div class="tw-m-auto"><bit-icon [icon]="icon"></bit-icon></div>
|
||||
</div>
|
||||
<div class="tw-p-5" [ngClass]="{ 'tw-grayscale': report.requiresPremium }">
|
||||
<h3 class="tw-mb-4 tw-text-xl tw-font-bold">{{ report.title | i18n }}</h3>
|
||||
<p class="tw-mb-0">{{ report.description | i18n }}</p>
|
||||
<div class="tw-p-5" [ngClass]="{ 'tw-grayscale': disabled }">
|
||||
<h3 class="tw-mb-4 tw-text-xl tw-font-bold">{{ title }}</h3>
|
||||
<p class="tw-mb-0">{{ description }}</p>
|
||||
</div>
|
||||
<span
|
||||
bitBadge
|
||||
badgeType="success"
|
||||
class="tw-absolute tw-left-2 tw-top-2 tw-leading-none"
|
||||
*ngIf="premium"
|
||||
>{{ "premium" | i18n }}</span
|
||||
*ngIf="disabled"
|
||||
>
|
||||
<ng-container *ngIf="requiresPremium">{{ "premium" | i18n }}</ng-container>
|
||||
<ng-container *ngIf="!requiresPremium">{{ "upgrade" | i18n }}</ng-container>
|
||||
</span>
|
||||
</div>
|
||||
</a>
|
@ -0,0 +1,23 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
import { ReportVariant } from "../models/report-variant";
|
||||
|
||||
@Component({
|
||||
selector: "app-report-card",
|
||||
templateUrl: "report-card.component.html",
|
||||
})
|
||||
export class ReportCardComponent {
|
||||
@Input() title: string;
|
||||
@Input() description: string;
|
||||
@Input() route: string;
|
||||
@Input() icon: string;
|
||||
@Input() variant: ReportVariant;
|
||||
|
||||
protected get disabled() {
|
||||
return this.variant != ReportVariant.Enabled;
|
||||
}
|
||||
|
||||
protected get requiresPremium() {
|
||||
return this.variant == ReportVariant.RequiresPremium;
|
||||
}
|
||||
}
|
51
apps/web/src/app/reports/report-card/report-card.stories.ts
Normal file
51
apps/web/src/app/reports/report-card/report-card.stories.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { RouterTestingModule } from "@angular/router/testing";
|
||||
import { Meta, Story, moduleMetadata } from "@storybook/angular";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { BadgeModule, IconModule } from "@bitwarden/components";
|
||||
|
||||
import { PremiumBadgeComponent } from "../../components/premium-badge.component";
|
||||
import { PreloadedEnglishI18nModule } from "../../tests/preloaded-english-i18n.module";
|
||||
import { ReportVariant } from "../models/report-variant";
|
||||
|
||||
import { ReportCardComponent } from "./report-card.component";
|
||||
|
||||
export default {
|
||||
title: "Web/Reports/Card",
|
||||
component: ReportCardComponent,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [
|
||||
JslibModule,
|
||||
BadgeModule,
|
||||
IconModule,
|
||||
RouterTestingModule,
|
||||
PreloadedEnglishI18nModule,
|
||||
],
|
||||
declarations: [PremiumBadgeComponent],
|
||||
}),
|
||||
],
|
||||
args: {
|
||||
title: "Exposed Passwords",
|
||||
description:
|
||||
"Passwords exposed in a data breach are easy targets for attackers. Change these passwords to prevent potential break-ins.",
|
||||
icon: "reportExposedPasswords",
|
||||
variant: ReportVariant.Enabled,
|
||||
},
|
||||
} as Meta;
|
||||
|
||||
const Template: Story<ReportCardComponent> = (args: ReportCardComponent) => ({
|
||||
props: args,
|
||||
});
|
||||
|
||||
export const Enabled = Template.bind({});
|
||||
|
||||
export const RequiresPremium = Template.bind({});
|
||||
RequiresPremium.args = {
|
||||
variant: ReportVariant.RequiresPremium,
|
||||
};
|
||||
|
||||
export const RequiresUpgrade = Template.bind({});
|
||||
RequiresUpgrade.args = {
|
||||
variant: ReportVariant.RequiresUpgrade,
|
||||
};
|
@ -1,11 +0,0 @@
|
||||
<div class="page-header">
|
||||
<h1>{{ "reports" | i18n }}</h1>
|
||||
</div>
|
||||
|
||||
<p>{{ "reportsDesc" | i18n }}</p>
|
||||
|
||||
<div class="tw-inline-grid tw-grid-cols-3 tw-gap-4">
|
||||
<div *ngFor="let report of reports">
|
||||
<app-report-card [type]="report"></app-report-card>
|
||||
</div>
|
||||
</div>
|
@ -1,18 +0,0 @@
|
||||
import { Component } from "@angular/core";
|
||||
|
||||
import { ReportTypes } from "./report-card.component";
|
||||
|
||||
@Component({
|
||||
selector: "app-report-list",
|
||||
templateUrl: "report-list.component.html",
|
||||
})
|
||||
export class ReportListComponent {
|
||||
reports = [
|
||||
ReportTypes.exposedPasswords,
|
||||
ReportTypes.reusedPasswords,
|
||||
ReportTypes.weakPasswords,
|
||||
ReportTypes.unsecuredWebsites,
|
||||
ReportTypes.inactive2fa,
|
||||
ReportTypes.dataBreach,
|
||||
];
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
<div class="tw-inline-grid tw-grid-cols-3 tw-gap-4">
|
||||
<div *ngFor="let report of reports">
|
||||
<app-report-card
|
||||
[title]="report.title | i18n"
|
||||
[description]="report.description | i18n"
|
||||
[route]="report.route"
|
||||
[variant]="report.variant"
|
||||
[icon]="report.icon"
|
||||
></app-report-card>
|
||||
</div>
|
||||
</div>
|
@ -0,0 +1,11 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
import { ReportEntry } from "../models/report-entry";
|
||||
|
||||
@Component({
|
||||
selector: "app-report-list",
|
||||
templateUrl: "report-list.component.html",
|
||||
})
|
||||
export class ReportListComponent {
|
||||
@Input() reports: ReportEntry[];
|
||||
}
|
43
apps/web/src/app/reports/report-list/report-list.stories.ts
Normal file
43
apps/web/src/app/reports/report-list/report-list.stories.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import { RouterTestingModule } from "@angular/router/testing";
|
||||
import { Meta, Story, moduleMetadata } from "@storybook/angular";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { BadgeModule, IconModule } from "@bitwarden/components";
|
||||
|
||||
import { PremiumBadgeComponent } from "../../components/premium-badge.component";
|
||||
import { PreloadedEnglishI18nModule } from "../../tests/preloaded-english-i18n.module";
|
||||
import { ReportVariant } from "../models/report-variant";
|
||||
import { ReportCardComponent } from "../report-card/report-card.component";
|
||||
import { reports } from "../reports";
|
||||
|
||||
import { ReportListComponent } from "./report-list.component";
|
||||
|
||||
export default {
|
||||
title: "Web/Reports/List",
|
||||
component: ReportListComponent,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [
|
||||
JslibModule,
|
||||
BadgeModule,
|
||||
RouterTestingModule,
|
||||
PreloadedEnglishI18nModule,
|
||||
IconModule,
|
||||
],
|
||||
declarations: [PremiumBadgeComponent, ReportCardComponent],
|
||||
}),
|
||||
],
|
||||
args: {
|
||||
reports: Object.values(reports).map((report) => ({
|
||||
...report,
|
||||
variant:
|
||||
report.route == "breach-report" ? ReportVariant.Enabled : ReportVariant.RequiresPremium,
|
||||
})),
|
||||
},
|
||||
} as Meta;
|
||||
|
||||
const Template: Story<ReportListComponent> = (args: ReportListComponent) => ({
|
||||
props: args,
|
||||
});
|
||||
|
||||
export const Default = Template.bind({});
|
@ -4,10 +4,10 @@ import { Subscription } from "rxjs";
|
||||
import { filter } from "rxjs/operators";
|
||||
|
||||
@Component({
|
||||
selector: "app-reports",
|
||||
templateUrl: "reports.component.html",
|
||||
selector: "app-reports-layout",
|
||||
templateUrl: "reports-layout.component.html",
|
||||
})
|
||||
export class ReportsComponent implements OnDestroy {
|
||||
export class ReportsLayoutComponent implements OnDestroy {
|
||||
homepage = true;
|
||||
subscription: Subscription;
|
||||
|
@ -3,22 +3,24 @@ import { RouterModule, Routes } from "@angular/router";
|
||||
|
||||
import { AuthGuard } from "@bitwarden/angular/guards/auth.guard";
|
||||
|
||||
import { BreachReportComponent } from "./breach-report.component";
|
||||
import { ExposedPasswordsReportComponent } from "./exposed-passwords-report.component";
|
||||
import { InactiveTwoFactorReportComponent } from "./inactive-two-factor-report.component";
|
||||
import { ReportListComponent } from "./report-list.component";
|
||||
import { ReportsComponent } from "./reports.component";
|
||||
import { ReusedPasswordsReportComponent } from "./reused-passwords-report.component";
|
||||
import { UnsecuredWebsitesReportComponent } from "./unsecured-websites-report.component";
|
||||
import { WeakPasswordsReportComponent } from "./weak-passwords-report.component";
|
||||
import { HasPremiumGuard } from "../shared/guards/has-premium.guard";
|
||||
|
||||
import { BreachReportComponent } from "./pages/breach-report.component";
|
||||
import { ExposedPasswordsReportComponent } from "./pages/exposed-passwords-report.component";
|
||||
import { InactiveTwoFactorReportComponent } from "./pages/inactive-two-factor-report.component";
|
||||
import { ReportsHomeComponent } from "./pages/reports-home.component";
|
||||
import { ReusedPasswordsReportComponent } from "./pages/reused-passwords-report.component";
|
||||
import { UnsecuredWebsitesReportComponent } from "./pages/unsecured-websites-report.component";
|
||||
import { WeakPasswordsReportComponent } from "./pages/weak-passwords-report.component";
|
||||
import { ReportsLayoutComponent } from "./reports-layout.component";
|
||||
|
||||
const routes: Routes = [
|
||||
{
|
||||
path: "",
|
||||
component: ReportsComponent,
|
||||
component: ReportsLayoutComponent,
|
||||
canActivate: [AuthGuard],
|
||||
children: [
|
||||
{ path: "", pathMatch: "full", component: ReportListComponent, data: { homepage: true } },
|
||||
{ path: "", pathMatch: "full", component: ReportsHomeComponent, data: { homepage: true } },
|
||||
{
|
||||
path: "breach-report",
|
||||
component: BreachReportComponent,
|
||||
@ -28,26 +30,31 @@ const routes: Routes = [
|
||||
path: "reused-passwords-report",
|
||||
component: ReusedPasswordsReportComponent,
|
||||
data: { titleId: "reusedPasswordsReport" },
|
||||
canActivate: [HasPremiumGuard],
|
||||
},
|
||||
{
|
||||
path: "unsecured-websites-report",
|
||||
component: UnsecuredWebsitesReportComponent,
|
||||
data: { titleId: "unsecuredWebsitesReport" },
|
||||
canActivate: [HasPremiumGuard],
|
||||
},
|
||||
{
|
||||
path: "weak-passwords-report",
|
||||
component: WeakPasswordsReportComponent,
|
||||
data: { titleId: "weakPasswordsReport" },
|
||||
canActivate: [HasPremiumGuard],
|
||||
},
|
||||
{
|
||||
path: "exposed-passwords-report",
|
||||
component: ExposedPasswordsReportComponent,
|
||||
data: { titleId: "exposedPasswordsReport" },
|
||||
canActivate: [HasPremiumGuard],
|
||||
},
|
||||
{
|
||||
path: "inactive-two-factor-report",
|
||||
component: InactiveTwoFactorReportComponent,
|
||||
data: { titleId: "inactive2faReport" },
|
||||
canActivate: [HasPremiumGuard],
|
||||
},
|
||||
],
|
||||
},
|
||||
|
34
apps/web/src/app/reports/reports.module.ts
Normal file
34
apps/web/src/app/reports/reports.module.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { SharedModule } from "../modules/shared.module";
|
||||
|
||||
import { BreachReportComponent } from "./pages/breach-report.component";
|
||||
import { ExposedPasswordsReportComponent } from "./pages/exposed-passwords-report.component";
|
||||
import { InactiveTwoFactorReportComponent } from "./pages/inactive-two-factor-report.component";
|
||||
import { ReportsHomeComponent } from "./pages/reports-home.component";
|
||||
import { ReusedPasswordsReportComponent } from "./pages/reused-passwords-report.component";
|
||||
import { UnsecuredWebsitesReportComponent } from "./pages/unsecured-websites-report.component";
|
||||
import { WeakPasswordsReportComponent } from "./pages/weak-passwords-report.component";
|
||||
import { ReportCardComponent } from "./report-card/report-card.component";
|
||||
import { ReportListComponent } from "./report-list/report-list.component";
|
||||
import { ReportsLayoutComponent } from "./reports-layout.component";
|
||||
import { ReportsRoutingModule } from "./reports-routing.module";
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule, SharedModule, ReportsRoutingModule],
|
||||
declarations: [
|
||||
BreachReportComponent,
|
||||
ExposedPasswordsReportComponent,
|
||||
InactiveTwoFactorReportComponent,
|
||||
ReportCardComponent,
|
||||
ReportListComponent,
|
||||
ReportsLayoutComponent,
|
||||
ReportsHomeComponent,
|
||||
ReusedPasswordsReportComponent,
|
||||
UnsecuredWebsitesReportComponent,
|
||||
WeakPasswordsReportComponent,
|
||||
WeakPasswordsReportComponent,
|
||||
],
|
||||
})
|
||||
export class ReportsModule {}
|
51
apps/web/src/app/reports/reports.ts
Normal file
51
apps/web/src/app/reports/reports.ts
Normal file
@ -0,0 +1,51 @@
|
||||
import { ReportEntry } from "./models/report-entry";
|
||||
|
||||
export enum ReportType {
|
||||
ExposedPasswords = "exposedPasswords",
|
||||
ReusedPasswords = "reusedPasswords",
|
||||
WeakPasswords = "weakPasswords",
|
||||
UnsecuredWebsites = "unsecuredWebsites",
|
||||
Inactive2fa = "inactive2fa",
|
||||
DataBreach = "dataBreach",
|
||||
}
|
||||
|
||||
type ReportWithoutVariant = Omit<ReportEntry, "variant">;
|
||||
|
||||
export const reports: Record<ReportType, ReportWithoutVariant> = {
|
||||
[ReportType.ExposedPasswords]: {
|
||||
title: "exposedPasswordsReport",
|
||||
description: "exposedPasswordsReportDesc",
|
||||
route: "exposed-passwords-report",
|
||||
icon: "reportExposedPasswords",
|
||||
},
|
||||
[ReportType.ReusedPasswords]: {
|
||||
title: "reusedPasswordsReport",
|
||||
description: "reusedPasswordsReportDesc",
|
||||
route: "reused-passwords-report",
|
||||
icon: "reportReusedPasswords",
|
||||
},
|
||||
[ReportType.WeakPasswords]: {
|
||||
title: "weakPasswordsReport",
|
||||
description: "weakPasswordsReportDesc",
|
||||
route: "weak-passwords-report",
|
||||
icon: "reportWeakPasswords",
|
||||
},
|
||||
[ReportType.UnsecuredWebsites]: {
|
||||
title: "unsecuredWebsitesReport",
|
||||
description: "unsecuredWebsitesReportDesc",
|
||||
route: "unsecured-websites-report",
|
||||
icon: "reportUnsecuredWebsites",
|
||||
},
|
||||
[ReportType.Inactive2fa]: {
|
||||
title: "inactive2faReport",
|
||||
description: "inactive2faReportDesc",
|
||||
route: "inactive-two-factor-report",
|
||||
icon: "reportInactiveTwoFactor",
|
||||
},
|
||||
[ReportType.DataBreach]: {
|
||||
title: "dataBreachReport",
|
||||
description: "breachDesc",
|
||||
route: "breach-report",
|
||||
icon: "reportBreach",
|
||||
},
|
||||
};
|
@ -7,7 +7,7 @@ import { LogService } from "@bitwarden/common/abstractions/log.service";
|
||||
import { PaymentMethodType } from "@bitwarden/common/enums/paymentMethodType";
|
||||
import { ThemeType } from "@bitwarden/common/enums/themeType";
|
||||
|
||||
import ThemeVariables from "src/scss/export.module.scss";
|
||||
import ThemeVariables from "../../scss/export.module.scss";
|
||||
|
||||
const lightInputColor = ThemeVariables.lightInputColor;
|
||||
const lightInputPlaceholderColor = ThemeVariables.lightInputPlaceholderColor;
|
||||
|
31
apps/web/src/app/shared/guards/has-premium.guard.ts
Normal file
31
apps/web/src/app/shared/guards/has-premium.guard.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { Injectable } from "@angular/core";
|
||||
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from "@angular/router";
|
||||
|
||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
|
||||
@Injectable({
|
||||
providedIn: "root",
|
||||
})
|
||||
export class HasPremiumGuard implements CanActivate {
|
||||
constructor(
|
||||
private router: Router,
|
||||
private stateService: StateService,
|
||||
private messagingService: MessagingService
|
||||
) {}
|
||||
|
||||
async canActivate(route: ActivatedRouteSnapshot, routerState: RouterStateSnapshot) {
|
||||
const userHasPremium = await this.stateService.getCanAccessPremium();
|
||||
|
||||
if (!userHasPremium) {
|
||||
this.messagingService.send("premiumRequired");
|
||||
}
|
||||
|
||||
// Prevent trapping the user on the login page, since that's an awful UX flow
|
||||
if (!userHasPremium && this.router.url === "/login") {
|
||||
return this.router.createUrlTree(["/"]);
|
||||
}
|
||||
|
||||
return userHasPremium;
|
||||
}
|
||||
}
|
38
apps/web/src/app/tests/preloaded-english-i18n.module.ts
Normal file
38
apps/web/src/app/tests/preloaded-english-i18n.module.ts
Normal file
@ -0,0 +1,38 @@
|
||||
import { APP_INITIALIZER, NgModule } from "@angular/core";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||
import { I18nService as BaseI18nService } from "@bitwarden/common/services/i18n.service";
|
||||
|
||||
import * as eng from "../../locales/en/messages.json";
|
||||
|
||||
class PreloadedEnglishI18nService extends BaseI18nService {
|
||||
constructor() {
|
||||
super("en", "", () => {
|
||||
return Promise.resolve(eng);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function i18nInitializer(i18nService: I18nService): () => Promise<void> {
|
||||
return async () => {
|
||||
await (i18nService as any).init();
|
||||
};
|
||||
}
|
||||
|
||||
// This is a helper I18nService implementation that loads the english `message.json` eliminating
|
||||
// the need for fetching them dynamically. It should only be used within storybook.
|
||||
@NgModule({
|
||||
providers: [
|
||||
{
|
||||
provide: I18nService,
|
||||
useClass: PreloadedEnglishI18nService,
|
||||
},
|
||||
{
|
||||
provide: APP_INITIALIZER,
|
||||
useFactory: i18nInitializer,
|
||||
deps: [I18nService],
|
||||
multi: true,
|
||||
},
|
||||
],
|
||||
})
|
||||
export class PreloadedEnglishI18nModule {}
|
@ -3,6 +3,7 @@
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"module": "ES2020",
|
||||
"resolveJsonModule": true,
|
||||
"paths": {
|
||||
"tldjs": ["../../libs/common/src/misc/tldjs.noop"],
|
||||
"src/*": ["src/*"],
|
||||
|
24
libs/components/src/icon/icon.component.ts
Normal file
24
libs/components/src/icon/icon.component.ts
Normal file
@ -0,0 +1,24 @@
|
||||
import { Component, HostBinding, Input } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
|
||||
import { Icon, IconSvg } from "./icons";
|
||||
|
||||
@Component({
|
||||
selector: "bit-icon",
|
||||
template: ``,
|
||||
})
|
||||
export class BitIconComponent {
|
||||
@Input() icon: Icon;
|
||||
|
||||
constructor(private domSanitizer: DomSanitizer) {}
|
||||
|
||||
@HostBinding("innerHtml")
|
||||
protected get innerHtml() {
|
||||
const svg = IconSvg[this.icon];
|
||||
if (svg == null) {
|
||||
return "Unknown icon";
|
||||
}
|
||||
|
||||
return this.domSanitizer.bypassSecurityTrustHtml(IconSvg[this.icon]);
|
||||
}
|
||||
}
|
11
libs/components/src/icon/icon.module.ts
Normal file
11
libs/components/src/icon/icon.module.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { CommonModule } from "@angular/common";
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { BitIconComponent } from "./icon.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [CommonModule],
|
||||
declarations: [BitIconComponent],
|
||||
exports: [BitIconComponent],
|
||||
})
|
||||
export class IconModule {}
|
27
libs/components/src/icon/icon.stories.ts
Normal file
27
libs/components/src/icon/icon.stories.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { Meta, Story } from "@storybook/angular";
|
||||
|
||||
import { BitIconComponent } from "./icon.component";
|
||||
|
||||
export default {
|
||||
title: "Component Library/Icon",
|
||||
component: BitIconComponent,
|
||||
args: {
|
||||
icon: "reportExposedPasswords",
|
||||
},
|
||||
} as Meta;
|
||||
|
||||
const Template: Story<BitIconComponent> = (args: BitIconComponent) => ({
|
||||
props: args,
|
||||
template: `
|
||||
<div class="tw-bg-primary-500 tw-p-5">
|
||||
<bit-icon [icon]="icon" class="tw-text-primary-300"></bit-icon>
|
||||
</div>
|
||||
`,
|
||||
});
|
||||
|
||||
export const ReportExposedPasswords = Template.bind({});
|
||||
|
||||
export const UnknownIcon = Template.bind({});
|
||||
UnknownIcon.args = {
|
||||
icon: "unknown",
|
||||
};
|
@ -1,32 +1,5 @@
|
||||
import { Component, Input, OnInit } from "@angular/core";
|
||||
import { DomSanitizer } from "@angular/platform-browser";
|
||||
|
||||
import { MessagingService } from "@bitwarden/common/abstractions/messaging.service";
|
||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
||||
|
||||
export enum ReportTypes {
|
||||
"exposedPasswords" = "exposedPasswords",
|
||||
"reusedPasswords" = "reusedPasswords",
|
||||
"weakPasswords" = "weakPasswords",
|
||||
"unsecuredWebsites" = "unsecuredWebsites",
|
||||
"inactive2fa" = "inactive2fa",
|
||||
"dataBreach" = "dataBreach",
|
||||
}
|
||||
|
||||
type ReportEntry = {
|
||||
title: string;
|
||||
description: string;
|
||||
route: string;
|
||||
icon: string;
|
||||
requiresPremium: boolean;
|
||||
};
|
||||
|
||||
const reports: Record<ReportTypes, ReportEntry> = {
|
||||
exposedPasswords: {
|
||||
title: "exposedPasswordsReport",
|
||||
description: "exposedPasswordsReportDesc",
|
||||
route: "exposed-passwords-report",
|
||||
icon: `
|
||||
export const IconSvg = {
|
||||
reportExposedPasswords: `
|
||||
<svg width="101" height="77" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.374 50.192a26.42 26.42 0 0 0 9.111 1.608c14.34 0 25.965-11.372 25.965-25.4 0-.337-.007-.673-.02-1.008h25.299v34.85H32.374v-10.05Z" fill="currentColor" />
|
||||
<path d="M15.805 26.4c0 14.028 11.625 25.4 25.965 25.4s25.964-11.372 25.964-25.4C67.734 12.372 56.11 1 41.77 1 27.43 1 15.805 12.372 15.805 26.4Z" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
@ -40,26 +13,14 @@ const reports: Record<ReportTypes, ReportEntry> = {
|
||||
<path d="M16.852 33.375h28.375a4 4 0 0 1 4 4v1.757a4 4 0 0 1-4 4H22.174M66.523 33.375h-3.539a4 4 0 0 0-4 4v3.761c0 1.102.894 1.996 1.996 1.996v0" stroke="#fff" stroke-width="2" stroke-linecap="round" />
|
||||
</svg>
|
||||
`,
|
||||
requiresPremium: true,
|
||||
},
|
||||
reusedPasswords: {
|
||||
title: "reusedPasswordsReport",
|
||||
description: "reusedPasswordsReportDesc",
|
||||
route: "reused-passwords-report",
|
||||
icon: `
|
||||
reportReusedPasswords: `
|
||||
<svg width="102" height="102" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M57.983 15.06a35.664 35.664 0 0 1 14.531 6.27c16.164 11.78 19.585 34.613 7.643 51a37.227 37.227 0 0 1-6.81 7.138m-32.842 6.697a35.708 35.708 0 0 1-11.239-5.495c-16.163-11.78-19.585-34.613-7.642-51a37.55 37.55 0 0 1 3.295-3.929" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M93.909 64.598H7.72c-.708 0-1.275-.662-1.275-1.49V40.273c0-.828.567-1.49 1.275-1.49H93.91c.708 0 1.275.663 1.275 1.49v22.837c.047.827-.567 1.49-1.275 1.49Z" fill="currentColor" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<path d="M21.532 52.186v-5.965M21.532 52.187l5.748-1.844M21.532 52.186l3.524 4.881M21.531 52.186l-3.47 4.881M21.532 52.187l-5.694-1.844M40.944 52.186v-5.965M40.944 52.187l5.694-1.844M40.944 52.187l3.525 4.88M40.944 52.187l-3.525 4.88M40.944 52.187l-5.694-1.844M54.849 57.337h11.294M74.21 57.337h11.295M41.75 83l.71 4.75-4.75.71M58.664 18.66 56 14.665 59.996 12" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
`,
|
||||
requiresPremium: true,
|
||||
},
|
||||
weakPasswords: {
|
||||
title: "weakPasswordsReport",
|
||||
description: "weakPasswordsReportDesc",
|
||||
route: "weak-passwords-report",
|
||||
icon: `
|
||||
reportWeakPasswords: `
|
||||
<svg width="78" height="78" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M66.493 64.415V77H9.979V64.324M9.979 44.065V32.106h56.514v12.148" stroke="#fff" stroke-width="2" stroke-linejoin="round" />
|
||||
<path d="M75.44 64.852H2.085c-.603 0-1.085-.555-1.085-1.25V44.448c0-.694.482-1.25 1.085-1.25H75.44c.603 0 1.085.556 1.085 1.25v19.156c.04.694-.482 1.25-1.085 1.25Z" fill="currentColor" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
@ -67,13 +28,7 @@ const reports: Record<ReportTypes, ReportEntry> = {
|
||||
<path d="M20.863 31.364c-.274-5.285 0-15.817 1.093-18.863 1.276-3.554 6.233-10.826 15.856-11.482 4.83-.273 15.2 2.296 18.043 14.763" stroke="#fff" stroke-width="2" />
|
||||
</svg>
|
||||
`,
|
||||
requiresPremium: true,
|
||||
},
|
||||
unsecuredWebsites: {
|
||||
title: "unsecuredWebsitesReport",
|
||||
description: "unsecuredWebsitesReportDesc",
|
||||
route: "unsecured-websites-report",
|
||||
icon: `
|
||||
reportUnsecuredWebsites: `
|
||||
<svg width="113" height="76" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M1.71 12.983h110.362v55.11a6 6 0 0 1-6 6H7.711a6 6 0 0 1-6-6v-55.11Z" fill="currentColor" />
|
||||
<rect x="1" y="1.073" width="110.5" height="73.454" rx="9" stroke="#fff" stroke-width="2" />
|
||||
@ -83,13 +38,7 @@ const reports: Record<ReportTypes, ReportEntry> = {
|
||||
<rect width="96.673" height="6.886" rx="3.443" transform="matrix(-1 0 0 1 104.373 18.721)" stroke="#fff" />
|
||||
</svg>
|
||||
`,
|
||||
requiresPremium: true,
|
||||
},
|
||||
inactive2fa: {
|
||||
title: "inactive2faReport",
|
||||
description: "inactive2faReportDesc",
|
||||
route: "inactive-two-factor-report",
|
||||
icon: `
|
||||
reportInactiveTwoFactor: `
|
||||
<svg width="42" height="75" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="currentColor" stroke="#fff" stroke-width="2" d="M1 13.121h39.595v48.758H1z" />
|
||||
<rect x="1" y="1" width="39.595" height="73" rx="8" stroke="#fff" stroke-width="2" />
|
||||
@ -97,13 +46,7 @@ const reports: Record<ReportTypes, ReportEntry> = {
|
||||
<path d="m33.584 29.293-1.295 4.625-4.625-1.295M8.523 44.725l1.441-4.581 4.582 1.441" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" />
|
||||
</svg>
|
||||
`,
|
||||
requiresPremium: true,
|
||||
},
|
||||
dataBreach: {
|
||||
title: "dataBreachReport",
|
||||
description: "breachDesc",
|
||||
route: "breach-report",
|
||||
icon: `
|
||||
reportBreach: `
|
||||
<svg width="58" height="75" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M39.569 74H13.007a7 7 0 0 1-7-7V31.077a7 7 0 0 1 7-7h19.101a7 7 0 0 1 4.988 2.088l7.46 7.576a7 7 0 0 1 2.013 4.912V67a7 7 0 0 1-7 7Z" fill="#175DDC" stroke="#fff" stroke-width="2" />
|
||||
<path d="M44.576 69.055H18.015a7 7 0 0 1-7-7V26.132a7 7 0 0 1 7-7h19.1a7 7 0 0 1 4.988 2.088l7.46 7.576a7 7 0 0 1 2.013 4.911v28.348a7 7 0 0 1-7 7Z" fill="#175DDC" stroke="#fff" stroke-width="2" />
|
||||
@ -115,52 +58,6 @@ const reports: Record<ReportTypes, ReportEntry> = {
|
||||
<ellipse rx="1.252" ry="1.236" transform="rotate(-.479 2802.219 -1964.476) skewX(.012)" fill="#fff" />
|
||||
</svg>
|
||||
`,
|
||||
requiresPremium: false,
|
||||
},
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: "app-report-card",
|
||||
templateUrl: "report-card.component.html",
|
||||
})
|
||||
export class ReportCardComponent implements OnInit {
|
||||
@Input() type: ReportTypes;
|
||||
|
||||
report: ReportEntry;
|
||||
|
||||
hasPremium: boolean;
|
||||
|
||||
constructor(
|
||||
private stateService: StateService,
|
||||
private messagingService: MessagingService,
|
||||
private sanitizer: DomSanitizer
|
||||
) {}
|
||||
|
||||
async ngOnInit() {
|
||||
this.report = reports[this.type];
|
||||
|
||||
this.hasPremium = await this.stateService.getCanAccessPremium();
|
||||
}
|
||||
|
||||
get premium() {
|
||||
return this.report.requiresPremium && !this.hasPremium;
|
||||
}
|
||||
|
||||
get route() {
|
||||
if (this.premium) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.report.route;
|
||||
}
|
||||
|
||||
get icon() {
|
||||
return this.sanitizer.bypassSecurityTrustHtml(this.report.icon);
|
||||
}
|
||||
|
||||
click() {
|
||||
if (this.premium) {
|
||||
this.messagingService.send("premiumRequired");
|
||||
}
|
||||
}
|
||||
}
|
||||
export type Icon = keyof typeof IconSvg;
|
1
libs/components/src/icon/index.ts
Normal file
1
libs/components/src/icon/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from "./icon.module";
|
@ -1,11 +1,12 @@
|
||||
export * from "./badge";
|
||||
export * from "./banner";
|
||||
export * from "./button";
|
||||
export * from "./toggle-group";
|
||||
export * from "./callout";
|
||||
export * from "./form-field";
|
||||
export * from "./icon";
|
||||
export * from "./menu";
|
||||
export * from "./modal";
|
||||
export * from "./utils/i18n-mock.service";
|
||||
export * from "./tabs";
|
||||
export * from "./submit-button";
|
||||
export * from "./tabs";
|
||||
export * from "./toggle-group";
|
||||
export * from "./utils/i18n-mock.service";
|
||||
|
@ -1,7 +1,11 @@
|
||||
/* eslint-disable */
|
||||
const config = require("./libs/components/tailwind.config.base");
|
||||
|
||||
config.content = ["./libs/components/src/**/*.{html,ts,mdx}", "./.storybook/preview.js"];
|
||||
config.content = [
|
||||
"./libs/components/src/**/*.{html,ts,mdx}",
|
||||
"./apps/web/src/**/*.{html,ts,mdx}",
|
||||
"./.storybook/preview.js",
|
||||
];
|
||||
config.safelist = [
|
||||
{
|
||||
pattern: /tw-bg-(.*)/,
|
||||
|
@ -13,6 +13,7 @@
|
||||
"declaration": false,
|
||||
"outDir": "dist",
|
||||
"baseUrl": ".",
|
||||
"resolveJsonModule": true,
|
||||
"paths": {
|
||||
"@bitwarden/common/*": ["./libs/common/src/*"],
|
||||
"@bitwarden/angular/*": ["./libs/angular/src/*"],
|
||||
@ -26,6 +27,6 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"include": ["apps/*/src/**/*.stories.ts", "libs/*/src/**/*"],
|
||||
"exclude": ["apps/*/src/**/*.spec.ts", "libs/*/src/**/*.spec.ts"]
|
||||
"include": ["apps/web/src/**/*", "libs/*/src/**/*"],
|
||||
"exclude": ["apps/web/src/**/*.spec.ts", "libs/*/src/**/*.spec.ts"]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user