diff --git a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html
index af4eb182eec..d2f5cc38013 100644
--- a/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html
+++ b/apps/web/src/app/vault/individual-vault/vault-header/vault-header.component.html
@@ -76,7 +76,7 @@
- {{ "new" | i18n }}
+ {{ getButtonLabel() | i18n }}
@for (item of cipherMenuItems$ | async; track item.type) {
diff --git a/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts b/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts
index 0a755a9cdb4..1a592809691 100644
--- a/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts
+++ b/libs/vault/src/components/new-cipher-menu/new-cipher-menu.component.ts
@@ -1,6 +1,7 @@
import { CommonModule } from "@angular/common";
import { Component, input, output } from "@angular/core";
-import { map, shareReplay } from "rxjs";
+import { toObservable } from "@angular/core/rxjs-interop";
+import { combineLatest, map, shareReplay } from "rxjs";
import { JslibModule } from "@bitwarden/angular/jslib.module";
import { CipherType } from "@bitwarden/common/vault/enums";
@@ -38,10 +39,18 @@ export class NewCipherMenuComponent {
/**
* Returns an observable that emits the cipher menu items, filtered by the restricted types.
*/
- cipherMenuItems$ = this.restrictedItemTypesService.restricted$.pipe(
- map((restrictedTypes) => {
+ cipherMenuItems$ = combineLatest([
+ this.restrictedItemTypesService.restricted$,
+ toObservable(this.canCreateCipher),
+ toObservable(this.canCreateSshKey),
+ ]).pipe(
+ map(([restrictedTypes, canCreateCipher, canCreateSshKey]) => {
+ // If user cannot create ciphers at all, return empty array
+ if (!canCreateCipher) {
+ return [];
+ }
return CIPHER_MENU_ITEMS.filter((item) => {
- if (!this.canCreateSshKey() && item.type === CipherType.SshKey) {
+ if (!canCreateSshKey && item.type === CipherType.SshKey) {
return false;
}
return !restrictedTypes.some((restrictedType) => restrictedType.cipherType === item.type);
@@ -49,4 +58,40 @@ export class NewCipherMenuComponent {
}),
shareReplay({ bufferSize: 1, refCount: true }),
);
+
+ /**
+ * Returns the appropriate button label based on what can be created.
+ * If only collections can be created (no ciphers or folders), show "New Collection".
+ * Otherwise, show "New".
+ */
+ protected getButtonLabel(): string {
+ const canCreateCipher = this.canCreateCipher();
+ const canCreateFolder = this.canCreateFolder();
+ const canCreateCollection = this.canCreateCollection();
+
+ // If only collections can be created, be specific
+ if (!canCreateCipher && !canCreateFolder && canCreateCollection) {
+ return "newCollection";
+ }
+
+ return "new";
+ }
+
+ /**
+ * Returns true if only collections can be created (no other options).
+ * When this is true, the button should directly create a collection instead of showing a dropdown.
+ */
+ protected isOnlyCollectionCreation(): boolean {
+ return !this.canCreateCipher() && !this.canCreateFolder() && this.canCreateCollection();
+ }
+
+ /**
+ * Handles the button click. If only collections can be created, directly emit the collection event.
+ * Otherwise, the menu trigger will handle opening the dropdown.
+ */
+ protected handleButtonClick(): void {
+ if (this.isOnlyCollectionCreation()) {
+ this.collectionAdded.emit();
+ }
+ }
}