1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-10 12:33:26 +00:00

my organization page

This commit is contained in:
Kyle Spearrin
2018-07-16 12:42:49 -04:00
parent a1d52af0ba
commit 786f6953e7
12 changed files with 288 additions and 6 deletions

View File

@@ -0,0 +1,55 @@
<div class="page-header">
<h1>{{'myOrganization' | i18n}}</h1>
</div>
<div *ngIf="loading">
<i class="fa fa-spinner fa-spin text-muted"></i>
</div>
<form *ngIf="org && !loading" #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
<div class="row">
<div class="col-6">
<div class="form-group">
<label for="name">{{'organizationName' | i18n}}</label>
<input id="name" class="form-control" type="text" name="Name" [(ngModel)]="org.name">
</div>
<div class="form-group">
<label for="billingEmail">{{'billingEmail' | i18n}}</label>
<input id="billingEmail" class="form-control" type="text" name="BillingEmail" [(ngModel)]="org.billingEmail">
</div>
<div class="form-group">
<label for="businessName">{{'businessName' | i18n}}</label>
<input id="businessName" class="form-control" type="text" name="BusinessName" [(ngModel)]="org.businessName">
</div>
</div>
<div class="col-6">
<app-avatar data="{{org.name}}" dynamic="true" width="75" height="75" fontSize="35"></app-avatar>
</div>
</div>
<button type="submit" class="btn btn-primary btn-submit" appBlurClick [disabled]="form.loading">
<i class="fa fa-spinner fa-spin"></i>
<span>{{'save' | i18n}}</span>
</button>
</form>
<div class="secondary-header border-0 mb-0">
<h1>{{'taxInformation' | i18n}}</h1>
</div>
<div class="mb-3" *ngIf="org && (org.businessAddress1 || org.businessTaxNumber)">
<div>{{org.businessAddress1}}</div>
<div>{{org.businessAddress2}}</div>
<div>{{org.businessAddress3}}</div>
<div>{{org.businessCountry}}</div>
<div>{{org.businessTaxNumber}}</div>
</div>
<p>{{'taxInformationDesc' | i18n}}</p>
<a href="https://bitwarden.com/contact/" target="_blank" rel="noopener" class="btn btn-outline-secondary">
{{'contactSupport' | i18n}}
</a>
<div class="secondary-header text-danger border-0 mb-0">
<h1>{{'dangerZone' | i18n}}</h1>
</div>
<div class="card border-danger">
<div class="card-body">
<p>{{'dangerZoneDesc' | i18n}}</p>
<button type="button" class="btn btn-outline-danger" (click)="deleteOrganization()" appBlurClick>{{'deleteOrganization' | i18n}}</button>
</div>
</div>
<ng-template #deleteOrganizationTemplate></ng-template>

View File

@@ -0,0 +1,81 @@
import {
Component,
ComponentFactoryResolver,
ViewChild,
ViewContainerRef,
} from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { ApiService } from 'jslib/abstractions/api.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { SyncService } from 'jslib/abstractions/sync.service';
import { OrganizationUpdateRequest } from 'jslib/models/request/organizationUpdateRequest';
import { OrganizationResponse } from 'jslib/models/response/organizationResponse';
import { ModalComponent } from '../../modal.component';
import { DeleteOrganizationComponent } from './delete-organization.component';
@Component({
selector: 'app-org-account',
templateUrl: 'account.component.html',
})
export class AccountComponent {
@ViewChild('deleteOrganizationTemplate', { read: ViewContainerRef }) deleteModalRef: ViewContainerRef;
loading = true;
org: OrganizationResponse;
formPromise: Promise<any>;
private organizationId: string;
private modal: ModalComponent = null;
constructor(private componentFactoryResolver: ComponentFactoryResolver,
private apiService: ApiService, private i18nService: I18nService,
private analytics: Angulartics2, private toasterService: ToasterService,
private route: ActivatedRoute, private syncService: SyncService) { }
async ngOnInit() {
this.route.parent.parent.params.subscribe(async (params) => {
this.organizationId = params.organizationId;
try {
this.org = await this.apiService.getOrganization(this.organizationId);
} catch { }
});
this.loading = false;
}
async submit() {
try {
const request = new OrganizationUpdateRequest();
request.name = this.org.name;
request.businessName = this.org.businessName;
request.billingEmail = this.org.billingEmail;
this.formPromise = this.apiService.putOrganization(this.organizationId, request).then(() => {
return this.syncService.fullSync(true);
});
await this.formPromise;
this.analytics.eventTrack.next({ action: 'Updated Organization Settings' });
this.toasterService.popAsync('success', null, this.i18nService.t('organizationUpdated'));
} catch { }
}
deleteOrganization() {
if (this.modal != null) {
this.modal.close();
}
const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent);
this.modal = this.deleteModalRef.createComponent(factory).instance;
const childComponent = this.modal.show<DeleteOrganizationComponent>(
DeleteOrganizationComponent, this.deleteModalRef);
childComponent.organizationId = this.organizationId;
this.modal.onClosed.subscribe(async () => {
this.modal = null;
});
}
}

View File

@@ -0,0 +1,26 @@
<div class="modal fade">
<div class="modal-dialog">
<form class="modal-content" #form (ngSubmit)="submit()" [appApiAction]="formPromise" ngNativeValidate>
<div class="modal-header">
<h2 class="modal-title">{{'deleteOrganization' | i18n}}</h2>
<button type="button" class="close" data-dismiss="modal" attr.aria-label="{{'close' | i18n}}">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<p>{{'deleteOrganizationDesc' | i18n}}</p>
<app-callout type="warning">{{'deleteOrganizationWarning' | i18n}}</app-callout>
<label for="masterPassword">{{'masterPass' | i18n}}</label>
<input id="masterPassword" type="password" name="MasterPasswordHash" class="form-control" [(ngModel)]="masterPassword" required
appAutofocus appInputVerbatim>
</div>
<div class="modal-footer">
<button appBlurClick type="submit" class="btn btn-danger btn-submit" [disabled]="form.loading">
<i class="fa fa-spinner fa-spin"></i>
<span>{{'deleteOrganization' | i18n}}</span>
</button>
<button type="button" class="btn btn-outline-secondary" data-dismiss="modal">{{'close' | i18n}}</button>
</div>
</form>
</div>
</div>

View File

@@ -0,0 +1,45 @@
import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { ToasterService } from 'angular2-toaster';
import { Angulartics2 } from 'angulartics2';
import { ApiService } from 'jslib/abstractions/api.service';
import { CryptoService } from 'jslib/abstractions/crypto.service';
import { I18nService } from 'jslib/abstractions/i18n.service';
import { PasswordVerificationRequest } from 'jslib/models/request/passwordVerificationRequest';
@Component({
selector: 'app-delete-organization',
templateUrl: 'delete-organization.component.html',
})
export class DeleteOrganizationComponent {
organizationId: string;
masterPassword: string;
formPromise: Promise<any>;
constructor(private apiService: ApiService, private i18nService: I18nService,
private analytics: Angulartics2, private toasterService: ToasterService,
private cryptoService: CryptoService, private router: Router) { }
async submit() {
if (this.masterPassword == null || this.masterPassword === '') {
this.toasterService.popAsync('error', this.i18nService.t('errorOccurred'),
this.i18nService.t('masterPassRequired'));
return;
}
const request = new PasswordVerificationRequest();
request.masterPasswordHash = await this.cryptoService.hashPassword(this.masterPassword, null);
try {
this.formPromise = this.apiService.postDeleteOrganization(this.organizationId, request);
await this.formPromise;
this.analytics.eventTrack.next({ action: 'Deleted Organization' });
this.toasterService.popAsync('success', this.i18nService.t('organizationDeleted'),
this.i18nService.t('organizationDeletedDesc'));
this.router.navigate(['/']);
} catch { }
}
}

View File

@@ -0,0 +1,20 @@
<div class="container page-content">
<div class="row">
<div class="col-3">
<div class="card">
<div class="card-header">{{'settings' | i18n}}</div>
<div class="list-group list-group-flush">
<a routerLink="account" class="list-group-item" routerLinkActive="active">
{{'myOrganization' | i18n}}
</a>
<a routerLink="billing" class="list-group-item" routerLinkActive="active">
{{'billingAndLicensing' | i18n}}
</a>
</div>
</div>
</div>
<div class="col-9">
<router-outlet></router-outlet>
</div>
</div>
</div>

View File

@@ -0,0 +1,7 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-org-settings',
templateUrl: 'settings.component.html',
})
export class SettingsComponent { }