diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html
index a7bfd1d8be..0c1e8e2bd4 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.html
@@ -27,12 +27,21 @@
{{ "project" | i18n }}
-
+
+
+
+
+
+
+
+
+ {{ "projectName" | i18n }}
+
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts
index 6d300d057a..bcc1e521e7 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/secrets/dialog/secret-dialog.component.ts
@@ -1,14 +1,16 @@
import { DialogRef, DIALOG_DATA } from "@angular/cdk/dialog";
import { Component, Inject, OnInit } from "@angular/core";
import { FormControl, FormGroup, Validators } from "@angular/forms";
-import { lastValueFrom, Subject } from "rxjs";
+import { lastValueFrom, Subject, takeUntil } from "rxjs";
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
+import { Utils } from "@bitwarden/common/misc/utils";
import { DialogService } from "@bitwarden/components";
import { ProjectListView } from "../../models/view/project-list.view";
+import { ProjectView } from "../../models/view/project.view";
import { SecretListView } from "../../models/view/secret-list.view";
import { SecretProjectView } from "../../models/view/secret-project.view";
import { SecretView } from "../../models/view/secret.view";
@@ -39,11 +41,14 @@ export class SecretDialogComponent implements OnInit {
value: new FormControl("", [Validators.required]),
notes: new FormControl(""),
project: new FormControl("", [Validators.required]),
+ newProjectName: new FormControl(""),
});
private destroy$ = new Subject();
private loading = true;
projects: ProjectListView[];
+ addNewProject = false;
+ newProjectGuid = Utils.newGuid();
constructor(
public dialogRef: DialogRef,
@@ -74,6 +79,10 @@ export class SecretDialogComponent implements OnInit {
this.formGroup.get("project").removeValidators(Validators.required);
this.formGroup.get("project").updateValueAndValidity();
}
+
+ if (this.data.projectId == null || this.data.projectId == "") {
+ this.addNewProjectOptionToProjectsDropDown();
+ }
}
async loadData() {
@@ -87,6 +96,7 @@ export class SecretDialogComponent implements OnInit {
value: secret.value,
notes: secret.note,
project: secret.projects[0]?.id ?? "",
+ newProjectName: "",
});
this.loading = false;
@@ -111,6 +121,32 @@ export class SecretDialogComponent implements OnInit {
this.destroy$.complete();
}
+ private addNewProjectOptionToProjectsDropDown() {
+ this.formGroup
+ .get("project")
+ .valueChanges.pipe(takeUntil(this.destroy$))
+ .subscribe((val: string) => {
+ this.dropDownSelected(val);
+ });
+
+ const addNewProject = new ProjectListView();
+ addNewProject.name = this.i18nService.t("newProject");
+ addNewProject.id = this.newProjectGuid;
+ this.projects.unshift(addNewProject);
+ }
+
+ private dropDownSelected(val: string) {
+ this.addNewProject = val == this.newProjectGuid;
+
+ if (this.addNewProject) {
+ this.formGroup.get("newProjectName").addValidators([Validators.required]);
+ } else {
+ this.formGroup.get("newProjectName").clearValidators();
+ }
+
+ this.formGroup.updateValueAndValidity();
+ }
+
get title() {
return this.data.operation === OperationType.Add ? "newSecret" : "editSecret";
}
@@ -127,6 +163,12 @@ export class SecretDialogComponent implements OnInit {
}
const secretView = this.getSecretView();
+
+ if (this.addNewProject) {
+ const newProject = await this.createProject(this.getNewProjectView());
+ secretView.projects = [newProject];
+ }
+
if (this.data.operation === OperationType.Add) {
await this.createSecret(secretView);
} else {
@@ -140,6 +182,10 @@ export class SecretDialogComponent implements OnInit {
return this.data.operation === OperationType.Edit;
}
+ private async createProject(projectView: ProjectView) {
+ return await this.projectService.create(this.data.organizationId, projectView);
+ }
+
protected openDeleteSecretDialog() {
const secretListView: SecretListView[] = this.getSecretListView();
@@ -163,6 +209,13 @@ export class SecretDialogComponent implements OnInit {
this.platformUtilsService.showToast("success", null, this.i18nService.t("secretCreated"));
}
+ private getNewProjectView() {
+ const projectView = new ProjectView();
+ projectView.organizationId = this.data.organizationId;
+ projectView.name = this.formGroup.value.newProjectName;
+ return projectView;
+ }
+
private async updateSecret(secretView: SecretView) {
await this.secretService.update(this.data.organizationId, secretView);
this.platformUtilsService.showToast("success", null, this.i18nService.t("secretEdited"));
diff --git a/bitwarden_license/bit-web/src/app/secrets-manager/shared/sm-shared.module.ts b/bitwarden_license/bit-web/src/app/secrets-manager/shared/sm-shared.module.ts
index e1cba09183..6d59503b50 100644
--- a/bitwarden_license/bit-web/src/app/secrets-manager/shared/sm-shared.module.ts
+++ b/bitwarden_license/bit-web/src/app/secrets-manager/shared/sm-shared.module.ts
@@ -1,6 +1,11 @@
import { NgModule } from "@angular/core";
-import { MultiSelectModule, SearchModule, NoItemsModule } from "@bitwarden/components";
+import {
+ MultiSelectModule,
+ SearchModule,
+ SelectModule,
+ NoItemsModule,
+} from "@bitwarden/components";
import { CoreOrganizationModule } from "@bitwarden/web-vault/app/admin-console/organizations/core";
import { DynamicAvatarComponent } from "@bitwarden/web-vault/app/components/dynamic-avatar.component";
import { ProductSwitcherModule } from "@bitwarden/web-vault/app/layouts/product-switcher/product-switcher.module";
@@ -22,6 +27,7 @@ import { SecretsListComponent } from "./secrets-list.component";
MultiSelectModule,
CoreOrganizationModule,
NoItemsModule,
+ SelectModule,
DynamicAvatarComponent,
SearchModule,
],
@@ -37,6 +43,7 @@ import { SecretsListComponent } from "./secrets-list.component";
ProjectsListComponent,
SearchModule,
SecretsListComponent,
+ SelectModule,
SharedModule,
],
declarations: [