diff --git a/apps/browser/src/_locales/en/messages.json b/apps/browser/src/_locales/en/messages.json index 97841f2d45..22e0eba1b4 100644 --- a/apps/browser/src/_locales/en/messages.json +++ b/apps/browser/src/_locales/en/messages.json @@ -3607,6 +3607,15 @@ "filters": { "message": "Filters" }, + "personalDetails": { + "message": "Personal details" + }, + "identification": { + "message": "Identification" + }, + "contactInfo": { + "message": "Contact info" + }, "downloadAttachment": { "message": "Download - $ITEMNAME$", "placeholders": { diff --git a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts index c203620ed6..b6ea888fb9 100644 --- a/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts +++ b/apps/browser/src/vault/popup/components/vault-v2/attachments/open-attachments/open-attachments.component.ts @@ -55,6 +55,9 @@ export class OpenAttachmentsComponent implements OnInit { } async ngOnInit(): Promise { + if (!this.cipherId) { + return; + } const cipherDomain = await this.cipherService.get(this.cipherId); const cipher = await cipherDomain.decrypt( await this.cipherService.getKeyForCipherKeyDecryption(cipherDomain), diff --git a/libs/vault/src/cipher-form/cipher-form-container.ts b/libs/vault/src/cipher-form/cipher-form-container.ts index 8c6c232629..0e4e34a6be 100644 --- a/libs/vault/src/cipher-form/cipher-form-container.ts +++ b/libs/vault/src/cipher-form/cipher-form-container.ts @@ -1,6 +1,7 @@ import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; import { CardDetailsSectionComponent } from "./components/card-details-section/card-details-section.component"; +import { IdentitySectionComponent } from "./components/identity/identity.component"; import { ItemDetailsSectionComponent } from "./components/item-details/item-details-section.component"; /** @@ -10,6 +11,7 @@ import { ItemDetailsSectionComponent } from "./components/item-details/item-deta export type CipherForm = { itemDetails?: ItemDetailsSectionComponent["itemDetailsForm"]; cardDetails?: CardDetailsSectionComponent["cardDetailsForm"]; + identityDetails?: IdentitySectionComponent["identityForm"]; }; /** diff --git a/libs/vault/src/cipher-form/components/cipher-form.component.html b/libs/vault/src/cipher-form/components/cipher-form.component.html index d57681dad6..8b5d470899 100644 --- a/libs/vault/src/cipher-form/components/cipher-form.component.html +++ b/libs/vault/src/cipher-form/components/cipher-form.component.html @@ -6,6 +6,12 @@ [originalCipherView]="originalCipherView" > + + + + +

{{ "personalDetails" | i18n }}

+
+ + + + {{ "title" | i18n }} + + + + + + + + + {{ "firstName" | i18n }} + + + + + + {{ "lastName" | i18n }} + + + + + + {{ "username" | i18n }} + + + + + + {{ "company" | i18n }} + + + + +
+ + +

{{ "identification" | i18n }}

+
+ + + + {{ "ssn" | i18n }} + + + + + + + {{ "passportNumber" | i18n }} + + + + + + + {{ "licenseNumber" | i18n }} + + + + +
+ + +

{{ "contactInfo" | i18n }}

+
+ + + + {{ "email" | i18n }} + + + + + + {{ "phone" | i18n }} + + + + +
+ + +

{{ "address" | i18n }}

+
+ + + + {{ "address1" | i18n }} + + + + + + {{ "address2" | i18n }} + + + + + + {{ "address3" | i18n }} + + + + + + {{ "cityTown" | i18n }} + + + + + + {{ "stateProvince" | i18n }} + + + + + + {{ "zipPostalCode" | i18n }} + + + + + + {{ "country" | i18n }} + + + + +
+ diff --git a/libs/vault/src/cipher-form/components/identity/identity.component.ts b/libs/vault/src/cipher-form/components/identity/identity.component.ts new file mode 100644 index 0000000000..c1f4450361 --- /dev/null +++ b/libs/vault/src/cipher-form/components/identity/identity.component.ts @@ -0,0 +1,136 @@ +import { CommonModule } from "@angular/common"; +import { Component, Input, OnInit } from "@angular/core"; +import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; +import { FormBuilder, ReactiveFormsModule } from "@angular/forms"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { CipherView } from "@bitwarden/common/vault/models/view/cipher.view"; +import { IdentityView } from "@bitwarden/common/vault/models/view/identity.view"; +import { + ButtonModule, + SectionComponent, + SectionHeaderComponent, + CardComponent, + FormFieldModule, + IconButtonModule, + SelectModule, +} from "@bitwarden/components"; + +import { CipherFormContainer } from "../../cipher-form-container"; + +@Component({ + standalone: true, + selector: "vault-identity-section", + templateUrl: "./identity.component.html", + imports: [ + CommonModule, + ButtonModule, + JslibModule, + ReactiveFormsModule, + SectionComponent, + SectionHeaderComponent, + CardComponent, + FormFieldModule, + IconButtonModule, + SelectModule, + ], +}) +export class IdentitySectionComponent implements OnInit { + @Input() originalCipherView: CipherView; + @Input() disabled: boolean; + identityTitleOptions = [ + { name: "-- " + this.i18nService.t("select") + " --", value: null }, + { name: this.i18nService.t("mr"), value: this.i18nService.t("mr") }, + { name: this.i18nService.t("mrs"), value: this.i18nService.t("mrs") }, + { name: this.i18nService.t("ms"), value: this.i18nService.t("ms") }, + { name: this.i18nService.t("mx"), value: this.i18nService.t("mx") }, + { name: this.i18nService.t("dr"), value: this.i18nService.t("dr") }, + ]; + + protected identityForm = this.formBuilder.group({ + title: [null], + firstName: [""], + lastName: [""], + username: [""], + company: [""], + ssn: [""], + passportNumber: [""], + licenseNumber: [""], + email: [""], + phone: [""], + address1: [""], + address2: [""], + address3: [""], + city: [""], + state: [""], + postalCode: [""], + country: [""], + }); + + constructor( + private cipherFormContainer: CipherFormContainer, + private formBuilder: FormBuilder, + private i18nService: I18nService, + ) { + this.cipherFormContainer.registerChildForm("identityDetails", this.identityForm); + this.identityForm.valueChanges.pipe(takeUntilDestroyed()).subscribe((value) => { + const data = new IdentityView(); + data.title = value.title; + data.firstName = value.firstName; + data.lastName = value.lastName; + data.username = value.username; + data.company = value.company; + data.ssn = value.ssn; + data.passportNumber = value.passportNumber; + data.licenseNumber = value.licenseNumber; + data.email = value.email; + data.phone = value.phone; + data.address1 = value.address1; + data.address2 = value.address2; + data.address3 = value.address3; + data.city = value.city; + data.state = value.state; + data.postalCode = value.postalCode; + data.country = value.country; + + this.cipherFormContainer.patchCipher({ + identity: data, + }); + }); + } + + ngOnInit() { + // If true will disable all inputs + if (this.disabled) { + this.identityForm.disable(); + } + + if (this.originalCipherView && this.originalCipherView.id) { + this.populateFormData(); + } + } + + populateFormData() { + const { identity } = this.originalCipherView; + this.identityForm.setValue({ + title: identity.title, + firstName: identity.firstName, + lastName: identity.lastName, + username: identity.username, + company: identity.company, + ssn: identity.ssn, + passportNumber: identity.passportNumber, + licenseNumber: identity.licenseNumber, + email: identity.email, + phone: identity.phone, + address1: identity.address1, + address2: identity.address2, + address3: identity.address3, + city: identity.city, + state: identity.state, + postalCode: identity.postalCode, + country: identity.country, + }); + } +}