mirror of
https://github.com/bitwarden/clients.git
synced 2026-06-13 21:01:11 +08:00
[PM-35058]welcome modal post scan state (#20672)
This commit is contained in:
parent
90d13593d4
commit
cdbe896d63
11
apps/web/src/images/access-intelligence/data-is-in.svg
Normal file
11
apps/web/src/images/access-intelligence/data-is-in.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 49 KiB |
@ -208,6 +208,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"yourDataIsInLetsPutItToWork": {
|
||||
"message": "Your data is in. Let's put it to work."
|
||||
},
|
||||
"takeAQuickTourOfAccessIntelligence": {
|
||||
"message": "Take a quick tour of Access Intelligence and see exactly how to turn your org's data into action."
|
||||
},
|
||||
"startTour": {
|
||||
"message": "Start tour"
|
||||
},
|
||||
"noDataInOrgTitle": {
|
||||
"message": "No data found"
|
||||
},
|
||||
|
||||
@ -22,6 +22,7 @@ import { DefaultAdminTaskService } from "../../vault/services/default-admin-task
|
||||
|
||||
import { AccessIntelligenceRoutingModule } from "./access-intelligence-routing.module";
|
||||
import { NewApplicationsDialogComponent } from "./activity/application-review-dialog/new-applications-dialog.component";
|
||||
import { OnboardingService } from "./onboarding/services/onboarding.service";
|
||||
import { RiskInsightsComponent } from "./risk-insights.component";
|
||||
import { AccessIntelligencePageComponent } from "./v2/access-intelligence-page/access-intelligence-page.component";
|
||||
|
||||
@ -69,6 +70,7 @@ import { AccessIntelligencePageComponent } from "./v2/access-intelligence-page/a
|
||||
LogService,
|
||||
],
|
||||
}),
|
||||
safeProvider(OnboardingService),
|
||||
],
|
||||
})
|
||||
export class AccessIntelligenceModule {}
|
||||
|
||||
@ -0,0 +1,50 @@
|
||||
import { inject, Injectable } from "@angular/core";
|
||||
import { firstValueFrom, map } from "rxjs";
|
||||
|
||||
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
|
||||
import {
|
||||
ACCESS_INTELLIGENCE_WELCOME_DIALOG_DISK,
|
||||
StateProvider,
|
||||
UserKeyDefinition,
|
||||
} from "@bitwarden/state";
|
||||
|
||||
const ACCESS_INTELLIGENCE_WELCOME_DIALOG_ACKNOWLEDGED_KEY = new UserKeyDefinition<boolean>(
|
||||
ACCESS_INTELLIGENCE_WELCOME_DIALOG_DISK,
|
||||
"accessIntelligenceWelcomeDialogCompleted",
|
||||
{
|
||||
deserializer: (value) => value,
|
||||
clearOn: [],
|
||||
},
|
||||
);
|
||||
|
||||
@Injectable()
|
||||
export class OnboardingService {
|
||||
private accountService = inject(AccountService);
|
||||
private stateProvider = inject(StateProvider);
|
||||
|
||||
async isWelcomeDialogAcknowledged(): Promise<boolean> {
|
||||
const account = await firstValueFrom(this.accountService.activeAccount$);
|
||||
if (!account) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const acknowledged = await firstValueFrom(
|
||||
this.stateProvider
|
||||
.getUserState$(ACCESS_INTELLIGENCE_WELCOME_DIALOG_ACKNOWLEDGED_KEY, account.id)
|
||||
.pipe(map((v) => v ?? false)),
|
||||
);
|
||||
|
||||
return acknowledged;
|
||||
}
|
||||
|
||||
async setWelcomeDialogAcknowledged(value = true) {
|
||||
const account = await firstValueFrom(this.accountService.activeAccount$);
|
||||
if (account) {
|
||||
await this.stateProvider.setUserState(
|
||||
ACCESS_INTELLIGENCE_WELCOME_DIALOG_ACKNOWLEDGED_KEY,
|
||||
value,
|
||||
account.id,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
<bit-dialog dialogSize="large">
|
||||
<div bitDialogContent class="tw-flex tw-flex-col tw-text-center tw-gap-4">
|
||||
<div class="tw-flex tw-justify-center tw-items-center tw-mb-4 tw-w-full">
|
||||
<img src="/images/access-intelligence/data-is-in.svg" class="tw-w-full tw-mx-auto" />
|
||||
</div>
|
||||
<div class="tw-flex tw-flex-col tw-w-[65%] tw-mx-auto">
|
||||
<h2 bitTypography="h2">{{ "yourDataIsInLetsPutItToWork" | i18n }}</h2>
|
||||
<p bitTypography="body1">
|
||||
{{ "takeAQuickTourOfAccessIntelligence" | i18n }}
|
||||
</p>
|
||||
<div class="tw-flex tw-justify-center tw-gap-6 tw-m-4">
|
||||
<button type="button" bitButton buttonType="secondary" (click)="onSkip()">
|
||||
{{ "skip" | i18n }}
|
||||
</button>
|
||||
<button type="button" bitButton buttonType="primary" (click)="onStartTour()">
|
||||
{{ "startTour" | i18n }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</bit-dialog>
|
||||
@ -0,0 +1,46 @@
|
||||
import { ComponentFixture, TestBed } from "@angular/core/testing";
|
||||
import { of } from "rxjs";
|
||||
|
||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||
import { ButtonModule, DialogModule, DialogRef, TypographyModule } from "@bitwarden/components";
|
||||
|
||||
import { OnboardingService } from "./services/onboarding.service";
|
||||
import { WelcomeModalDialogComponent } from "./welcome-modal-dialog.component";
|
||||
|
||||
const mockDialogRef = {
|
||||
close: jest.fn(),
|
||||
afterClosed: jest.fn().mockReturnValue(of(undefined)),
|
||||
closed: of(undefined),
|
||||
} as unknown as import("@bitwarden/components").DialogRef<any, any>;
|
||||
|
||||
describe("WelcomeModalDialogComponent", () => {
|
||||
let component: WelcomeModalDialogComponent;
|
||||
let fixture: ComponentFixture<WelcomeModalDialogComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
const mockI18nService = {
|
||||
t: jest.fn((key: string) => key),
|
||||
};
|
||||
const mockOnboardingService = {
|
||||
setWelcomeDialogAcknowledged: jest.fn().mockResolvedValue(undefined),
|
||||
isWelcomeDialogAcknowledged: jest.fn().mockResolvedValue(false),
|
||||
};
|
||||
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [WelcomeModalDialogComponent, TypographyModule, ButtonModule, DialogModule],
|
||||
providers: [
|
||||
{ provide: I18nService, useValue: mockI18nService },
|
||||
{ provide: OnboardingService, useValue: mockOnboardingService },
|
||||
{ provide: DialogRef, useValue: mockDialogRef },
|
||||
],
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(WelcomeModalDialogComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it("should create", () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,67 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
inject,
|
||||
Injector,
|
||||
runInInjectionContext,
|
||||
} from "@angular/core";
|
||||
|
||||
import {
|
||||
ButtonModule,
|
||||
DialogModule,
|
||||
DialogRef,
|
||||
DialogService,
|
||||
TypographyModule,
|
||||
} from "@bitwarden/components";
|
||||
import { LogService } from "@bitwarden/logging";
|
||||
import { I18nPipe } from "@bitwarden/ui-common";
|
||||
|
||||
import { OnboardingService } from "./services/onboarding.service";
|
||||
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: "app-welcome-modal-dialog",
|
||||
imports: [ButtonModule, TypographyModule, DialogModule, I18nPipe],
|
||||
templateUrl: "./welcome-modal-dialog.component.html",
|
||||
})
|
||||
export class WelcomeModalDialogComponent {
|
||||
private readonly dialogRef = inject(DialogRef<WelcomeModalDialogComponent>);
|
||||
private readonly onboardingService = inject(OnboardingService);
|
||||
|
||||
protected async onStartTour() {
|
||||
// invoke the dialog here
|
||||
await this.dialogRef.close();
|
||||
}
|
||||
|
||||
protected async onSkip() {
|
||||
await this.onboardingService
|
||||
.setWelcomeDialogAcknowledged()
|
||||
.then(() => {
|
||||
return this.dialogRef.close();
|
||||
})
|
||||
.catch(() => {});
|
||||
}
|
||||
|
||||
static async showWelcomeDialog(
|
||||
injector: Injector,
|
||||
dialogService: DialogService,
|
||||
): Promise<DialogRef<unknown, WelcomeModalDialogComponent> | undefined> {
|
||||
return runInInjectionContext(injector, async () => {
|
||||
const logger = inject(LogService);
|
||||
const onboardingService = inject(OnboardingService);
|
||||
const acknowledged = await onboardingService.isWelcomeDialogAcknowledged();
|
||||
if (acknowledged) {
|
||||
logger.info(
|
||||
"[Access Intelligence Onboarding] Welcome dialog already acknowledged, skipping dialog display.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const dialog = dialogService.open(WelcomeModalDialogComponent, {
|
||||
width: "600px",
|
||||
disableClose: true,
|
||||
});
|
||||
return dialog;
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -106,4 +106,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@if (isDevMode || adoptionUxImprovementsEnabled) {
|
||||
<dirt-dev-menu
|
||||
(beginTour)="beginOnboardingTour()"
|
||||
(importData)="goToImportPage()"
|
||||
></dirt-dev-menu>
|
||||
}
|
||||
</ng-container>
|
||||
|
||||
@ -10,6 +10,8 @@ import {
|
||||
inject,
|
||||
signal,
|
||||
ChangeDetectionStrategy,
|
||||
isDevMode,
|
||||
Injector,
|
||||
} from "@angular/core";
|
||||
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
@ -57,6 +59,8 @@ import { ApplicationsComponent } from "./all-applications/applications.component
|
||||
import { CriticalApplicationsComponent } from "./critical-applications/critical-applications.component";
|
||||
import { EmptyStateCardComponent } from "./empty-state-card.component";
|
||||
import { RiskInsightsTabType } from "./models/risk-insights.models";
|
||||
import { WelcomeModalDialogComponent } from "./onboarding/welcome-modal-dialog.component";
|
||||
import { DevMenuComponent } from "./shared/dev-menu.component";
|
||||
import { PageLoadingComponent } from "./shared/page-loading.component";
|
||||
import { ReportLoadingComponent } from "./shared/report-loading.component";
|
||||
import { RiskInsightsDrawerDialogComponent } from "./shared/risk-insights-drawer-dialog.component";
|
||||
@ -73,6 +77,7 @@ type ProgressStep = ReportProgress | null;
|
||||
AsyncActionsModule,
|
||||
ButtonModule,
|
||||
CommonModule,
|
||||
DevMenuComponent,
|
||||
IconModule,
|
||||
CriticalApplicationsComponent,
|
||||
EmptyStateCardComponent,
|
||||
@ -96,6 +101,8 @@ export class RiskInsightsComponent implements OnInit, OnDestroy {
|
||||
private destroyRef = inject(DestroyRef);
|
||||
protected ReportStatusEnum = ReportStatus;
|
||||
protected milestone11Enabled: boolean = false;
|
||||
protected adoptionUxImprovementsEnabled: boolean = false;
|
||||
protected isDevMode = isDevMode();
|
||||
|
||||
tabIndex: RiskInsightsTabType = RiskInsightsTabType.AllActivity;
|
||||
|
||||
@ -137,6 +144,7 @@ export class RiskInsightsComponent implements OnInit, OnDestroy {
|
||||
private logService: LogService,
|
||||
private configService: ConfigService,
|
||||
private toastService: ToastService,
|
||||
private injector: Injector,
|
||||
) {
|
||||
this.route.queryParams
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
@ -176,6 +184,10 @@ export class RiskInsightsComponent implements OnInit, OnDestroy {
|
||||
FeatureFlag.Milestone11AppPageImprovements,
|
||||
);
|
||||
|
||||
this.adoptionUxImprovementsEnabled = await this.configService.getFeatureFlag(
|
||||
FeatureFlag.AccessIntelligenceAdoptionUxImprovements,
|
||||
);
|
||||
|
||||
// Subscribe to report data updates
|
||||
// This declarative pattern ensures proper cleanup and prevents memory leaks
|
||||
this.dataService.enrichedReportData$
|
||||
@ -266,7 +278,7 @@ export class RiskInsightsComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
|
||||
if (this.invokedFrom()?.source && this.invokedFrom()?.status) {
|
||||
this.handleReturnParams(this.invokedFrom()?.source, this.invokedFrom()?.status);
|
||||
await this.handleReturnParams(this.invokedFrom()?.source, this.invokedFrom()?.status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -372,11 +384,16 @@ export class RiskInsightsComponent implements OnInit, OnDestroy {
|
||||
}
|
||||
};
|
||||
|
||||
private handleReturnParams(source: string | undefined, status: string | undefined): void {
|
||||
private async handleReturnParams(
|
||||
source: string | undefined,
|
||||
status: string | undefined,
|
||||
): Promise<void> {
|
||||
if (source === "import" && status === "success") {
|
||||
this.generateReport();
|
||||
await this.beginOnboardingTour();
|
||||
}
|
||||
|
||||
await this.beginOnboardingTour();
|
||||
this.clearQueryParams(this.router, this.route, ["source", "status"]);
|
||||
}
|
||||
|
||||
@ -389,4 +406,10 @@ export class RiskInsightsComponent implements OnInit, OnDestroy {
|
||||
replaceUrl: true,
|
||||
});
|
||||
}
|
||||
|
||||
protected async beginOnboardingTour(): Promise<void> {
|
||||
if (this.adoptionUxImprovementsEnabled) {
|
||||
await WelcomeModalDialogComponent.showWelcomeDialog(this.injector, this.dialogService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
@if (isOpen()) {
|
||||
<div
|
||||
class="tw-fixed tw-top-16 tw-right-4 tw-z-50 tw-min-w-[180px] tw-overflow-hidden tw-rounded-lg tw-border tw-border-secondary-100 tw-bg-background tw-shadow-lg"
|
||||
>
|
||||
<div class="tw-border-b tw-border-secondary-100 tw-bg-background-alt tw-px-3 tw-py-1.5">
|
||||
<span class="tw-text-[10px] tw-font-semibold tw-uppercase tw-tracking-widest tw-text-muted">
|
||||
Dev Tools
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
class="tw-block tw-w-full tw-cursor-pointer tw-border-none tw-bg-background tw-px-3 tw-py-1.5 tw-text-left !tw-text-main hover:tw-bg-hover-default"
|
||||
(click)="onImportData()"
|
||||
>
|
||||
Import Data from Access Intelligence
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
class="tw-block tw-w-full tw-cursor-pointer tw-border-none tw-bg-background tw-px-3 tw-py-1.5 tw-text-left !tw-text-main hover:tw-bg-hover-default"
|
||||
(click)="onBeginTour()"
|
||||
>
|
||||
Begin Tour
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
class="tw-block tw-w-full tw-cursor-pointer tw-border-none tw-bg-background tw-px-3 tw-py-1.5 tw-text-left !tw-text-main hover:tw-bg-hover-default"
|
||||
(click)="onResetWelcomeDialogAck()"
|
||||
>
|
||||
Undo Welcome Dialog Acknowledgement
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<button
|
||||
type="button"
|
||||
class="tw-block tw-w-full tw-cursor-pointer tw-border-none tw-bg-background tw-px-3 tw-py-1.5 tw-text-left !tw-text-main hover:tw-bg-hover-default"
|
||||
(click)="onShowWelcomeDialogAckState()"
|
||||
>
|
||||
Acknowledged? {{ welcomeDialogAcked() }}
|
||||
<bit-badge variant="secondary">Click to refresh</bit-badge>
|
||||
</button>
|
||||
</div>
|
||||
<div class="tw-border-t tw-border-secondary-100 tw-px-3 tw-py-1.5">
|
||||
<span class="tw-text-[10px] tw-text-muted">Press Esc or click outside to close</span>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
ElementRef,
|
||||
HostListener,
|
||||
inject,
|
||||
isDevMode,
|
||||
OnInit,
|
||||
output,
|
||||
signal,
|
||||
} from "@angular/core";
|
||||
|
||||
import { BadgeModule } from "@bitwarden/components";
|
||||
import { LogService } from "@bitwarden/logging";
|
||||
|
||||
import { OnboardingService } from "../onboarding/services/onboarding.service";
|
||||
|
||||
/*This component is a dev menu only.
|
||||
* It is not intended for production use and will be removed before release a
|
||||
* after the feature flag is removed. It is only intended for use in development and testing.
|
||||
* No language translations are required and therefore no use of i18n pipe or service.
|
||||
*/
|
||||
@Component({
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
selector: "dirt-dev-menu",
|
||||
templateUrl: "./dev-menu.component.html",
|
||||
imports: [BadgeModule],
|
||||
})
|
||||
export class DevMenuComponent implements OnInit {
|
||||
private readonly elementRef = inject(ElementRef);
|
||||
private readonly onboardingService = inject(OnboardingService);
|
||||
private readonly logger = inject(LogService);
|
||||
protected readonly welcomeDialogAcked = signal(false);
|
||||
|
||||
readonly beginTour = output<void>();
|
||||
readonly importData = output<void>();
|
||||
protected readonly isOpen = signal(false);
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
const isAck = await this.onboardingService.isWelcomeDialogAcknowledged();
|
||||
this.welcomeDialogAcked.set(isAck);
|
||||
}
|
||||
|
||||
@HostListener("document:keydown", ["$event"])
|
||||
onKeyDown(event: KeyboardEvent): void {
|
||||
if (!isDevMode()) {
|
||||
return;
|
||||
}
|
||||
if (event.shiftKey && event.key === "?") {
|
||||
this.isOpen.update((open) => !open);
|
||||
} else if (event.key === "Escape") {
|
||||
this.isOpen.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
@HostListener("document:click", ["$event"])
|
||||
onDocumentClick(event: MouseEvent): void {
|
||||
if (!isDevMode()) {
|
||||
return;
|
||||
}
|
||||
if (this.isOpen() && !this.elementRef.nativeElement.contains(event.target)) {
|
||||
this.isOpen.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
protected onBeginTour(): void {
|
||||
this.isOpen.set(false);
|
||||
this.beginTour.emit();
|
||||
}
|
||||
|
||||
protected onImportData(): void {
|
||||
this.isOpen.set(false);
|
||||
this.importData.emit();
|
||||
}
|
||||
|
||||
protected async onResetWelcomeDialogAck(): Promise<void> {
|
||||
try {
|
||||
await this.onboardingService.setWelcomeDialogAcknowledged(false);
|
||||
this.welcomeDialogAcked.set(false);
|
||||
this.logger.info("Reset Access Intelligence welcome dialog acknowledged state.");
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
"Failed to reset Access Intelligence welcome dialog acknowledged state.",
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
protected async onShowWelcomeDialogAckState(): Promise<void> {
|
||||
try {
|
||||
const isAck = await this.onboardingService.isWelcomeDialogAcknowledged();
|
||||
this.welcomeDialogAcked.set(isAck);
|
||||
this.logger.info(`Access Intelligence welcome dialog acknowledged state: ${isAck}.`);
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
"Failed to get Access Intelligence welcome dialog acknowledged state.",
|
||||
error,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -104,4 +104,11 @@
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
||||
@if (isDevMode() || adoptionUxImprovementsEnabled()) {
|
||||
<dirt-dev-menu
|
||||
(beginTour)="beginOnboardingTour()"
|
||||
(importData)="goToImportPage()"
|
||||
></dirt-dev-menu>
|
||||
}
|
||||
</ng-container>
|
||||
|
||||
@ -9,6 +9,8 @@ import {
|
||||
OnInit,
|
||||
signal,
|
||||
ChangeDetectionStrategy,
|
||||
Injector,
|
||||
isDevMode,
|
||||
} from "@angular/core";
|
||||
import { toObservable, toSignal, takeUntilDestroyed } from "@angular/core/rxjs-interop";
|
||||
import { ActivatedRoute, Router } from "@angular/router";
|
||||
@ -45,6 +47,8 @@ import { HeaderModule } from "@bitwarden/web-vault/app/layouts/header/header.mod
|
||||
|
||||
import { EmptyStateCardComponent } from "../../empty-state-card.component";
|
||||
import { RiskInsightsTabType } from "../../models/risk-insights.models";
|
||||
import { WelcomeModalDialogComponent } from "../../onboarding/welcome-modal-dialog.component";
|
||||
import { DevMenuComponent } from "../../shared/dev-menu.component";
|
||||
import { PageLoadingComponent } from "../../shared/page-loading.component";
|
||||
import { ReportLoadingComponent } from "../../shared/report-loading.component";
|
||||
import { ActivityTabComponent } from "../activity-tab/activity-tab.component";
|
||||
@ -84,6 +88,7 @@ type ProgressStep = ReportProgress | null;
|
||||
PageLoadingComponent,
|
||||
TabsModule,
|
||||
ReportLoadingComponent,
|
||||
DevMenuComponent,
|
||||
],
|
||||
animations: [
|
||||
trigger("fadeIn", [
|
||||
@ -155,6 +160,12 @@ export class AccessIntelligencePageComponent implements OnInit, OnDestroy {
|
||||
|
||||
protected readonly invokedFrom = signal<{ source: string; status: string } | null>(null);
|
||||
|
||||
readonly adoptionUxImprovementsEnabled = toSignal<boolean>(
|
||||
this.configService.getFeatureFlag$(FeatureFlag.AccessIntelligenceAdoptionUxImprovements),
|
||||
);
|
||||
|
||||
protected readonly isDevMode = signal<boolean>(isDevMode());
|
||||
|
||||
constructor(
|
||||
private readonly route: ActivatedRoute,
|
||||
private readonly router: Router,
|
||||
@ -164,6 +175,7 @@ export class AccessIntelligencePageComponent implements OnInit, OnDestroy {
|
||||
private readonly dialogService: DialogService,
|
||||
private readonly logService: LogService,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly injector: Injector,
|
||||
) {
|
||||
this.route.queryParams
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
@ -203,7 +215,7 @@ export class AccessIntelligencePageComponent implements OnInit, OnDestroy {
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
async ngOnInit() {
|
||||
this.route.paramMap
|
||||
.pipe(
|
||||
takeUntilDestroyed(this.destroyRef),
|
||||
@ -225,7 +237,7 @@ export class AccessIntelligencePageComponent implements OnInit, OnDestroy {
|
||||
void this.currentDialogRef()?.close();
|
||||
|
||||
if (this.invokedFrom()?.source && this.invokedFrom()?.status) {
|
||||
this.handleReturnParams(this.invokedFrom()?.source, this.invokedFrom()?.status);
|
||||
await this.handleReturnParams(this.invokedFrom()?.source, this.invokedFrom()?.status);
|
||||
}
|
||||
}
|
||||
|
||||
@ -412,9 +424,13 @@ export class AccessIntelligencePageComponent implements OnInit, OnDestroy {
|
||||
}));
|
||||
}
|
||||
|
||||
private handleReturnParams(source: string | undefined, status: string | undefined): void {
|
||||
private async handleReturnParams(
|
||||
source: string | undefined,
|
||||
status: string | undefined,
|
||||
): Promise<void> {
|
||||
if (source === "import" && status === "success") {
|
||||
this.generateReport();
|
||||
await this.beginOnboardingTour();
|
||||
}
|
||||
|
||||
this.clearQueryParams(this.router, this.route, ["source", "status"]);
|
||||
@ -429,4 +445,10 @@ export class AccessIntelligencePageComponent implements OnInit, OnDestroy {
|
||||
replaceUrl: true,
|
||||
});
|
||||
}
|
||||
|
||||
protected async beginOnboardingTour(): Promise<void> {
|
||||
if (this.adoptionUxImprovementsEnabled()) {
|
||||
await WelcomeModalDialogComponent.showWelcomeDialog(this.injector, this.dialogService);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,6 +71,7 @@ export enum FeatureFlag {
|
||||
Milestone11AppPageImprovements = "pm-30538-dirt-milestone-11-app-page-improvements",
|
||||
AccessIntelligenceTrendChart = "pm-26961-access-intelligence-trend-chart",
|
||||
AccessIntelligenceNewArchitecture = "pm-31936-access-intelligence-new-architecture",
|
||||
AccessIntelligenceAdoptionUxImprovements = "pm-34723-access-intelligence-adoption-ux-improvements",
|
||||
|
||||
/* Vault */
|
||||
PM32009NewItemTypes = "pm-32009-new-item-types",
|
||||
@ -149,6 +150,7 @@ export const DefaultFeatureFlagValue = {
|
||||
[FeatureFlag.Milestone11AppPageImprovements]: FALSE,
|
||||
[FeatureFlag.AccessIntelligenceTrendChart]: FALSE,
|
||||
[FeatureFlag.AccessIntelligenceNewArchitecture]: FALSE,
|
||||
[FeatureFlag.AccessIntelligenceAdoptionUxImprovements]: FALSE,
|
||||
|
||||
/* Vault */
|
||||
[FeatureFlag.PM32009NewItemTypes]: FALSE,
|
||||
|
||||
@ -240,6 +240,13 @@ export const WELCOME_EXTENSION_DIALOG_DISK = new StateDefinition(
|
||||
web: "disk-local",
|
||||
},
|
||||
);
|
||||
export const ACCESS_INTELLIGENCE_WELCOME_DIALOG_DISK = new StateDefinition(
|
||||
"accessIntelligenceWelcomeDialog",
|
||||
"disk",
|
||||
{
|
||||
web: "disk-local",
|
||||
},
|
||||
);
|
||||
|
||||
// KM
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user