mirror of
https://github.com/bitwarden/browser
synced 2026-01-10 12:33:26 +00:00
my organization page
This commit is contained in:
55
src/app/organizations/settings/account.component.html
Normal file
55
src/app/organizations/settings/account.component.html
Normal 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>
|
||||
81
src/app/organizations/settings/account.component.ts
Normal file
81
src/app/organizations/settings/account.component.ts
Normal 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;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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">×</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>
|
||||
@@ -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 { }
|
||||
}
|
||||
}
|
||||
20
src/app/organizations/settings/settings.component.html
Normal file
20
src/app/organizations/settings/settings.component.html
Normal 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>
|
||||
7
src/app/organizations/settings/settings.component.ts
Normal file
7
src/app/organizations/settings/settings.component.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-org-settings',
|
||||
templateUrl: 'settings.component.html',
|
||||
})
|
||||
export class SettingsComponent { }
|
||||
Reference in New Issue
Block a user