src/app/pages/accounts/account-details/account-details.component.ts
changeDetection | ChangeDetectionStrategy.OnPush |
selector | app-account-details |
styleUrls | ./account-details.component.scss |
templateUrl | ./account-details.component.html |
constructor(formBuilder: FormBuilder, locationService: LocationService, transactionService: TransactionService, userService: UserService, route: ActivatedRoute, router: Router, tokenService: TokenService, loggingService: LoggingService, blockSyncService: BlockSyncService, cdr: ChangeDetectorRef, snackBar: MatSnackBar)
|
||||||||||||||||||||||||||||||||||||
Parameters :
|
buildAccountsInfoForm |
buildAccountsInfoForm()
|
Returns :
void
|
copyAddress |
copyAddress()
|
Returns :
void
|
doTransactionFilter | ||||||
doTransactionFilter(value: string)
|
||||||
Parameters :
Returns :
void
|
doUserFilter | ||||||
doUserFilter(value: string)
|
||||||
Parameters :
Returns :
void
|
downloadCsv |
downloadCsv(data: any, filename: string)
|
Returns :
void
|
filterAccounts |
filterAccounts()
|
Returns :
void
|
filterTransactions |
filterTransactions()
|
Returns :
void
|
getKeyValue | ||||||
getKeyValue(obj: any)
|
||||||
Parameters :
Returns :
string
|
Async loadAccount |
loadAccount()
|
Returns :
Promise<void>
|
loadSearchData |
loadSearchData()
|
Returns :
void
|
ngAfterViewInit |
ngAfterViewInit()
|
Returns :
void
|
Async ngOnInit |
ngOnInit()
|
Returns :
Promise<void>
|
populateAccountsInfoForm | ||||||
populateAccountsInfoForm(accountInfo: AccountDetails)
|
||||||
Parameters :
Returns :
void
|
populateDataTables |
populateDataTables()
|
Returns :
void
|
queryLocationAndCategory | ||||||
queryLocationAndCategory(accountInfo: AccountDetails)
|
||||||
Parameters :
Returns :
void
|
resetPin |
resetPin()
|
Returns :
void
|
Async saveInfo |
saveInfo()
|
Returns :
Promise<void>
|
viewAccount | ||||
viewAccount(account)
|
||||
Parameters :
Returns :
void
|
viewHistory | ||||
viewHistory(history)
|
||||
Parameters :
Returns :
void
|
viewTransaction | ||||
viewTransaction(transaction)
|
||||
Parameters :
Returns :
void
|
account |
Type : AccountDetails
|
accountAddress |
Type : string
|
accountInfoForm |
Type : FormGroup
|
accounts |
Type : Array<AccountDetails>
|
Default value : []
|
accountsLoading |
Type : boolean
|
Default value : true
|
accountStatus |
Type : any
|
accountsType |
Type : string
|
Default value : 'all'
|
accountTypes |
Type : Array<string>
|
area |
Type : string
|
areaNames |
Type : Array<string>
|
areaType |
Type : string
|
areaTypes |
Type : Array<string>
|
bloxbergLink |
Type : string
|
categories |
Type : Array<string>
|
category |
Type : string
|
genders |
Type : Array<string>
|
histories |
Type : Array<any>
|
Default value : []
|
history |
Type : any
|
historyDataSource |
Type : MatTableDataSource<any>
|
historyDefaultPageSize |
Type : number
|
Default value : 5
|
historyDisplayedColumns |
Type : Array<string>
|
Default value : [
'actor',
'signer',
'message',
'sequence',
'dependencies',
'timestamp',
]
|
historyLoading |
Type : boolean
|
Default value : true
|
historyPageSizeOptions |
Type : Array<number>
|
Default value : [5, 10, 20, 50, 100]
|
historyTablePaginator |
Type : MatPaginator
|
Decorators :
@ViewChild('HistoryTablePaginator', {static: true})
|
historyTableSort |
Type : MatSort
|
Decorators :
@ViewChild('HistoryTableSort', {static: true})
|
matcher |
Type : CustomErrorStateMatcher
|
Default value : new CustomErrorStateMatcher()
|
submitted |
Type : boolean
|
Default value : false
|
tokenSymbol |
Type : string
|
transaction |
Type : any
|
transactions |
Type : Array<Transaction>
|
transactionsDataSource |
Type : MatTableDataSource<any>
|
transactionsDefaultPageSize |
Type : number
|
Default value : 10
|
transactionsDisplayedColumns |
Type : Array<string>
|
Default value : ['sender', 'recipient', 'value', 'created', 'type']
|
transactionsLoading |
Type : boolean
|
Default value : true
|
transactionsPageSizeOptions |
Type : Array<number>
|
Default value : [10, 20, 50, 100]
|
transactionsType |
Type : string
|
Default value : 'all'
|
transactionsTypes |
Type : Array<string>
|
transactionTablePaginator |
Type : MatPaginator
|
Decorators :
@ViewChild('TransactionTablePaginator', {static: true})
|
transactionTableSort |
Type : MatSort
|
Decorators :
@ViewChild('TransactionTableSort', {static: true})
|
userDataSource |
Type : MatTableDataSource<any>
|
userDisplayedColumns |
Type : Array<string>
|
Default value : ['name', 'phone', 'created', 'balance', 'location']
|
usersDefaultPageSize |
Type : number
|
Default value : 10
|
usersPageSizeOptions |
Type : Array<number>
|
Default value : [10, 20, 50, 100]
|
userTablePaginator |
Type : MatPaginator
|
Decorators :
@ViewChild('UserTablePaginator', {static: true})
|
userTableSort |
Type : MatSort
|
Decorators :
@ViewChild('UserTableSort', {static: true})
|
accountInfoFormStub |
getaccountInfoFormStub()
|
import {
AfterViewInit,
ChangeDetectionStrategy,
ChangeDetectorRef,
Component,
OnInit,
ViewChild,
} from '@angular/core';
import { MatTableDataSource } from '@angular/material/table';
import { MatPaginator } from '@angular/material/paginator';
import { MatSort } from '@angular/material/sort';
import {
BlockSyncService,
LocationService,
LoggingService,
TokenService,
TransactionService,
UserService,
} from '@app/_services';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { first } from 'rxjs/operators';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { copyToClipboard, CustomErrorStateMatcher, exportCsv } from '@app/_helpers';
import { MatSnackBar } from '@angular/material/snack-bar';
import { add0x, strip0x } from '@src/assets/js/ethtx/dist/hex';
import { environment } from '@src/environments/environment';
import { AccountDetails, Transaction } from '@app/_models';
@Component({
selector: 'app-account-details',
templateUrl: './account-details.component.html',
styleUrls: ['./account-details.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AccountDetailsComponent implements OnInit, AfterViewInit {
transactionsDataSource: MatTableDataSource<any>;
transactionsDisplayedColumns: Array<string> = ['sender', 'recipient', 'value', 'created', 'type'];
transactionsDefaultPageSize: number = 10;
transactionsPageSizeOptions: Array<number> = [10, 20, 50, 100];
@ViewChild('TransactionTablePaginator', { static: true }) transactionTablePaginator: MatPaginator;
@ViewChild('TransactionTableSort', { static: true }) transactionTableSort: MatSort;
userDataSource: MatTableDataSource<any>;
userDisplayedColumns: Array<string> = ['name', 'phone', 'created', 'balance', 'location'];
usersDefaultPageSize: number = 10;
usersPageSizeOptions: Array<number> = [10, 20, 50, 100];
@ViewChild('UserTablePaginator', { static: true }) userTablePaginator: MatPaginator;
@ViewChild('UserTableSort', { static: true }) userTableSort: MatSort;
historyDataSource: MatTableDataSource<any>;
historyDisplayedColumns: Array<string> = [
'actor',
'signer',
'message',
'sequence',
'dependencies',
'timestamp',
];
historyDefaultPageSize: number = 5;
historyPageSizeOptions: Array<number> = [5, 10, 20, 50, 100];
@ViewChild('HistoryTablePaginator', { static: true }) historyTablePaginator: MatPaginator;
@ViewChild('HistoryTableSort', { static: true }) historyTableSort: MatSort;
accountInfoForm: FormGroup;
account: AccountDetails;
accountAddress: string;
accountStatus: any;
accounts: Array<AccountDetails> = [];
accountsType: string = 'all';
categories: Array<string>;
areaNames: Array<string>;
areaTypes: Array<string>;
transaction: any;
transactions: Array<Transaction>;
transactionsType: string = 'all';
accountTypes: Array<string>;
transactionsTypes: Array<string>;
genders: Array<string>;
matcher: CustomErrorStateMatcher = new CustomErrorStateMatcher();
submitted: boolean = false;
bloxbergLink: string;
tokenSymbol: string;
category: string;
area: string;
areaType: string;
accountsLoading: boolean = true;
transactionsLoading: boolean = true;
histories: Array<any> = [];
history: any;
historyLoading: boolean = true;
constructor(
private formBuilder: FormBuilder,
private locationService: LocationService,
private transactionService: TransactionService,
private userService: UserService,
private route: ActivatedRoute,
private router: Router,
private tokenService: TokenService,
private loggingService: LoggingService,
private blockSyncService: BlockSyncService,
private cdr: ChangeDetectorRef,
private snackBar: MatSnackBar
) {
this.route.paramMap.subscribe((params: Params) => {
this.accountAddress = add0x(params.get('id'));
this.bloxbergLink =
'https://blockexplorer.bloxberg.org/address/' + this.accountAddress + '/transactions';
});
}
async ngOnInit(): Promise<void> {
this.buildAccountsInfoForm();
this.transactionService.resetTransactionsList();
await this.blockSyncService.blockSync(this.accountAddress);
this.userService.resetAccountsList();
await this.loadAccount();
this.populateDataTables();
this.loadSearchData();
this.tokenService.load.subscribe(async (status: boolean) => {
if (status) {
this.tokenSymbol = await this.tokenService.getTokenSymbol();
}
});
}
ngAfterViewInit(): void {
if (this.userDataSource) {
this.userDataSource.paginator = this.userTablePaginator;
this.userDataSource.sort = this.userTableSort;
}
if (this.transactionsDataSource) {
this.transactionsDataSource.paginator = this.transactionTablePaginator;
this.transactionsDataSource.sort = this.transactionTableSort;
}
if (this.historyDataSource) {
this.historyDataSource.paginator = this.historyTablePaginator;
this.historyDataSource.sort = this.historyTableSort;
}
}
doTransactionFilter(value: string): void {
this.transactionsDataSource.filter = value.trim().toLocaleLowerCase();
}
doUserFilter(value: string): void {
this.userDataSource.filter = value.trim().toLocaleLowerCase();
}
viewTransaction(transaction): void {
this.transaction = transaction;
}
viewHistory(history): void {
this.history = history;
}
viewAccount(account): void {
this.router.navigateByUrl(
`/accounts/${strip0x(account.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`
);
}
get accountInfoFormStub(): any {
return this.accountInfoForm.controls;
}
async saveInfo(): Promise<void> {
this.submitted = true;
if (this.accountInfoForm.invalid || !confirm(`Change user's profile information?`)) {
return;
}
const status = await this.userService.changeAccountInfo(
this.accountAddress,
this.accountInfoFormStub.firstName.value + ', ' + this.accountInfoFormStub.lastName.value,
this.accountInfoFormStub.phoneNumber.value,
this.accountInfoFormStub.age.value,
this.accountInfoFormStub.type.value,
this.accountInfoFormStub.bio.value,
this.accountInfoFormStub.gender.value,
this.accountInfoFormStub.businessCategory.value,
this.accountInfoFormStub.userLocation.value,
this.accountInfoFormStub.location.value,
this.accountInfoFormStub.locationType.value,
this.account.vcard?.tel[0].value
);
if (status) {
await this.loadAccount();
}
this.submitted = false;
}
filterAccounts(): void {
if (this.accountsType === 'all') {
this.userService.accountsSubject.subscribe((accounts) => {
this.userDataSource.data = accounts;
this.accounts = accounts;
});
} else {
this.userDataSource.data = this.accounts.filter(
(account) => account.type === this.accountsType
);
}
}
filterTransactions(): void {
if (this.transactionsType === 'all') {
this.transactionService.transactionsSubject.subscribe((transactions) => {
this.transactionsDataSource.data = transactions;
this.transactions = transactions;
});
} else {
this.transactionsDataSource.data = this.transactions.filter(
(transaction) => transaction.type + 's' === this.transactionsType
);
}
}
resetPin(): void {
if (!confirm(`Reset user's pin?`)) {
return;
}
this.userService
.resetPin(this.account.vcard.tel[0].value)
.pipe(first())
.subscribe((res) => {
this.loggingService.sendInfoLevelMessage(`Response: ${res}`);
});
}
downloadCsv(data: any, filename: string): void {
exportCsv(data, filename);
}
copyAddress(): void {
if (copyToClipboard(this.accountAddress)) {
this.snackBar.open(this.accountAddress + ' copied successfully!', 'Close', {
duration: 3000,
});
}
}
getKeyValue(obj: any): string {
let str = '';
if (obj instanceof Object) {
for (const [key, value] of Object.entries(obj)) {
str += `${key}: ${value} `;
}
}
return str;
}
buildAccountsInfoForm(): void {
this.accountInfoForm = this.formBuilder.group({
firstName: ['', Validators.required],
lastName: ['', Validators.required],
phoneNumber: ['', Validators.required],
age: [''],
type: ['', Validators.required],
bio: ['', Validators.required],
gender: ['', Validators.required],
businessCategory: ['', Validators.required],
userLocation: ['', Validators.required],
location: ['', Validators.required],
locationType: ['', Validators.required],
});
}
populateAccountsInfoForm(accountInfo: AccountDetails): void {
const fullName = accountInfo.vcard?.fn[0].value.split(' ');
this.accountInfoForm.patchValue({
firstName: fullName[0].split(',')[0],
lastName: fullName.slice(1).join(' '),
phoneNumber: accountInfo.vcard?.tel[0].value,
age: accountInfo.age,
type: accountInfo.type,
bio: accountInfo.products,
gender: accountInfo.gender,
businessCategory: accountInfo.category || this.category || 'other',
userLocation: accountInfo.location.area_name,
location: accountInfo.location.area || this.area || 'other',
locationType: accountInfo.location.area_type || this.areaType || 'other',
});
}
populateDataTables(): void {
this.userService.accountsSubject.subscribe((accounts) => {
this.userDataSource = new MatTableDataSource<any>(accounts);
this.userDataSource.paginator = this.userTablePaginator;
this.userDataSource.sort = this.userTableSort;
this.accounts = accounts;
if (accounts.length > 0) {
this.accountsLoading = false;
}
this.cdr.detectChanges();
});
this.transactionService.transactionsSubject.subscribe((transactions) => {
this.transactionsDataSource = new MatTableDataSource<any>(transactions);
this.transactionsDataSource.paginator = this.transactionTablePaginator;
this.transactionsDataSource.sort = this.transactionTableSort;
this.transactions = transactions;
if (transactions.length > 0) {
this.transactionsLoading = false;
}
this.cdr.detectChanges();
});
this.userService.historySubject.subscribe(async (histories) => {
this.historyDataSource = new MatTableDataSource<any>(histories);
this.historyDataSource.paginator = this.historyTablePaginator;
this.historyDataSource.sort = this.historyTableSort;
this.histories = histories;
if (histories.length > 0) {
this.historyLoading = false;
}
this.cdr.detectChanges();
});
}
queryLocationAndCategory(accountInfo: AccountDetails): void {
this.locationService.areaNamesSubject.subscribe((response) => {
this.area = this.locationService.getAreaNameByLocation(
accountInfo.location.area_name,
response
);
this.cdr.detectChanges();
this.locationService.areaTypesSubject.subscribe((result) => {
this.areaType = this.locationService.getAreaTypeByArea(this.area, result);
this.cdr.detectChanges();
});
});
this.userService.categoriesSubject.subscribe((result) => {
this.category = this.userService.getCategoryByProduct(accountInfo.products[0], result);
this.cdr.detectChanges();
});
}
loadSearchData(): void {
this.userService.getCategories();
this.userService.categoriesSubject.subscribe((res) => {
this.categories = Object.keys(res);
});
this.locationService.getAreaNames();
this.locationService.areaNamesSubject.subscribe((res) => {
this.areaNames = Object.keys(res);
});
this.locationService.getAreaTypes();
this.locationService.areaTypesSubject.subscribe((res) => {
this.areaTypes = Object.keys(res);
});
this.userService
.getAccountTypes()
.pipe(first())
.subscribe((res) => (this.accountTypes = res));
this.userService
.getTransactionTypes()
.pipe(first())
.subscribe((res) => (this.transactionsTypes = res));
this.userService
.getGenders()
.pipe(first())
.subscribe((res) => (this.genders = res));
}
async loadAccount(): Promise<void> {
(await this.userService.getAccountByAddress(this.accountAddress, 100, true)).subscribe(
async (res) => {
if (res !== undefined) {
this.account = res;
this.cdr.detectChanges();
this.queryLocationAndCategory(this.account);
this.populateAccountsInfoForm(this.account);
this.userService
.getAccountStatus(this.account.vcard?.tel[0].value)
.pipe(first())
.subscribe((response) => (this.accountStatus = response.status));
} else {
alert('Account not found!');
}
}
);
}
}
<!-- Begin page -->
<div class="wrapper">
<app-sidebar></app-sidebar>
<!-- ============================================================== -->
<!-- Start Page Content here -->
<!-- ============================================================== -->
<div id="content">
<app-topbar></app-topbar>
<!-- Start Content-->
<div class="container-fluid" appMenuSelection>
<nav aria-label="breadcrumb">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a routerLink="/home">Home</a></li>
<li class="breadcrumb-item"><a routerLink="/accounts">Accounts</a></li>
<li *ngIf="account" class="breadcrumb-item active" aria-current="page">
{{ account?.vcard?.fn[0].value }}
</li>
</ol>
</nav>
<div *ngIf="!account" class="text-center">
<div class="spinner-grow text-primary m-1" role="status" style="width: 3rem; height: 3rem">
<span class="sr-only">Loading...</span>
</div>
<div class="spinner-grow text-primary m-1" role="status" style="width: 3rem; height: 3rem">
<span class="sr-only">Loading...</span>
</div>
<div class="spinner-grow text-primary m-1" role="status" style="width: 3rem; height: 3rem">
<span class="sr-only">Loading...</span>
</div>
</div>
<div *ngIf="account" class="card mb-3">
<div class="row card-body">
<h3>
<strong> {{ account?.vcard?.fn[0].value }} </strong>
</h3>
<span class="ml-auto"
><strong>Balance:</strong> {{ account?.balance | tokenRatio }}
{{ tokenSymbol | uppercase }}</span
>
<span class="ml-2"
><strong>Created:</strong> {{ account?.date_registered | unixDate }}</span
>
<span class="ml-2"
><strong>Address:</strong>
<a href="{{ bloxbergLink }}" target="_blank"> {{ accountAddress }} </a>
<img
src="assets/images/checklist.svg"
class="ml-2"
height="20rem"
(click)="copyAddress()"
alt="Copy"
/>
</span>
</div>
</div>
<div *ngIf="account" class="card mt-3 mb-3">
<div class="card-body">
<form [formGroup]="accountInfoForm" (ngSubmit)="saveInfo()">
<div class="row form-inline">
<div class="col-md-6 col-lg-4">
<mat-form-field appearance="outline">
<mat-label>First Name: *</mat-label>
<input
matInput
type="text"
id="firstName"
placeholder="{{ account?.vcard?.fn[0].value.split(' ')[0] }}"
value="{{ account?.vcard?.fn[0].value.split(' ')[0] }}"
formControlName="firstName"
[errorStateMatcher]="matcher"
/>
<mat-error *ngIf="submitted && accountInfoFormStub.firstName.errors"
>First Name is required.</mat-error
>
</mat-form-field>
</div>
<div class="col-md-6 col-lg-4">
<mat-form-field appearance="outline">
<mat-label>Last Name(s): *</mat-label>
<input
matInput
type="text"
id="lastName"
placeholder="{{ account?.vcard?.fn[0].value.split(' ').slice(1).join(' ') }}"
value="{{ account?.vcard?.fn[0].value.split(' ').slice(1).join(' ') }}"
formControlName="lastName"
[errorStateMatcher]="matcher"
/>
<mat-error *ngIf="submitted && accountInfoFormStub.lastName.errors"
>Last Name is required.</mat-error
>
</mat-form-field>
</div>
<div class="col-md-6 col-lg-4">
<mat-form-field appearance="outline">
<mat-label>Phone Number: </mat-label>
<input
matInput
type="text"
id="phoneNumber"
placeholder="{{ account?.vcard?.tel[0].value }}"
value="{{ account?.vcard?.tel[0].value }}"
formControlName="phoneNumber"
[errorStateMatcher]="matcher"
/>
<mat-error *ngIf="submitted && accountInfoFormStub.phoneNumber.errors"
>Phone Number is required.</mat-error
>
</mat-form-field>
</div>
<div class="col-md-6 col-lg-4">
<mat-form-field appearance="outline">
<mat-label> ACCOUNT TYPE: </mat-label>
<mat-select
id="accountType"
[(value)]="account.type"
formControlName="type"
[errorStateMatcher]="matcher"
>
<mat-option *ngFor="let accountType of accountTypes" [value]="accountType">
{{ accountType | uppercase }}
</mat-option>
</mat-select>
<mat-error *ngIf="submitted && accountInfoFormStub.type.errors"
>Type is required.</mat-error
>
</mat-form-field>
</div>
<div class="col-md-6 col-lg-4">
<mat-form-field appearance="outline">
<mat-label> GENDER: </mat-label>
<mat-select
id="gender"
[(value)]="account.gender"
formControlName="gender"
[errorStateMatcher]="matcher"
>
<mat-option *ngFor="let gender of genders" [value]="gender">
{{ gender | uppercase }}
</mat-option>
</mat-select>
<mat-error *ngIf="submitted && accountInfoFormStub.gender.errors"
>Gender is required.</mat-error
>
</mat-form-field>
</div>
<div class="col-md-6 col-lg-4">
<mat-form-field appearance="outline">
<mat-label>Age: </mat-label>
<input
matInput
type="text"
id="age"
placeholder="{{ account?.age }}"
value="{{ account?.age }}"
formControlName="age"
[errorStateMatcher]="matcher"
/>
<mat-error *ngIf="submitted && accountInfoFormStub.age.errors"
>Age is required.</mat-error
>
</mat-form-field>
</div>
<div class="col-md-6 col-lg-4">
<mat-form-field appearance="outline">
<mat-label>Bio: </mat-label>
<input
matInput
type="text"
id="bio"
placeholder="{{ account?.products }}"
value="{{ account?.products }}"
formControlName="bio"
[errorStateMatcher]="matcher"
/>
<mat-error *ngIf="submitted && accountInfoFormStub.bio.errors"
>Bio is required.</mat-error
>
</mat-form-field>
</div>
<div class="col-md-6 col-lg-4">
<mat-form-field appearance="outline">
<mat-label> BUSINESS CATEGORY: </mat-label>
<mat-select
id="businessCategory"
[(value)]="category"
formControlName="businessCategory"
[errorStateMatcher]="matcher"
>
<mat-option *ngFor="let category of categories" [value]="category">
{{ category | titlecase }}
</mat-option>
</mat-select>
<mat-error *ngIf="submitted && accountInfoFormStub.businessCategory.errors">
Category is required.
</mat-error>
</mat-form-field>
</div>
<div class="col-md-6 col-lg-4">
<mat-form-field appearance="outline">
<mat-label>User Location: </mat-label>
<input
matInput
type="text"
id="userLocation"
placeholder="{{ account?.location.area_name }}"
value="{{ account?.location.area_name }}"
formControlName="userLocation"
[errorStateMatcher]="matcher"
/>
<mat-error *ngIf="submitted && accountInfoFormStub.userLocation.errors">
User Location is required.
</mat-error>
</mat-form-field>
</div>
<div class="col-md-6 col-lg-4">
<mat-form-field appearance="outline">
<mat-label> LOCATION: </mat-label>
<mat-select
id="location"
[(value)]="area"
formControlName="location"
[errorStateMatcher]="matcher"
>
<mat-option *ngFor="let area of areaNames" [value]="area">
{{ area | uppercase }}
</mat-option>
</mat-select>
<mat-error *ngIf="submitted && accountInfoFormStub.location.errors"
>Location is required.</mat-error
>
</mat-form-field>
</div>
<div class="col-md-6 col-lg-4">
<mat-form-field appearance="outline">
<mat-label> LOCATION TYPE: </mat-label>
<mat-select
id="locationType"
[(value)]="areaType"
formControlName="locationType"
[errorStateMatcher]="matcher"
>
<mat-option *ngFor="let type of areaTypes" [value]="type">
{{ type | uppercase }}
</mat-option>
</mat-select>
<mat-error *ngIf="submitted && accountInfoFormStub.locationType.errors"
>Location Type is required.</mat-error
>
</mat-form-field>
</div>
<div class="col-md-6 col-lg-4">
<button
mat-raised-button
color="primary"
type="button"
class="btn btn-outline-success mb-3"
(click)="resetPin()"
>
Reset Pin
</button>
</div>
<div class="col-md-6 col-lg-4">
<button
mat-raised-button
color="warn"
type="button"
class="btn btn-outline-danger mb-3"
>
Delete User
</button>
</div>
<div class="col-md-6 col-lg-4">
<button
mat-raised-button
color="primary"
type="submit"
class="btn btn-outline-primary"
>
SAVE DETAILS
</button>
</div>
</div>
</form>
</div>
</div>
<div class="card mb-3">
<mat-card-title class="card-header"> USER </mat-card-title>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-bordered table-hover">
<caption>
1 user
</caption>
<thead class="thead-dark">
<tr>
<th scope="col">NAME</th>
<th scope="col">BALANCE</th>
<th scope="col">CREATED</th>
<th scope="col">STATUS</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ account?.vcard?.fn[0].value || accountAddress }}</td>
<td>{{ account?.balance | tokenRatio }} {{ tokenSymbol | uppercase }}</td>
<td>{{ account?.date_registered | unixDate }}</td>
<td>
<span class="badge badge-success badge-pill">
{{ accountStatus }}
</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="card mt-1">
<app-account-history
*ngIf="history"
[account]="history?.snapshot.data"
(closeWindow)="history = $event"
></app-account-history>
<mat-card-title class="card-header"> HISTORY </mat-card-title>
<div class="card-body">
<div *ngIf="historyLoading">
<h2 class="text-center"><strong>Loading History!</strong></h2>
<mat-progress-bar [mode]="'query'"></mat-progress-bar>
</div>
<mat-table
class="mat-elevation-z10"
[dataSource]="historyDataSource"
matSort
matSortActive="timestamp"
#HistoryTableSort="matSort"
matSortDirection="asc"
matSortDisableClear
>
<ng-container matColumnDef="actor">
<mat-header-cell *matHeaderCellDef mat-sort-header>Actor</mat-header-cell>
<mat-cell *matCellDef="let history">
{{ history?.change.actor }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="signer">
<mat-header-cell *matHeaderCellDef mat-sort-header>Signer</mat-header-cell>
<mat-cell *matCellDef="let history">
{{ history?.snapshot.signature?.data | signatureUser | async }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="message">
<mat-header-cell *matHeaderCellDef mat-sort-header>Message</mat-header-cell>
<mat-cell *matCellDef="let history">
{{ history?.change.message }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="sequence">
<mat-header-cell *matHeaderCellDef mat-sort-header>Sequence</mat-header-cell>
<mat-cell *matCellDef="let history">
{{ history?.change.seq }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="dependencies">
<mat-header-cell *matHeaderCellDef mat-sort-header>Dependencies</mat-header-cell>
<mat-cell *matCellDef="let history">
{{ getKeyValue(history?.change.deps) }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="timestamp">
<mat-header-cell *matHeaderCellDef mat-sort-header>ChangedAt</mat-header-cell>
<mat-cell *matCellDef="let history">
{{ history?.snapshot.timestamp | unixDate }}
</mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="historyDisplayedColumns"></mat-header-row>
<mat-row
*matRowDef="let history; columns: historyDisplayedColumns"
(click)="viewHistory(history)"
matRipple
></mat-row>
</mat-table>
<mat-paginator
#HistoryTablePaginator="matPaginator"
[pageSize]="historyDefaultPageSize"
[pageSizeOptions]="historyPageSizeOptions"
showFirstLastButtons
></mat-paginator>
</div>
</div>
<mat-tab-group dynamicHeight mat-align-tabs="start">
<mat-tab label="Transactions">
<app-transaction-details
[transaction]="transaction"
(closeWindow)="transaction = $event"
></app-transaction-details>
<div class="card mt-1">
<div class="card-header">
<div class="row">
<mat-form-field appearance="outline">
<mat-label> TRANSACTION TYPE </mat-label>
<mat-select
id="transferSelect"
[(value)]="transactionsType"
(selectionChange)="filterTransactions()"
>
<mat-option value="all">ALL TRANSFERS</mat-option>
<mat-option
*ngFor="let transactionType of transactionsTypes"
[value]="transactionType"
>
{{ transactionType | uppercase }}
</mat-option>
</mat-select>
</mat-form-field>
<button
mat-raised-button
color="primary"
type="button"
class="btn btn-outline-primary ml-auto mr-2"
(click)="downloadCsv(transactions, 'transactions')"
>
EXPORT
</button>
</div>
</div>
<div class="card-body">
<mat-form-field appearance="outline">
<mat-label> Filter </mat-label>
<input
matInput
type="text"
(keyup)="doTransactionFilter($event.target.value)"
placeholder="Filter"
/>
<mat-icon matSuffix>search</mat-icon>
</mat-form-field>
<div *ngIf="transactionsLoading">
<h2 class="text-center"><strong>Loading Transactions!</strong></h2>
<mat-progress-bar [mode]="'query'"></mat-progress-bar>
</div>
<table
mat-table
class="mat-elevation-z10"
[dataSource]="transactionsDataSource"
matSort
matSortActive="created"
#TransactionTableSort="matSort"
matSortDirection="asc"
matSortDisableClear
>
<ng-container matColumnDef="sender">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Sender</th>
<td mat-cell *matCellDef="let transaction">
{{ transaction?.sender?.vcard.fn[0].value || transaction.from }}
</td>
</ng-container>
<ng-container matColumnDef="recipient">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Recipient</th>
<td mat-cell *matCellDef="let transaction">
{{ transaction?.recipient?.vcard.fn[0].value || transaction.to }}
</td>
</ng-container>
<ng-container matColumnDef="value">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Value</th>
<td mat-cell *matCellDef="let transaction">
<span *ngIf="transaction.type == 'transaction'"
>{{ transaction?.value | tokenRatio }} {{ tokenSymbol | uppercase }}</span
>
<span *ngIf="transaction.type == 'conversion'"
>{{ transaction?.toValue | tokenRatio }} {{ tokenSymbol | uppercase }}</span
>
</td>
</ng-container>
<ng-container matColumnDef="created">
<th mat-header-cell *matHeaderCellDef mat-sort-header>Created</th>
<td mat-cell *matCellDef="let transaction">
{{ transaction?.tx.timestamp | unixDate }}
</td>
</ng-container>
<ng-container matColumnDef="type">
<th mat-header-cell *matHeaderCellDef mat-sort-header>TYPE</th>
<td mat-cell *matCellDef="let transaction">
<span class="badge badge-success badge-pill"> {{ transaction?.type }} </span>
</td>
</ng-container>
<tr mat-header-row *matHeaderRowDef="transactionsDisplayedColumns"></tr>
<tr
mat-row
*matRowDef="let transaction; columns: transactionsDisplayedColumns"
matRipple
(click)="viewTransaction(transaction)"
></tr>
</table>
<mat-paginator
#TransactionTablePaginator="matPaginator"
[pageSize]="transactionsDefaultPageSize"
[pageSizeOptions]="transactionsPageSizeOptions"
showFirstLastButtons
></mat-paginator>
</div>
</div>
</mat-tab>
<mat-tab label="Users">
<div class="card mt-1">
<mat-card-title class="card-header"> Accounts </mat-card-title>
<div class="card-body">
<div class="row card-header">
<mat-form-field appearance="outline">
<mat-label> ACCOUNT TYPE </mat-label>
<mat-select
id="typeSelect"
[(value)]="accountsType"
(selectionChange)="filterAccounts()"
>
<mat-option value="all">ALL</mat-option>
<mat-option *ngFor="let accountType of accountTypes" [value]="accountType">
{{ accountType | uppercase }}
</mat-option>
</mat-select>
</mat-form-field>
<button
mat-raised-button
color="primary"
type="button"
class="btn btn-outline-primary ml-auto mr-2"
(click)="downloadCsv(accounts, 'accounts')"
>
EXPORT
</button>
</div>
<mat-form-field appearance="outline">
<mat-label> Filter </mat-label>
<input
matInput
type="text"
(keyup)="doUserFilter($event.target.value)"
placeholder="Filter"
/>
<mat-icon matSuffix>search</mat-icon>
</mat-form-field>
<div *ngIf="accountsLoading">
<h2 class="text-center"><strong>Loading Accounts!</strong></h2>
<mat-progress-bar [mode]="'query'"></mat-progress-bar>
</div>
<mat-table
class="mat-elevation-z10"
[dataSource]="userDataSource"
matSort
#UserTableSort="matSort"
matSortActive="created"
matSortDirection="desc"
matSortDisableClear
>
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef mat-sort-header> NAME </mat-header-cell>
<mat-cell *matCellDef="let user"> {{ user?.vcard.fn[0].value }} </mat-cell>
</ng-container>
<ng-container matColumnDef="phone">
<mat-header-cell *matHeaderCellDef mat-sort-header>
PHONE NUMBER
</mat-header-cell>
<mat-cell *matCellDef="let user"> {{ user?.vcard.tel[0].value }} </mat-cell>
</ng-container>
<ng-container matColumnDef="created">
<mat-header-cell *matHeaderCellDef mat-sort-header> CREATED </mat-header-cell>
<mat-cell *matCellDef="let user">
{{ user?.date_registered | unixDate }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="balance">
<mat-header-cell *matHeaderCellDef mat-sort-header> BALANCE </mat-header-cell>
<mat-cell *matCellDef="let user">
{{ user?.balance | tokenRatio }} {{ tokenSymbol | uppercase }}
</mat-cell>
</ng-container>
<ng-container matColumnDef="location">
<mat-header-cell *matHeaderCellDef mat-sort-header> LOCATION </mat-header-cell>
<mat-cell *matCellDef="let user"> {{ user?.location.area_name }} </mat-cell>
</ng-container>
<mat-header-row *matHeaderRowDef="userDisplayedColumns"></mat-header-row>
<mat-row
*matRowDef="let account; columns: userDisplayedColumns"
(click)="viewAccount(account)"
matRipple
></mat-row>
</mat-table>
<mat-paginator
#UserTablePaginator="matPaginator"
[pageSize]="usersDefaultPageSize"
[pageSizeOptions]="usersPageSizeOptions"
showFirstLastButtons
></mat-paginator>
</div>
</div>
</mat-tab>
</mat-tab-group>
</div>
<app-footer appMenuSelection></app-footer>
</div>
<!-- ============================================================== -->
<!-- End Page content -->
<!-- ============================================================== -->
</div>
./account-details.component.scss