mirror of
https://github.com/bitwarden/browser
synced 2025-12-22 03:03:43 +00:00
PM-3231 Vault Onboarding Part 1 (#6905)
* Onboarding Component moved to web for sharing. Vault Onboarding Component created for new users. Still behind feature flag.
This commit is contained in:
@@ -1,25 +0,0 @@
|
||||
<ng-template #content>
|
||||
<i class="bwi bwi-fw !tw-mr-4" [ngClass]="completed ? 'bwi-check tw-text-success' : icon"></i
|
||||
><span
|
||||
[ngClass]="{
|
||||
'tw-text-primary-700 tw-line-through tw-decoration-primary-700 tw-opacity-50': completed
|
||||
}"
|
||||
>{{ title }}<i class="bwi bwi-angle-right tw-ml-1"></i
|
||||
></span>
|
||||
</ng-template>
|
||||
|
||||
<li class="tw-list-none">
|
||||
<a bitLink *ngIf="route" [routerLink]="route">
|
||||
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||
</a>
|
||||
<button type="button" bitLink *ngIf="!route">
|
||||
<ng-container *ngTemplateOutlet="content"></ng-container>
|
||||
</button>
|
||||
<div
|
||||
class="tw-ml-8 tw-mt-1 tw-text-sm"
|
||||
[ngClass]="{ 'tw-opacity-50': completed }"
|
||||
(click)="handleClick($event)"
|
||||
>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</li>
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Component, Input } from "@angular/core";
|
||||
|
||||
@Component({
|
||||
selector: "sm-onboarding-task",
|
||||
templateUrl: "./onboarding-task.component.html",
|
||||
host: {
|
||||
class: "tw-max-w-max",
|
||||
},
|
||||
})
|
||||
export class OnboardingTaskComponent {
|
||||
@Input()
|
||||
completed = false;
|
||||
|
||||
@Input()
|
||||
icon = "bwi-info-circle";
|
||||
|
||||
@Input()
|
||||
title: string;
|
||||
|
||||
@Input()
|
||||
route: string | any[];
|
||||
|
||||
handleClick(ev: MouseEvent) {
|
||||
/**
|
||||
* If the main `ng-content` is clicked, we don't want to trigger the task's click handler.
|
||||
*/
|
||||
ev.stopPropagation();
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<details #details class="tw-rounded-sm tw-bg-background-alt tw-text-main" (toggle)="toggle()" open>
|
||||
<summary class="tw-list-none tw-p-2 tw-px-4">
|
||||
<div class="tw-flex tw-select-none tw-items-center tw-gap-4">
|
||||
<i class="bwi bwi-dashboard tw-text-3xl tw-text-primary-500" aria-hidden="true"></i>
|
||||
<div class="tw-text-lg">{{ title }}</div>
|
||||
<bit-progress class="tw-flex-1" [showText]="false" [barWidth]="barWidth"></bit-progress>
|
||||
<span *ngIf="tasks.length > 0; else spinner">
|
||||
{{ "complete" | i18n: amountCompleted : tasks.length }}
|
||||
</span>
|
||||
<i
|
||||
class="bwi tw-my-auto"
|
||||
[ngClass]="open ? 'bwi-angle-down' : 'bwi-angle-up'"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
</div>
|
||||
</summary>
|
||||
<ul class="tw-mb-0 tw-ml-6 tw-flex tw-flex-col tw-gap-4">
|
||||
<ng-content></ng-content>
|
||||
</ul>
|
||||
<div class="tw-p-4 tw-pt-0">
|
||||
<button bitLink type="button" class="tw-ml-auto tw-block" (click)="dismiss.emit()">
|
||||
{{ "dismiss" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<ng-template #spinner>
|
||||
<i class="bwi bwi-spinner bwi-spin"></i>
|
||||
</ng-template>
|
||||
@@ -1,29 +0,0 @@
|
||||
import { Component, ContentChildren, EventEmitter, Input, Output, QueryList } from "@angular/core";
|
||||
|
||||
import { OnboardingTaskComponent } from "./onboarding-task.component";
|
||||
|
||||
@Component({
|
||||
selector: "sm-onboarding",
|
||||
templateUrl: "./onboarding.component.html",
|
||||
})
|
||||
export class OnboardingComponent {
|
||||
@ContentChildren(OnboardingTaskComponent) tasks: QueryList<OnboardingTaskComponent>;
|
||||
@Input() title: string;
|
||||
|
||||
@Output() dismiss = new EventEmitter<void>();
|
||||
|
||||
protected open = true;
|
||||
protected visible = false;
|
||||
|
||||
protected get amountCompleted(): number {
|
||||
return this.tasks.filter((task) => task.completed).length;
|
||||
}
|
||||
|
||||
protected get barWidth(): number {
|
||||
return this.tasks.length === 0 ? 0 : (this.amountCompleted / this.tasks.length) * 100;
|
||||
}
|
||||
|
||||
protected toggle() {
|
||||
this.open = !this.open;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { ProgressModule } from "@bitwarden/components";
|
||||
import { SharedModule } from "@bitwarden/web-vault/app/shared";
|
||||
|
||||
import { OnboardingTaskComponent } from "./onboarding-task.component";
|
||||
import { OnboardingComponent } from "./onboarding.component";
|
||||
|
||||
@NgModule({
|
||||
imports: [SharedModule, ProgressModule],
|
||||
exports: [OnboardingComponent, OnboardingTaskComponent],
|
||||
declarations: [OnboardingComponent, OnboardingTaskComponent],
|
||||
})
|
||||
export class OnboardingModule {}
|
||||
@@ -1,84 +0,0 @@
|
||||
import { importProvidersFrom } from "@angular/core";
|
||||
import { RouterModule } from "@angular/router";
|
||||
import { Meta, Story, applicationConfig, moduleMetadata } from "@storybook/angular";
|
||||
import { delay, of, startWith } from "rxjs";
|
||||
|
||||
import { JslibModule } from "@bitwarden/angular/jslib.module";
|
||||
import { LinkModule, IconModule, ProgressModule } from "@bitwarden/components";
|
||||
import { PreloadedEnglishI18nModule } from "@bitwarden/web-vault/app/core/tests";
|
||||
|
||||
import { OnboardingTaskComponent } from "./onboarding-task.component";
|
||||
import { OnboardingComponent } from "./onboarding.component";
|
||||
|
||||
export default {
|
||||
title: "Web/Onboarding",
|
||||
component: OnboardingComponent,
|
||||
decorators: [
|
||||
moduleMetadata({
|
||||
imports: [JslibModule, RouterModule, LinkModule, IconModule, ProgressModule],
|
||||
declarations: [OnboardingTaskComponent],
|
||||
}),
|
||||
applicationConfig({
|
||||
providers: [
|
||||
importProvidersFrom(RouterModule.forRoot([], { useHash: true })),
|
||||
importProvidersFrom(PreloadedEnglishI18nModule),
|
||||
],
|
||||
}),
|
||||
],
|
||||
} as Meta;
|
||||
|
||||
const Template: Story = (args) => ({
|
||||
props: {
|
||||
createServiceAccount: false,
|
||||
importSecrets$: of(false),
|
||||
createSecret: false,
|
||||
createProject: false,
|
||||
...args,
|
||||
},
|
||||
template: `
|
||||
<sm-onboarding title="Get started">
|
||||
<sm-onboarding-task
|
||||
[title]="'createServiceAccount' | i18n"
|
||||
icon="bwi-cli"
|
||||
[completed]="createServiceAccount"
|
||||
>
|
||||
<span>
|
||||
{{ "downloadThe" | i18n }} <a bitLink routerLink="">{{ "smCLI" | i18n }}</a>
|
||||
</span>
|
||||
</sm-onboarding-task>
|
||||
<sm-onboarding-task
|
||||
[title]="'createProject' | i18n"
|
||||
icon="bwi-collection"
|
||||
[completed]="createProject"
|
||||
></sm-onboarding-task>
|
||||
<sm-onboarding-task
|
||||
[title]="'importSecrets' | i18n"
|
||||
icon="bwi-download"
|
||||
[completed]="importSecrets$ | async"
|
||||
></sm-onboarding-task>
|
||||
<sm-onboarding-task
|
||||
[title]="'createSecret' | i18n"
|
||||
icon="bwi-key"
|
||||
[completed]="createSecret"
|
||||
></sm-onboarding-task>
|
||||
</sm-onboarding>
|
||||
`,
|
||||
});
|
||||
|
||||
export const Empty = Template.bind({});
|
||||
|
||||
export const Partial = Template.bind({});
|
||||
Partial.args = {
|
||||
...Template.args,
|
||||
createServiceAccount: true,
|
||||
createProject: true,
|
||||
};
|
||||
|
||||
export const Full = Template.bind({});
|
||||
Full.args = {
|
||||
...Template.args,
|
||||
createServiceAccount: true,
|
||||
createProject: true,
|
||||
createSecret: true,
|
||||
importSecrets$: of(true).pipe(delay(0), startWith(false)),
|
||||
};
|
||||
@@ -3,8 +3,8 @@
|
||||
</app-header>
|
||||
|
||||
<div *ngIf="!loading && view$ | async as view; else spinner">
|
||||
<sm-onboarding [title]="'getStarted' | i18n" *ngIf="showOnboarding" (dismiss)="hideOnboarding()">
|
||||
<sm-onboarding-task
|
||||
<app-onboarding [title]="'getStarted' | i18n" *ngIf="showOnboarding" (dismiss)="hideOnboarding()">
|
||||
<app-onboarding-task
|
||||
[title]="'createServiceAccount' | i18n"
|
||||
(click)="openServiceAccountDialog()"
|
||||
icon="bwi-cli"
|
||||
@@ -16,29 +16,29 @@
|
||||
"smCLI" | i18n
|
||||
}}</a>
|
||||
</span>
|
||||
</sm-onboarding-task>
|
||||
<sm-onboarding-task
|
||||
</app-onboarding-task>
|
||||
<app-onboarding-task
|
||||
*ngIf="userIsAdmin"
|
||||
[title]="'createProject' | i18n"
|
||||
(click)="openNewProjectDialog()"
|
||||
icon="bwi-collection"
|
||||
[completed]="view.tasks.createProject"
|
||||
></sm-onboarding-task>
|
||||
<sm-onboarding-task
|
||||
></app-onboarding-task>
|
||||
<app-onboarding-task
|
||||
*ngIf="userIsAdmin"
|
||||
[title]="'importSecrets' | i18n"
|
||||
[route]="['settings', 'import']"
|
||||
icon="bwi-download"
|
||||
[completed]="view.tasks.importSecrets"
|
||||
></sm-onboarding-task>
|
||||
<sm-onboarding-task
|
||||
></app-onboarding-task>
|
||||
<app-onboarding-task
|
||||
*ngIf="view.tasks.createProject"
|
||||
[title]="'createSecret' | i18n"
|
||||
(click)="openSecretDialog()"
|
||||
icon="bwi-key"
|
||||
[completed]="view.tasks.createSecret"
|
||||
></sm-onboarding-task>
|
||||
</sm-onboarding>
|
||||
></app-onboarding-task>
|
||||
</app-onboarding>
|
||||
|
||||
<div class="tw-mt-6 tw-flex tw-flex-col tw-gap-6">
|
||||
<sm-section>
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { NgModule } from "@angular/core";
|
||||
|
||||
import { OnboardingModule } from "../../../../../../apps/web/src/app/shared/components/onboarding/onboarding.module";
|
||||
import { SecretsManagerSharedModule } from "../shared/sm-shared.module";
|
||||
|
||||
import { OnboardingModule } from "./onboarding.module";
|
||||
import { OverviewRoutingModule } from "./overview-routing.module";
|
||||
import { OverviewComponent } from "./overview.component";
|
||||
import { SectionComponent } from "./section.component";
|
||||
|
||||
Reference in New Issue
Block a user