diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 5b306fdb2a..b6968f1ff8 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -3544,12 +3544,6 @@ "contactYourOrgAdmin": { "message": "Items in deactivated organizations cannot be accessed. Contact your organization owner for assistance." }, - "itemDetails": { - "message": "Item details" - }, - "itemName": { - "message": "Item name" - }, "additionalInformation": { "message": "Additional information" }, @@ -3643,6 +3637,24 @@ "loading": { "message": "Loading" }, + "assign": { + "message": "Assign" + }, + "bulkCollectionAssignmentDialogDescription": { + "message": "Only organization members with access to these collections will be able to see the items." + }, + "bulkCollectionAssignmentWarning": { + "message": "You have selected $TOTAL_COUNT$ items. You cannot update $READONLY_COUNT$ of the items because you do not have edit permissions.", + "placeholders": { + "total_count": { + "content": "$1", + "example": "10" + }, + "readonly_count": { + "content": "$2" + } + } + }, "addField": { "message": "Add field" }, @@ -3726,6 +3738,46 @@ } } }, + "selectCollectionsToAssign": { + "message": "Select collections to assign" + }, + "personalItemsTransferWarning": { + "message": "$PERSONAL_ITEMS_COUNT$ will be permanently transferred to the selected organization. You will no longer own these items.", + "placeholders": { + "personal_items_count": { + "content": "$1", + "example": "2 items" + } + } + }, + "personalItemsWithOrgTransferWarning": { + "message": "$PERSONAL_ITEMS_COUNT$ will be permanently transferred to $ORG$. You will no longer own these items.", + "placeholders": { + "personal_items_count": { + "content": "$1", + "example": "2 items" + }, + "org": { + "content": "$2", + "example": "Organization name" + } + } + }, + "successfullyAssignedCollections": { + "message": "Successfully assigned collections" + }, + "nothingSelected": { + "message": "You have not selected anything." + }, + "movedItemsToOrg": { + "message": "Selected items moved to $ORGNAME$", + "placeholders": { + "orgname": { + "content": "$1", + "example": "Company Name" + } + } + }, "reorderFieldDown":{ "message": "$LABEL$ moved down, position $INDEX$ of $LENGTH$", "placeholders": { diff --git a/apps/browser/src/popup/app-routing.animations.ts b/apps/browser/src/popup/app-routing.animations.ts index 065331bd41..227ede146b 100644 --- a/apps/browser/src/popup/app-routing.animations.ts +++ b/apps/browser/src/popup/app-routing.animations.ts @@ -177,6 +177,9 @@ export const routerTransition = trigger("routerTransition", [ transition("tabs => account-security", inSlideLeft), transition("account-security => tabs", outSlideRight), + transition("tabs => assign-collections", inSlideLeft), + transition("assign-collections => tabs", outSlideRight), + // Vault settings transition("tabs => vault-settings", inSlideLeft), transition("vault-settings => tabs", outSlideRight), diff --git a/apps/browser/src/popup/app-routing.module.ts b/apps/browser/src/popup/app-routing.module.ts index 12d92249fe..53ab778c31 100644 --- a/apps/browser/src/popup/app-routing.module.ts +++ b/apps/browser/src/popup/app-routing.module.ts @@ -71,6 +71,7 @@ import { VaultItemsComponent } from "../vault/popup/components/vault/vault-items import { VaultV2Component } from "../vault/popup/components/vault/vault-v2.component"; import { ViewComponent } from "../vault/popup/components/vault/view.component"; import { AddEditV2Component } from "../vault/popup/components/vault-v2/add-edit/add-edit-v2.component"; +import { AssignCollections } from "../vault/popup/components/vault-v2/assign-collections/assign-collections.component"; import { AttachmentsV2Component } from "../vault/popup/components/vault-v2/attachments/attachments-v2.component"; import { ViewV2Component } from "../vault/popup/components/vault-v2/view-v2/view-v2.component"; import { AppearanceComponent } from "../vault/popup/settings/appearance.component"; @@ -408,6 +409,12 @@ const routes: Routes = [ }, ], }, + { + path: "assign-collections", + component: AssignCollections, + canActivate: [canAccessFeature(FeatureFlag.ExtensionRefresh, true, "/")], + data: { state: "assign-collections" }, + }, ...extensionRefreshSwap(AboutPageComponent, AboutPageV2Component, { path: "about", canActivate: [AuthGuard], diff --git a/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.html b/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.html new file mode 100644 index 0000000000..8e8ce1f997 --- /dev/null +++ b/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.html @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + {{ "cancel" | i18n }} + + + diff --git a/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts b/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts new file mode 100644 index 0000000000..a3ebadb7e2 --- /dev/null +++ b/apps/browser/src/vault/popup/components/vault-v2/assign-collections/assign-collections.component.ts @@ -0,0 +1,81 @@ +import { CommonModule, Location } from "@angular/common"; +import { Component } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { ReactiveFormsModule } from "@angular/forms"; +import { ActivatedRoute } from "@angular/router"; +import { Observable, combineLatest, first, switchMap } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { OrganizationId } from "@bitwarden/common/types/guid"; +import { CipherService } from "@bitwarden/common/vault/abstractions/cipher.service"; +import { CollectionService } from "@bitwarden/common/vault/abstractions/collection.service"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { + ButtonModule, + CardComponent, + SelectModule, + FormFieldModule, + AsyncActionsModule, +} from "@bitwarden/components"; +import { AssignCollectionsComponent, CollectionAssignmentParams } from "@bitwarden/vault"; + +import { PopOutComponent } from "../../../../../platform/popup/components/pop-out.component"; +import { PopupFooterComponent } from "../../../../../platform/popup/layout/popup-footer.component"; +import { PopupHeaderComponent } from "../../../../../platform/popup/layout/popup-header.component"; +import { PopupPageComponent } from "../../../../../platform/popup/layout/popup-page.component"; + +@Component({ + standalone: true, + selector: "app-assign-collections", + templateUrl: "./assign-collections.component.html", + imports: [ + AsyncActionsModule, + ButtonModule, + CommonModule, + JslibModule, + SelectModule, + FormFieldModule, + AssignCollectionsComponent, + CardComponent, + ReactiveFormsModule, + PopupPageComponent, + PopupHeaderComponent, + PopupFooterComponent, + PopOutComponent, + ], +}) +export class AssignCollections { + /** Params needed to populate the assign collections component */ + params: CollectionAssignmentParams; + + constructor( + private location: Location, + private collectionService: CollectionService, + private cipherService: CipherService, + route: ActivatedRoute, + ) { + const $cipher: Observable = route.queryParams.pipe( + switchMap(({ cipherId }) => this.cipherService.get(cipherId)), + switchMap((cipherDomain) => + this.cipherService + .getKeyForCipherKeyDecryption(cipherDomain) + .then(cipherDomain.decrypt.bind(cipherDomain)), + ), + ); + + combineLatest([$cipher, this.collectionService.decryptedCollections$]) + .pipe(takeUntilDestroyed(), first()) + .subscribe(([cipherView, collections]) => { + this.params = { + ciphers: [cipherView], + organizationId: (cipherView?.organizationId as OrganizationId) ?? null, + availableCollections: collections.filter((c) => !c.readOnly), + }; + }); + } + + /** Navigates the user back to the previous screen */ + navigateBack() { + this.location.back(); + } +} diff --git a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html index 05a6b54d4d..0a0f44e8e0 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html +++ b/apps/browser/src/vault/popup/components/vault-v2/item-more-options/item-more-options.component.html @@ -28,9 +28,14 @@ {{ "clone" | i18n }} - + diff --git a/apps/web/src/app/vault/components/assign-collections/assign-collections-web.component.html b/apps/web/src/app/vault/components/assign-collections/assign-collections-web.component.html index f05262832c..4f5b6234ad 100644 --- a/apps/web/src/app/vault/components/assign-collections/assign-collections-web.component.html +++ b/apps/web/src/app/vault/components/assign-collections/assign-collections-web.component.html @@ -9,8 +9,7 @@
@@ -18,8 +17,7 @@