From e44816800220c80b375028e1f73f4518d561a27d Mon Sep 17 00:00:00 2001 From: Thomas Rittson <31796059+eliykat@users.noreply.github.com> Date: Thu, 18 Apr 2024 03:00:00 +1000 Subject: [PATCH] [AC-2444] Add deep links to unassigned items banner (#8720) * Add link to Admin Console * update date on self-hosted banner --- apps/browser/src/_locales/en/messages.json | 19 ++++++++--- .../vault/current-tab.component.html | 12 ++++++- .../layouts/header/web-header.component.html | 11 ++++++- apps/web/src/locales/en/messages.json | 20 +++++++---- .../unassigned-items-banner.service.spec.ts | 11 ++++++- .../unassigned-items-banner.service.ts | 33 +++++++++++++++++-- 6 files changed, 90 insertions(+), 16 deletions(-) diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index f599df470b..36e3ce65a8 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -3006,11 +3006,22 @@ "passkeyRemoved": { "message": "Passkey removed" }, - "unassignedItemsBanner": { - "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerNotice": { + "message": "Notice: Unassigned organization items are no longer visible in the All Vaults view and only accessible via the Admin Console." }, - "unassignedItemsBannerSelfHost": { - "message": "Notice: On May 2, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console. Assign these items to a collection from the Admin Console to make them visible." + "unassignedItemsBannerSelfHostNotice": { + "message": "Notice: On May 16, 2024, unassigned organization items will no longer be visible in the All Vaults view and will only be accessible via the Admin Console." + }, + "unassignedItemsBannerCTAPartOne": { + "message": "Assign these items to a collection from the", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "unassignedItemsBannerCTAPartTwo": { + "message": "to make them visible.", + "description": "This will be part of a larger sentence, which will read like so: Assign these items to a collection from the Admin Console to make them visible." + }, + "adminConsole": { + "message": "Admin Console" }, "errorAssigningTargetCollection": { "message": "Error assigning target collection." diff --git a/apps/browser/src/vault/popup/components/vault/current-tab.component.html b/apps/browser/src/vault/popup/components/vault/current-tab.component.html index fc8b4212ba..0b2e16d09d 100644 --- a/apps/browser/src/vault/popup/components/vault/current-tab.component.html +++ b/apps/browser/src/vault/popup/components/vault/current-tab.component.html @@ -40,12 +40,22 @@ *ngIf=" (unassignedItemsBannerEnabled$ | async) && (unassignedItemsBannerService.showBanner$ | async) && - (unassignedItemsBannerService.bannerText$ | async) + !(unassignedItemsBannerService.loading$ | async) " type="info" >

{{ unassignedItemsBannerService.bannerText$ | async | i18n }} + {{ "unassignedItemsBannerCTAPartOne" | i18n }} + {{ "adminConsole" | i18n }} + {{ "unassignedItemsBannerCTAPartTwo" | i18n }} {{ unassignedItemsBannerService.bannerText$ | async | i18n }} + {{ "unassignedItemsBannerCTAPartOne" | i18n }} + {{ "adminConsole" | i18n }} + {{ "unassignedItemsBannerCTAPartTwo" | i18n }} { let stateProvider: FakeStateProvider; let apiService: MockProxy; let environmentService: MockProxy; + let organizationService: MockProxy; const sutFactory = () => - new UnassignedItemsBannerService(stateProvider, apiService, environmentService); + new UnassignedItemsBannerService( + stateProvider, + apiService, + environmentService, + organizationService, + ); beforeEach(() => { const fakeAccountService = mockAccountServiceWith("userId" as UserId); @@ -22,6 +29,8 @@ describe("UnassignedItemsBanner", () => { apiService = mock(); environmentService = mock(); environmentService.environment$ = of(null); + organizationService = mock(); + organizationService.organizations$ = of([]); }); it("shows the banner if showBanner local state is true", async () => { diff --git a/libs/angular/src/services/unassigned-items-banner.service.ts b/libs/angular/src/services/unassigned-items-banner.service.ts index 13a745fb82..db93d4c4fc 100644 --- a/libs/angular/src/services/unassigned-items-banner.service.ts +++ b/libs/angular/src/services/unassigned-items-banner.service.ts @@ -1,6 +1,10 @@ import { Injectable } from "@angular/core"; -import { concatMap, map } from "rxjs"; +import { combineLatest, concatMap, map, startWith } from "rxjs"; +import { + OrganizationService, + canAccessOrgAdmin, +} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction"; import { EnvironmentService, Region, @@ -40,18 +44,41 @@ export class UnassignedItemsBannerService { }), ); + private adminConsoleOrg$ = this.organizationService.organizations$.pipe( + map((orgs) => orgs.find((o) => canAccessOrgAdmin(o))), + ); + + adminConsoleUrl$ = combineLatest([ + this.adminConsoleOrg$, + this.environmentService.environment$, + ]).pipe( + map(([org, environment]) => { + if (org == null || environment == null) { + return "#"; + } + + return environment.getWebVaultUrl() + "/#/organizations/" + org.id; + }), + ); + bannerText$ = this.environmentService.environment$.pipe( map((e) => e?.getRegion() == Region.SelfHosted - ? "unassignedItemsBannerSelfHost" - : "unassignedItemsBanner", + ? "unassignedItemsBannerSelfHostNotice" + : "unassignedItemsBannerNotice", ), ); + loading$ = combineLatest([this.adminConsoleUrl$, this.bannerText$]).pipe( + startWith(true), + map(() => false), + ); + constructor( private stateProvider: StateProvider, private apiService: UnassignedItemsBannerApiService, private environmentService: EnvironmentService, + private organizationService: OrganizationService, ) {} async hideBanner() {