feat(console): onboarding flow (#5225)

Implements an onboarding UI for users
This commit is contained in:
Max Peintner
2023-02-24 18:01:05 +01:00
committed by GitHub
parent a7cc907ab7
commit f8ddc844f8
39 changed files with 1248 additions and 26 deletions
+40 -1
View File
@@ -212,7 +212,46 @@
<ng-template #shortcutKeyRef>
<ng-container *ngIf="(isHandset$ | async) === false">
<span class="fill-space"></span>
<ng-template cnslHasRole [hasRole]="['iam.read']">
<span class="fill-space"></span>
<ng-container *ngIf="!adminService.hideOnboarding && (adminService.progressAllDone | async) === false">
<button
cdkOverlayOrigin
#trigger="cdkOverlayOrigin"
matRipple
class="progress-bar"
(click)="showInstanceProgress = !showInstanceProgress"
>
<mat-progress-bar
class="progress"
mode="determinate"
[value]="adminService.progressPercentage | async"
></mat-progress-bar>
</button>
<ng-template
cdkConnectedOverlay
[cdkConnectedOverlayOrigin]="trigger"
[flexibleDimensions]="true"
[lockPosition]="true"
[cdkConnectedOverlayOffsetY]="10"
[cdkConnectedOverlayHasBackdrop]="true"
[cdkConnectedOverlayPositions]="positions"
cdkConnectedOverlayBackdropClass="transparent-backdrop"
[cdkConnectedOverlayOpen]="showInstanceProgress"
(backdropClick)="showInstanceProgress = false"
(detach)="showInstanceProgress = false"
>
<cnsl-onboarding-card
(dismissedCard)="dismissOnboarding()"
class="onboarding_card"
*ngIf="org && showInstanceProgress"
>
</cnsl-onboarding-card>
</ng-template>
</ng-container>
</ng-template>
<div (click)="openHelp()" class="nav-shortcut-action-key" matTooltip="{{ 'MENU.OPENSHORTCUTSTOOLTIP' | translate }}">
<div class="nav-key-overlay"></div>
<span>?</span>
@@ -181,8 +181,39 @@
flex: 1;
}
.progress-bar {
margin-right: 0.5rem;
width: 100px;
min-width: 50px;
background: if($is-dark-theme, #ffffff20, #00000010);
border: none;
padding: 2px;
border-radius: 50vw;
&:hover {
background: if($is-dark-theme, #ffffff, $primary-color);
transition: background ease 0.15s;
}
.progress {
border-radius: 50vw;
height: 8px;
.mat-progress-bar-buffer {
background-color: if($is-dark-theme, rgb(69, 91, 84), #cccccc) !important;
}
.mat-progress-bar-background {
fill: if($is-dark-theme, rgb(69, 91, 84), #cccccc) !important;
}
.mat-progress-bar-fill:after {
background-color: var(--success);
}
}
}
.nav-shortcut-action-key {
display: flex;
flex-shrink: 0;
align-items: center;
justify-content: center;
font-size: 12px;
@@ -213,3 +244,9 @@
}
}
}
.onboarding_card {
border-radius: 0.5rem;
box-shadow: 0 0 15px 0 rgb(0 0 0 / 10%);
border: 1px solid rgba(#8795a1, 0.2);
}
+19 -8
View File
@@ -1,17 +1,20 @@
import { animate, keyframes, style, transition, trigger } from '@angular/animations';
import { BreakpointObserver } from '@angular/cdk/layout';
import { ConnectedPosition, ConnectionPositionPair } from '@angular/cdk/overlay';
import { HttpClient } from '@angular/common/http';
import { Component, ElementRef, Input, OnDestroy, ViewChild } from '@angular/core';
import { UntypedFormControl } from '@angular/forms';
import { Router } from '@angular/router';
import { BehaviorSubject, combineLatest, forkJoin, map, merge, Observable, Subject, switchMap, take } from 'rxjs';
import { BehaviorSubject, combineLatest, map, Observable, Subject, take, tap } from 'rxjs';
import { Org } from 'src/app/proto/generated/zitadel/org_pb';
import { User } from 'src/app/proto/generated/zitadel/user_pb';
import { AdminService } from 'src/app/services/admin.service';
import { AuthenticationService } from 'src/app/services/authentication.service';
import { BreadcrumbService, BreadcrumbType } from 'src/app/services/breadcrumb.service';
import { GrpcAuthService } from 'src/app/services/grpc-auth.service';
import { KeyboardShortcutsService } from 'src/app/services/keyboard-shortcuts/keyboard-shortcuts.service';
import { ManagementService } from 'src/app/services/mgmt.service';
import { StorageLocation, StorageService } from 'src/app/services/storage.service';
@Component({
selector: 'cnsl-nav',
@@ -73,6 +76,7 @@ export class NavComponent implements OnDestroy {
@Input() public isDarkTheme: boolean = true;
@Input() public user!: User.AsObject;
public showInstanceProgress: boolean = false;
public isHandset$: Observable<boolean> = this.breakpointObserver.observe('(max-width: 599px)').pipe(
map((result) => {
return result.matches;
@@ -83,14 +87,19 @@ export class NavComponent implements OnDestroy {
public filterControl: UntypedFormControl = new UntypedFormControl('');
public orgLoading$: BehaviorSubject<any> = new BehaviorSubject(false);
public showAccount: boolean = false;
public hideAdminWarn: boolean = true;
private destroy$: Subject<void> = new Subject();
public BreadcrumbType: any = BreadcrumbType;
public customerPortalLink: string = '';
public positions: ConnectedPosition[] = [
new ConnectionPositionPair({ originX: 'start', originY: 'bottom' }, { overlayX: 'start', overlayY: 'top' }, 0, 10),
new ConnectionPositionPair({ originX: 'end', originY: 'bottom' }, { overlayX: 'end', overlayY: 'top' }, 0, 10),
];
constructor(
public authService: GrpcAuthService,
public adminService: AdminService,
public authenticationService: AuthenticationService,
public breadcrumbService: BreadcrumbService,
public mgmtService: ManagementService,
@@ -98,8 +107,8 @@ export class NavComponent implements OnDestroy {
private breakpointObserver: BreakpointObserver,
private http: HttpClient,
private shortcutService: KeyboardShortcutsService,
private storageService: StorageService,
) {
this.hideAdminWarn = localStorage.getItem('hideAdministratorWarning') === 'true' ? true : false;
this.loadEnvironment();
}
@@ -114,16 +123,18 @@ export class NavComponent implements OnDestroy {
});
}
public toggleAdminHide(): void {
this.hideAdminWarn = !this.hideAdminWarn;
localStorage.setItem('hideAdministratorWarning', this.hideAdminWarn.toString());
}
public ngOnDestroy() {
this.destroy$.next();
this.destroy$.complete();
}
public dismissOnboarding(): void {
this.showInstanceProgress = false;
this.adminService.hideOnboarding = true;
this.storageService.setItem('onboarding-dismissed', 'true', StorageLocation.local);
this.adminService.progressAllDone.next(true);
}
public get isUserLinkActive(): boolean {
const url = this.router.url;
return url.substring(0, 6) === '/users';
@@ -12,12 +12,15 @@ import { TranslateModule } from '@ngx-translate/core';
import { HasRoleModule } from 'src/app/directives/has-role/has-role.module';
import { HasRolePipeModule } from 'src/app/pipes/has-role-pipe/has-role-pipe.module';
import { MatLegacyProgressBarModule } from '@angular/material/legacy-progress-bar';
import OnboardingCardModule from '../onboarding-card/onboarding-card.module';
import { NavComponent } from './nav.component';
@NgModule({
declarations: [NavComponent],
imports: [
CommonModule,
OnboardingCardModule,
FormsModule,
ReactiveFormsModule,
TranslateModule,
@@ -25,6 +28,7 @@ import { NavComponent } from './nav.component';
RouterModule,
MatTooltipModule,
HasRolePipeModule,
MatLegacyProgressBarModule,
HasRoleModule,
MatMenuModule,
MatButtonModule,