File

src/app/pages/accounts/account-details/account-details.component.ts

Implements

OnInit AfterViewInit

Metadata

changeDetection ChangeDetectionStrategy.OnPush
selector app-account-details
styleUrls ./account-details.component.scss
templateUrl ./account-details.component.html

Index

Properties
Methods
Accessors

Constructor

constructor(formBuilder: FormBuilder, locationService: LocationService, transactionService: TransactionService, userService: UserService, route: ActivatedRoute, router: Router, tokenService: TokenService, loggingService: LoggingService, blockSyncService: BlockSyncService, cdr: ChangeDetectorRef, snackBar: MatSnackBar)
Parameters :
Name Type Optional
formBuilder FormBuilder No
locationService LocationService No
transactionService TransactionService No
userService UserService No
route ActivatedRoute No
router Router No
tokenService TokenService No
loggingService LoggingService No
blockSyncService BlockSyncService No
cdr ChangeDetectorRef No
snackBar MatSnackBar No

Methods

buildAccountsInfoForm
buildAccountsInfoForm()
Returns : void
copyAddress
copyAddress()
Returns : void
doTransactionFilter
doTransactionFilter(value: string)
Parameters :
Name Type Optional
value string No
Returns : void
doUserFilter
doUserFilter(value: string)
Parameters :
Name Type Optional
value string No
Returns : void
downloadCsv
downloadCsv(data: any, filename: string)
Parameters :
Name Type Optional
data any No
filename string No
Returns : void
filterAccounts
filterAccounts()
Returns : void
filterTransactions
filterTransactions()
Returns : void
getKeyValue
getKeyValue(obj: any)
Parameters :
Name Type Optional
obj any No
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 :
Name Type Optional
accountInfo AccountDetails No
Returns : void
populateDataTables
populateDataTables()
Returns : void
queryLocationAndCategory
queryLocationAndCategory(accountInfo: AccountDetails)
Parameters :
Name Type Optional
accountInfo AccountDetails No
Returns : void
resetPin
resetPin()
Returns : void
Async saveInfo
saveInfo()
Returns : Promise<void>
viewAccount
viewAccount(account)
Parameters :
Name Optional
account No
Returns : void
viewHistory
viewHistory(history)
Parameters :
Name Optional
history No
Returns : void
viewTransaction
viewTransaction(transaction)
Parameters :
Name Optional
transaction No
Returns : void

Properties

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})

Accessors

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

Legend
Html element
Component
Html element with directive

result-matching ""

    No results matching ""