mirror of
https://github.com/bitwarden/desktop.git
synced 2024-12-28 17:18:24 +01:00
api actions on add/edit
This commit is contained in:
parent
619d3fd075
commit
1c41dfa196
@ -1,5 +1,5 @@
|
||||
<div id="login">
|
||||
<form #form (ngSubmit)="onSubmit()" [appApiForm]="formPromise">
|
||||
<form #form (ngSubmit)="onSubmit()" [appApiAction]="formPromise">
|
||||
<div class="content">
|
||||
<img src="../../images/logo@2x.png" alt="bitwarden">
|
||||
<p>{{'loginOrCreateNewAccount' | i18n}}</p>
|
||||
|
@ -11,7 +11,7 @@ import { ServicesModule } from './services/services.module';
|
||||
import { ToasterModule } from 'angular2-toaster';
|
||||
|
||||
import { AddEditComponent } from './vault/add-edit.component';
|
||||
import { ApiFormDirective } from './directives/api-form.directive';
|
||||
import { ApiActionDirective } from './directives/api-action.directive';
|
||||
import { AppComponent } from './app.component';
|
||||
import { AttachmentsComponent } from './vault/attachments.component';
|
||||
import { AutofocusDirective } from './directives/autofocus.directive';
|
||||
@ -47,7 +47,7 @@ import { ViewComponent } from './vault/view.component';
|
||||
],
|
||||
declarations: [
|
||||
AddEditComponent,
|
||||
ApiFormDirective,
|
||||
ApiActionDirective,
|
||||
AppComponent,
|
||||
AttachmentsComponent,
|
||||
AutofocusDirective,
|
||||
|
@ -8,21 +8,21 @@ import {
|
||||
import { ValidationService } from '../services/validation.service';
|
||||
|
||||
@Directive({
|
||||
selector: '[appApiForm]',
|
||||
selector: '[appApiAction]',
|
||||
})
|
||||
export class ApiFormDirective implements OnChanges {
|
||||
@Input() appApiForm: Promise<any>;
|
||||
export class ApiActionDirective implements OnChanges {
|
||||
@Input() appApiAction: Promise<any>;
|
||||
|
||||
constructor(private el: ElementRef, private validationService: ValidationService) { }
|
||||
|
||||
ngOnChanges(changes: any) {
|
||||
if (this.appApiForm == null || this.appApiForm.then == null) {
|
||||
if (this.appApiAction == null || this.appApiAction.then == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.el.nativeElement.loading = true;
|
||||
|
||||
this.appApiForm.then((response: any) => {
|
||||
this.appApiAction.then((response: any) => {
|
||||
this.el.nativeElement.loading = false;
|
||||
}, (e: any) => {
|
||||
this.el.nativeElement.loading = false;
|
@ -1,265 +1,272 @@
|
||||
<div class="content">
|
||||
<div class="inner-content" *ngIf="cipher">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'itemInformation' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" *ngIf="!editMode" appBoxRow>
|
||||
<label for="type">{{'type' | i18n}}</label>
|
||||
<select id="type" name="Type" [(ngModel)]="cipher.type">
|
||||
<option *ngFor="let o of typeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="content">
|
||||
<div class="inner-content" *ngIf="cipher">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{title}}
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="name">{{'name' | i18n}}</label>
|
||||
<input id="name" type="text" name="Name" [(ngModel)]="cipher.name" [appAutofocus]="!editMode">
|
||||
</div>
|
||||
<!-- Login -->
|
||||
<div *ngIf="cipher.type === cipherType.Login">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="loginUri">{{'uri' | i18n}}</label>
|
||||
<input id="loginUri" type="text" name="Login.Uri" [(ngModel)]="cipher.login.uri">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="loginUsername">{{'username' | i18n}}</label>
|
||||
<input id="loginUsername" type="text" name="Login.Username"
|
||||
[(ngModel)]="cipher.login.username">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="loginPassword">{{'password' | i18n}}</label>
|
||||
<input id="loginPassword" class="monospaced" type="{{showPassword ? 'text' : 'password'}}"
|
||||
name="Login.Password" [(ngModel)]="cipher.login.password">
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick title="{{'toggleVisibility' | i18n}}"
|
||||
(click)="togglePassword()">
|
||||
<i class="fa fa-lg"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick title="{{'generatePassword' | i18n}}"
|
||||
(click)="generatePassword()">
|
||||
<i class="fa fa-lg fa-refresh"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card -->
|
||||
<div *ngIf="cipher.type === cipherType.Card">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardCardholderName">{{'cardholderName' | i18n}}</label>
|
||||
<input id="cardCardholderName" type="text" name="Card.CardCardholderName"
|
||||
[(ngModel)]="cipher.card.cardholderName">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardNumber">{{'number' | i18n}}</label>
|
||||
<input id="cardNumber" type="text" name="Card.Number" [(ngModel)]="cipher.card.number">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardBrand">{{'brand' | i18n}}</label>
|
||||
<select id="cardBrand" name="Card.Brand" [(ngModel)]="cipher.card.brand">
|
||||
<option *ngFor="let o of cardBrandOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" *ngIf="!editMode" appBoxRow>
|
||||
<label for="type">{{'type' | i18n}}</label>
|
||||
<select id="type" name="Type" [(ngModel)]="cipher.type">
|
||||
<option *ngFor="let o of typeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardExpMonth">{{'expirationMonth' | i18n}}</label>
|
||||
<select id="cardExpMonth" name="Card.ExpMonth" [(ngModel)]="cipher.card.expMonth">
|
||||
<option *ngFor="let o of cardExpMonthOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
<label for="name">{{'name' | i18n}}</label>
|
||||
<input id="name" type="text" name="Name" [(ngModel)]="cipher.name" [appAutofocus]="!editMode">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardExpYear">{{'expirationYear' | i18n}}</label>
|
||||
<input id="cardExpYear" type="text" name="Card.ExpYear" [(ngModel)]="cipher.card.expYear"
|
||||
placeholder="{{'ex' | i18n}} 2019">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardCode">{{'securityCode' | i18n}}</label>
|
||||
<input id="cardCode" type="text" name="Card.Code" [(ngModel)]="cipher.card.code">
|
||||
</div>
|
||||
</div>
|
||||
<!-- Identity -->
|
||||
<div *ngIf="cipher.type === cipherType.Identity">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idTitle">{{'title' | i18n}}</label>
|
||||
<select id="idTitle" name="Identity.Title" [(ngModel)]="cipher.identity.title">
|
||||
<option *ngFor="let o of identityTitleOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idFirstName">{{'firstName' | i18n}}</label>
|
||||
<input id="idFirstName" type="text" name="Identity.FirstName"
|
||||
[(ngModel)]="cipher.identity.firstName">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idMiddleName">{{'middleName' | i18n}}</label>
|
||||
<input id="idMiddleName" type="text" name="Identity.MiddleName"
|
||||
[(ngModel)]="cipher.identity.middleName">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idLastName">{{'lastName' | i18n}}</label>
|
||||
<input id="idLastName" type="text" name="Identity.LastName"
|
||||
[(ngModel)]="cipher.identity.lastName">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idUsername">{{'username' | i18n}}</label>
|
||||
<input id="idUsername" type="text" name="Identity.Username"
|
||||
[(ngModel)]="cipher.identity.username">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idCompany">{{'company' | i18n}}</label>
|
||||
<input id="idCompany" type="text" name="Identity.Company"
|
||||
[(ngModel)]="cipher.identity.company">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idSsn">{{'ssn' | i18n}}</label>
|
||||
<input id="idSsn" type="text" name="Identity.SSN" [(ngModel)]="cipher.identity.ssn">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idPassportNumber">{{'passportNumber' | i18n}}</label>
|
||||
<input id="idPassportNumber" type="text" name="Identity.PassportNumber"
|
||||
[(ngModel)]="cipher.identity.passportNumber">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idLicenseNumber">{{'licenseNumber' | i18n}}</label>
|
||||
<input id="idLicenseNumber" type="text" name="Identity.LicenseNumber"
|
||||
[(ngModel)]="cipher.identity.licenseNumber">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idEmail">{{'email' | i18n}}</label>
|
||||
<input id="idEmail" type="text" name="Identity.Email" [(ngModel)]="cipher.identity.email">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idPhone">{{'phone' | i18n}}</label>
|
||||
<input id="idPhone" type="text" name="Identity.Phone" [(ngModel)]="cipher.identity.phone">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idAddress1">{{'address1' | i18n}}</label>
|
||||
<input id="idAddress1" type="text" name="Identity.Address1"
|
||||
[(ngModel)]="cipher.identity.address1">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idAddress2">{{'address2' | i18n}}</label>
|
||||
<input id="idAddress2" type="text" name="Identity.Address2"
|
||||
[(ngModel)]="cipher.identity.address2">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idAddress3">{{'address3' | i18n}}</label>
|
||||
<input id="idAddress3" type="text" name="Identity.Address3"
|
||||
[(ngModel)]="cipher.identity.address3">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idCity">{{'cityTown' | i18n}}</label>
|
||||
<input id="idCity" type="text" name="Identity.City" [(ngModel)]="cipher.identity.city">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idState">{{'stateProvince' | i18n}}</label>
|
||||
<input id="idState" type="text" name="Identity.State" [(ngModel)]="cipher.identity.state">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idPostalCode">{{'zipPostalCode' | i18n}}</label>
|
||||
<input id="idPostalCode" type="text" name="Identity.PostalCode"
|
||||
[(ngModel)]="cipher.identity.postalCode">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idCountry">{{'country' | i18n}}</label>
|
||||
<input id="idCountry" type="text" name="Identity.Country"
|
||||
[(ngModel)]="cipher.identity.country">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" *ngIf="cipher.type === cipherType.Login" appBoxRow>
|
||||
<label for="loginTotp">{{'authenticatorKeyTotp' | i18n}}</label>
|
||||
<input id="loginTotp" type="text" name="Login.Totp" class="monospaced"
|
||||
[(ngModel)]="cipher.login.totp">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="folder">{{'folder' | i18n}}</label>
|
||||
<select id="folder" name="FolderId" [(ngModel)]="cipher.folderId">
|
||||
<option *ngFor="let f of folders" [ngValue]="f.id">{{f.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="favorite">{{'favorite' | i18n}}</label>
|
||||
<input id="favorite" type="checkbox" name="Favorite" [(ngModel)]="cipher.favorite">
|
||||
</div>
|
||||
<a class="box-content-row box-content-row-flex" href="#" appStopClick appBlurClick
|
||||
(click)="attachments()" *ngIf="editMode">
|
||||
<div class="row-main">{{'attachments' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right row-sub-icon"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<label for="notes">{{'notes' | i18n}}</label>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<textarea id="notes" name="Notes" rows="6" [(ngModel)]="cipher.notes"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'customFields' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div *ngIf="cipher.hasFields">
|
||||
<div class="box-content-row box-content-row-cf" appBoxRow
|
||||
*ngFor="let f of cipher.fields; let i = index"
|
||||
[ngClass]="{'box-content-row-checkbox': f.type === fieldType.Boolean}">
|
||||
<a href="#" appStopClick (click)="removeField(f)" title="{{'remove' | i18n}}">
|
||||
<i class="fa fa-close fa-lg"></i>
|
||||
</a>
|
||||
<label for="fieldName{{i}}" class="sr-only">{{'name' | i18n}}</label>
|
||||
<label for="fieldValue{{i}}" class="sr-only">{{'value' | i18n}}</label>
|
||||
<div class="row-main">
|
||||
<input id="fieldName{{i}}" type="text" name="Field.Name{{i}}" [(ngModel)]="f.name"
|
||||
class="row-label" placeholder="{{'name' | i18n}}">
|
||||
<input id="fieldValue{{i}}" type="text" name="Field.Value{{i}}" [(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Text" placeholder="{{'value' | i18n}}">
|
||||
<input id="fieldValue{{i}}" type="{{f.showValue ? 'text' : 'password'}}"
|
||||
name="Field.Value{{i}}" [(ngModel)]="f.value" class="monospaced"
|
||||
*ngIf="f.type === fieldType.Hidden" placeholder="{{'value' | i18n}}">
|
||||
<!-- Login -->
|
||||
<div *ngIf="cipher.type === cipherType.Login">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="loginUri">{{'uri' | i18n}}</label>
|
||||
<input id="loginUri" type="text" name="Login.Uri" [(ngModel)]="cipher.login.uri">
|
||||
</div>
|
||||
<input id="fieldValue{{i}}" name="Field.Value{{i}}" type="checkbox"
|
||||
[(ngModel)]="f.value" *ngIf="f.type === fieldType.Boolean">
|
||||
<div class="action-buttons" *ngIf="f.type === fieldType.Hidden">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick title="{{'toggleVisibility' | i18n}}"
|
||||
(click)="toggleFieldValue(f)">
|
||||
<i class="fa fa-lg"
|
||||
[ngClass]="{'fa-eye': !f.showValue, 'fa-eye-slash': f.showValue}"></i>
|
||||
</a>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="loginUsername">{{'username' | i18n}}</label>
|
||||
<input id="loginUsername" type="text" name="Login.Username"
|
||||
[(ngModel)]="cipher.login.username">
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-flex" appBoxRow>
|
||||
<div class="row-main">
|
||||
<label for="loginPassword">{{'password' | i18n}}</label>
|
||||
<input id="loginPassword" class="monospaced"
|
||||
type="{{showPassword ? 'text' : 'password'}}" name="Login.Password"
|
||||
[(ngModel)]="cipher.login.password">
|
||||
</div>
|
||||
<div class="action-buttons">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick
|
||||
title="{{'toggleVisibility' | i18n}}" (click)="togglePassword()">
|
||||
<i class="fa fa-lg"
|
||||
[ngClass]="{'fa-eye': !showPassword, 'fa-eye-slash': showPassword}"></i>
|
||||
</a>
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick
|
||||
title="{{'generatePassword' | i18n}}" (click)="generatePassword()">
|
||||
<i class="fa fa-lg fa-refresh"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Card -->
|
||||
<div *ngIf="cipher.type === cipherType.Card">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardCardholderName">{{'cardholderName' | i18n}}</label>
|
||||
<input id="cardCardholderName" type="text" name="Card.CardCardholderName"
|
||||
[(ngModel)]="cipher.card.cardholderName">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardNumber">{{'number' | i18n}}</label>
|
||||
<input id="cardNumber" type="text" name="Card.Number" [(ngModel)]="cipher.card.number">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardBrand">{{'brand' | i18n}}</label>
|
||||
<select id="cardBrand" name="Card.Brand" [(ngModel)]="cipher.card.brand">
|
||||
<option *ngFor="let o of cardBrandOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardExpMonth">{{'expirationMonth' | i18n}}</label>
|
||||
<select id="cardExpMonth" name="Card.ExpMonth" [(ngModel)]="cipher.card.expMonth">
|
||||
<option *ngFor="let o of cardExpMonthOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardExpYear">{{'expirationYear' | i18n}}</label>
|
||||
<input id="cardExpYear" type="text" name="Card.ExpYear" [(ngModel)]="cipher.card.expYear"
|
||||
placeholder="{{'ex' | i18n}} 2019">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="cardCode">{{'securityCode' | i18n}}</label>
|
||||
<input id="cardCode" type="text" name="Card.Code" [(ngModel)]="cipher.card.code">
|
||||
</div>
|
||||
</div>
|
||||
<!-- Identity -->
|
||||
<div *ngIf="cipher.type === cipherType.Identity">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idTitle">{{'title' | i18n}}</label>
|
||||
<select id="idTitle" name="Identity.Title" [(ngModel)]="cipher.identity.title">
|
||||
<option *ngFor="let o of identityTitleOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idFirstName">{{'firstName' | i18n}}</label>
|
||||
<input id="idFirstName" type="text" name="Identity.FirstName"
|
||||
[(ngModel)]="cipher.identity.firstName">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idMiddleName">{{'middleName' | i18n}}</label>
|
||||
<input id="idMiddleName" type="text" name="Identity.MiddleName"
|
||||
[(ngModel)]="cipher.identity.middleName">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idLastName">{{'lastName' | i18n}}</label>
|
||||
<input id="idLastName" type="text" name="Identity.LastName"
|
||||
[(ngModel)]="cipher.identity.lastName">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idUsername">{{'username' | i18n}}</label>
|
||||
<input id="idUsername" type="text" name="Identity.Username"
|
||||
[(ngModel)]="cipher.identity.username">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idCompany">{{'company' | i18n}}</label>
|
||||
<input id="idCompany" type="text" name="Identity.Company"
|
||||
[(ngModel)]="cipher.identity.company">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idSsn">{{'ssn' | i18n}}</label>
|
||||
<input id="idSsn" type="text" name="Identity.SSN" [(ngModel)]="cipher.identity.ssn">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idPassportNumber">{{'passportNumber' | i18n}}</label>
|
||||
<input id="idPassportNumber" type="text" name="Identity.PassportNumber"
|
||||
[(ngModel)]="cipher.identity.passportNumber">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idLicenseNumber">{{'licenseNumber' | i18n}}</label>
|
||||
<input id="idLicenseNumber" type="text" name="Identity.LicenseNumber"
|
||||
[(ngModel)]="cipher.identity.licenseNumber">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idEmail">{{'email' | i18n}}</label>
|
||||
<input id="idEmail" type="text" name="Identity.Email" [(ngModel)]="cipher.identity.email">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idPhone">{{'phone' | i18n}}</label>
|
||||
<input id="idPhone" type="text" name="Identity.Phone" [(ngModel)]="cipher.identity.phone">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idAddress1">{{'address1' | i18n}}</label>
|
||||
<input id="idAddress1" type="text" name="Identity.Address1"
|
||||
[(ngModel)]="cipher.identity.address1">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idAddress2">{{'address2' | i18n}}</label>
|
||||
<input id="idAddress2" type="text" name="Identity.Address2"
|
||||
[(ngModel)]="cipher.identity.address2">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idAddress3">{{'address3' | i18n}}</label>
|
||||
<input id="idAddress3" type="text" name="Identity.Address3"
|
||||
[(ngModel)]="cipher.identity.address3">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idCity">{{'cityTown' | i18n}}</label>
|
||||
<input id="idCity" type="text" name="Identity.City" [(ngModel)]="cipher.identity.city">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idState">{{'stateProvince' | i18n}}</label>
|
||||
<input id="idState" type="text" name="Identity.State" [(ngModel)]="cipher.identity.state">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idPostalCode">{{'zipPostalCode' | i18n}}</label>
|
||||
<input id="idPostalCode" type="text" name="Identity.PostalCode"
|
||||
[(ngModel)]="cipher.identity.postalCode">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="idCountry">{{'country' | i18n}}</label>
|
||||
<input id="idCountry" type="text" name="Identity.Country"
|
||||
[(ngModel)]="cipher.identity.country">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<a href="#" appStopClick (click)="addField()">
|
||||
<i class="fa fa-plus-circle fa-fw fa-lg"></i> {{'newCustomField' | i18n}}
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" *ngIf="cipher.type === cipherType.Login" appBoxRow>
|
||||
<label for="loginTotp">{{'authenticatorKeyTotp' | i18n}}</label>
|
||||
<input id="loginTotp" type="text" name="Login.Totp" class="monospaced"
|
||||
[(ngModel)]="cipher.login.totp">
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<label for="folder">{{'folder' | i18n}}</label>
|
||||
<select id="folder" name="FolderId" [(ngModel)]="cipher.folderId">
|
||||
<option *ngFor="let f of folders" [ngValue]="f.id">{{f.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="box-content-row box-content-row-checkbox" appBoxRow>
|
||||
<label for="favorite">{{'favorite' | i18n}}</label>
|
||||
<input id="favorite" type="checkbox" name="Favorite" [(ngModel)]="cipher.favorite">
|
||||
</div>
|
||||
<a class="box-content-row box-content-row-flex" href="#" appStopClick appBlurClick
|
||||
(click)="attachments()" *ngIf="editMode">
|
||||
<div class="row-main">{{'attachments' | i18n}}</div>
|
||||
<i class="fa fa-chevron-right row-sub-icon"></i>
|
||||
</a>
|
||||
<label for="addFieldType" class="sr-only">{{'type' | i18n}}</label>
|
||||
<select id="addFieldType" [(ngModel)]="addFieldType" class="field-type">
|
||||
<option *ngFor="let o of addFieldTypeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
<label for="notes">{{'notes' | i18n}}</label>
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<textarea id="notes" name="Notes" rows="6" [(ngModel)]="cipher.notes"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
{{'customFields' | i18n}}
|
||||
</div>
|
||||
<div class="box-content">
|
||||
<div *ngIf="cipher.hasFields">
|
||||
<div class="box-content-row box-content-row-cf" appBoxRow
|
||||
*ngFor="let f of cipher.fields; let i = index"
|
||||
[ngClass]="{'box-content-row-checkbox': f.type === fieldType.Boolean}">
|
||||
<a href="#" appStopClick (click)="removeField(f)" title="{{'remove' | i18n}}">
|
||||
<i class="fa fa-close fa-lg"></i>
|
||||
</a>
|
||||
<label for="fieldName{{i}}" class="sr-only">{{'name' | i18n}}</label>
|
||||
<label for="fieldValue{{i}}" class="sr-only">{{'value' | i18n}}</label>
|
||||
<div class="row-main">
|
||||
<input id="fieldName{{i}}" type="text" name="Field.Name{{i}}" [(ngModel)]="f.name"
|
||||
class="row-label" placeholder="{{'name' | i18n}}">
|
||||
<input id="fieldValue{{i}}" type="text" name="Field.Value{{i}}" [(ngModel)]="f.value"
|
||||
*ngIf="f.type === fieldType.Text" placeholder="{{'value' | i18n}}">
|
||||
<input id="fieldValue{{i}}" type="{{f.showValue ? 'text' : 'password'}}"
|
||||
name="Field.Value{{i}}" [(ngModel)]="f.value" class="monospaced"
|
||||
*ngIf="f.type === fieldType.Hidden" placeholder="{{'value' | i18n}}">
|
||||
</div>
|
||||
<input id="fieldValue{{i}}" name="Field.Value{{i}}" type="checkbox"
|
||||
[(ngModel)]="f.value" *ngIf="f.type === fieldType.Boolean">
|
||||
<div class="action-buttons" *ngIf="f.type === fieldType.Hidden">
|
||||
<a class="row-btn" href="#" appStopClick appBlurClick
|
||||
title="{{'toggleVisibility' | i18n}}" (click)="toggleFieldValue(f)">
|
||||
<i class="fa fa-lg"
|
||||
[ngClass]="{'fa-eye': !f.showValue, 'fa-eye-slash': f.showValue}"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="box-content-row" appBoxRow>
|
||||
<a href="#" appStopClick (click)="addField()">
|
||||
<i class="fa fa-plus-circle fa-fw fa-lg"></i> {{'newCustomField' | i18n}}
|
||||
</a>
|
||||
<label for="addFieldType" class="sr-only">{{'type' | i18n}}</label>
|
||||
<select id="addFieldType" name="AddFieldType" [(ngModel)]="addFieldType" class="field-type">
|
||||
<option *ngFor="let o of addFieldTypeOptions" [ngValue]="o.value">{{o.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button appBlurClick (click)="save()" class="primary" title="{{'save' | i18n}}">
|
||||
<i class="fa fa-save fa-lg"></i> {{'save' | i18n}}
|
||||
</button>
|
||||
<button appBlurClick (click)="cancel()" title="{{'cancel' | i18n}}">
|
||||
{{'cancel' | i18n}}
|
||||
</button>
|
||||
<div class="right">
|
||||
<button appBlurClick (click)="delete()" class="danger" title="{{'delete' | i18n}}" *ngIf="editMode">
|
||||
<i class="fa fa-trash-o fa-lg"></i>
|
||||
<div class="footer">
|
||||
<button appBlurClick type="submit" class="primary" title="{{'save' | i18n}}" [disabled]="form.loading">
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading"></i>
|
||||
</button>
|
||||
<button appBlurClick type="button" (click)="cancel()" title="{{'cancel' | i18n}}">
|
||||
{{'cancel' | i18n}}
|
||||
</button>
|
||||
<div class="right">
|
||||
<button #deleteBtn appBlurClick type="button" (click)="delete()" class="danger"
|
||||
title="{{'delete' | i18n}}" *ngIf="editMode" [disabled]="deleteBtn.loading"
|
||||
[appApiAction]="deletePromise">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -44,6 +44,9 @@ export class AddEditComponent implements OnChanges {
|
||||
editMode: boolean = false;
|
||||
cipher: CipherView;
|
||||
folders: FolderView[];
|
||||
title: string;
|
||||
formPromise: Promise<any>;
|
||||
deletePromise: Promise<any>;
|
||||
showPassword: boolean = false;
|
||||
cipherType = CipherType;
|
||||
fieldType = FieldType;
|
||||
@ -109,9 +112,11 @@ export class AddEditComponent implements OnChanges {
|
||||
|
||||
if (this.editMode) {
|
||||
this.editMode = true;
|
||||
this.title = this.i18nService.t('editItem');
|
||||
const cipher = await this.cipherService.get(this.cipherId);
|
||||
this.cipher = await cipher.decrypt();
|
||||
} else {
|
||||
this.title = this.i18nService.t('addItem');
|
||||
this.cipher = new CipherView();
|
||||
this.cipher.folderId = this.folderId;
|
||||
this.cipher.type = CipherType.Login;
|
||||
@ -125,7 +130,7 @@ export class AddEditComponent implements OnChanges {
|
||||
this.folders = await this.folderService.getAllDecrypted();
|
||||
}
|
||||
|
||||
async save() {
|
||||
async submit() {
|
||||
if (this.cipher.name == null || this.cipher.name === '') {
|
||||
this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'),
|
||||
this.i18nService.t('nameRequired'));
|
||||
@ -133,10 +138,14 @@ export class AddEditComponent implements OnChanges {
|
||||
}
|
||||
|
||||
const cipher = await this.cipherService.encrypt(this.cipher);
|
||||
await this.cipherService.saveWithServer(cipher);
|
||||
this.analytics.eventTrack.next({ action: this.editMode ? 'Edited Cipher' : 'Added Cipher' });
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t(this.editMode ? 'editedItem' : 'addedItem'));
|
||||
this.onSavedCipher.emit(this.cipher);
|
||||
|
||||
try {
|
||||
this.formPromise = this.cipherService.saveWithServer(cipher);
|
||||
await this.formPromise;
|
||||
this.analytics.eventTrack.next({ action: this.editMode ? 'Edited Cipher' : 'Added Cipher' });
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t(this.editMode ? 'editedItem' : 'addedItem'));
|
||||
this.onSavedCipher.emit(this.cipher);
|
||||
} catch { }
|
||||
}
|
||||
|
||||
addField() {
|
||||
@ -169,10 +178,13 @@ export class AddEditComponent implements OnChanges {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.cipherService.deleteWithServer(this.cipher.id);
|
||||
this.analytics.eventTrack.next({ action: 'Deleted Cipher' });
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('deletedItem'));
|
||||
this.onDeletedCipher.emit(this.cipher);
|
||||
try {
|
||||
this.deletePromise = this.cipherService.deleteWithServer(this.cipher.id);
|
||||
await this.deletePromise;
|
||||
this.analytics.eventTrack.next({ action: 'Deleted Cipher' });
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('deletedItem'));
|
||||
this.onDeletedCipher.emit(this.cipher);
|
||||
} catch { }
|
||||
}
|
||||
|
||||
generatePassword() {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<div class="modal fade">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise">
|
||||
<div class="modal-body">
|
||||
<div class="box">
|
||||
<div class="box-header">
|
||||
@ -16,17 +16,20 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="primary" appBlurClick (click)="save()">
|
||||
<i class="fa fa-lg fa-save"></i> {{'save' | i18n}}
|
||||
<button appBlurClick type="submit" class="primary" title="{{'save' | i18n}}" [disabled]="form.loading">
|
||||
<i class="fa fa-save fa-lg fa-fw" [hidden]="form.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!form.loading"></i>
|
||||
</button>
|
||||
<button type="button" data-dismiss="modal">{{'close' | i18n}}</button>
|
||||
<div class="right">
|
||||
<button appBlurClick (click)="delete()" class="danger" title="{{'delete' | i18n}}"
|
||||
*ngIf="editMode">
|
||||
<i class="fa fa-trash-o fa-lg"></i>
|
||||
<button #deleteBtn appBlurClick type="button" (click)="delete()" class="danger"
|
||||
title="{{'delete' | i18n}}" *ngIf="editMode" [disabled]="deleteBtn.loading"
|
||||
[appApiAction]="deletePromise">
|
||||
<i class="fa fa-trash-o fa-lg fa-fw" [hidden]="deleteBtn.loading"></i>
|
||||
<i class="fa fa-spinner fa-spin fa-lg fa-fw" [hidden]="!deleteBtn.loading"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -28,6 +28,8 @@ export class FolderAddEditComponent implements OnInit {
|
||||
editMode: boolean = false;
|
||||
folder: FolderView = new FolderView();
|
||||
title: string;
|
||||
formPromise: Promise<any>;
|
||||
deletePromise: Promise<any>;
|
||||
|
||||
constructor(private folderService: FolderService, private i18nService: I18nService,
|
||||
private analytics: Angulartics2, private toasterService: ToasterService) { }
|
||||
@ -45,19 +47,22 @@ export class FolderAddEditComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
async save() {
|
||||
async submit() {
|
||||
if (this.folder.name == null || this.folder.name === '') {
|
||||
this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'),
|
||||
this.i18nService.t('nameRequired'));
|
||||
return;
|
||||
}
|
||||
|
||||
const folder = await this.folderService.encrypt(this.folder);
|
||||
await this.folderService.saveWithServer(folder);
|
||||
this.analytics.eventTrack.next({ action: this.editMode ? 'Edited Folder' : 'Added Folder' });
|
||||
this.toasterService.popAsync('success', null,
|
||||
this.i18nService.t(this.editMode ? 'editedFolder' : 'addedFolder'));
|
||||
this.onSavedFolder.emit(this.folder);
|
||||
try {
|
||||
const folder = await this.folderService.encrypt(this.folder);
|
||||
this.formPromise = this.folderService.saveWithServer(folder);;
|
||||
await this.formPromise;
|
||||
this.analytics.eventTrack.next({ action: this.editMode ? 'Edited Folder' : 'Added Folder' });
|
||||
this.toasterService.popAsync('success', null,
|
||||
this.i18nService.t(this.editMode ? 'editedFolder' : 'addedFolder'));
|
||||
this.onSavedFolder.emit(this.folder);
|
||||
} catch { }
|
||||
}
|
||||
|
||||
async delete() {
|
||||
@ -65,9 +70,12 @@ export class FolderAddEditComponent implements OnInit {
|
||||
return;
|
||||
}
|
||||
|
||||
await this.folderService.deleteWithServer(this.folder.id);
|
||||
this.analytics.eventTrack.next({ action: 'Deleted Folder' });
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('deletedFolder'));
|
||||
this.onDeletedFolder.emit(this.folder);
|
||||
try {
|
||||
this.deletePromise = this.folderService.deleteWithServer(this.folder.id);
|
||||
await this.deletePromise;
|
||||
this.analytics.eventTrack.next({ action: 'Deleted Folder' });
|
||||
this.toasterService.popAsync('success', null, this.i18nService.t('deletedFolder'));
|
||||
this.onDeletedFolder.emit(this.folder);
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +45,7 @@
|
||||
<li *ngFor="let f of folders" [ngClass]="{active: selectedFolder && f.id === selectedFolderId}">
|
||||
<a href="#" appStopClick appBlurClick (click)="folder(f)">
|
||||
<i class="fa-li fa fa-caret-right"></i> {{f.name}}
|
||||
<span appStopProp (click)="editFolder(f)" title="{{'editFolder' | i18n}}">
|
||||
<span appStopProp appStopClick (click)="editFolder(f)" title="{{'editFolder' | i18n}}">
|
||||
<i class="fa fa-pencil fa-fw"></i>
|
||||
</span>
|
||||
</a>
|
||||
|
@ -231,5 +231,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">
|
||||
<button appBlurClick (click)="edit()" title="{{'editItem' | i18n}}">{{'edit' | i18n}}</button>
|
||||
<button appBlurClick class="primary" (click)="edit()" title="{{'edit' | i18n}}">
|
||||
<i class="fa fa-pencil fa-fw fa-lg"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
@ -59,9 +59,6 @@
|
||||
"editItem": {
|
||||
"message": "Edit Item"
|
||||
},
|
||||
"itemInformation": {
|
||||
"message": "Item Information"
|
||||
},
|
||||
"emailAddress": {
|
||||
"message": "Email Address"
|
||||
},
|
||||
|
@ -21,7 +21,7 @@
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
background-color: darken($button-backgound-color, 1.5%);
|
||||
opacity: 0.65;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
|
@ -125,6 +125,11 @@
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
> form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
#groupings, #items, #details {
|
||||
|
Loading…
Reference in New Issue
Block a user