Merge branch 'master' into spencer/docs
# Conflicts: # package-lock.json # package.json # src/app/_eth/accountIndex.ts # src/app/_eth/token-registry.ts # src/app/_guards/auth.guard.ts # src/app/_guards/role.guard.ts # src/app/_helpers/array-sum.ts # src/app/_helpers/clipboard-copy.ts # src/app/_helpers/custom-error-state-matcher.ts # src/app/_helpers/custom.validator.ts # src/app/_helpers/export-csv.ts # src/app/_helpers/global-error-handler.ts # src/app/_helpers/http-getter.ts # src/app/_helpers/mock-backend.ts # src/app/_helpers/read-csv.ts # src/app/_helpers/schema-validation.ts # src/app/_services/user.service.spec.ts
This commit is contained in:
commit
5228842e61
1
.husky/.gitignore
vendored
Normal file
1
.husky/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
_
|
30
.husky/_/husky.sh
Normal file
30
.husky/_/husky.sh
Normal file
@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
if [ -z "$husky_skip_init" ]; then
|
||||
debug () {
|
||||
[ "$HUSKY_DEBUG" = "1" ] && echo "husky (debug) - $1"
|
||||
}
|
||||
|
||||
readonly hook_name="$(basename "$0")"
|
||||
debug "starting $hook_name..."
|
||||
|
||||
if [ "$HUSKY" = "0" ]; then
|
||||
debug "HUSKY env variable is set to 0, skipping hook"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -f ~/.huskyrc ]; then
|
||||
debug "sourcing ~/.huskyrc"
|
||||
. ~/.huskyrc
|
||||
fi
|
||||
|
||||
export readonly husky_skip_init=1
|
||||
sh -e "$0" "$@"
|
||||
exitCode="$?"
|
||||
|
||||
if [ $exitCode != 0 ]; then
|
||||
echo "husky - $hook_name hook exited with code $exitCode (error)"
|
||||
exit $exitCode
|
||||
fi
|
||||
|
||||
exit 0
|
||||
fi
|
4
.prettierignore
Normal file
4
.prettierignore
Normal file
@ -0,0 +1,4 @@
|
||||
package.json
|
||||
package-lock.json
|
||||
yarn.lock
|
||||
dist
|
9
.prettierrc
Normal file
9
.prettierrc
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"printWidth": 100,
|
||||
"semi": true,
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "always"
|
||||
}
|
10
angular.json
10
angular.json
@ -26,7 +26,8 @@
|
||||
"aot": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
@ -68,7 +69,9 @@
|
||||
"maximumWarning": "6kb",
|
||||
"maximumError": "10kb"
|
||||
}
|
||||
]
|
||||
],
|
||||
"serviceWorker": true,
|
||||
"ngswConfigPath": "ngsw-config.json"
|
||||
},
|
||||
"dev": {
|
||||
"fileReplacements": [
|
||||
@ -110,7 +113,8 @@
|
||||
"codeCoverage": true,
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets"
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
|
31
ngsw-config.json
Normal file
31
ngsw-config.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/service-worker/config/schema.json",
|
||||
"index": "/index.html",
|
||||
"assetGroups": [
|
||||
{
|
||||
"name": "app",
|
||||
"installMode": "prefetch",
|
||||
"resources": {
|
||||
"files": [
|
||||
"/favicon.ico",
|
||||
"/index.html",
|
||||
"/manifest.webmanifest",
|
||||
"/*.css",
|
||||
"/*.js",
|
||||
"/assets/*.png"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "assets",
|
||||
"installMode": "lazy",
|
||||
"updateMode": "prefetch",
|
||||
"resources": {
|
||||
"files": [
|
||||
"/assets/**",
|
||||
"/*.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
5313
package-lock.json
generated
5313
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@ -8,10 +8,16 @@
|
||||
"start:prod": "ng serve --prod",
|
||||
"build:dev": "ng build -c dev",
|
||||
"build:prod": "ng build --prod",
|
||||
"start:pwa": "npm run build:prod && http-server -p 4200 dist/cic-staff-client",
|
||||
"test:dev": "ng test",
|
||||
"format:check": "prettier --config ./.prettierrc --list-different \"src/{app,environments,assets}/**/*.{ts,js,json,css,scss}\"",
|
||||
"format:refactor": "prettier --config ./.prettierrc --write \"src/{app,environments,assets}/**/*.{ts,js,json,css,scss}\"",
|
||||
"format:fix": "pretty-quick --staged",
|
||||
"lint": "ng lint",
|
||||
"e2e": "ng e2e",
|
||||
"precommit": "format:fix && lint",
|
||||
"postinstall": "node patch-webpack.js",
|
||||
"prepare": "husky install",
|
||||
"compodoc": "npx compodoc -p tsconfig.doc.json -d docs/compodoc -n CICADA",
|
||||
"typedoc": "npx typedoc --tsconfig tsconfig.json --exclude **/*.spec.ts --out docs/typedoc --name CICADA src",
|
||||
"docs": "npm run typedoc && npm run compodoc"
|
||||
@ -28,6 +34,7 @@
|
||||
"@angular/platform-browser": "~10.2.0",
|
||||
"@angular/platform-browser-dynamic": "~10.2.0",
|
||||
"@angular/router": "~10.2.0",
|
||||
"@angular/service-worker": "~10.2.0",
|
||||
"@popperjs/core": "^2.5.4",
|
||||
"angular-datatables": "^9.0.2",
|
||||
"block-syncer": "^0.2.4",
|
||||
@ -39,6 +46,7 @@
|
||||
"datatables.net": "^1.10.22",
|
||||
"datatables.net-dt": "^1.10.22",
|
||||
"ethers": "^5.0.31",
|
||||
"http-server": "^0.12.3",
|
||||
"jquery": "^3.5.1",
|
||||
"mocha": "^8.2.1",
|
||||
"moolb": "^0.1.0",
|
||||
@ -66,6 +74,7 @@
|
||||
"@types/node": "^12.20.6",
|
||||
"codelyzer": "^6.0.0",
|
||||
"dotenv": "^8.2.0",
|
||||
"husky": "^6.0.0",
|
||||
"jasmine-core": "~3.6.0",
|
||||
"jasmine-spec-reporter": "~5.0.0",
|
||||
"karma": "~5.0.0",
|
||||
@ -74,12 +83,22 @@
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "^1.5.0",
|
||||
"karma-junit-reporter": "^2.0.1",
|
||||
"prettier": "^2.3.0",
|
||||
"pretty-quick": "^3.1.0",
|
||||
"protractor": "~7.0.0",
|
||||
"secp256k1": "^4.0.2",
|
||||
"ts-node": "~8.3.0",
|
||||
"tslint": "~6.1.0",
|
||||
"tslint-angular": "^3.0.3",
|
||||
"tslint-config-prettier": "^1.18.0",
|
||||
"tslint-jasmine-rules": "^1.6.1",
|
||||
"typedoc": "^0.20.36",
|
||||
"typescript": "~4.0.2",
|
||||
"yargs": "^13.3.2"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "pretty-quick --staged & ng lint"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ export class AccountIndex {
|
||||
* @returns true - If the address has been registered in the accounts registry.
|
||||
*/
|
||||
public async haveAccount(address: string): Promise<boolean> {
|
||||
return await this.contract.methods.accountIndex(address).call() !== 0;
|
||||
return (await this.contract.methods.accountIndex(address).call()) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1,6 +1,6 @@
|
||||
// Application imports
|
||||
import { TokenRegistry } from '@app/_eth/token-registry';
|
||||
import {environment} from '@src/environments/environment';
|
||||
import { environment } from '@src/environments/environment';
|
||||
|
||||
describe('TokenRegistry', () => {
|
||||
it('should create an instance', () => {
|
||||
|
@ -2,7 +2,7 @@
|
||||
import Web3 from 'web3';
|
||||
|
||||
// Application imports
|
||||
import {environment} from '@src/environments/environment';
|
||||
import { environment } from '@src/environments/environment';
|
||||
|
||||
|
||||
/** Fetch the token registry contract's ABI. */
|
||||
|
@ -12,7 +12,7 @@ import { Observable } from 'rxjs';
|
||||
* @implements CanActivate
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthGuard implements CanActivate {
|
||||
|
||||
@ -34,12 +34,12 @@ export class AuthGuard implements CanActivate {
|
||||
*/
|
||||
canActivate(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
||||
state: RouterStateSnapshot
|
||||
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
||||
if (localStorage.getItem(btoa('CICADA_PRIVATE_KEY'))) {
|
||||
return true;
|
||||
}
|
||||
this.router.navigate(['/auth']);
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ import { Observable } from 'rxjs';
|
||||
* @implements CanActivate
|
||||
*/
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RoleGuard implements CanActivate {
|
||||
|
||||
@ -34,7 +34,8 @@ export class RoleGuard implements CanActivate {
|
||||
*/
|
||||
canActivate(
|
||||
route: ActivatedRouteSnapshot,
|
||||
state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
||||
state: RouterStateSnapshot
|
||||
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
||||
const currentUser = JSON.parse(localStorage.getItem(atob('CICADA_USER')));
|
||||
if (currentUser) {
|
||||
if (route.data.roles && route.data.roles.indexOf(currentUser.role) === -1) {
|
||||
@ -44,8 +45,7 @@ export class RoleGuard implements CanActivate {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.router.navigate(['/auth'], { queryParams: { returnUrl: state.url }});
|
||||
this.router.navigate(['/auth'], { queryParams: { returnUrl: state.url } });
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -14,7 +14,6 @@ function arraySum(arr: Array<number>): number {
|
||||
return arr.reduce((accumulator, current) => accumulator + current, 0);
|
||||
}
|
||||
|
||||
|
||||
/** @exports */
|
||||
export {
|
||||
arraySum
|
||||
};
|
||||
export { arraySum };
|
||||
|
@ -44,7 +44,7 @@ function copyToClipboard(text: any): boolean {
|
||||
// copy the text
|
||||
document.execCommand('copy');
|
||||
} catch (err) {
|
||||
window.alert('Your Browser Doesn\'t support this! Error : ' + err);
|
||||
window.alert('Your Browser Does not support this! Error : ' + err);
|
||||
return false;
|
||||
}
|
||||
// remove the selection range (Chrome throws a warning if we don't.)
|
||||
@ -61,6 +61,4 @@ function copyToClipboard(text: any): boolean {
|
||||
}
|
||||
|
||||
/** @exports */
|
||||
export {
|
||||
copyToClipboard
|
||||
};
|
||||
export { copyToClipboard };
|
||||
|
@ -1,13 +1,13 @@
|
||||
// Core imports
|
||||
import {FormControl, FormGroupDirective, NgForm} from '@angular/forms';
|
||||
import {ErrorStateMatcher} from '@angular/material/core';
|
||||
import { ErrorStateMatcher } from '@angular/material/core';
|
||||
import { FormControl, FormGroupDirective, NgForm } from '@angular/forms';
|
||||
|
||||
/**
|
||||
* Custom provider that defines how form controls behave with regards to displaying error messages.
|
||||
*
|
||||
* @implements ErrorStateMatcher
|
||||
*/
|
||||
export class CustomErrorStateMatcher implements ErrorStateMatcher{
|
||||
export class CustomErrorStateMatcher implements ErrorStateMatcher {
|
||||
/**
|
||||
* Checks whether an invalid input has been made and an error should be made.
|
||||
*
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Core imports
|
||||
import {AbstractControl, ValidationErrors} from '@angular/forms';
|
||||
import { AbstractControl, ValidationErrors } from '@angular/forms';
|
||||
|
||||
/**
|
||||
* Provides methods to perform custom validation to form inputs.
|
||||
|
@ -12,7 +12,7 @@ function exportCsv(arrayData: Array<any>, filename: string, delimiter: string =
|
||||
return;
|
||||
}
|
||||
let csv: string = Object.keys(arrayData[0]).join(delimiter) + '\n';
|
||||
arrayData.forEach(obj => {
|
||||
arrayData.forEach((obj) => {
|
||||
const row: Array<any> = [];
|
||||
for (const key in obj) {
|
||||
if (obj.hasOwnProperty(key)) {
|
||||
@ -22,7 +22,7 @@ function exportCsv(arrayData: Array<any>, filename: string, delimiter: string =
|
||||
csv += row.join(delimiter) + '\n';
|
||||
});
|
||||
|
||||
const csvData: Blob = new Blob([csv], {type: 'text/csv'});
|
||||
const csvData: Blob = new Blob([csv], { type: 'text/csv' });
|
||||
const csvUrl: string = URL.createObjectURL(csvData);
|
||||
|
||||
const downloadLink: HTMLAnchorElement = document.createElement('a');
|
||||
@ -35,6 +35,4 @@ function exportCsv(arrayData: Array<any>, filename: string, delimiter: string =
|
||||
}
|
||||
|
||||
/** @exports */
|
||||
export {
|
||||
exportCsv
|
||||
};
|
||||
export { exportCsv };
|
||||
|
@ -71,9 +71,9 @@ export class GlobalErrorHandler extends ErrorHandler {
|
||||
|
||||
const isWarning: boolean = this.isWarning(errorTraceString);
|
||||
if (isWarning) {
|
||||
this.loggingService.sendWarnLevelMessage(errorTraceString, {error});
|
||||
this.loggingService.sendWarnLevelMessage(errorTraceString, { error });
|
||||
} else {
|
||||
this.loggingService.sendErrorLevelMessage(errorTraceString, this, {error});
|
||||
this.loggingService.sendErrorLevelMessage(errorTraceString, this, { error });
|
||||
}
|
||||
|
||||
throw error;
|
||||
@ -110,14 +110,30 @@ export class GlobalErrorHandler extends ErrorHandler {
|
||||
const route: string = this.router.url;
|
||||
if (error instanceof HttpErrorResponse) {
|
||||
this.loggingService.sendErrorLevelMessage(
|
||||
`There was an HTTP error on route ${route}.\n${error.message}.\nStatus code: ${(error as HttpErrorResponse).status}`,
|
||||
this, {error});
|
||||
`There was an HTTP error on route ${route}.\n${error.message}.\nStatus code: ${
|
||||
(error as HttpErrorResponse).status
|
||||
}`,
|
||||
this,
|
||||
{ error }
|
||||
);
|
||||
} else if (error instanceof TypeError) {
|
||||
this.loggingService.sendErrorLevelMessage(`There was a Type error on route ${route}.\n${error.message}`, this, {error});
|
||||
this.loggingService.sendErrorLevelMessage(
|
||||
`There was a Type error on route ${route}.\n${error.message}`,
|
||||
this,
|
||||
{ error }
|
||||
);
|
||||
} else if (error instanceof Error) {
|
||||
this.loggingService.sendErrorLevelMessage(`There was a general error on route ${route}.\n${error.message}`, this, {error});
|
||||
this.loggingService.sendErrorLevelMessage(
|
||||
`There was a general error on route ${route}.\n${error.message}`,
|
||||
this,
|
||||
{ error }
|
||||
);
|
||||
} else {
|
||||
this.loggingService.sendErrorLevelMessage(`Nobody threw an error but something happened on route ${route}!`, this, {error});
|
||||
this.loggingService.sendErrorLevelMessage(
|
||||
`Nobody threw an error but something happened on route ${route}!`,
|
||||
this,
|
||||
{ error }
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,8 @@ function HttpGetter(): void {}
|
||||
* @param filename - The filename to fetch.
|
||||
* @returns The HTTP response text.
|
||||
*/
|
||||
HttpGetter.prototype.get = (filename: string) => new Promise((resolve, reject) => {
|
||||
HttpGetter.prototype.get = (filename) =>
|
||||
new Promise((resolve, reject) => {
|
||||
const xhr: XMLHttpRequest = new XMLHttpRequest();
|
||||
xhr.addEventListener('load', (e) => {
|
||||
if (xhr.status === 200) {
|
||||
@ -18,9 +19,7 @@ HttpGetter.prototype.get = (filename: string) => new Promise((resolve, reject) =
|
||||
});
|
||||
xhr.open('GET', filename);
|
||||
xhr.send();
|
||||
});
|
||||
});
|
||||
|
||||
/** @exports */
|
||||
export {
|
||||
HttpGetter
|
||||
};
|
||||
export { HttpGetter };
|
||||
|
@ -19,7 +19,7 @@ const actions: Array<Action> = [
|
||||
{ id: 3, user: 'Will', role: 'superadmin', action: 'Reclaim RSV 1000', approval: true },
|
||||
{ id: 4, user: 'Vivian', role: 'enroller', action: 'Complete user profile', approval: true },
|
||||
{ id: 5, user: 'Jack', role: 'enroller', action: 'Reclaim RSV 200', approval: false },
|
||||
{ id: 6, user: 'Patience', role: 'enroller', action: 'Change user information', approval: false }
|
||||
{ id: 6, user: 'Patience', role: 'enroller', action: 'Change user information', approval: false },
|
||||
];
|
||||
|
||||
/** A mock of curated area names. */
|
||||
@ -112,103 +112,634 @@ const areaTypes: Array<AreaType> = [
|
||||
const categories: Array<Category> = [
|
||||
{
|
||||
name: 'system',
|
||||
products: ['system', 'office main', 'office main phone']
|
||||
products: ['system', 'office main', 'office main phone'],
|
||||
},
|
||||
{
|
||||
name: 'education',
|
||||
products: ['book', 'coach', 'teacher', 'sch', 'school', 'pry', 'education', 'student', 'mwalimu', 'maalim', 'consultant', 'consult',
|
||||
'college', 'university', 'lecturer', 'primary', 'secondary', 'daycare', 'babycare', 'baby care', 'elim', 'eimu', 'nursery',
|
||||
'red cross', 'volunteer', 'instructor', 'journalist', 'lesson', 'academy', 'headmistress', 'headteacher', 'cyber', 'researcher',
|
||||
'professor', 'demo', 'expert', 'tution', 'tuition', 'children', 'headmaster', 'educator', 'Marital counsellor', 'counsellor',
|
||||
'trainer', 'vijana', 'youth', 'intern', 'redcross', 'KRCS', 'danish', 'science', 'data', 'facilitator', 'vitabu', 'kitabu']
|
||||
products: [
|
||||
'book',
|
||||
'coach',
|
||||
'teacher',
|
||||
'sch',
|
||||
'school',
|
||||
'pry',
|
||||
'education',
|
||||
'student',
|
||||
'mwalimu',
|
||||
'maalim',
|
||||
'consultant',
|
||||
'consult',
|
||||
'college',
|
||||
'university',
|
||||
'lecturer',
|
||||
'primary',
|
||||
'secondary',
|
||||
'daycare',
|
||||
'babycare',
|
||||
'baby care',
|
||||
'elim',
|
||||
'eimu',
|
||||
'nursery',
|
||||
'red cross',
|
||||
'volunteer',
|
||||
'instructor',
|
||||
'journalist',
|
||||
'lesson',
|
||||
'academy',
|
||||
'headmistress',
|
||||
'headteacher',
|
||||
'cyber',
|
||||
'researcher',
|
||||
'professor',
|
||||
'demo',
|
||||
'expert',
|
||||
'tution',
|
||||
'tuition',
|
||||
'children',
|
||||
'headmaster',
|
||||
'educator',
|
||||
'Marital counsellor',
|
||||
'counsellor',
|
||||
'trainer',
|
||||
'vijana',
|
||||
'youth',
|
||||
'intern',
|
||||
'redcross',
|
||||
'KRCS',
|
||||
'danish',
|
||||
'science',
|
||||
'data',
|
||||
'facilitator',
|
||||
'vitabu',
|
||||
'kitabu',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'faith',
|
||||
products: ['pastor', 'imam', 'madrasa', 'religous', 'religious', 'ustadh', 'ustadhi', 'Marital counsellor', 'counsellor', 'church',
|
||||
'kanisa', 'mksiti', 'donor']
|
||||
products: [
|
||||
'pastor',
|
||||
'imam',
|
||||
'madrasa',
|
||||
'religous',
|
||||
'religious',
|
||||
'ustadh',
|
||||
'ustadhi',
|
||||
'Marital counsellor',
|
||||
'counsellor',
|
||||
'church',
|
||||
'kanisa',
|
||||
'mksiti',
|
||||
'donor',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'government',
|
||||
products: ['elder', 'chief', 'police', 'government', 'country', 'county', 'soldier', 'village admin', 'ward', 'leader', 'kra',
|
||||
'mailman', 'immagration', 'immigration']
|
||||
products: [
|
||||
'elder',
|
||||
'chief',
|
||||
'police',
|
||||
'government',
|
||||
'country',
|
||||
'county',
|
||||
'soldier',
|
||||
'village admin',
|
||||
'ward',
|
||||
'leader',
|
||||
'kra',
|
||||
'mailman',
|
||||
'immagration',
|
||||
'immigration',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'environment',
|
||||
products: ['conservation', 'toilet', 'choo', 'garbage', 'fagio', 'waste', 'tree', 'taka', 'scrap', 'cleaning', 'gardener', 'rubbish',
|
||||
'usafi', 'mazingira', 'miti', 'trash', 'cleaner', 'plastic', 'collection', 'seedling', 'seedlings', 'recycling']
|
||||
products: [
|
||||
'conservation',
|
||||
'toilet',
|
||||
'choo',
|
||||
'garbage',
|
||||
'fagio',
|
||||
'waste',
|
||||
'tree',
|
||||
'taka',
|
||||
'scrap',
|
||||
'cleaning',
|
||||
'gardener',
|
||||
'rubbish',
|
||||
'usafi',
|
||||
'mazingira',
|
||||
'miti',
|
||||
'trash',
|
||||
'cleaner',
|
||||
'plastic',
|
||||
'collection',
|
||||
'seedling',
|
||||
'seedlings',
|
||||
'recycling',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'farming',
|
||||
products: ['farm', 'farmer', 'farming', 'mkulima', 'kulima', 'ukulima', 'wakulima', 'jembe', 'shamba']
|
||||
products: [
|
||||
'farm',
|
||||
'farmer',
|
||||
'farming',
|
||||
'mkulima',
|
||||
'kulima',
|
||||
'ukulima',
|
||||
'wakulima',
|
||||
'jembe',
|
||||
'shamba',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'labour',
|
||||
products: ['artist', 'agent', 'guard', 'askari', 'accountant', 'baker', 'beadwork', 'beauty', 'business', 'barber', 'casual',
|
||||
'electrian', 'caretaker', 'car wash', 'capenter', 'construction', 'chef', 'catering', 'cobler', 'cobbler', 'carwash', 'dhobi',
|
||||
'landlord', 'design', 'carpenter', 'fundi', 'hawking', 'hawker', 'househelp', 'hsehelp', 'house help', 'help', 'housegirl', 'kushona',
|
||||
'juakali', 'jualikali', 'juacali', 'jua kali', 'shepherd', 'makuti', 'kujenga', 'kinyozi', 'kazi', 'knitting', 'kufua', 'fua',
|
||||
'hustler', 'biashara', 'labour', 'labor', 'laundry', 'repair', 'hair', 'posho', 'mill', 'mtambo', 'uvuvi', 'engineer', 'manager',
|
||||
'tailor', 'nguo', 'mason', 'mtumba', 'garage', 'mechanic', 'mjenzi', 'mfugaji', 'painter', 'receptionist', 'printing', 'programming',
|
||||
'plumb', 'charging', 'salon', 'mpishi', 'msusi', 'mgema', 'footballer', 'photocopy', 'peddler', 'staff', 'sales', 'service', 'saloon',
|
||||
'seremala', 'security', 'insurance', 'secretary', 'shoe', 'shepard', 'shephard', 'tout', 'tv', 'mvuvi', 'mawe', 'majani', 'maembe',
|
||||
'freelance', 'mjengo', 'electronics', 'photographer', 'programmer', 'electrician', 'washing', 'bricks', 'welder', 'welding',
|
||||
'working', 'worker', 'watchman', 'waiter', 'waitress', 'viatu', 'yoga', 'guitarist', 'house', 'artisan', 'musician', 'trade',
|
||||
'makonge', 'ujenzi', 'vendor', 'watchlady', 'marketing', 'beautician', 'photo', 'metal work', 'supplier', 'law firm', 'brewer']
|
||||
products: [
|
||||
'artist',
|
||||
'agent',
|
||||
'guard',
|
||||
'askari',
|
||||
'accountant',
|
||||
'baker',
|
||||
'beadwork',
|
||||
'beauty',
|
||||
'business',
|
||||
'barber',
|
||||
'casual',
|
||||
'electrian',
|
||||
'caretaker',
|
||||
'car wash',
|
||||
'capenter',
|
||||
'construction',
|
||||
'chef',
|
||||
'catering',
|
||||
'cobler',
|
||||
'cobbler',
|
||||
'carwash',
|
||||
'dhobi',
|
||||
'landlord',
|
||||
'design',
|
||||
'carpenter',
|
||||
'fundi',
|
||||
'hawking',
|
||||
'hawker',
|
||||
'househelp',
|
||||
'hsehelp',
|
||||
'house help',
|
||||
'help',
|
||||
'housegirl',
|
||||
'kushona',
|
||||
'juakali',
|
||||
'jualikali',
|
||||
'juacali',
|
||||
'jua kali',
|
||||
'shepherd',
|
||||
'makuti',
|
||||
'kujenga',
|
||||
'kinyozi',
|
||||
'kazi',
|
||||
'knitting',
|
||||
'kufua',
|
||||
'fua',
|
||||
'hustler',
|
||||
'biashara',
|
||||
'labour',
|
||||
'labor',
|
||||
'laundry',
|
||||
'repair',
|
||||
'hair',
|
||||
'posho',
|
||||
'mill',
|
||||
'mtambo',
|
||||
'uvuvi',
|
||||
'engineer',
|
||||
'manager',
|
||||
'tailor',
|
||||
'nguo',
|
||||
'mason',
|
||||
'mtumba',
|
||||
'garage',
|
||||
'mechanic',
|
||||
'mjenzi',
|
||||
'mfugaji',
|
||||
'painter',
|
||||
'receptionist',
|
||||
'printing',
|
||||
'programming',
|
||||
'plumb',
|
||||
'charging',
|
||||
'salon',
|
||||
'mpishi',
|
||||
'msusi',
|
||||
'mgema',
|
||||
'footballer',
|
||||
'photocopy',
|
||||
'peddler',
|
||||
'staff',
|
||||
'sales',
|
||||
'service',
|
||||
'saloon',
|
||||
'seremala',
|
||||
'security',
|
||||
'insurance',
|
||||
'secretary',
|
||||
'shoe',
|
||||
'shepard',
|
||||
'shephard',
|
||||
'tout',
|
||||
'tv',
|
||||
'mvuvi',
|
||||
'mawe',
|
||||
'majani',
|
||||
'maembe',
|
||||
'freelance',
|
||||
'mjengo',
|
||||
'electronics',
|
||||
'photographer',
|
||||
'programmer',
|
||||
'electrician',
|
||||
'washing',
|
||||
'bricks',
|
||||
'welder',
|
||||
'welding',
|
||||
'working',
|
||||
'worker',
|
||||
'watchman',
|
||||
'waiter',
|
||||
'waitress',
|
||||
'viatu',
|
||||
'yoga',
|
||||
'guitarist',
|
||||
'house',
|
||||
'artisan',
|
||||
'musician',
|
||||
'trade',
|
||||
'makonge',
|
||||
'ujenzi',
|
||||
'vendor',
|
||||
'watchlady',
|
||||
'marketing',
|
||||
'beautician',
|
||||
'photo',
|
||||
'metal work',
|
||||
'supplier',
|
||||
'law firm',
|
||||
'brewer',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'food',
|
||||
products: ['avocado', 'bhajia', 'bajia', 'mbonga', 'bofu', 'beans', 'biscuits', 'biringanya', 'banana', 'bananas', 'crisps', 'chakula',
|
||||
'coconut', 'chapati', 'cereal', 'chipo', 'chapo', 'chai', 'chips', 'cassava', 'cake', 'cereals', 'cook', 'corn', 'coffee', 'chicken',
|
||||
'dagaa', 'donut', 'dough', 'groundnuts', 'hotel', 'holel', 'hoteli', 'butcher', 'butchery', 'fruit', 'food', 'fruits', 'fish',
|
||||
'githeri', 'grocery', 'grocer', 'pojo', 'papa', 'goats', 'mabenda', 'mbenda', 'poultry', 'soda', 'peanuts', 'potatoes', 'samosa',
|
||||
'soko', 'samaki', 'tomato', 'tomatoes', 'mchele', 'matunda', 'mango', 'melon', 'mellon', 'nyanya', 'nyama', 'omena', 'umena', 'ndizi',
|
||||
'njugu', 'kamba kamba', 'khaimati', 'kaimati', 'kunde', 'kuku', 'kahawa', 'keki', 'muguka', 'miraa', 'milk', 'choma', 'maziwa',
|
||||
'mboga', 'mbog', 'busaa', 'chumvi', 'cabbages', 'mabuyu', 'machungwa', 'mbuzi', 'mnazi', 'mchicha', 'ngombe', 'ngano', 'nazi',
|
||||
'oranges', 'peanuts', 'mkate', 'bread', 'mikate', 'vitungu', 'sausages', 'maize', 'mbata', 'mchuzi', 'mchuuzi', 'mandazi', 'mbaazi',
|
||||
'mahindi', 'maandazi', 'mogoka', 'meat', 'mhogo', 'mihogo', 'muhogo', 'maharagwe', 'miwa', 'mahamri', 'mitumba', 'simsim', 'porridge',
|
||||
'pilau', 'vegetable', 'egg', 'mayai', 'mifugo', 'unga', 'good', 'sima', 'sweet', 'sweats', 'sambusa', 'snacks', 'sugar', 'suger',
|
||||
'ugoro', 'sukari', 'soup', 'spinach', 'smokie', 'smokies', 'sukuma', 'tea', 'uji', 'ugali', 'uchuzi', 'uchuuzi', 'viazi', 'yoghurt',
|
||||
'yogurt', 'wine', 'marondo', 'maandzi', 'matoke', 'omeno', 'onions', 'nzugu', 'korosho', 'barafu', 'juice']
|
||||
products: [
|
||||
'avocado',
|
||||
'bhajia',
|
||||
'bajia',
|
||||
'mbonga',
|
||||
'bofu',
|
||||
'beans',
|
||||
'biscuits',
|
||||
'biringanya',
|
||||
'banana',
|
||||
'bananas',
|
||||
'crisps',
|
||||
'chakula',
|
||||
'coconut',
|
||||
'chapati',
|
||||
'cereal',
|
||||
'chipo',
|
||||
'chapo',
|
||||
'chai',
|
||||
'chips',
|
||||
'cassava',
|
||||
'cake',
|
||||
'cereals',
|
||||
'cook',
|
||||
'corn',
|
||||
'coffee',
|
||||
'chicken',
|
||||
'dagaa',
|
||||
'donut',
|
||||
'dough',
|
||||
'groundnuts',
|
||||
'hotel',
|
||||
'holel',
|
||||
'hoteli',
|
||||
'butcher',
|
||||
'butchery',
|
||||
'fruit',
|
||||
'food',
|
||||
'fruits',
|
||||
'fish',
|
||||
'githeri',
|
||||
'grocery',
|
||||
'grocer',
|
||||
'pojo',
|
||||
'papa',
|
||||
'goats',
|
||||
'mabenda',
|
||||
'mbenda',
|
||||
'poultry',
|
||||
'soda',
|
||||
'peanuts',
|
||||
'potatoes',
|
||||
'samosa',
|
||||
'soko',
|
||||
'samaki',
|
||||
'tomato',
|
||||
'tomatoes',
|
||||
'mchele',
|
||||
'matunda',
|
||||
'mango',
|
||||
'melon',
|
||||
'mellon',
|
||||
'nyanya',
|
||||
'nyama',
|
||||
'omena',
|
||||
'umena',
|
||||
'ndizi',
|
||||
'njugu',
|
||||
'kamba kamba',
|
||||
'khaimati',
|
||||
'kaimati',
|
||||
'kunde',
|
||||
'kuku',
|
||||
'kahawa',
|
||||
'keki',
|
||||
'muguka',
|
||||
'miraa',
|
||||
'milk',
|
||||
'choma',
|
||||
'maziwa',
|
||||
'mboga',
|
||||
'mbog',
|
||||
'busaa',
|
||||
'chumvi',
|
||||
'cabbages',
|
||||
'mabuyu',
|
||||
'machungwa',
|
||||
'mbuzi',
|
||||
'mnazi',
|
||||
'mchicha',
|
||||
'ngombe',
|
||||
'ngano',
|
||||
'nazi',
|
||||
'oranges',
|
||||
'peanuts',
|
||||
'mkate',
|
||||
'bread',
|
||||
'mikate',
|
||||
'vitungu',
|
||||
'sausages',
|
||||
'maize',
|
||||
'mbata',
|
||||
'mchuzi',
|
||||
'mchuuzi',
|
||||
'mandazi',
|
||||
'mbaazi',
|
||||
'mahindi',
|
||||
'maandazi',
|
||||
'mogoka',
|
||||
'meat',
|
||||
'mhogo',
|
||||
'mihogo',
|
||||
'muhogo',
|
||||
'maharagwe',
|
||||
'miwa',
|
||||
'mahamri',
|
||||
'mitumba',
|
||||
'simsim',
|
||||
'porridge',
|
||||
'pilau',
|
||||
'vegetable',
|
||||
'egg',
|
||||
'mayai',
|
||||
'mifugo',
|
||||
'unga',
|
||||
'good',
|
||||
'sima',
|
||||
'sweet',
|
||||
'sweats',
|
||||
'sambusa',
|
||||
'snacks',
|
||||
'sugar',
|
||||
'suger',
|
||||
'ugoro',
|
||||
'sukari',
|
||||
'soup',
|
||||
'spinach',
|
||||
'smokie',
|
||||
'smokies',
|
||||
'sukuma',
|
||||
'tea',
|
||||
'uji',
|
||||
'ugali',
|
||||
'uchuzi',
|
||||
'uchuuzi',
|
||||
'viazi',
|
||||
'yoghurt',
|
||||
'yogurt',
|
||||
'wine',
|
||||
'marondo',
|
||||
'maandzi',
|
||||
'matoke',
|
||||
'omeno',
|
||||
'onions',
|
||||
'nzugu',
|
||||
'korosho',
|
||||
'barafu',
|
||||
'juice',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'water',
|
||||
products: ['maji', 'water']
|
||||
products: ['maji', 'water'],
|
||||
},
|
||||
{
|
||||
name: 'health',
|
||||
products: ['agrovet', 'dispensary', 'barakoa', 'chemist', 'Chemicals', 'chv', 'doctor', 'daktari', 'dawa', 'hospital', 'herbalist',
|
||||
'mganga', 'sabuni', 'soap', 'nurse', 'heath', 'community health worker', 'clinic', 'clinical', 'mask', 'medicine', 'lab technician',
|
||||
'pharmacy', 'cosmetics', 'veterinary', 'vet', 'sickly', 'emergency response', 'emergency']
|
||||
products: [
|
||||
'agrovet',
|
||||
'dispensary',
|
||||
'barakoa',
|
||||
'chemist',
|
||||
'Chemicals',
|
||||
'chv',
|
||||
'doctor',
|
||||
'daktari',
|
||||
'dawa',
|
||||
'hospital',
|
||||
'herbalist',
|
||||
'mganga',
|
||||
'sabuni',
|
||||
'soap',
|
||||
'nurse',
|
||||
'heath',
|
||||
'community health worker',
|
||||
'clinic',
|
||||
'clinical',
|
||||
'mask',
|
||||
'medicine',
|
||||
'lab technician',
|
||||
'pharmacy',
|
||||
'cosmetics',
|
||||
'veterinary',
|
||||
'vet',
|
||||
'sickly',
|
||||
'emergency response',
|
||||
'emergency',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'savings',
|
||||
products: ['chama', 'group', 'savings', 'loan', 'silc', 'vsla', 'credit', 'finance']
|
||||
products: ['chama', 'group', 'savings', 'loan', 'silc', 'vsla', 'credit', 'finance'],
|
||||
},
|
||||
{
|
||||
name: 'shop',
|
||||
products: ['bag', 'bead', 'belt', 'bedding', 'jik', 'bed', 'cement', 'botique', 'boutique', 'lines', 'kibanda', 'kiosk', 'spareparts',
|
||||
'candy', 'cloth', 'electricals', 'mutumba', 'cafe', 'leso', 'lesso', 'duka', 'spare parts', 'socks', 'malimali', 'mitungi',
|
||||
'mali mali', 'hardware', 'detergent', 'detergents', 'dera', 'retail', 'kamba', 'pombe', 'pampers', 'pool', 'phone', 'simu', 'mangwe',
|
||||
'mikeka', 'movie', 'shop', 'acces', 'mchanga', 'uto', 'airtime', 'matress', 'mattress', 'mattresses', 'mpsea', 'mpesa', 'shirt',
|
||||
'wholesaler', 'perfume', 'playstation', 'tissue', 'vikapu', 'uniform', 'flowers', 'vitenge', 'utencils', 'utensils', 'station',
|
||||
'jewel', 'pool table', 'club', 'pub', 'bar', 'furniture', 'm-pesa', 'vyombo']
|
||||
products: [
|
||||
'bag',
|
||||
'bead',
|
||||
'belt',
|
||||
'bedding',
|
||||
'jik',
|
||||
'bed',
|
||||
'cement',
|
||||
'botique',
|
||||
'boutique',
|
||||
'lines',
|
||||
'kibanda',
|
||||
'kiosk',
|
||||
'spareparts',
|
||||
'candy',
|
||||
'cloth',
|
||||
'electricals',
|
||||
'mutumba',
|
||||
'cafe',
|
||||
'leso',
|
||||
'lesso',
|
||||
'duka',
|
||||
'spare parts',
|
||||
'socks',
|
||||
'malimali',
|
||||
'mitungi',
|
||||
'mali mali',
|
||||
'hardware',
|
||||
'detergent',
|
||||
'detergents',
|
||||
'dera',
|
||||
'retail',
|
||||
'kamba',
|
||||
'pombe',
|
||||
'pampers',
|
||||
'pool',
|
||||
'phone',
|
||||
'simu',
|
||||
'mangwe',
|
||||
'mikeka',
|
||||
'movie',
|
||||
'shop',
|
||||
'acces',
|
||||
'mchanga',
|
||||
'uto',
|
||||
'airtime',
|
||||
'matress',
|
||||
'mattress',
|
||||
'mattresses',
|
||||
'mpsea',
|
||||
'mpesa',
|
||||
'shirt',
|
||||
'wholesaler',
|
||||
'perfume',
|
||||
'playstation',
|
||||
'tissue',
|
||||
'vikapu',
|
||||
'uniform',
|
||||
'flowers',
|
||||
'vitenge',
|
||||
'utencils',
|
||||
'utensils',
|
||||
'station',
|
||||
'jewel',
|
||||
'pool table',
|
||||
'club',
|
||||
'pub',
|
||||
'bar',
|
||||
'furniture',
|
||||
'm-pesa',
|
||||
'vyombo',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'transport',
|
||||
products: ['kebeba', 'beba', 'bebabeba', 'bike', 'bicycle', 'matatu', 'boda', 'bodaboda', 'cart', 'carrier', 'tour', 'travel', 'driver',
|
||||
'dereva', 'tout', 'conductor', 'kubeba', 'tuktuk', 'taxi', 'piki', 'pikipiki', 'manamba', 'trasportion', 'mkokoteni', 'mover',
|
||||
'motorist', 'motorbike', 'transport', 'transpoter', 'gari', 'magari', 'makanga', 'car']
|
||||
products: [
|
||||
'kebeba',
|
||||
'beba',
|
||||
'bebabeba',
|
||||
'bike',
|
||||
'bicycle',
|
||||
'matatu',
|
||||
'boda',
|
||||
'bodaboda',
|
||||
'cart',
|
||||
'carrier',
|
||||
'tour',
|
||||
'travel',
|
||||
'driver',
|
||||
'dereva',
|
||||
'tout',
|
||||
'conductor',
|
||||
'kubeba',
|
||||
'tuktuk',
|
||||
'taxi',
|
||||
'piki',
|
||||
'pikipiki',
|
||||
'manamba',
|
||||
'trasportion',
|
||||
'mkokoteni',
|
||||
'mover',
|
||||
'motorist',
|
||||
'motorbike',
|
||||
'transport',
|
||||
'transpoter',
|
||||
'gari',
|
||||
'magari',
|
||||
'makanga',
|
||||
'car',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'fuel/energy',
|
||||
products: ['timber', 'timberyard', 'biogas', 'charcol', 'charcoal', 'kuni', 'mbao', 'fuel', 'makaa', 'mafuta', 'moto', 'solar', 'stima',
|
||||
'fire', 'firewood', 'wood', 'oil', 'taa', 'gas', 'paraffin', 'parrafin', 'parafin', 'petrol', 'petro', 'kerosine', 'kerosene',
|
||||
'diesel']
|
||||
products: [
|
||||
'timber',
|
||||
'timberyard',
|
||||
'biogas',
|
||||
'charcol',
|
||||
'charcoal',
|
||||
'kuni',
|
||||
'mbao',
|
||||
'fuel',
|
||||
'makaa',
|
||||
'mafuta',
|
||||
'moto',
|
||||
'solar',
|
||||
'stima',
|
||||
'fire',
|
||||
'firewood',
|
||||
'wood',
|
||||
'oil',
|
||||
'taa',
|
||||
'gas',
|
||||
'paraffin',
|
||||
'parrafin',
|
||||
'parafin',
|
||||
'petrol',
|
||||
'petro',
|
||||
'kerosine',
|
||||
'kerosene',
|
||||
'diesel',
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'other',
|
||||
products: ['other', 'none', 'unknown', 'none']
|
||||
}
|
||||
products: ['other', 'none', 'unknown', 'none'],
|
||||
},
|
||||
];
|
||||
|
||||
/** A mock of curated genders */
|
||||
@ -336,22 +867,26 @@ export class MockBackendInterceptor implements HttpInterceptor {
|
||||
}
|
||||
|
||||
function getAreaNames(): Observable<HttpResponse<any>> {
|
||||
const areaNameList: Array<string> = areaNames.map(areaName => areaName.name);
|
||||
const areaNameList: Array<string> = areaNames.map((areaName) => areaName.name);
|
||||
return ok(areaNameList);
|
||||
}
|
||||
|
||||
function getAreaNameByLocation(): Observable<HttpResponse<any>> {
|
||||
const queriedAreaName: AreaName = areaNames.find(areaName => areaName.locations.includes(stringFromUrl()));
|
||||
const queriedAreaName: AreaName = areaNames.find((areaName) =>
|
||||
areaName.locations.includes(stringFromUrl())
|
||||
);
|
||||
return ok(queriedAreaName.name);
|
||||
}
|
||||
|
||||
function getAreaTypes(): Observable<HttpResponse<any>> {
|
||||
const areaTypeList: Array<string> = areaTypes.map(areaType => areaType.name);
|
||||
const areaTypeList: Array<string> = areaTypes.map((areaType) => areaType.name);
|
||||
return ok(areaTypeList);
|
||||
}
|
||||
|
||||
function getAreaTypeByArea(): Observable<HttpResponse<any>> {
|
||||
const queriedAreaType: AreaType = areaTypes.find(areaType => areaType.area.includes(stringFromUrl()));
|
||||
const queriedAreaType: AreaType = areaTypes.find((areaType) =>
|
||||
areaType.area.includes(stringFromUrl())
|
||||
);
|
||||
return ok(queriedAreaType.name);
|
||||
}
|
||||
|
||||
@ -412,5 +947,5 @@ export class MockBackendInterceptor implements HttpInterceptor {
|
||||
export const MockBackendProvider = {
|
||||
provide: HTTP_INTERCEPTORS,
|
||||
useClass: MockBackendInterceptor,
|
||||
multi: true
|
||||
multi: true,
|
||||
};
|
||||
|
@ -1,7 +1,7 @@
|
||||
/** An object defining the properties of the data read. */
|
||||
const objCsv: { size: number, dataFile: any } = {
|
||||
const objCsv: { size: number; dataFile: any } = {
|
||||
size: 0,
|
||||
dataFile: []
|
||||
dataFile: [],
|
||||
};
|
||||
|
||||
/**
|
||||
@ -14,7 +14,7 @@ function readCsv(input: any): Array<any> | void {
|
||||
if (input.files && input.files[0]) {
|
||||
const reader: FileReader = new FileReader();
|
||||
reader.readAsBinaryString(input.files[0]);
|
||||
reader.onload = event => {
|
||||
reader.onload = (event) => {
|
||||
objCsv.size = event.total;
|
||||
objCsv.dataFile = event.target.result;
|
||||
return parseData(objCsv.dataFile);
|
||||
@ -31,7 +31,7 @@ function readCsv(input: any): Array<any> | void {
|
||||
function parseData(data: any): Array<any> {
|
||||
const csvData: Array<any> = [];
|
||||
const lineBreak: Array<any> = data.split('\n');
|
||||
lineBreak.forEach(res => {
|
||||
lineBreak.forEach((res) => {
|
||||
csvData.push(res.split(','));
|
||||
});
|
||||
console.table(csvData);
|
||||
@ -39,6 +39,4 @@ function parseData(data: any): Array<any> {
|
||||
}
|
||||
|
||||
/** @exports */
|
||||
export {
|
||||
readCsv
|
||||
};
|
||||
export { readCsv };
|
||||
|
@ -10,7 +10,7 @@ async function personValidation(person: any): Promise<void> {
|
||||
const personValidationErrors: any = await validatePerson(person);
|
||||
|
||||
if (personValidationErrors) {
|
||||
personValidationErrors.map(error => console.error(`${error.message}`));
|
||||
personValidationErrors.map((error) => console.error(`${error.message}`));
|
||||
}
|
||||
}
|
||||
|
||||
@ -23,12 +23,9 @@ async function vcardValidation(vcard: any): Promise<void> {
|
||||
const vcardValidationErrors: any = await validateVcard(vcard);
|
||||
|
||||
if (vcardValidationErrors) {
|
||||
vcardValidationErrors.map(error => console.error(`${error.message}`));
|
||||
vcardValidationErrors.map((error) => console.error(`${error.message}`));
|
||||
}
|
||||
}
|
||||
|
||||
/** @exports */
|
||||
export {
|
||||
personValidation,
|
||||
vcardValidation,
|
||||
};
|
||||
export { personValidation, vcardValidation };
|
||||
|
@ -3,11 +3,11 @@ import { TestBed } from '@angular/core/testing';
|
||||
import { ErrorInterceptor } from '@app/_interceptors/error.interceptor';
|
||||
|
||||
describe('ErrorInterceptor', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
providers: [
|
||||
ErrorInterceptor
|
||||
]
|
||||
}));
|
||||
beforeEach(() =>
|
||||
TestBed.configureTestingModule({
|
||||
providers: [ErrorInterceptor],
|
||||
})
|
||||
);
|
||||
|
||||
it('should be created', () => {
|
||||
const interceptor: ErrorInterceptor = TestBed.inject(ErrorInterceptor);
|
||||
|
@ -1,18 +1,18 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
HttpRequest,
|
||||
HttpHandler,
|
||||
HttpEvent,
|
||||
HttpInterceptor, HttpErrorResponse
|
||||
HttpInterceptor,
|
||||
HttpErrorResponse,
|
||||
} from '@angular/common/http';
|
||||
import {Observable, throwError} from 'rxjs';
|
||||
import {catchError} from 'rxjs/operators';
|
||||
import {ErrorDialogService, LoggingService} from '@app/_services';
|
||||
import {Router} from '@angular/router';
|
||||
import { Observable, throwError } from 'rxjs';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { ErrorDialogService, LoggingService } from '@app/_services';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Injectable()
|
||||
export class ErrorInterceptor implements HttpInterceptor {
|
||||
|
||||
constructor(
|
||||
private errorDialogService: ErrorDialogService,
|
||||
private loggingService: LoggingService,
|
||||
@ -29,9 +29,11 @@ export class ErrorInterceptor implements HttpInterceptor {
|
||||
} else {
|
||||
// The backend returned an unsuccessful response code.
|
||||
// The response body may contain clues as to what went wrong.
|
||||
errorMessage = `Backend returned code ${err.status}, body was: ${JSON.stringify(err.error)}`;
|
||||
errorMessage = `Backend returned code ${err.status}, body was: ${JSON.stringify(
|
||||
err.error
|
||||
)}`;
|
||||
}
|
||||
this.loggingService.sendErrorLevelMessage(errorMessage, this, {error: err});
|
||||
this.loggingService.sendErrorLevelMessage(errorMessage, this, { error: err });
|
||||
switch (err.status) {
|
||||
case 401: // unauthorized
|
||||
this.router.navigateByUrl('/auth').then();
|
||||
|
@ -3,11 +3,11 @@ import { TestBed } from '@angular/core/testing';
|
||||
import { HttpConfigInterceptor } from './http-config.interceptor';
|
||||
|
||||
describe('HttpConfigInterceptor', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
providers: [
|
||||
HttpConfigInterceptor
|
||||
]
|
||||
}));
|
||||
beforeEach(() =>
|
||||
TestBed.configureTestingModule({
|
||||
providers: [HttpConfigInterceptor],
|
||||
})
|
||||
);
|
||||
|
||||
it('should be created', () => {
|
||||
const interceptor: HttpConfigInterceptor = TestBed.inject(HttpConfigInterceptor);
|
||||
|
@ -1,15 +1,9 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
HttpRequest,
|
||||
HttpHandler,
|
||||
HttpEvent,
|
||||
HttpInterceptor
|
||||
} from '@angular/common/http';
|
||||
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class HttpConfigInterceptor implements HttpInterceptor {
|
||||
|
||||
constructor() {}
|
||||
|
||||
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
|
@ -3,11 +3,11 @@ import { TestBed } from '@angular/core/testing';
|
||||
import { LoggingInterceptor } from './logging.interceptor';
|
||||
|
||||
describe('LoggingInterceptor', () => {
|
||||
beforeEach(() => TestBed.configureTestingModule({
|
||||
providers: [
|
||||
LoggingInterceptor
|
||||
]
|
||||
}));
|
||||
beforeEach(() =>
|
||||
TestBed.configureTestingModule({
|
||||
providers: [LoggingInterceptor],
|
||||
})
|
||||
);
|
||||
|
||||
it('should be created', () => {
|
||||
const interceptor: LoggingInterceptor = TestBed.inject(LoggingInterceptor);
|
||||
|
@ -4,18 +4,15 @@ import {
|
||||
HttpHandler,
|
||||
HttpEvent,
|
||||
HttpInterceptor,
|
||||
HttpResponse
|
||||
HttpResponse,
|
||||
} from '@angular/common/http';
|
||||
import {Observable} from 'rxjs';
|
||||
import {LoggingService} from '@app/_services/logging.service';
|
||||
import {finalize, tap} from 'rxjs/operators';
|
||||
import { Observable } from 'rxjs';
|
||||
import { LoggingService } from '@app/_services/logging.service';
|
||||
import { finalize, tap } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class LoggingInterceptor implements HttpInterceptor {
|
||||
|
||||
constructor(
|
||||
private loggingService: LoggingService
|
||||
) {}
|
||||
constructor(private loggingService: LoggingService) {}
|
||||
|
||||
intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
return next.handle(request);
|
||||
|
@ -20,24 +20,34 @@ interface AccountDetails {
|
||||
products: string[];
|
||||
category?: string;
|
||||
vcard: {
|
||||
email: [{
|
||||
email: [
|
||||
{
|
||||
value: string;
|
||||
}];
|
||||
fn: [{
|
||||
}
|
||||
];
|
||||
fn: [
|
||||
{
|
||||
value: string;
|
||||
}];
|
||||
n: [{
|
||||
}
|
||||
];
|
||||
n: [
|
||||
{
|
||||
value: string[];
|
||||
}];
|
||||
tel: [{
|
||||
}
|
||||
];
|
||||
tel: [
|
||||
{
|
||||
meta: {
|
||||
TYP: string[];
|
||||
},
|
||||
};
|
||||
value: string;
|
||||
}],
|
||||
version: [{
|
||||
}
|
||||
];
|
||||
version: [
|
||||
{
|
||||
value: string;
|
||||
}];
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
@ -75,31 +85,35 @@ const defaultAccount: AccountDetails = {
|
||||
},
|
||||
products: [],
|
||||
vcard: {
|
||||
email: [{
|
||||
email: [
|
||||
{
|
||||
value: '',
|
||||
}],
|
||||
fn: [{
|
||||
},
|
||||
],
|
||||
fn: [
|
||||
{
|
||||
value: 'Sarafu Contract',
|
||||
}],
|
||||
n: [{
|
||||
},
|
||||
],
|
||||
n: [
|
||||
{
|
||||
value: ['Sarafu', 'Contract'],
|
||||
}],
|
||||
tel: [{
|
||||
},
|
||||
],
|
||||
tel: [
|
||||
{
|
||||
meta: {
|
||||
TYP: [],
|
||||
},
|
||||
value: '',
|
||||
}],
|
||||
version: [{
|
||||
},
|
||||
],
|
||||
version: [
|
||||
{
|
||||
value: '3.0',
|
||||
}],
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
export {
|
||||
AccountDetails,
|
||||
Signature,
|
||||
Meta,
|
||||
MetaResponse,
|
||||
defaultAccount
|
||||
};
|
||||
export { AccountDetails, Signature, Meta, MetaResponse, defaultAccount };
|
||||
|
@ -21,9 +21,4 @@ interface AreaType {
|
||||
area: Array<string>;
|
||||
}
|
||||
|
||||
export {
|
||||
Action,
|
||||
Category,
|
||||
AreaName,
|
||||
AreaType
|
||||
};
|
||||
export { Action, Category, AreaName, AreaType };
|
||||
|
@ -17,7 +17,4 @@ class W3 {
|
||||
provider: any;
|
||||
}
|
||||
|
||||
export {
|
||||
Settings,
|
||||
W3
|
||||
};
|
||||
export { Settings, W3 };
|
||||
|
@ -6,6 +6,4 @@ interface Staff {
|
||||
userid: string;
|
||||
}
|
||||
|
||||
export {
|
||||
Staff
|
||||
};
|
||||
export { Staff };
|
||||
|
@ -8,12 +8,10 @@ interface Token {
|
||||
'0xa686005CE37Dce7738436256982C3903f2E4ea8E'?: {
|
||||
weight: string;
|
||||
balance: string;
|
||||
}
|
||||
};
|
||||
};
|
||||
reserveRatio?: string;
|
||||
owner?: string;
|
||||
}
|
||||
|
||||
export {
|
||||
Token
|
||||
};
|
||||
export { Token };
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {AccountDetails} from '@app/_models/account';
|
||||
import { AccountDetails } from '@app/_models/account';
|
||||
|
||||
class BlocksBloom {
|
||||
low: number;
|
||||
@ -43,10 +43,4 @@ class Conversion {
|
||||
tx: Tx;
|
||||
}
|
||||
|
||||
export {
|
||||
BlocksBloom,
|
||||
TxToken,
|
||||
Tx,
|
||||
Transaction,
|
||||
Conversion
|
||||
};
|
||||
export { BlocksBloom, TxToken, Tx, Transaction, Conversion };
|
||||
|
@ -31,8 +31,7 @@ interface MutableKeyStore extends KeyStore {
|
||||
sign(plainText: string): Promise<any>;
|
||||
}
|
||||
|
||||
class MutablePgpKeyStore implements MutableKeyStore{
|
||||
|
||||
class MutablePgpKeyStore implements MutableKeyStore {
|
||||
async loadKeyring(): Promise<void> {
|
||||
await keyring.load();
|
||||
await keyring.store();
|
||||
@ -77,8 +76,8 @@ class MutablePgpKeyStore implements MutableKeyStore{
|
||||
|
||||
async isValidKey(key): Promise<boolean> {
|
||||
// There is supposed to be an openpgp.readKey() method but I can't find it?
|
||||
const _key = await openpgp.key.readArmored(key);
|
||||
return !_key.err;
|
||||
const testKey = await openpgp.key.readArmored(key);
|
||||
return !testKey.err;
|
||||
}
|
||||
|
||||
async isEncryptedPrivateKey(privateKey: any): Promise<boolean> {
|
||||
@ -93,8 +92,12 @@ class MutablePgpKeyStore implements MutableKeyStore{
|
||||
|
||||
getFingerprint(): string {
|
||||
// TODO Handle multiple keys
|
||||
return keyring.privateKeys && keyring.privateKeys.keys[0] && keyring.privateKeys.keys[0].keyPacket &&
|
||||
keyring.privateKeys.keys[0].keyPacket.fingerprint;
|
||||
return (
|
||||
keyring.privateKeys &&
|
||||
keyring.privateKeys.keys[0] &&
|
||||
keyring.privateKeys.keys[0].keyPacket &&
|
||||
keyring.privateKeys.keys[0].keyPacket.fingerprint
|
||||
);
|
||||
}
|
||||
|
||||
getKeyId(key: any): string {
|
||||
@ -103,7 +106,11 @@ class MutablePgpKeyStore implements MutableKeyStore{
|
||||
|
||||
getPrivateKeyId(): string {
|
||||
// TODO is there a library that comes with angular for doing this?
|
||||
return keyring.privateKeys && keyring.privateKeys.keys[0] && keyring.privateKeys.keys[0].getKeyId().toHex();
|
||||
return (
|
||||
keyring.privateKeys &&
|
||||
keyring.privateKeys.keys[0] &&
|
||||
keyring.privateKeys.keys[0].getKeyId().toHex()
|
||||
);
|
||||
}
|
||||
|
||||
getKeysForId(keyId: string): Array<any> {
|
||||
@ -159,7 +166,4 @@ class MutablePgpKeyStore implements MutableKeyStore{
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
MutablePgpKeyStore,
|
||||
MutableKeyStore
|
||||
};
|
||||
export { MutablePgpKeyStore, MutableKeyStore };
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { PGPSigner } from '@app/_pgp/pgp-signer';
|
||||
import {MutableKeyStore, MutablePgpKeyStore} from '@app/_pgp/pgp-key-store';
|
||||
import { MutableKeyStore, MutablePgpKeyStore } from '@app/_pgp/pgp-key-store';
|
||||
const keystore: MutableKeyStore = new MutablePgpKeyStore();
|
||||
|
||||
describe('PgpSigner', () => {
|
||||
|
@ -1,5 +1,5 @@
|
||||
import {MutableKeyStore} from '@app/_pgp/pgp-key-store';
|
||||
import {LoggingService} from '@app/_services/logging.service';
|
||||
import { MutableKeyStore } from '@app/_pgp/pgp-key-store';
|
||||
import { LoggingService } from '@app/_services/logging.service';
|
||||
|
||||
const openpgp = require('openpgp');
|
||||
|
||||
@ -7,12 +7,12 @@ interface Signable {
|
||||
digest(): string;
|
||||
}
|
||||
|
||||
type Signature = {
|
||||
engine: string
|
||||
algo: string
|
||||
data: string
|
||||
interface Signature {
|
||||
engine: string;
|
||||
algo: string;
|
||||
data: string;
|
||||
digest: string;
|
||||
};
|
||||
}
|
||||
|
||||
interface Signer {
|
||||
onsign(signature: Signature): void;
|
||||
@ -24,7 +24,6 @@ interface Signer {
|
||||
}
|
||||
|
||||
class PGPSigner implements Signer {
|
||||
|
||||
engine = 'pgp';
|
||||
algo = 'sha256';
|
||||
dgst: string;
|
||||
@ -50,7 +49,9 @@ class PGPSigner implements Signer {
|
||||
}
|
||||
|
||||
public verify(digest: string, signature: Signature): void {
|
||||
openpgp.signature.readArmored(signature.data).then((sig) => {
|
||||
openpgp.signature
|
||||
.readArmored(signature.data)
|
||||
.then((sig) => {
|
||||
const opts = {
|
||||
message: openpgp.cleartext.fromText(digest),
|
||||
publicKeys: this.keyStore.getTrustedKeys(),
|
||||
@ -65,11 +66,16 @@ class PGPSigner implements Signer {
|
||||
return;
|
||||
}
|
||||
}
|
||||
this.loggingService.sendErrorLevelMessage(`Checked ${i} signature(s) but none valid`, this, {error: '404 Not found!'});
|
||||
this.loggingService.sendErrorLevelMessage(
|
||||
`Checked ${i} signature(s) but none valid`,
|
||||
this,
|
||||
{ error: '404 Not found!' }
|
||||
);
|
||||
this.onverify(false);
|
||||
});
|
||||
}).catch((e) => {
|
||||
this.loggingService.sendErrorLevelMessage(e.message, this, {error: e});
|
||||
})
|
||||
.catch((e) => {
|
||||
this.loggingService.sendErrorLevelMessage(e.message, this, { error: e });
|
||||
this.onverify(false);
|
||||
});
|
||||
}
|
||||
@ -86,7 +92,9 @@ class PGPSigner implements Signer {
|
||||
privateKeys: [pk],
|
||||
detached: true,
|
||||
};
|
||||
openpgp.sign(opts).then((s) => {
|
||||
openpgp
|
||||
.sign(opts)
|
||||
.then((s) => {
|
||||
this.signature = {
|
||||
engine: this.engine,
|
||||
algo: this.algo,
|
||||
@ -95,16 +103,12 @@ class PGPSigner implements Signer {
|
||||
digest,
|
||||
};
|
||||
this.onsign(this.signature);
|
||||
}).catch((e) => {
|
||||
this.loggingService.sendErrorLevelMessage(e.message, this, {error: e});
|
||||
})
|
||||
.catch((e) => {
|
||||
this.loggingService.sendErrorLevelMessage(e.message, this, { error: e });
|
||||
this.onsign(undefined);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
Signable,
|
||||
Signature,
|
||||
Signer,
|
||||
PGPSigner
|
||||
};
|
||||
export { Signable, Signature, Signer, PGPSigner };
|
||||
|
@ -1,15 +1,15 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {hobaParseChallengeHeader} from '@src/assets/js/hoba.js';
|
||||
import {signChallenge} from '@src/assets/js/hoba-pgp.js';
|
||||
import {environment} from '@src/environments/environment';
|
||||
import {LoggingService} from '@app/_services/logging.service';
|
||||
import {MutableKeyStore, MutablePgpKeyStore} from '@app/_pgp';
|
||||
import {ErrorDialogService} from '@app/_services/error-dialog.service';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {HttpError} from '@app/_helpers/global-error-handler';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { hobaParseChallengeHeader } from '@src/assets/js/hoba.js';
|
||||
import { signChallenge } from '@src/assets/js/hoba-pgp.js';
|
||||
import { environment } from '@src/environments/environment';
|
||||
import { LoggingService } from '@app/_services/logging.service';
|
||||
import { MutableKeyStore, MutablePgpKeyStore } from '@app/_pgp';
|
||||
import { ErrorDialogService } from '@app/_services/error-dialog.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpError } from '@app/_helpers/global-error-handler';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class AuthService {
|
||||
sessionToken: any;
|
||||
@ -95,50 +95,53 @@ export class AuthService {
|
||||
xhr.send();
|
||||
}
|
||||
|
||||
|
||||
login(): boolean {
|
||||
if (this.sessionToken !== undefined) {
|
||||
try {
|
||||
this.getWithToken();
|
||||
return true;
|
||||
} catch (e) {
|
||||
this.loggingService.sendErrorLevelMessage('Login token failed', this, {error: e});
|
||||
this.loggingService.sendErrorLevelMessage('Login token failed', this, { error: e });
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
this.getChallenge();
|
||||
} catch (e) {
|
||||
this.loggingService.sendErrorLevelMessage('Login challenge failed', this, {error: e});
|
||||
this.loggingService.sendErrorLevelMessage('Login challenge failed', this, { error: e });
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
async loginResponse(o: { challenge: string, realm: any }): Promise<any> {
|
||||
async loginResponse(o: { challenge: string; realm: any }): Promise<any> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
const r = await signChallenge(o.challenge,
|
||||
const r = await signChallenge(
|
||||
o.challenge,
|
||||
o.realm,
|
||||
environment.cicMetaUrl,
|
||||
this.mutableKeyStore);
|
||||
this.mutableKeyStore
|
||||
);
|
||||
const sessionTokenResult: boolean = await this.sendResponse(r);
|
||||
} catch (error) {
|
||||
if (error instanceof HttpError) {
|
||||
if (error.status === 403) {
|
||||
this.errorDialogService.openDialog({ message: 'You are not authorized to use this system' });
|
||||
this.errorDialogService.openDialog({
|
||||
message: 'You are not authorized to use this system',
|
||||
});
|
||||
}
|
||||
if (error.status === 401) {
|
||||
this.errorDialogService.openDialog({
|
||||
message: 'Unable to authenticate with the service. ' +
|
||||
message:
|
||||
'Unable to authenticate with the service. ' +
|
||||
'Please speak with the staff at Grassroots ' +
|
||||
'Economics for requesting access ' +
|
||||
'staff@grassrootseconomics.net.'
|
||||
'staff@grassrootseconomics.net.',
|
||||
});
|
||||
}
|
||||
}
|
||||
// TODO define this error
|
||||
this.errorDialogService.openDialog({message: 'Incorrect key passphrase.'});
|
||||
this.errorDialogService.openDialog({ message: 'Incorrect key passphrase.' });
|
||||
resolve(false);
|
||||
}
|
||||
});
|
||||
@ -164,7 +167,11 @@ export class AuthService {
|
||||
const key = await this.mutableKeyStore.importPrivateKey(privateKeyArmored);
|
||||
localStorage.setItem(btoa('CICADA_PRIVATE_KEY'), privateKeyArmored);
|
||||
} catch (err) {
|
||||
this.loggingService.sendErrorLevelMessage(`Failed to set key: ${err.message || err.statusText}`, this, {error: err});
|
||||
this.loggingService.sendErrorLevelMessage(
|
||||
`Failed to set key: ${err.message || err.statusText}`,
|
||||
this,
|
||||
{ error: err }
|
||||
);
|
||||
this.errorDialogService.openDialog({
|
||||
message: `Failed to set key: ${err.message || err.statusText}`,
|
||||
});
|
||||
@ -177,18 +184,17 @@ export class AuthService {
|
||||
logout(): void {
|
||||
sessionStorage.removeItem(btoa('CICADA_SESSION_TOKEN'));
|
||||
this.sessionToken = undefined;
|
||||
window.location.reload(true);
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
getTrustedUsers(): any {
|
||||
const trustedUsers: Array<any> = [];
|
||||
this.mutableKeyStore.getPublicKeys().forEach(key => trustedUsers.push(key.users[0].userId));
|
||||
this.mutableKeyStore.getPublicKeys().forEach((key) => trustedUsers.push(key.users[0].userId));
|
||||
return trustedUsers;
|
||||
}
|
||||
|
||||
async getPublicKeys(): Promise<any> {
|
||||
return await fetch(environment.publicKeysUrl)
|
||||
.then(res => {
|
||||
return await fetch(environment.publicKeysUrl).then((res) => {
|
||||
if (!res.ok) {
|
||||
// TODO does angular recommend an error interface?
|
||||
throw Error(`${res.statusText} - ${res.status}`);
|
||||
|
@ -1,17 +1,15 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { BlockSyncService } from '@app/_services/block-sync.service';
|
||||
import {TransactionService} from '@app/_services/transaction.service';
|
||||
import {TransactionServiceStub} from '@src/testing';
|
||||
import { TransactionService } from '@app/_services/transaction.service';
|
||||
import { TransactionServiceStub } from '@src/testing';
|
||||
|
||||
describe('BlockSyncService', () => {
|
||||
let service: BlockSyncService;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
{ provide: TransactionService, useClass: TransactionServiceStub }
|
||||
]
|
||||
providers: [{ provide: TransactionService, useClass: TransactionServiceStub }],
|
||||
});
|
||||
service = TestBed.inject(BlockSyncService);
|
||||
});
|
||||
|
@ -1,14 +1,14 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {Settings} from '@app/_models';
|
||||
import {TransactionHelper} from 'cic-client';
|
||||
import {first} from 'rxjs/operators';
|
||||
import {TransactionService} from '@app/_services/transaction.service';
|
||||
import {environment} from '@src/environments/environment';
|
||||
import {LoggingService} from '@app/_services/logging.service';
|
||||
import {RegistryService} from '@app/_services/registry.service';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { Settings } from '@app/_models';
|
||||
import { TransactionHelper } from 'cic-client';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { TransactionService } from '@app/_services/transaction.service';
|
||||
import { environment } from '@src/environments/environment';
|
||||
import { LoggingService } from '@app/_services/logging.service';
|
||||
import { RegistryService } from '@app/_services/registry.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class BlockSyncService {
|
||||
readyStateTarget: number = 2;
|
||||
@ -17,8 +17,8 @@ export class BlockSyncService {
|
||||
constructor(
|
||||
private transactionService: TransactionService,
|
||||
private loggingService: LoggingService,
|
||||
private registryService: RegistryService,
|
||||
) { }
|
||||
private registryService: RegistryService
|
||||
) {}
|
||||
|
||||
blockSync(address: string = null, offset: number = 0, limit: number = 100): void {
|
||||
this.transactionService.resetTransactionsList();
|
||||
@ -43,7 +43,14 @@ export class BlockSyncService {
|
||||
settings.registry.load();
|
||||
}
|
||||
|
||||
readyStateProcessor(settings: Settings, bit: number, address: string, offset: number, limit: number): void {
|
||||
readyStateProcessor(
|
||||
settings: Settings,
|
||||
bit: number,
|
||||
address: string,
|
||||
offset: number,
|
||||
limit: number
|
||||
): void {
|
||||
// tslint:disable-next-line:no-bitwise
|
||||
this.readyState |= bit;
|
||||
if (this.readyStateTarget === this.readyState && this.readyStateTarget) {
|
||||
const wHeadSync: Worker = new Worker('./../assets/js/block-sync/head.js');
|
||||
@ -54,11 +61,17 @@ export class BlockSyncService {
|
||||
w3_provider: settings.w3.provider,
|
||||
});
|
||||
if (address === null) {
|
||||
this.transactionService.getAllTransactions(offset, limit).pipe(first()).subscribe(res => {
|
||||
this.transactionService
|
||||
.getAllTransactions(offset, limit)
|
||||
.pipe(first())
|
||||
.subscribe((res) => {
|
||||
this.fetcher(settings, res);
|
||||
});
|
||||
} else {
|
||||
this.transactionService.getAddressTransactions(address, offset, limit).pipe(first()).subscribe(res => {
|
||||
this.transactionService
|
||||
.getAddressTransactions(address, offset, limit)
|
||||
.pipe(first())
|
||||
.subscribe((res) => {
|
||||
this.fetcher(settings, res);
|
||||
});
|
||||
}
|
||||
@ -81,7 +94,14 @@ export class BlockSyncService {
|
||||
});
|
||||
}
|
||||
|
||||
async scan(settings: Settings, lo: number, hi: number, bloomBlockBytes: Uint8Array, bloomBlocktxBytes: Uint8Array, bloomRounds: any): Promise<void> {
|
||||
async scan(
|
||||
settings: Settings,
|
||||
lo: number,
|
||||
hi: number,
|
||||
bloomBlockBytes: Uint8Array,
|
||||
bloomBlocktxBytes: Uint8Array,
|
||||
bloomRounds: any
|
||||
): Promise<void> {
|
||||
const w: Worker = new Worker('./../assets/js/block-sync/ondemand.js');
|
||||
w.onmessage = (m) => {
|
||||
settings.txHelper.processReceipt(m.data);
|
||||
@ -90,10 +110,7 @@ export class BlockSyncService {
|
||||
w3_provider: settings.w3.provider,
|
||||
lo,
|
||||
hi,
|
||||
filters: [
|
||||
bloomBlockBytes,
|
||||
bloomBlocktxBytes,
|
||||
],
|
||||
filters: [bloomBlockBytes, bloomBlocktxBytes],
|
||||
filter_rounds: bloomRounds,
|
||||
});
|
||||
}
|
||||
@ -101,12 +118,19 @@ export class BlockSyncService {
|
||||
fetcher(settings: Settings, transactionsInfo: any): void {
|
||||
const blockFilterBinstr: string = window.atob(transactionsInfo.block_filter);
|
||||
const bOne: Uint8Array = new Uint8Array(blockFilterBinstr.length);
|
||||
bOne.map((e, i, v) => v[i] = blockFilterBinstr.charCodeAt(i));
|
||||
bOne.map((e, i, v) => (v[i] = blockFilterBinstr.charCodeAt(i)));
|
||||
|
||||
const blocktxFilterBinstr: string = window.atob(transactionsInfo.blocktx_filter);
|
||||
const bTwo: Uint8Array = new Uint8Array(blocktxFilterBinstr.length);
|
||||
bTwo.map((e, i, v) => v[i] = blocktxFilterBinstr.charCodeAt(i));
|
||||
bTwo.map((e, i, v) => (v[i] = blocktxFilterBinstr.charCodeAt(i)));
|
||||
|
||||
settings.scanFilter(settings, transactionsInfo.low, transactionsInfo.high, bOne, bTwo, transactionsInfo.filter_rounds);
|
||||
settings.scanFilter(
|
||||
settings,
|
||||
transactionsInfo.low,
|
||||
transactionsInfo.high,
|
||||
bOne,
|
||||
bTwo,
|
||||
transactionsInfo.filter_rounds
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {MatDialog, MatDialogRef} from '@angular/material/dialog';
|
||||
import {ErrorDialogComponent} from '@app/shared/error-dialog/error-dialog.component';
|
||||
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
|
||||
import { ErrorDialogComponent } from '@app/shared/error-dialog/error-dialog.component';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class ErrorDialogService {
|
||||
public isDialogOpen: boolean = false;
|
||||
|
||||
constructor(
|
||||
public dialog: MatDialog,
|
||||
) { }
|
||||
constructor(public dialog: MatDialog) {}
|
||||
|
||||
openDialog(data): any {
|
||||
if (this.isDialogOpen) {
|
||||
@ -19,9 +17,9 @@ export class ErrorDialogService {
|
||||
this.isDialogOpen = true;
|
||||
const dialogRef: MatDialogRef<any> = this.dialog.open(ErrorDialogComponent, {
|
||||
width: '300px',
|
||||
data
|
||||
data,
|
||||
});
|
||||
|
||||
dialogRef.afterClosed().subscribe(() => this.isDialogOpen = false);
|
||||
dialogRef.afterClosed().subscribe(() => (this.isDialogOpen = false));
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,14 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {Observable} from 'rxjs';
|
||||
import {environment} from '@src/environments/environment';
|
||||
import {first} from 'rxjs/operators';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import { Observable } from 'rxjs';
|
||||
import { environment } from '@src/environments/environment';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LocationService {
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
) { }
|
||||
constructor(private httpClient: HttpClient) {}
|
||||
|
||||
getAreaNames(): Observable<any> {
|
||||
return this.httpClient.get(`${environment.cicMetaUrl}/areanames`);
|
||||
@ -26,6 +23,8 @@ export class LocationService {
|
||||
}
|
||||
|
||||
getAreaTypeByArea(area: string): Observable<any> {
|
||||
return this.httpClient.get(`${environment.cicMetaUrl}/areatypes/${area.toLowerCase()}`).pipe(first());
|
||||
return this.httpClient
|
||||
.get(`${environment.cicMetaUrl}/areatypes/${area.toLowerCase()}`)
|
||||
.pipe(first());
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,8 @@
|
||||
import {Injectable, isDevMode} from '@angular/core';
|
||||
import {NGXLogger} from 'ngx-logger';
|
||||
import { Injectable, isDevMode } from '@angular/core';
|
||||
import { NGXLogger } from 'ngx-logger';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LoggingService {
|
||||
env: string;
|
||||
|
@ -1,17 +1,22 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import Web3 from 'web3';
|
||||
import {environment} from '@src/environments/environment';
|
||||
import {CICRegistry, FileGetter} from 'cic-client';
|
||||
import {HttpGetter} from '@app/_helpers';
|
||||
import { environment } from '@src/environments/environment';
|
||||
import { CICRegistry, FileGetter } from 'cic-client';
|
||||
import { HttpGetter } from '@app/_helpers';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RegistryService {
|
||||
web3: Web3 = new Web3(environment.web3Provider);
|
||||
fileGetter: FileGetter = new HttpGetter();
|
||||
registry: CICRegistry = new CICRegistry(this.web3, environment.registryAddress, 'CICRegistry', this.fileGetter,
|
||||
['../../assets/js/block-sync/data']);
|
||||
registry: CICRegistry = new CICRegistry(
|
||||
this.web3,
|
||||
environment.registryAddress,
|
||||
'CICRegistry',
|
||||
this.fileGetter,
|
||||
['../../assets/js/block-sync/data']
|
||||
);
|
||||
|
||||
constructor() {
|
||||
this.registry.declaratorHelper.addTrust(environment.trustedDeclaratorAddress);
|
||||
|
@ -1,34 +1,33 @@
|
||||
import { EventEmitter, Injectable } from '@angular/core';
|
||||
import {environment} from '@src/environments/environment';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {CICRegistry} from 'cic-client';
|
||||
import {TokenRegistry} from '@app/_eth';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {RegistryService} from '@app/_services/registry.service';
|
||||
import { environment } from '@src/environments/environment';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { CICRegistry } from 'cic-client';
|
||||
import { TokenRegistry } from '@app/_eth';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { RegistryService } from '@app/_services/registry.service';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class TokenService {
|
||||
registry: CICRegistry;
|
||||
tokenRegistry: TokenRegistry;
|
||||
LoadEvent: EventEmitter<number> = new EventEmitter<number>();
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
private registryService: RegistryService,
|
||||
) {
|
||||
constructor(private httpClient: HttpClient, private registryService: RegistryService) {
|
||||
this.registry = registryService.getRegistry();
|
||||
this.registry.load();
|
||||
this.registry.onload = async (address: string): Promise<void> => {
|
||||
this.tokenRegistry = new TokenRegistry(await this.registry.getContractAddressByName('TokenRegistry'));
|
||||
this.tokenRegistry = new TokenRegistry(
|
||||
await this.registry.getContractAddressByName('TokenRegistry')
|
||||
);
|
||||
this.LoadEvent.next(Date.now());
|
||||
};
|
||||
}
|
||||
|
||||
async getTokens(): Promise<Array<Promise<string>>> {
|
||||
const count: number = await this.tokenRegistry.totalTokens();
|
||||
return Array.from({length: count}, async (v, i) => await this.tokenRegistry.entry(i));
|
||||
return Array.from({ length: count }, async (v, i) => await this.tokenRegistry.entry(i));
|
||||
}
|
||||
|
||||
getTokenBySymbol(symbol: string): Observable<any> {
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TransactionService } from '@app/_services/transaction.service';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||
|
||||
describe('TransactionService', () => {
|
||||
let httpClient: HttpClient;
|
||||
@ -11,7 +11,7 @@ describe('TransactionService', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule]
|
||||
imports: [HttpClientTestingModule],
|
||||
});
|
||||
httpClient = TestBed.inject(HttpClient);
|
||||
httpTestingController = TestBed.inject(HttpTestingController);
|
||||
|
@ -1,26 +1,26 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import {first} from 'rxjs/operators';
|
||||
import {BehaviorSubject, Observable} from 'rxjs';
|
||||
import {environment} from '@src/environments/environment';
|
||||
import {Envelope, User} from 'cic-client-meta';
|
||||
import {UserService} from '@app/_services/user.service';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { BehaviorSubject, Observable } from 'rxjs';
|
||||
import { environment } from '@src/environments/environment';
|
||||
import { Envelope, User } from 'cic-client-meta';
|
||||
import { UserService } from '@app/_services/user.service';
|
||||
import { Keccak } from 'sha3';
|
||||
import { utils } from 'ethers';
|
||||
import {add0x, fromHex, strip0x, toHex} from '@src/assets/js/ethtx/dist/hex';
|
||||
import {Tx} from '@src/assets/js/ethtx/dist';
|
||||
import {toValue} from '@src/assets/js/ethtx/dist/tx';
|
||||
import { add0x, fromHex, strip0x, toHex } from '@src/assets/js/ethtx/dist/hex';
|
||||
import { Tx } from '@src/assets/js/ethtx/dist';
|
||||
import { toValue } from '@src/assets/js/ethtx/dist/tx';
|
||||
import * as secp256k1 from 'secp256k1';
|
||||
import {AuthService} from '@app/_services/auth.service';
|
||||
import {defaultAccount} from '@app/_models';
|
||||
import {LoggingService} from '@app/_services/logging.service';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {CICRegistry} from 'cic-client';
|
||||
import {RegistryService} from '@app/_services/registry.service';
|
||||
import { AuthService } from '@app/_services/auth.service';
|
||||
import { defaultAccount } from '@app/_models';
|
||||
import { LoggingService } from '@app/_services/logging.service';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { CICRegistry } from 'cic-client';
|
||||
import { RegistryService } from '@app/_services/registry.service';
|
||||
import Web3 from 'web3';
|
||||
const vCard = require('vcard-parser');
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class TransactionService {
|
||||
transactions: any[] = [];
|
||||
@ -35,7 +35,7 @@ export class TransactionService {
|
||||
private authService: AuthService,
|
||||
private userService: UserService,
|
||||
private loggingService: LoggingService,
|
||||
private registryService: RegistryService,
|
||||
private registryService: RegistryService
|
||||
) {
|
||||
this.web3 = this.registryService.getWeb3();
|
||||
this.registry = registryService.getRegistry();
|
||||
@ -51,36 +51,58 @@ export class TransactionService {
|
||||
}
|
||||
|
||||
async setTransaction(transaction, cacheSize: number): Promise<void> {
|
||||
if (this.transactions.find(cachedTx => cachedTx.tx.txHash === transaction.tx.txHash)) { return; }
|
||||
if (this.transactions.find((cachedTx) => cachedTx.tx.txHash === transaction.tx.txHash)) {
|
||||
return;
|
||||
}
|
||||
transaction.value = Number(transaction.value);
|
||||
transaction.type = 'transaction';
|
||||
try {
|
||||
this.userService.getAccountDetailsFromMeta(await User.toKey(transaction.from)).pipe(first()).subscribe((res) => {
|
||||
this.userService
|
||||
.getAccountDetailsFromMeta(await User.toKey(transaction.from))
|
||||
.pipe(first())
|
||||
.subscribe(
|
||||
(res) => {
|
||||
transaction.sender = this.getAccountInfo(res.body);
|
||||
}, error => {
|
||||
},
|
||||
(error) => {
|
||||
transaction.sender = defaultAccount;
|
||||
});
|
||||
this.userService.getAccountDetailsFromMeta(await User.toKey(transaction.to)).pipe(first()).subscribe((res) => {
|
||||
}
|
||||
);
|
||||
this.userService
|
||||
.getAccountDetailsFromMeta(await User.toKey(transaction.to))
|
||||
.pipe(first())
|
||||
.subscribe(
|
||||
(res) => {
|
||||
transaction.recipient = this.getAccountInfo(res.body);
|
||||
}, error => {
|
||||
},
|
||||
(error) => {
|
||||
transaction.recipient = defaultAccount;
|
||||
});
|
||||
}
|
||||
);
|
||||
} finally {
|
||||
this.addTransaction(transaction, cacheSize);
|
||||
}
|
||||
}
|
||||
|
||||
async setConversion(conversion, cacheSize): Promise<void> {
|
||||
if (this.transactions.find(cachedTx => cachedTx.tx.txHash === conversion.tx.txHash)) { return; }
|
||||
if (this.transactions.find((cachedTx) => cachedTx.tx.txHash === conversion.tx.txHash)) {
|
||||
return;
|
||||
}
|
||||
conversion.type = 'conversion';
|
||||
conversion.fromValue = Number(conversion.fromValue);
|
||||
conversion.toValue = Number(conversion.toValue);
|
||||
try {
|
||||
this.userService.getAccountDetailsFromMeta(await User.toKey(conversion.trader)).pipe(first()).subscribe((res) => {
|
||||
this.userService
|
||||
.getAccountDetailsFromMeta(await User.toKey(conversion.trader))
|
||||
.pipe(first())
|
||||
.subscribe(
|
||||
(res) => {
|
||||
conversion.sender = conversion.recipient = this.getAccountInfo(res.body);
|
||||
}, error => {
|
||||
},
|
||||
(error) => {
|
||||
conversion.sender = conversion.recipient = defaultAccount;
|
||||
});
|
||||
}
|
||||
);
|
||||
} finally {
|
||||
this.addTransaction(conversion, cacheSize);
|
||||
}
|
||||
@ -100,19 +122,29 @@ export class TransactionService {
|
||||
}
|
||||
|
||||
getAccountInfo(account: string): any {
|
||||
let accountInfo = Envelope.fromJSON(JSON.stringify(account)).unwrap().m.data;
|
||||
const accountInfo = Envelope.fromJSON(JSON.stringify(account)).unwrap().m.data;
|
||||
accountInfo.vcard = vCard.parse(atob(accountInfo.vcard));
|
||||
return accountInfo;
|
||||
}
|
||||
|
||||
async transferRequest(tokenAddress: string, senderAddress: string, recipientAddress: string, value: number): Promise<any> {
|
||||
const transferAuthAddress = await this.registry.getContractAddressByName('TransferAuthorization');
|
||||
async transferRequest(
|
||||
tokenAddress: string,
|
||||
senderAddress: string,
|
||||
recipientAddress: string,
|
||||
value: number
|
||||
): Promise<any> {
|
||||
const transferAuthAddress = await this.registry.getContractAddressByName(
|
||||
'TransferAuthorization'
|
||||
);
|
||||
const hashFunction = new Keccak(256);
|
||||
hashFunction.update('createRequest(address,address,address,uint256)');
|
||||
const hash = hashFunction.digest();
|
||||
const methodSignature = hash.toString('hex').substring(0, 8);
|
||||
const abiCoder = new utils.AbiCoder();
|
||||
const abi = await abiCoder.encode(['address', 'address', 'address', 'uint256'], [senderAddress, recipientAddress, tokenAddress, value]);
|
||||
const abi = await abiCoder.encode(
|
||||
['address', 'address', 'address', 'uint256'],
|
||||
[senderAddress, recipientAddress, tokenAddress, value]
|
||||
);
|
||||
const data = fromHex(methodSignature + strip0x(abi));
|
||||
const tx = new Tx(environment.bloxbergChainId);
|
||||
tx.nonce = await this.web3.eth.getTransactionCount(senderAddress);
|
||||
|
@ -1,8 +1,8 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { UserService } from '@app/_services/user.service';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||
|
||||
describe('UserService', () => {
|
||||
let httpClient: HttpClient;
|
||||
@ -11,7 +11,7 @@ describe('UserService', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [HttpClientTestingModule]
|
||||
imports: [HttpClientTestingModule],
|
||||
});
|
||||
httpClient = TestBed.inject(HttpClient);
|
||||
httpTestingController = TestBed.inject(HttpTestingController);
|
||||
@ -22,13 +22,33 @@ describe('UserService', () => {
|
||||
expect(service).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should return user for available id', () => {
|
||||
expect(service.getAccountById(1)).toEqual({
|
||||
id: 1,
|
||||
name: 'John Doe',
|
||||
phone: '+25412345678',
|
||||
address: '0xc86ff893ac40d3950b4d5f94a9b837258b0a9865',
|
||||
type: 'user',
|
||||
created: '08/16/2020',
|
||||
balance: '12987',
|
||||
failedPinAttempts: 1,
|
||||
status: 'approved',
|
||||
bio: 'Bodaboda',
|
||||
gender: 'male'
|
||||
});
|
||||
});
|
||||
|
||||
it('should not return user for unavailable id', () => {
|
||||
expect(service.getAccountById(9999999999)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return action for available id', () => {
|
||||
expect(service.getActionById('1')).toEqual({
|
||||
id: 1,
|
||||
user: 'Tom',
|
||||
role: 'enroller',
|
||||
action: 'Disburse RSV 100',
|
||||
approval: false
|
||||
approval: false,
|
||||
});
|
||||
});
|
||||
|
||||
@ -43,7 +63,7 @@ describe('UserService', () => {
|
||||
user: 'Tom',
|
||||
role: 'enroller',
|
||||
action: 'Disburse RSV 100',
|
||||
approval: true
|
||||
approval: true,
|
||||
});
|
||||
});
|
||||
|
||||
@ -54,7 +74,7 @@ describe('UserService', () => {
|
||||
user: 'Christine',
|
||||
role: 'admin',
|
||||
action: 'Change user phone number',
|
||||
approval: false
|
||||
approval: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,32 +1,34 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
import {BehaviorSubject, Observable, Subject} from 'rxjs';
|
||||
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http';
|
||||
import {environment} from '@src/environments/environment';
|
||||
import {first} from 'rxjs/operators';
|
||||
import {ArgPair, Envelope, Phone, Syncable, User} from 'cic-client-meta';
|
||||
import {AccountDetails} from '@app/_models';
|
||||
import {LoggingService} from '@app/_services/logging.service';
|
||||
import {TokenService} from '@app/_services/token.service';
|
||||
import {AccountIndex} from '@app/_eth';
|
||||
import {MutableKeyStore, PGPSigner, Signer} from '@app/_pgp';
|
||||
import {RegistryService} from '@app/_services/registry.service';
|
||||
import {CICRegistry} from 'cic-client';
|
||||
import {AuthService} from '@app/_services/auth.service';
|
||||
import {personValidation, vcardValidation} from '@app/_helpers';
|
||||
import {add0x} from '@src/assets/js/ethtx/dist/hex';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { BehaviorSubject, Observable, Subject } from 'rxjs';
|
||||
import { HttpClient, HttpHeaders, HttpParams } from '@angular/common/http';
|
||||
import { environment } from '@src/environments/environment';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { ArgPair, Envelope, Phone, Syncable, User } from 'cic-client-meta';
|
||||
import { AccountDetails } from '@app/_models';
|
||||
import { LoggingService } from '@app/_services/logging.service';
|
||||
import { TokenService } from '@app/_services/token.service';
|
||||
import { AccountIndex } from '@app/_eth';
|
||||
import { MutableKeyStore, PGPSigner, Signer } from '@app/_pgp';
|
||||
import { RegistryService } from '@app/_services/registry.service';
|
||||
import { CICRegistry } from 'cic-client';
|
||||
import { AuthService } from '@app/_services/auth.service';
|
||||
import { personValidation, vcardValidation } from '@app/_helpers';
|
||||
import { add0x } from '@src/assets/js/ethtx/dist/hex';
|
||||
const vCard = require('vcard-parser');
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class UserService {
|
||||
headers: HttpHeaders = new HttpHeaders({'x-cic-automerge': 'client'});
|
||||
headers: HttpHeaders = new HttpHeaders({ 'x-cic-automerge': 'client' });
|
||||
keystore: MutableKeyStore;
|
||||
signer: Signer;
|
||||
registry: CICRegistry;
|
||||
|
||||
accounts: Array<AccountDetails> = [];
|
||||
private accountsList: BehaviorSubject<Array<AccountDetails>> = new BehaviorSubject<Array<AccountDetails>>(this.accounts);
|
||||
private accountsList: BehaviorSubject<Array<AccountDetails>> = new BehaviorSubject<
|
||||
Array<AccountDetails>
|
||||
>(this.accounts);
|
||||
accountsSubject: Observable<Array<AccountDetails>> = this.accountsList.asObservable();
|
||||
|
||||
actions: Array<any> = [];
|
||||
@ -38,7 +40,7 @@ export class UserService {
|
||||
private loggingService: LoggingService,
|
||||
private tokenService: TokenService,
|
||||
private registryService: RegistryService,
|
||||
private authService: AuthService,
|
||||
private authService: AuthService
|
||||
) {
|
||||
this.authService.init().then(() => {
|
||||
this.keystore = authService.mutableKeyStore;
|
||||
@ -50,28 +52,38 @@ export class UserService {
|
||||
|
||||
resetPin(phone: string): Observable<any> {
|
||||
const params: HttpParams = new HttpParams().set('phoneNumber', phone);
|
||||
return this.httpClient.get(`${environment.cicUssdUrl}/pin`, {params});
|
||||
return this.httpClient.get(`${environment.cicUssdUrl}/pin`, { params });
|
||||
}
|
||||
|
||||
getAccountStatus(phone: string): Observable<any> {
|
||||
const params: HttpParams = new HttpParams().set('phoneNumber', phone);
|
||||
return this.httpClient.get(`${environment.cicUssdUrl}/pin`, {params});
|
||||
return this.httpClient.get(`${environment.cicUssdUrl}/pin`, { params });
|
||||
}
|
||||
|
||||
getLockedAccounts(offset: number, limit: number): Observable<any> {
|
||||
return this.httpClient.get(`${environment.cicUssdUrl}/accounts/locked/${offset}/${limit}`);
|
||||
}
|
||||
|
||||
async changeAccountInfo(address: string, name: string, phoneNumber: string, age: string, type: string, bio: string, gender: string,
|
||||
businessCategory: string, userLocation: string, location: string, locationType: string
|
||||
async changeAccountInfo(
|
||||
address: string,
|
||||
name: string,
|
||||
phoneNumber: string,
|
||||
age: string,
|
||||
type: string,
|
||||
bio: string,
|
||||
gender: string,
|
||||
businessCategory: string,
|
||||
userLocation: string,
|
||||
location: string,
|
||||
locationType: string
|
||||
): Promise<any> {
|
||||
const accountInfo: any = {
|
||||
vcard: {
|
||||
fn: [{}],
|
||||
n: [{}],
|
||||
tel: [{}]
|
||||
tel: [{}],
|
||||
},
|
||||
location: {}
|
||||
location: {},
|
||||
};
|
||||
accountInfo.vcard.fn[0].value = name;
|
||||
accountInfo.vcard.n[0].value = name.split(' ');
|
||||
@ -87,33 +99,52 @@ export class UserService {
|
||||
await vcardValidation(accountInfo.vcard);
|
||||
accountInfo.vcard = btoa(vCard.generate(accountInfo.vcard));
|
||||
const accountKey: string = await User.toKey(address);
|
||||
this.getAccountDetailsFromMeta(accountKey).pipe(first()).subscribe(async res => {
|
||||
this.getAccountDetailsFromMeta(accountKey)
|
||||
.pipe(first())
|
||||
.subscribe(
|
||||
async (res) => {
|
||||
const syncableAccount: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
|
||||
const update: Array<ArgPair> = [];
|
||||
for (const prop in accountInfo) {
|
||||
for (const prop of Object.keys(accountInfo)) {
|
||||
update.push(new ArgPair(prop, accountInfo[prop]));
|
||||
}
|
||||
syncableAccount.update(update, 'client-branch');
|
||||
await personValidation(syncableAccount.m.data);
|
||||
await this.updateMeta(syncableAccount, accountKey, this.headers);
|
||||
}, async error => {
|
||||
this.loggingService.sendErrorLevelMessage('Can\'t find account info in meta service', this, {error});
|
||||
},
|
||||
async (error) => {
|
||||
this.loggingService.sendErrorLevelMessage(
|
||||
'Cannot find account info in meta service',
|
||||
this,
|
||||
{ error }
|
||||
);
|
||||
const syncableAccount: Syncable = new Syncable(accountKey, accountInfo);
|
||||
await this.updateMeta(syncableAccount, accountKey, this.headers);
|
||||
});
|
||||
}
|
||||
);
|
||||
return accountKey;
|
||||
}
|
||||
|
||||
async updateMeta(syncableAccount: Syncable, accountKey: string, headers: HttpHeaders): Promise<any> {
|
||||
const envelope: Envelope = await this.wrap(syncableAccount , this.signer);
|
||||
async updateMeta(
|
||||
syncableAccount: Syncable,
|
||||
accountKey: string,
|
||||
headers: HttpHeaders
|
||||
): Promise<any> {
|
||||
const envelope: Envelope = await this.wrap(syncableAccount, this.signer);
|
||||
const reqBody: string = envelope.toJSON();
|
||||
this.httpClient.put(`${environment.cicMetaUrl}/${accountKey}`, reqBody , { headers }).pipe(first()).subscribe(res => {
|
||||
this.httpClient
|
||||
.put(`${environment.cicMetaUrl}/${accountKey}`, reqBody, { headers })
|
||||
.pipe(first())
|
||||
.subscribe((res) => {
|
||||
this.loggingService.sendInfoLevelMessage(`Response: ${res}`);
|
||||
});
|
||||
}
|
||||
|
||||
getActions(): void {
|
||||
this.httpClient.get(`${environment.cicCacheUrl}/actions`).pipe(first()).subscribe(res => this.actionsList.next(res));
|
||||
this.httpClient
|
||||
.get(`${environment.cicCacheUrl}/actions`)
|
||||
.pipe(first())
|
||||
.subscribe((res) => this.actionsList.next(res));
|
||||
}
|
||||
|
||||
getActionById(id: string): Observable<any> {
|
||||
@ -148,22 +179,33 @@ export class UserService {
|
||||
|
||||
async loadAccounts(limit: number = 100, offset: number = 0): Promise<void> {
|
||||
this.resetAccountsList();
|
||||
const accountIndexAddress: string = await this.registry.getContractAddressByName('AccountRegistry');
|
||||
const accountIndexAddress: string = await this.registry.getContractAddressByName(
|
||||
'AccountRegistry'
|
||||
);
|
||||
const accountIndexQuery = new AccountIndex(accountIndexAddress);
|
||||
const accountAddresses: Array<string> = await accountIndexQuery.last(await accountIndexQuery.totalAccounts());
|
||||
const accountAddresses: Array<string> = await accountIndexQuery.last(
|
||||
await accountIndexQuery.totalAccounts()
|
||||
);
|
||||
this.loggingService.sendInfoLevelMessage(accountAddresses);
|
||||
for (const accountAddress of accountAddresses.slice(offset, offset + limit)) {
|
||||
await this.getAccountByAddress(accountAddress, limit);
|
||||
}
|
||||
}
|
||||
|
||||
async getAccountByAddress(accountAddress: string, limit: number = 100): Promise<Observable<AccountDetails>> {
|
||||
let accountSubject: Subject<any> = new Subject<any>();
|
||||
this.getAccountDetailsFromMeta(await User.toKey(add0x(accountAddress))).pipe(first()).subscribe(async res => {
|
||||
async getAccountByAddress(
|
||||
accountAddress: string,
|
||||
limit: number = 100
|
||||
): Promise<Observable<AccountDetails>> {
|
||||
const accountSubject: Subject<any> = new Subject<any>();
|
||||
this.getAccountDetailsFromMeta(await User.toKey(add0x(accountAddress)))
|
||||
.pipe(first())
|
||||
.subscribe(async (res) => {
|
||||
const account: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
|
||||
const accountInfo = account.m.data;
|
||||
await personValidation(accountInfo);
|
||||
accountInfo.balance = await this.tokenService.getTokenBalance(accountInfo.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]);
|
||||
accountInfo.balance = await this.tokenService.getTokenBalance(
|
||||
accountInfo.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0]
|
||||
);
|
||||
accountInfo.vcard = vCard.parse(atob(accountInfo.vcard));
|
||||
await vcardValidation(accountInfo.vcard);
|
||||
this.accounts.unshift(accountInfo);
|
||||
@ -176,13 +218,18 @@ export class UserService {
|
||||
return accountSubject.asObservable();
|
||||
}
|
||||
|
||||
async getAccountByPhone(phoneNumber: string, limit: number = 100): Promise<Observable<AccountDetails>> {
|
||||
let accountSubject: Subject<any> = new Subject<any>();
|
||||
this.getAccountDetailsFromMeta(await Phone.toKey(phoneNumber)).pipe(first()).subscribe(async res => {
|
||||
async getAccountByPhone(
|
||||
phoneNumber: string,
|
||||
limit: number = 100
|
||||
): Promise<Observable<AccountDetails>> {
|
||||
const accountSubject: Subject<any> = new Subject<any>();
|
||||
this.getAccountDetailsFromMeta(await Phone.toKey(phoneNumber))
|
||||
.pipe(first())
|
||||
.subscribe(async (res) => {
|
||||
const response: Syncable = Envelope.fromJSON(JSON.stringify(res)).unwrap();
|
||||
const address: string = response.m.data;
|
||||
const account: Observable<AccountDetails> = await this.getAccountByAddress(address, limit);
|
||||
account.subscribe(result => {
|
||||
account.subscribe((result) => {
|
||||
accountSubject.next(result);
|
||||
});
|
||||
});
|
||||
@ -194,7 +241,9 @@ export class UserService {
|
||||
this.accountsList.next(this.accounts);
|
||||
}
|
||||
|
||||
searchAccountByName(name: string): any { return; }
|
||||
searchAccountByName(name: string): any {
|
||||
return;
|
||||
}
|
||||
|
||||
getCategories(): Observable<any> {
|
||||
return this.httpClient.get(`${environment.cicMetaUrl}/categories`);
|
||||
|
@ -1,17 +1,23 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import {Routes, RouterModule, PreloadAllModules} from '@angular/router';
|
||||
import {AuthGuard} from '@app/_guards';
|
||||
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';
|
||||
import { AuthGuard } from '@app/_guards';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'auth', loadChildren: () => import('@app/auth/auth.module').then(m => m.AuthModule) },
|
||||
{ path: '', loadChildren: () => import('@pages/pages.module').then(m => m.PagesModule), canActivate: [AuthGuard] },
|
||||
{ path: '**', redirectTo: '', pathMatch: 'full' }
|
||||
{ path: 'auth', loadChildren: () => import('@app/auth/auth.module').then((m) => m.AuthModule) },
|
||||
{
|
||||
path: '',
|
||||
loadChildren: () => import('@pages/pages.module').then((m) => m.PagesModule),
|
||||
canActivate: [AuthGuard],
|
||||
},
|
||||
{ path: '**', redirectTo: '', pathMatch: 'full' },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes, {
|
||||
preloadingStrategy: PreloadAllModules
|
||||
})],
|
||||
exports: [RouterModule]
|
||||
imports: [
|
||||
RouterModule.forRoot(routes, {
|
||||
preloadingStrategy: PreloadAllModules,
|
||||
}),
|
||||
],
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
export class AppRoutingModule {}
|
||||
|
@ -1 +1,2 @@
|
||||
<app-network-status></app-network-status>
|
||||
<router-outlet (activate)="onResize(mediaQuery)"></router-outlet>
|
||||
|
@ -1,24 +1,20 @@
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { RouterTestingModule } from '@angular/router/testing';
|
||||
import { AppComponent } from '@app/app.component';
|
||||
import {TransactionService} from '@app/_services';
|
||||
import {FooterStubComponent, SidebarStubComponent, TopbarStubComponent, TransactionServiceStub} from '@src/testing';
|
||||
import { TransactionService } from '@app/_services';
|
||||
import {
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent,
|
||||
TransactionServiceStub,
|
||||
} from '@src/testing';
|
||||
|
||||
describe('AppComponent', () => {
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
imports: [
|
||||
RouterTestingModule
|
||||
],
|
||||
declarations: [
|
||||
AppComponent,
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent
|
||||
],
|
||||
providers: [
|
||||
{ provide: TransactionService, useClass: TransactionServiceStub }
|
||||
]
|
||||
imports: [RouterTestingModule],
|
||||
declarations: [AppComponent, FooterStubComponent, SidebarStubComponent, TopbarStubComponent],
|
||||
providers: [{ provide: TransactionService, useClass: TransactionServiceStub }],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
|
@ -1,14 +1,20 @@
|
||||
import {ChangeDetectionStrategy, Component, HostListener} from '@angular/core';
|
||||
import {AuthService, ErrorDialogService, LoggingService, TransactionService} from '@app/_services';
|
||||
import {catchError} from 'rxjs/operators';
|
||||
import {ChangeDetectionStrategy, Component, HostListener, OnInit} from '@angular/core';
|
||||
import {
|
||||
AuthService,
|
||||
ErrorDialogService,
|
||||
LoggingService,
|
||||
TransactionService,
|
||||
} from '@app/_services';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { SwUpdate } from '@angular/service-worker';
|
||||
|
||||
@Component({
|
||||
selector: 'app-root',
|
||||
templateUrl: './app.component.html',
|
||||
styleUrls: ['./app.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AppComponent {
|
||||
export class AppComponent implements OnInit {
|
||||
title = 'CICADA';
|
||||
readyStateTarget: number = 3;
|
||||
readyState: number = 0;
|
||||
@ -18,7 +24,8 @@ export class AppComponent {
|
||||
private authService: AuthService,
|
||||
private transactionService: TransactionService,
|
||||
private loggingService: LoggingService,
|
||||
private errorDialogService: ErrorDialogService
|
||||
private errorDialogService: ErrorDialogService,
|
||||
private swUpdate: SwUpdate
|
||||
) {
|
||||
(async () => {
|
||||
try {
|
||||
@ -31,14 +38,26 @@ export class AppComponent {
|
||||
const publicKeys = await this.authService.getPublicKeys();
|
||||
await this.authService.mutableKeyStore.importPublicKey(publicKeys);
|
||||
} catch (error) {
|
||||
this.errorDialogService.openDialog({message: 'Trusted keys endpoint can\'t be reached. Please try again later.'});
|
||||
this.errorDialogService.openDialog({
|
||||
message: 'Trusted keys endpoint cannot be reached. Please try again later.',
|
||||
});
|
||||
// TODO do something to halt user progress...show a sad cicada page 🦗?
|
||||
}
|
||||
})();
|
||||
this.mediaQuery.addListener(this.onResize);
|
||||
this.mediaQuery.addEventListener('change', this.onResize);
|
||||
this.onResize(this.mediaQuery);
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.swUpdate.isEnabled) {
|
||||
this.swUpdate.available.subscribe(() => {
|
||||
if (confirm('New Version available. Load New Version?')) {
|
||||
window.location.reload();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Load resize
|
||||
onResize(e): void {
|
||||
const sidebar: HTMLElement = document.getElementById('sidebar');
|
||||
|
@ -1,27 +1,23 @@
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {ErrorHandler, NgModule} from '@angular/core';
|
||||
import { BrowserModule } from '@angular/platform-browser';
|
||||
import { ErrorHandler, NgModule } from '@angular/core';
|
||||
|
||||
import {AppRoutingModule} from '@app/app-routing.module';
|
||||
import {AppComponent} from '@app/app.component';
|
||||
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
|
||||
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
|
||||
import {
|
||||
GlobalErrorHandler,
|
||||
MockBackendProvider,
|
||||
} from '@app/_helpers';
|
||||
import {DataTablesModule} from 'angular-datatables';
|
||||
import {SharedModule} from '@app/shared/shared.module';
|
||||
import {MatTableModule} from '@angular/material/table';
|
||||
import {AuthGuard} from '@app/_guards';
|
||||
import {LoggerModule} from 'ngx-logger';
|
||||
import {environment} from '@src/environments/environment';
|
||||
import {ErrorInterceptor, HttpConfigInterceptor, LoggingInterceptor} from '@app/_interceptors';
|
||||
import {MutablePgpKeyStore} from '@app/_pgp';
|
||||
import { AppRoutingModule } from '@app/app-routing.module';
|
||||
import { AppComponent } from '@app/app.component';
|
||||
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { HTTP_INTERCEPTORS, HttpClientModule } from '@angular/common/http';
|
||||
import { GlobalErrorHandler, MockBackendProvider } from '@app/_helpers';
|
||||
import { DataTablesModule } from 'angular-datatables';
|
||||
import { SharedModule } from '@app/shared/shared.module';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { AuthGuard } from '@app/_guards';
|
||||
import { LoggerModule } from 'ngx-logger';
|
||||
import { environment } from '@src/environments/environment';
|
||||
import { ErrorInterceptor, HttpConfigInterceptor, LoggingInterceptor } from '@app/_interceptors';
|
||||
import { MutablePgpKeyStore } from '@app/_pgp';
|
||||
import { ServiceWorkerModule } from '@angular/service-worker';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AppComponent
|
||||
],
|
||||
declarations: [AppComponent],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
AppRoutingModule,
|
||||
@ -34,8 +30,9 @@ import {MutablePgpKeyStore} from '@app/_pgp';
|
||||
level: environment.logLevel,
|
||||
serverLogLevel: environment.serverLogLevel,
|
||||
serverLoggingUrl: `${environment.loggingUrl}/api/logs/`,
|
||||
disableConsoleLogging: false
|
||||
})
|
||||
disableConsoleLogging: false,
|
||||
}),
|
||||
ServiceWorkerModule.register('ngsw-worker.js', { enabled: environment.production }),
|
||||
],
|
||||
providers: [
|
||||
AuthGuard,
|
||||
@ -47,6 +44,6 @@ import {MutablePgpKeyStore} from '@app/_pgp';
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },
|
||||
{ provide: HTTP_INTERCEPTORS, useClass: LoggingInterceptor, multi: true },
|
||||
],
|
||||
bootstrap: [AppComponent]
|
||||
bootstrap: [AppComponent],
|
||||
})
|
||||
export class AppModule { }
|
||||
export class AppModule {}
|
||||
|
@ -1,7 +1,9 @@
|
||||
import { PasswordToggleDirective } from '@app/auth/_directives/password-toggle.directive';
|
||||
import {ElementRef, Renderer2} from '@angular/core';
|
||||
import { ElementRef, Renderer2 } from '@angular/core';
|
||||
|
||||
// tslint:disable-next-line:prefer-const
|
||||
let elementRef: ElementRef;
|
||||
// tslint:disable-next-line:prefer-const
|
||||
let renderer: Renderer2;
|
||||
|
||||
describe('PasswordToggleDirective', () => {
|
||||
|
@ -1,7 +1,7 @@
|
||||
import {Directive, ElementRef, Input, Renderer2} from '@angular/core';
|
||||
import { Directive, ElementRef, Input, Renderer2 } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appPasswordToggle]'
|
||||
selector: '[appPasswordToggle]',
|
||||
})
|
||||
export class PasswordToggleDirective {
|
||||
@Input()
|
||||
@ -10,10 +10,7 @@ export class PasswordToggleDirective {
|
||||
@Input()
|
||||
iconId: string;
|
||||
|
||||
constructor(
|
||||
private elementRef: ElementRef,
|
||||
private renderer: Renderer2,
|
||||
) {
|
||||
constructor(private elementRef: ElementRef, private renderer: Renderer2) {
|
||||
this.renderer.listen(this.elementRef.nativeElement, 'click', () => {
|
||||
this.togglePasswordVisibility();
|
||||
});
|
||||
|
@ -5,11 +5,11 @@ import { AuthComponent } from '@app/auth/auth.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: AuthComponent },
|
||||
{ path: '**', redirectTo: '', pathMatch: 'full'},
|
||||
{ path: '**', redirectTo: '', pathMatch: 'full' },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AuthRoutingModule { }
|
||||
export class AuthRoutingModule {}
|
||||
|
@ -8,9 +8,8 @@ describe('AuthComponent', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ AuthComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
declarations: [AuthComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -1,14 +1,14 @@
|
||||
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
|
||||
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||
import {CustomErrorStateMatcher} from '@app/_helpers';
|
||||
import {AuthService} from '@app/_services';
|
||||
import {Router} from '@angular/router';
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { CustomErrorStateMatcher } from '@app/_helpers';
|
||||
import { AuthService } from '@app/_services';
|
||||
import { Router } from '@angular/router';
|
||||
|
||||
@Component({
|
||||
selector: 'app-auth',
|
||||
templateUrl: './auth.component.html',
|
||||
styleUrls: ['./auth.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AuthComponent implements OnInit {
|
||||
keyForm: FormGroup;
|
||||
@ -20,7 +20,7 @@ export class AuthComponent implements OnInit {
|
||||
private authService: AuthService,
|
||||
private formBuilder: FormBuilder,
|
||||
private router: Router
|
||||
) { }
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.keyForm = this.formBuilder.group({
|
||||
@ -33,12 +33,16 @@ export class AuthComponent implements OnInit {
|
||||
// }
|
||||
}
|
||||
|
||||
get keyFormStub(): any { return this.keyForm.controls; }
|
||||
get keyFormStub(): any {
|
||||
return this.keyForm.controls;
|
||||
}
|
||||
|
||||
async onSubmit(): Promise<void> {
|
||||
this.submitted = true;
|
||||
|
||||
if (this.keyForm.invalid) { return; }
|
||||
if (this.keyForm.invalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.loading = true;
|
||||
await this.authService.setKey(this.keyFormStub.key.value);
|
||||
|
@ -3,14 +3,13 @@ import { CommonModule } from '@angular/common';
|
||||
|
||||
import { AuthRoutingModule } from '@app/auth/auth-routing.module';
|
||||
import { AuthComponent } from '@app/auth/auth.component';
|
||||
import {ReactiveFormsModule} from '@angular/forms';
|
||||
import {PasswordToggleDirective} from '@app/auth/_directives/password-toggle.directive';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatRippleModule} from '@angular/material/core';
|
||||
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { PasswordToggleDirective } from '@app/auth/_directives/password-toggle.directive';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AuthComponent, PasswordToggleDirective],
|
||||
@ -23,6 +22,6 @@ import {MatRippleModule} from '@angular/material/core';
|
||||
MatInputModule,
|
||||
MatButtonModule,
|
||||
MatRippleModule,
|
||||
]
|
||||
],
|
||||
})
|
||||
export class AuthModule { }
|
||||
export class AuthModule {}
|
||||
|
@ -1,13 +1,19 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AccountDetailsComponent } from '@pages/accounts/account-details/account-details.component';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {AccountsModule} from '@pages/accounts/accounts.module';
|
||||
import {UserService} from '@app/_services';
|
||||
import {AppModule} from '@app/app.module';
|
||||
import {ActivatedRouteStub, FooterStubComponent, SidebarStubComponent, TopbarStubComponent, UserServiceStub} from '@src/testing';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { AccountsModule } from '@pages/accounts/accounts.module';
|
||||
import { UserService } from '@app/_services';
|
||||
import { AppModule } from '@app/app.module';
|
||||
import {
|
||||
ActivatedRouteStub,
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent,
|
||||
UserServiceStub,
|
||||
} from '@src/testing';
|
||||
|
||||
describe('AccountDetailsComponent', () => {
|
||||
let component: AccountDetailsComponent;
|
||||
@ -24,19 +30,14 @@ describe('AccountDetailsComponent', () => {
|
||||
AccountDetailsComponent,
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent
|
||||
],
|
||||
imports: [
|
||||
AccountsModule,
|
||||
AppModule,
|
||||
HttpClientTestingModule,
|
||||
TopbarStubComponent,
|
||||
],
|
||||
imports: [AccountsModule, AppModule, HttpClientTestingModule],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: route },
|
||||
{ provide: UserService, useClass: UserServiceStub }
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
{ provide: UserService, useClass: UserServiceStub },
|
||||
],
|
||||
}).compileComponents();
|
||||
httpClient = TestBed.inject(HttpClient);
|
||||
httpTestingController = TestBed.inject(HttpTestingController);
|
||||
});
|
||||
|
@ -1,37 +1,50 @@
|
||||
import {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, AreaName, AreaType, Category, Transaction} from '@app/_models';
|
||||
import {
|
||||
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, AreaName, AreaType, Category, Transaction } from '@app/_models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-account-details',
|
||||
templateUrl: './account-details.component.html',
|
||||
styleUrls: ['./account-details.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AccountDetailsComponent implements OnInit {
|
||||
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;
|
||||
@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;
|
||||
@ViewChild('UserTablePaginator', { static: true }) userTablePaginator: MatPaginator;
|
||||
@ViewChild('UserTableSort', { static: true }) userTableSort: MatSort;
|
||||
|
||||
accountInfoForm: FormGroup;
|
||||
account: AccountDetails;
|
||||
@ -63,7 +76,7 @@ export class AccountDetailsComponent implements OnInit {
|
||||
private loggingService: LoggingService,
|
||||
private blockSyncService: BlockSyncService,
|
||||
private cdr: ChangeDetectorRef,
|
||||
private snackBar: MatSnackBar,
|
||||
private snackBar: MatSnackBar
|
||||
) {
|
||||
this.accountInfoForm = this.formBuilder.group({
|
||||
name: ['', Validators.required],
|
||||
@ -79,8 +92,10 @@ export class AccountDetailsComponent implements OnInit {
|
||||
});
|
||||
this.route.paramMap.subscribe(async (params: Params) => {
|
||||
this.accountAddress = add0x(params.get('id'));
|
||||
this.bloxbergLink = 'https://blockexplorer.bloxberg.org/address/' + this.accountAddress + '/transactions';
|
||||
(await this.userService.getAccountByAddress(this.accountAddress, 100)).subscribe(async res => {
|
||||
this.bloxbergLink =
|
||||
'https://blockexplorer.bloxberg.org/address/' + this.accountAddress + '/transactions';
|
||||
(await this.userService.getAccountByAddress(this.accountAddress, 100)).subscribe(
|
||||
async (res) => {
|
||||
if (res !== undefined) {
|
||||
this.account = res;
|
||||
this.cdr.detectChanges();
|
||||
@ -102,26 +117,45 @@ export class AccountDetailsComponent implements OnInit {
|
||||
} else {
|
||||
alert('Account not found!');
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
this.blockSyncService.blockSync(this.accountAddress);
|
||||
});
|
||||
this.userService.getCategories().pipe(first()).subscribe(res => this.categories = res);
|
||||
this.locationService.getAreaNames().pipe(first()).subscribe(res => this.areaNames = res);
|
||||
this.locationService.getAreaTypes().pipe(first()).subscribe(res => this.areaTypes = 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);
|
||||
this.userService
|
||||
.getCategories()
|
||||
.pipe(first())
|
||||
.subscribe((res) => (this.categories = res));
|
||||
this.locationService
|
||||
.getAreaNames()
|
||||
.pipe(first())
|
||||
.subscribe((res) => (this.areaNames = res));
|
||||
this.locationService
|
||||
.getAreaTypes()
|
||||
.pipe(first())
|
||||
.subscribe((res) => (this.areaTypes = 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));
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.userService.accountsSubject.subscribe(accounts => {
|
||||
this.userService.accountsSubject.subscribe((accounts) => {
|
||||
this.userDataSource = new MatTableDataSource<any>(accounts);
|
||||
this.userDataSource.paginator = this.userTablePaginator;
|
||||
this.userDataSource.sort = this.userTableSort;
|
||||
this.accounts = accounts;
|
||||
});
|
||||
|
||||
this.transactionService.transactionsSubject.subscribe(transactions => {
|
||||
this.transactionService.transactionsSubject.subscribe((transactions) => {
|
||||
this.transactionsDataSource = new MatTableDataSource<any>(transactions);
|
||||
this.transactionsDataSource.paginator = this.transactionTablePaginator;
|
||||
this.transactionsDataSource.sort = this.transactionTableSort;
|
||||
@ -142,14 +176,20 @@ export class AccountDetailsComponent implements OnInit {
|
||||
}
|
||||
|
||||
viewAccount(account): void {
|
||||
this.router.navigateByUrl(`/accounts/${strip0x(account.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`);
|
||||
this.router.navigateByUrl(
|
||||
`/accounts/${strip0x(account.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`
|
||||
);
|
||||
}
|
||||
|
||||
get accountInfoFormStub(): any { return this.accountInfoForm.controls; }
|
||||
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; }
|
||||
if (this.accountInfoForm.invalid || !confirm(`Change user's profile information?`)) {
|
||||
return;
|
||||
}
|
||||
const accountKey = await this.userService.changeAccountInfo(
|
||||
this.accountAddress,
|
||||
this.accountInfoFormStub.name.value,
|
||||
@ -168,29 +208,38 @@ export class AccountDetailsComponent implements OnInit {
|
||||
|
||||
filterAccounts(): void {
|
||||
if (this.accountsType === 'all') {
|
||||
this.userService.accountsSubject.subscribe(accounts => {
|
||||
this.userService.accountsSubject.subscribe((accounts) => {
|
||||
this.userDataSource.data = accounts;
|
||||
this.accounts = accounts;
|
||||
});
|
||||
} else {
|
||||
this.userDataSource.data = this.accounts.filter(account => account.type === this.accountsType);
|
||||
this.userDataSource.data = this.accounts.filter(
|
||||
(account) => account.type === this.accountsType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
filterTransactions(): void {
|
||||
if (this.transactionsType === 'all') {
|
||||
this.transactionService.transactionsSubject.subscribe(transactions => {
|
||||
this.transactionService.transactionsSubject.subscribe((transactions) => {
|
||||
this.transactionsDataSource.data = transactions;
|
||||
this.transactions = transactions;
|
||||
});
|
||||
} else {
|
||||
this.transactionsDataSource.data = this.transactions.filter(transaction => transaction.type === this.transactionsType);
|
||||
this.transactionsDataSource.data = this.transactions.filter(
|
||||
(transaction) => transaction.type === this.transactionsType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
resetPin(): void {
|
||||
if (!confirm('Reset user\'s pin?')) { return; }
|
||||
this.userService.resetPin(this.account.vcard.tel[0].value).pipe(first()).subscribe(res => {
|
||||
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}`);
|
||||
});
|
||||
}
|
||||
@ -201,7 +250,9 @@ export class AccountDetailsComponent implements OnInit {
|
||||
|
||||
copyAddress(): void {
|
||||
if (copyToClipboard(this.accountAddress)) {
|
||||
this.snackBar.open(this.accountAddress + ' copied successfully!', 'Close', { duration: 3000 });
|
||||
this.snackBar.open(this.accountAddress + ' copied successfully!', 'Close', {
|
||||
duration: 3000,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,9 +8,8 @@ describe('AccountSearchComponent', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ AccountSearchComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
declarations: [AccountSearchComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -1,16 +1,16 @@
|
||||
import { Component, OnInit, ChangeDetectionStrategy } from '@angular/core';
|
||||
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||
import {CustomErrorStateMatcher} from '@app/_helpers';
|
||||
import {UserService} from '@app/_services';
|
||||
import {Router} from '@angular/router';
|
||||
import {strip0x} from '@src/assets/js/ethtx/dist/hex';
|
||||
import {environment} from '@src/environments/environment';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { CustomErrorStateMatcher } from '@app/_helpers';
|
||||
import { UserService } from '@app/_services';
|
||||
import { Router } from '@angular/router';
|
||||
import { strip0x } from '@src/assets/js/ethtx/dist/hex';
|
||||
import { environment } from '@src/environments/environment';
|
||||
|
||||
@Component({
|
||||
selector: 'app-account-search',
|
||||
templateUrl: './account-search.component.html',
|
||||
styleUrls: ['./account-search.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AccountSearchComponent implements OnInit {
|
||||
nameSearchForm: FormGroup;
|
||||
@ -27,8 +27,8 @@ export class AccountSearchComponent implements OnInit {
|
||||
constructor(
|
||||
private formBuilder: FormBuilder,
|
||||
private userService: UserService,
|
||||
private router: Router,
|
||||
) { }
|
||||
private router: Router
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.nameSearchForm = this.formBuilder.group({
|
||||
@ -42,13 +42,21 @@ export class AccountSearchComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
get nameSearchFormStub(): any { return this.nameSearchForm.controls; }
|
||||
get phoneSearchFormStub(): any { return this.phoneSearchForm.controls; }
|
||||
get addressSearchFormStub(): any { return this.addressSearchForm.controls; }
|
||||
get nameSearchFormStub(): any {
|
||||
return this.nameSearchForm.controls;
|
||||
}
|
||||
get phoneSearchFormStub(): any {
|
||||
return this.phoneSearchForm.controls;
|
||||
}
|
||||
get addressSearchFormStub(): any {
|
||||
return this.addressSearchForm.controls;
|
||||
}
|
||||
|
||||
onNameSearch(): void {
|
||||
this.nameSearchSubmitted = true;
|
||||
if (this.nameSearchForm.invalid) { return; }
|
||||
if (this.nameSearchForm.invalid) {
|
||||
return;
|
||||
}
|
||||
this.nameSearchLoading = true;
|
||||
this.userService.searchAccountByName(this.nameSearchFormStub.name.value);
|
||||
this.nameSearchLoading = false;
|
||||
@ -56,11 +64,17 @@ export class AccountSearchComponent implements OnInit {
|
||||
|
||||
async onPhoneSearch(): Promise<void> {
|
||||
this.phoneSearchSubmitted = true;
|
||||
if (this.phoneSearchForm.invalid) { return; }
|
||||
if (this.phoneSearchForm.invalid) {
|
||||
return;
|
||||
}
|
||||
this.phoneSearchLoading = true;
|
||||
(await this.userService.getAccountByPhone(this.phoneSearchFormStub.phoneNumber.value, 100)).subscribe(async res => {
|
||||
(
|
||||
await this.userService.getAccountByPhone(this.phoneSearchFormStub.phoneNumber.value, 100)
|
||||
).subscribe(async (res) => {
|
||||
if (res !== undefined) {
|
||||
await this.router.navigateByUrl(`/accounts/${strip0x(res.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`);
|
||||
await this.router.navigateByUrl(
|
||||
`/accounts/${strip0x(res.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`
|
||||
);
|
||||
} else {
|
||||
alert('Account not found!');
|
||||
}
|
||||
@ -70,11 +84,17 @@ export class AccountSearchComponent implements OnInit {
|
||||
|
||||
async onAddressSearch(): Promise<void> {
|
||||
this.addressSearchSubmitted = true;
|
||||
if (this.addressSearchForm.invalid) { return; }
|
||||
if (this.addressSearchForm.invalid) {
|
||||
return;
|
||||
}
|
||||
this.addressSearchLoading = true;
|
||||
(await this.userService.getAccountByAddress(this.addressSearchFormStub.address.value, 100)).subscribe(async res => {
|
||||
(
|
||||
await this.userService.getAccountByAddress(this.addressSearchFormStub.address.value, 100)
|
||||
).subscribe(async (res) => {
|
||||
if (res !== undefined) {
|
||||
await this.router.navigateByUrl(`/accounts/${strip0x(res.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`);
|
||||
await this.router.navigateByUrl(
|
||||
`/accounts/${strip0x(res.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`
|
||||
);
|
||||
} else {
|
||||
alert('Account not found!');
|
||||
}
|
||||
|
@ -2,20 +2,20 @@ import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
import { AccountsComponent } from '@pages/accounts/accounts.component';
|
||||
import {CreateAccountComponent} from '@pages/accounts/create-account/create-account.component';
|
||||
import {AccountDetailsComponent} from '@pages/accounts/account-details/account-details.component';
|
||||
import {AccountSearchComponent} from '@pages/accounts/account-search/account-search.component';
|
||||
import { CreateAccountComponent } from '@pages/accounts/create-account/create-account.component';
|
||||
import { AccountDetailsComponent } from '@pages/accounts/account-details/account-details.component';
|
||||
import { AccountSearchComponent } from '@pages/accounts/account-search/account-search.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: AccountsComponent },
|
||||
{ path: 'search', component: AccountSearchComponent },
|
||||
// { path: 'create', component: CreateAccountComponent },
|
||||
{ path: ':id', component: AccountDetailsComponent },
|
||||
{ path: '**', redirectTo: '', pathMatch: 'full' }
|
||||
{ path: '**', redirectTo: '', pathMatch: 'full' },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AccountsRoutingModule { }
|
||||
export class AccountsRoutingModule {}
|
||||
|
@ -1,12 +1,17 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AccountsComponent } from './accounts.component';
|
||||
import {FooterStubComponent, SidebarStubComponent, TopbarStubComponent, UserServiceStub} from '@src/testing';
|
||||
import {AccountsModule} from '@pages/accounts/accounts.module';
|
||||
import {AppModule} from '@app/app.module';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {UserService} from '@app/_services';
|
||||
import {
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent,
|
||||
UserServiceStub,
|
||||
} from '@src/testing';
|
||||
import { AccountsModule } from '@pages/accounts/accounts.module';
|
||||
import { AppModule } from '@app/app.module';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||
import { UserService } from '@app/_services';
|
||||
|
||||
describe('AccountsComponent', () => {
|
||||
let component: AccountsComponent;
|
||||
@ -20,18 +25,11 @@ describe('AccountsComponent', () => {
|
||||
AccountsComponent,
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent
|
||||
TopbarStubComponent,
|
||||
],
|
||||
imports: [
|
||||
AccountsModule,
|
||||
AppModule,
|
||||
HttpClientTestingModule,
|
||||
],
|
||||
providers: [
|
||||
{ provide: UserService, useClass: UserServiceStub }
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [AccountsModule, AppModule, HttpClientTestingModule],
|
||||
providers: [{ provide: UserService, useClass: UserServiceStub }],
|
||||
}).compileComponents();
|
||||
httpClient = TestBed.inject(HttpClient);
|
||||
httpTestingController = TestBed.inject(HttpTestingController);
|
||||
});
|
||||
|
@ -1,20 +1,20 @@
|
||||
import {ChangeDetectionStrategy, 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 {LoggingService, UserService} from '@app/_services';
|
||||
import {Router} from '@angular/router';
|
||||
import {exportCsv} from '@app/_helpers';
|
||||
import {strip0x} from '@src/assets/js/ethtx/dist/hex';
|
||||
import {first} from 'rxjs/operators';
|
||||
import {environment} from '@src/environments/environment';
|
||||
import {AccountDetails} from '@app/_models';
|
||||
import { ChangeDetectionStrategy, 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 { LoggingService, UserService } from '@app/_services';
|
||||
import { Router } from '@angular/router';
|
||||
import { exportCsv } from '@app/_helpers';
|
||||
import { strip0x } from '@src/assets/js/ethtx/dist/hex';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { environment } from '@src/environments/environment';
|
||||
import { AccountDetails } from '@app/_models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-accounts',
|
||||
templateUrl: './accounts.component.html',
|
||||
styleUrls: ['./accounts.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class AccountsComponent implements OnInit {
|
||||
dataSource: MatTableDataSource<any>;
|
||||
@ -32,21 +32,23 @@ export class AccountsComponent implements OnInit {
|
||||
private userService: UserService,
|
||||
private loggingService: LoggingService,
|
||||
private router: Router
|
||||
)
|
||||
{
|
||||
) {
|
||||
(async () => {
|
||||
try {
|
||||
// TODO it feels like this should be in the onInit handler
|
||||
await this.userService.loadAccounts(100);
|
||||
} catch (error) {
|
||||
this.loggingService.sendErrorLevelMessage('Failed to load accounts', this, {error});
|
||||
this.loggingService.sendErrorLevelMessage('Failed to load accounts', this, { error });
|
||||
}
|
||||
})();
|
||||
this.userService.getAccountTypes().pipe(first()).subscribe(res => this.accountTypes = res);
|
||||
this.userService
|
||||
.getAccountTypes()
|
||||
.pipe(first())
|
||||
.subscribe((res) => (this.accountTypes = res));
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.userService.accountsSubject.subscribe(accounts => {
|
||||
this.userService.accountsSubject.subscribe((accounts) => {
|
||||
this.dataSource = new MatTableDataSource<any>(accounts);
|
||||
this.dataSource.paginator = this.paginator;
|
||||
this.dataSource.sort = this.sort;
|
||||
@ -59,17 +61,19 @@ export class AccountsComponent implements OnInit {
|
||||
}
|
||||
|
||||
async viewAccount(account): Promise<void> {
|
||||
await this.router.navigateByUrl(`/accounts/${strip0x(account.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`);
|
||||
await this.router.navigateByUrl(
|
||||
`/accounts/${strip0x(account.identities.evm[`bloxberg:${environment.bloxbergChainId}`][0])}`
|
||||
);
|
||||
}
|
||||
|
||||
filterAccounts(): void {
|
||||
if (this.accountsType === 'all') {
|
||||
this.userService.accountsSubject.subscribe(accounts => {
|
||||
this.userService.accountsSubject.subscribe((accounts) => {
|
||||
this.dataSource.data = accounts;
|
||||
this.accounts = accounts;
|
||||
});
|
||||
} else {
|
||||
this.dataSource.data = this.accounts.filter(account => account.type === this.accountsType);
|
||||
this.dataSource.data = this.accounts.filter((account) => account.type === this.accountsType);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,35 +3,34 @@ import { CommonModule } from '@angular/common';
|
||||
|
||||
import { AccountsRoutingModule } from '@pages/accounts/accounts-routing.module';
|
||||
import { AccountsComponent } from '@pages/accounts/accounts.component';
|
||||
import {SharedModule} from '@app/shared/shared.module';
|
||||
import { SharedModule } from '@app/shared/shared.module';
|
||||
import { AccountDetailsComponent } from '@pages/accounts/account-details/account-details.component';
|
||||
import {DataTablesModule} from 'angular-datatables';
|
||||
import { DataTablesModule } from 'angular-datatables';
|
||||
import { CreateAccountComponent } from '@pages/accounts/create-account/create-account.component';
|
||||
import {MatTableModule} from '@angular/material/table';
|
||||
import {MatSortModule} from '@angular/material/sort';
|
||||
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||
import {MatPaginatorModule} from '@angular/material/paginator';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {TransactionsModule} from '@pages/transactions/transactions.module';
|
||||
import {MatTabsModule} from '@angular/material/tabs';
|
||||
import {MatRippleModule} from '@angular/material/core';
|
||||
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
|
||||
import {ReactiveFormsModule} from '@angular/forms';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { TransactionsModule } from '@pages/transactions/transactions.module';
|
||||
import { MatTabsModule } from '@angular/material/tabs';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { AccountSearchComponent } from './account-search/account-search.component';
|
||||
import {MatSnackBarModule} from '@angular/material/snack-bar';
|
||||
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AccountsComponent,
|
||||
AccountDetailsComponent,
|
||||
CreateAccountComponent,
|
||||
AccountSearchComponent
|
||||
AccountSearchComponent,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
@ -54,6 +53,6 @@ import {MatSnackBarModule} from '@angular/material/snack-bar';
|
||||
MatProgressSpinnerModule,
|
||||
ReactiveFormsModule,
|
||||
MatSnackBarModule,
|
||||
]
|
||||
],
|
||||
})
|
||||
export class AccountsModule { }
|
||||
export class AccountsModule {}
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { CreateAccountComponent } from '@pages/accounts/create-account/create-account.component';
|
||||
import {AccountsModule} from '@pages/accounts/accounts.module';
|
||||
import {AppModule} from '@app/app.module';
|
||||
import {FooterStubComponent, SidebarStubComponent, TopbarStubComponent} from '@src/testing';
|
||||
|
||||
import { AccountsModule } from '@pages/accounts/accounts.module';
|
||||
import { AppModule } from '@app/app.module';
|
||||
import { FooterStubComponent, SidebarStubComponent, TopbarStubComponent } from '@src/testing';
|
||||
|
||||
describe('CreateAccountComponent', () => {
|
||||
let component: CreateAccountComponent;
|
||||
@ -16,14 +15,10 @@ describe('CreateAccountComponent', () => {
|
||||
CreateAccountComponent,
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent
|
||||
TopbarStubComponent,
|
||||
],
|
||||
imports: [
|
||||
AccountsModule,
|
||||
AppModule
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [AccountsModule, AppModule],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -1,15 +1,15 @@
|
||||
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
|
||||
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||
import {LocationService, UserService} from '@app/_services';
|
||||
import {CustomErrorStateMatcher} from '@app/_helpers';
|
||||
import {first} from 'rxjs/operators';
|
||||
import {AreaName, Category} from '@app/_models';
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { LocationService, UserService } from '@app/_services';
|
||||
import { CustomErrorStateMatcher } from '@app/_helpers';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { AreaName, Category } from '@app/_models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-create-account',
|
||||
templateUrl: './create-account.component.html',
|
||||
styleUrls: ['./create-account.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class CreateAccountComponent implements OnInit {
|
||||
createForm: FormGroup;
|
||||
@ -24,7 +24,7 @@ export class CreateAccountComponent implements OnInit {
|
||||
private formBuilder: FormBuilder,
|
||||
private locationService: LocationService,
|
||||
private userService: UserService
|
||||
) { }
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.createForm = this.formBuilder.group({
|
||||
@ -37,19 +37,35 @@ export class CreateAccountComponent implements OnInit {
|
||||
location: ['', Validators.required],
|
||||
gender: ['', Validators.required],
|
||||
referrer: ['', Validators.required],
|
||||
businessCategory: ['', Validators.required]
|
||||
businessCategory: ['', Validators.required],
|
||||
});
|
||||
this.userService.getCategories().pipe(first()).subscribe(res => this.categories = res);
|
||||
this.locationService.getAreaNames().pipe(first()).subscribe(res => this.areaNames = res);
|
||||
this.userService.getAccountTypes().pipe(first()).subscribe(res => this.accountTypes = res);
|
||||
this.userService.getGenders().pipe(first()).subscribe(res => this.genders = res);
|
||||
this.userService
|
||||
.getCategories()
|
||||
.pipe(first())
|
||||
.subscribe((res) => (this.categories = res));
|
||||
this.locationService
|
||||
.getAreaNames()
|
||||
.pipe(first())
|
||||
.subscribe((res) => (this.areaNames = res));
|
||||
this.userService
|
||||
.getAccountTypes()
|
||||
.pipe(first())
|
||||
.subscribe((res) => (this.accountTypes = res));
|
||||
this.userService
|
||||
.getGenders()
|
||||
.pipe(first())
|
||||
.subscribe((res) => (this.genders = res));
|
||||
}
|
||||
|
||||
get createFormStub(): any { return this.createForm.controls; }
|
||||
get createFormStub(): any {
|
||||
return this.createForm.controls;
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
this.submitted = true;
|
||||
if (this.createForm.invalid || !confirm('Create account?')) { return; }
|
||||
if (this.createForm.invalid || !confirm('Create account?')) {
|
||||
return;
|
||||
}
|
||||
this.submitted = false;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,6 @@ const routes: Routes = [{ path: '', component: AdminComponent }];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class AdminRoutingModule { }
|
||||
export class AdminRoutingModule {}
|
||||
|
@ -1,12 +1,17 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { AdminComponent } from '@pages/admin/admin.component';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {AdminModule} from '@pages/admin/admin.module';
|
||||
import {FooterStubComponent, SidebarStubComponent, TopbarStubComponent, UserServiceStub} from '@src/testing';
|
||||
import {AppModule} from '@app/app.module';
|
||||
import {UserService} from '@app/_services';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||
import { AdminModule } from '@pages/admin/admin.module';
|
||||
import {
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent,
|
||||
UserServiceStub,
|
||||
} from '@src/testing';
|
||||
import { AppModule } from '@app/app.module';
|
||||
import { UserService } from '@app/_services';
|
||||
|
||||
describe('AdminComponent', () => {
|
||||
let component: AdminComponent;
|
||||
@ -21,18 +26,11 @@ describe('AdminComponent', () => {
|
||||
AdminComponent,
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent
|
||||
TopbarStubComponent,
|
||||
],
|
||||
imports: [
|
||||
AdminModule,
|
||||
AppModule,
|
||||
HttpClientTestingModule,
|
||||
],
|
||||
providers: [
|
||||
{ provide: UserService, useClass: UserServiceStub }
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [AdminModule, AppModule, HttpClientTestingModule],
|
||||
providers: [{ provide: UserService, useClass: UserServiceStub }],
|
||||
}).compileComponents();
|
||||
httpClient = TestBed.inject(HttpClient);
|
||||
httpTestingController = TestBed.inject(HttpTestingController);
|
||||
userService = new UserServiceStub();
|
||||
@ -55,7 +53,7 @@ describe('AdminComponent', () => {
|
||||
user: 'Tom',
|
||||
role: 'enroller',
|
||||
action: 'Disburse RSV 100',
|
||||
approval: false
|
||||
approval: false,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -1,12 +1,12 @@
|
||||
import {ChangeDetectionStrategy, 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 {LoggingService, UserService} from '@app/_services';
|
||||
import {animate, state, style, transition, trigger} from '@angular/animations';
|
||||
import {first} from 'rxjs/operators';
|
||||
import {exportCsv} from '@app/_helpers';
|
||||
import {Action} from '../../_models';
|
||||
import { ChangeDetectionStrategy, 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 { LoggingService, UserService } from '@app/_services';
|
||||
import { animate, state, style, transition, trigger } from '@angular/animations';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { exportCsv } from '@app/_helpers';
|
||||
import { Action } from '../../_models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-admin',
|
||||
@ -15,11 +15,11 @@ import {Action} from '../../_models';
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
animations: [
|
||||
trigger('detailExpand', [
|
||||
state('collapsed', style({height: '0px', minHeight: 0, visibility: 'hidden'})),
|
||||
state('expanded', style({height: '*', visibility: 'visible'})),
|
||||
state('collapsed', style({ height: '0px', minHeight: 0, visibility: 'hidden' })),
|
||||
state('expanded', style({ height: '*', visibility: 'visible' })),
|
||||
transition('expanded <=> collapsed', animate('225ms cubic-bezier(0.4, 0.0, 0.2, 1)')),
|
||||
])
|
||||
]
|
||||
]),
|
||||
],
|
||||
})
|
||||
export class AdminComponent implements OnInit {
|
||||
dataSource: MatTableDataSource<any>;
|
||||
@ -30,12 +30,9 @@ export class AdminComponent implements OnInit {
|
||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||
@ViewChild(MatSort) sort: MatSort;
|
||||
|
||||
constructor(
|
||||
private userService: UserService,
|
||||
private loggingService: LoggingService
|
||||
) {
|
||||
constructor(private userService: UserService, private loggingService: LoggingService) {
|
||||
this.userService.getActions();
|
||||
this.userService.actionsSubject.subscribe(actions => {
|
||||
this.userService.actionsSubject.subscribe((actions) => {
|
||||
this.dataSource = new MatTableDataSource<any>(actions);
|
||||
this.dataSource.paginator = this.paginator;
|
||||
this.dataSource.sort = this.sort;
|
||||
@ -43,8 +40,7 @@ export class AdminComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
ngOnInit(): void {}
|
||||
|
||||
doFilter(value: string): void {
|
||||
this.dataSource.filter = value.trim().toLocaleLowerCase();
|
||||
@ -55,14 +51,24 @@ export class AdminComponent implements OnInit {
|
||||
}
|
||||
|
||||
approveAction(action: any): void {
|
||||
if (!confirm('Approve action?')) { return; }
|
||||
this.userService.approveAction(action.id).pipe(first()).subscribe(res => this.loggingService.sendInfoLevelMessage(res));
|
||||
if (!confirm('Approve action?')) {
|
||||
return;
|
||||
}
|
||||
this.userService
|
||||
.approveAction(action.id)
|
||||
.pipe(first())
|
||||
.subscribe((res) => this.loggingService.sendInfoLevelMessage(res));
|
||||
this.userService.getActions();
|
||||
}
|
||||
|
||||
disapproveAction(action: any): void {
|
||||
if (!confirm('Disapprove action?')) { return; }
|
||||
this.userService.revokeAction(action.id).pipe(first()).subscribe(res => this.loggingService.sendInfoLevelMessage(res));
|
||||
if (!confirm('Disapprove action?')) {
|
||||
return;
|
||||
}
|
||||
this.userService
|
||||
.revokeAction(action.id)
|
||||
.pipe(first())
|
||||
.subscribe((res) => this.loggingService.sendInfoLevelMessage(res));
|
||||
this.userService.getActions();
|
||||
}
|
||||
|
||||
|
@ -3,17 +3,16 @@ import { CommonModule } from '@angular/common';
|
||||
|
||||
import { AdminRoutingModule } from '@pages/admin/admin-routing.module';
|
||||
import { AdminComponent } from '@pages/admin/admin.component';
|
||||
import {SharedModule} from '@app/shared/shared.module';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatTableModule} from '@angular/material/table';
|
||||
import {MatSortModule} from '@angular/material/sort';
|
||||
import {MatPaginatorModule} from '@angular/material/paginator';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatRippleModule} from '@angular/material/core';
|
||||
|
||||
import { SharedModule } from '@app/shared/shared.module';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
|
||||
@NgModule({
|
||||
declarations: [AdminComponent],
|
||||
@ -29,7 +28,7 @@ import {MatRippleModule} from '@angular/material/core';
|
||||
MatSortModule,
|
||||
MatPaginatorModule,
|
||||
MatButtonModule,
|
||||
MatRippleModule
|
||||
]
|
||||
MatRippleModule,
|
||||
],
|
||||
})
|
||||
export class AdminModule { }
|
||||
export class AdminModule {}
|
||||
|
@ -5,16 +5,32 @@ import { PagesComponent } from './pages.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'home', component: PagesComponent },
|
||||
{ path: 'tx', loadChildren: () => import('@pages/transactions/transactions.module').then(m => m.TransactionsModule) },
|
||||
{ path: 'settings', loadChildren: () => import('@pages/settings/settings.module').then(m => m.SettingsModule) },
|
||||
{ path: 'accounts', loadChildren: () => import('@pages/accounts/accounts.module').then(m => m.AccountsModule) },
|
||||
{ path: 'tokens', loadChildren: () => import('@pages/tokens/tokens.module').then(m => m.TokensModule) },
|
||||
{ path: 'admin', loadChildren: () => import('@pages/admin/admin.module').then(m => m.AdminModule) },
|
||||
{ path: '**', redirectTo: 'home', pathMatch: 'full'}
|
||||
{
|
||||
path: 'tx',
|
||||
loadChildren: () =>
|
||||
import('@pages/transactions/transactions.module').then((m) => m.TransactionsModule),
|
||||
},
|
||||
{
|
||||
path: 'settings',
|
||||
loadChildren: () => import('@pages/settings/settings.module').then((m) => m.SettingsModule),
|
||||
},
|
||||
{
|
||||
path: 'accounts',
|
||||
loadChildren: () => import('@pages/accounts/accounts.module').then((m) => m.AccountsModule),
|
||||
},
|
||||
{
|
||||
path: 'tokens',
|
||||
loadChildren: () => import('@pages/tokens/tokens.module').then((m) => m.TokensModule),
|
||||
},
|
||||
{
|
||||
path: 'admin',
|
||||
loadChildren: () => import('@pages/admin/admin.module').then((m) => m.AdminModule),
|
||||
},
|
||||
{ path: '**', redirectTo: 'home', pathMatch: 'full' },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class PagesRoutingModule { }
|
||||
export class PagesRoutingModule {}
|
||||
|
@ -8,9 +8,8 @@ describe('PagesComponent', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ PagesComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
declarations: [PagesComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -1,13 +1,13 @@
|
||||
import {ChangeDetectionStrategy, Component} from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-pages',
|
||||
templateUrl: './pages.component.html',
|
||||
styleUrls: ['./pages.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class PagesComponent {
|
||||
url: string = 'https://dashboard.sarafu.network/';
|
||||
|
||||
constructor() { }
|
||||
constructor() {}
|
||||
}
|
||||
|
@ -3,14 +3,13 @@ import { CommonModule } from '@angular/common';
|
||||
|
||||
import { PagesRoutingModule } from '@pages/pages-routing.module';
|
||||
import { PagesComponent } from '@pages/pages.component';
|
||||
import {SharedModule} from '@app/shared/shared.module';
|
||||
import {ChartsModule} from 'ng2-charts';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
|
||||
import { SharedModule } from '@app/shared/shared.module';
|
||||
import { ChartsModule } from 'ng2-charts';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
|
||||
@NgModule({
|
||||
declarations: [PagesComponent],
|
||||
@ -23,7 +22,7 @@ import {MatCardModule} from '@angular/material/card';
|
||||
MatFormFieldModule,
|
||||
MatSelectModule,
|
||||
MatInputModule,
|
||||
MatCardModule
|
||||
]
|
||||
MatCardModule,
|
||||
],
|
||||
})
|
||||
export class PagesModule { }
|
||||
export class PagesModule {}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { OrganizationComponent } from '@pages/settings/organization/organization.component';
|
||||
import {FooterStubComponent, SidebarStubComponent, TopbarStubComponent} from '@src/testing';
|
||||
import {SettingsModule} from '@pages/settings/settings.module';
|
||||
import {AppModule} from '@app/app.module';
|
||||
import { FooterStubComponent, SidebarStubComponent, TopbarStubComponent } from '@src/testing';
|
||||
import { SettingsModule } from '@pages/settings/settings.module';
|
||||
import { AppModule } from '@app/app.module';
|
||||
|
||||
describe('OrganizationComponent', () => {
|
||||
let component: OrganizationComponent;
|
||||
@ -15,14 +15,10 @@ describe('OrganizationComponent', () => {
|
||||
OrganizationComponent,
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent
|
||||
TopbarStubComponent,
|
||||
],
|
||||
imports: [
|
||||
AppModule,
|
||||
SettingsModule,
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [AppModule, SettingsModule],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -1,35 +1,37 @@
|
||||
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
|
||||
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
|
||||
import {CustomErrorStateMatcher} from '@app/_helpers';
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { CustomErrorStateMatcher } from '@app/_helpers';
|
||||
|
||||
@Component({
|
||||
selector: 'app-organization',
|
||||
templateUrl: './organization.component.html',
|
||||
styleUrls: ['./organization.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class OrganizationComponent implements OnInit {
|
||||
organizationForm: FormGroup;
|
||||
submitted: boolean = false;
|
||||
matcher: CustomErrorStateMatcher = new CustomErrorStateMatcher();
|
||||
|
||||
constructor(
|
||||
private formBuilder: FormBuilder
|
||||
) { }
|
||||
constructor(private formBuilder: FormBuilder) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.organizationForm = this.formBuilder.group({
|
||||
disbursement: ['', Validators.required],
|
||||
transfer: '',
|
||||
countryCode: ['', Validators.required]
|
||||
countryCode: ['', Validators.required],
|
||||
});
|
||||
}
|
||||
|
||||
get organizationFormStub(): any { return this.organizationForm.controls; }
|
||||
get organizationFormStub(): any {
|
||||
return this.organizationForm.controls;
|
||||
}
|
||||
|
||||
onSubmit(): void {
|
||||
this.submitted = true;
|
||||
if (this.organizationForm.invalid || !confirm('Set organization information?')) { return; }
|
||||
if (this.organizationForm.invalid || !confirm('Set organization information?')) {
|
||||
return;
|
||||
}
|
||||
this.submitted = false;
|
||||
}
|
||||
}
|
||||
|
@ -2,16 +2,16 @@ import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
import { SettingsComponent } from '@pages/settings/settings.component';
|
||||
import {OrganizationComponent} from '@pages/settings/organization/organization.component';
|
||||
import { OrganizationComponent } from '@pages/settings/organization/organization.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: SettingsComponent },
|
||||
{ path: 'organization', component: OrganizationComponent },
|
||||
{ path: '**', redirectTo: '', pathMatch: 'full' }
|
||||
{ path: '**', redirectTo: '', pathMatch: 'full' },
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class SettingsRoutingModule { }
|
||||
export class SettingsRoutingModule {}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SettingsComponent } from '@pages/settings/settings.component';
|
||||
import {FooterStubComponent, SidebarStubComponent, TopbarStubComponent} from '@src/testing';
|
||||
import {SettingsModule} from '@pages/settings/settings.module';
|
||||
import {AppModule} from '@app/app.module';
|
||||
import { FooterStubComponent, SidebarStubComponent, TopbarStubComponent } from '@src/testing';
|
||||
import { SettingsModule } from '@pages/settings/settings.module';
|
||||
import { AppModule } from '@app/app.module';
|
||||
|
||||
describe('SettingsComponent', () => {
|
||||
let component: SettingsComponent;
|
||||
@ -15,14 +15,10 @@ describe('SettingsComponent', () => {
|
||||
SettingsComponent,
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent
|
||||
TopbarStubComponent,
|
||||
],
|
||||
imports: [
|
||||
AppModule,
|
||||
SettingsModule,
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [AppModule, SettingsModule],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -1,16 +1,16 @@
|
||||
import {ChangeDetectionStrategy, 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 {AuthService} from '@app/_services';
|
||||
import {Staff} from '@app/_models/staff';
|
||||
import {exportCsv} from '@app/_helpers';
|
||||
import { ChangeDetectionStrategy, 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 { AuthService } from '@app/_services';
|
||||
import { Staff } from '@app/_models/staff';
|
||||
import { exportCsv } from '@app/_helpers';
|
||||
|
||||
@Component({
|
||||
selector: 'app-settings',
|
||||
templateUrl: './settings.component.html',
|
||||
styleUrls: ['./settings.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class SettingsComponent implements OnInit {
|
||||
date: string;
|
||||
@ -21,9 +21,7 @@ export class SettingsComponent implements OnInit {
|
||||
@ViewChild(MatPaginator) paginator: MatPaginator;
|
||||
@ViewChild(MatSort) sort: MatSort;
|
||||
|
||||
constructor(
|
||||
private authService: AuthService
|
||||
) { }
|
||||
constructor(private authService: AuthService) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
const d = new Date();
|
||||
|
@ -3,22 +3,21 @@ import { CommonModule } from '@angular/common';
|
||||
|
||||
import { SettingsRoutingModule } from '@pages/settings/settings-routing.module';
|
||||
import { SettingsComponent } from '@pages/settings/settings.component';
|
||||
import {SharedModule} from '@app/shared/shared.module';
|
||||
import { SharedModule } from '@app/shared/shared.module';
|
||||
import { OrganizationComponent } from '@pages/settings/organization/organization.component';
|
||||
import {MatTableModule} from '@angular/material/table';
|
||||
import {MatSortModule} from '@angular/material/sort';
|
||||
import {MatPaginatorModule} from '@angular/material/paginator';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatRadioModule} from '@angular/material/radio';
|
||||
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatMenuModule} from '@angular/material/menu';
|
||||
import {ReactiveFormsModule} from '@angular/forms';
|
||||
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatRadioModule } from '@angular/material/radio';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatMenuModule } from '@angular/material/menu';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
|
||||
@NgModule({
|
||||
declarations: [SettingsComponent, OrganizationComponent],
|
||||
@ -38,7 +37,7 @@ import {ReactiveFormsModule} from '@angular/forms';
|
||||
MatCheckboxModule,
|
||||
MatSelectModule,
|
||||
MatMenuModule,
|
||||
ReactiveFormsModule
|
||||
]
|
||||
ReactiveFormsModule,
|
||||
],
|
||||
})
|
||||
export class SettingsModule { }
|
||||
export class SettingsModule {}
|
||||
|
@ -1,11 +1,17 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TokenDetailsComponent } from '@pages/tokens/token-details/token-details.component';
|
||||
import {ActivatedRouteStub, FooterStubComponent, SidebarStubComponent, TokenServiceStub, TopbarStubComponent} from '@src/testing';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {TokenService} from '@app/_services';
|
||||
import {TokensModule} from '@pages/tokens/tokens.module';
|
||||
import {AppModule} from '@app/app.module';
|
||||
import {
|
||||
ActivatedRouteStub,
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TokenServiceStub,
|
||||
TopbarStubComponent,
|
||||
} from '@src/testing';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { TokenService } from '@app/_services';
|
||||
import { TokensModule } from '@pages/tokens/tokens.module';
|
||||
import { AppModule } from '@app/app.module';
|
||||
|
||||
describe('TokenDetailsComponent', () => {
|
||||
let component: TokenDetailsComponent;
|
||||
@ -20,18 +26,14 @@ describe('TokenDetailsComponent', () => {
|
||||
TokenDetailsComponent,
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent
|
||||
TopbarStubComponent,
|
||||
],
|
||||
providers: [
|
||||
{ provide: ActivatedRoute, useValue: route },
|
||||
{ provide: TokenService, useClass: TokenServiceStub }
|
||||
{ provide: TokenService, useClass: TokenServiceStub },
|
||||
],
|
||||
imports: [
|
||||
AppModule,
|
||||
TokensModule,
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [AppModule, TokensModule],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -1,30 +1,28 @@
|
||||
import {ChangeDetectionStrategy, Component, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute, Params} from '@angular/router';
|
||||
import {TokenService} from '@app/_services';
|
||||
import {first} from 'rxjs/operators';
|
||||
import {Token} from '../../../_models';
|
||||
import { ChangeDetectionStrategy, Component, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { TokenService } from '@app/_services';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { Token } from '../../../_models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-token-details',
|
||||
templateUrl: './token-details.component.html',
|
||||
styleUrls: ['./token-details.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class TokenDetailsComponent implements OnInit {
|
||||
token: Token;
|
||||
|
||||
constructor(
|
||||
private route: ActivatedRoute,
|
||||
private tokenService: TokenService
|
||||
) {
|
||||
constructor(private route: ActivatedRoute, private tokenService: TokenService) {
|
||||
this.route.paramMap.subscribe((params: Params) => {
|
||||
this.tokenService.getTokenBySymbol(params.get('id')).pipe(first()).subscribe(res => {
|
||||
this.tokenService
|
||||
.getTokenBySymbol(params.get('id'))
|
||||
.pipe(first())
|
||||
.subscribe((res) => {
|
||||
this.token = res;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
ngOnInit(): void {}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ import { NgModule } from '@angular/core';
|
||||
import { Routes, RouterModule } from '@angular/router';
|
||||
|
||||
import { TokensComponent } from '@pages/tokens/tokens.component';
|
||||
import {TokenDetailsComponent} from '@pages/tokens/token-details/token-details.component';
|
||||
import { TokenDetailsComponent } from '@pages/tokens/token-details/token-details.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: '', component: TokensComponent },
|
||||
@ -11,6 +11,6 @@ const routes: Routes = [
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class TokensRoutingModule { }
|
||||
export class TokensRoutingModule {}
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TokensComponent } from '@pages/tokens/tokens.component';
|
||||
import {FooterStubComponent, SidebarStubComponent, TopbarStubComponent} from '@src/testing';
|
||||
import {AppModule} from '@app/app.module';
|
||||
import {TokensModule} from '@pages/tokens/tokens.module';
|
||||
import { FooterStubComponent, SidebarStubComponent, TopbarStubComponent } from '@src/testing';
|
||||
import { AppModule } from '@app/app.module';
|
||||
import { TokensModule } from '@pages/tokens/tokens.module';
|
||||
|
||||
describe('TokensComponent', () => {
|
||||
let component: TokensComponent;
|
||||
@ -15,14 +15,10 @@ describe('TokensComponent', () => {
|
||||
TokensComponent,
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent
|
||||
TopbarStubComponent,
|
||||
],
|
||||
imports: [
|
||||
AppModule,
|
||||
TokensModule
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [AppModule, TokensModule],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -1,18 +1,18 @@
|
||||
import {ChangeDetectionStrategy, Component, OnInit, ViewChild} from '@angular/core';
|
||||
import {MatPaginator} from '@angular/material/paginator';
|
||||
import {MatSort} from '@angular/material/sort';
|
||||
import {LoggingService, TokenService} from '@app/_services';
|
||||
import {MatTableDataSource} from '@angular/material/table';
|
||||
import {Router} from '@angular/router';
|
||||
import {exportCsv} from '@app/_helpers';
|
||||
import {TokenRegistry} from '../../_eth';
|
||||
import {Token} from '../../_models';
|
||||
import { ChangeDetectionStrategy, Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { LoggingService, TokenService } from '@app/_services';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { Router } from '@angular/router';
|
||||
import { exportCsv } from '@app/_helpers';
|
||||
import { TokenRegistry } from '../../_eth';
|
||||
import { Token } from '../../_models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-tokens',
|
||||
templateUrl: './tokens.component.html',
|
||||
styleUrls: ['./tokens.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class TokensComponent implements OnInit {
|
||||
dataSource: MatTableDataSource<any>;
|
||||
@ -25,7 +25,7 @@ export class TokensComponent implements OnInit {
|
||||
private tokenService: TokenService,
|
||||
private loggingService: LoggingService,
|
||||
private router: Router
|
||||
) { }
|
||||
) {}
|
||||
|
||||
async ngOnInit(): Promise<void> {
|
||||
this.tokenService.LoadEvent.subscribe(async () => {
|
||||
|
@ -4,20 +4,19 @@ import { CommonModule } from '@angular/common';
|
||||
import { TokensRoutingModule } from '@pages/tokens/tokens-routing.module';
|
||||
import { TokensComponent } from '@pages/tokens/tokens.component';
|
||||
import { TokenDetailsComponent } from '@pages/tokens/token-details/token-details.component';
|
||||
import {SharedModule} from '@app/shared/shared.module';
|
||||
import {MatTableModule} from '@angular/material/table';
|
||||
import {MatPaginatorModule} from '@angular/material/paginator';
|
||||
import {MatSortModule} from '@angular/material/sort';
|
||||
import {MatPseudoCheckboxModule, MatRippleModule} from '@angular/material/core';
|
||||
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatSidenavModule} from '@angular/material/sidenav';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatToolbarModule} from '@angular/material/toolbar';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
|
||||
import { SharedModule } from '@app/shared/shared.module';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatPseudoCheckboxModule, MatRippleModule } from '@angular/material/core';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatSidenavModule } from '@angular/material/sidenav';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatToolbarModule } from '@angular/material/toolbar';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
|
||||
@NgModule({
|
||||
declarations: [TokensComponent, TokenDetailsComponent],
|
||||
@ -37,7 +36,7 @@ import {MatCardModule} from '@angular/material/card';
|
||||
MatButtonModule,
|
||||
MatToolbarModule,
|
||||
MatCardModule,
|
||||
MatRippleModule
|
||||
]
|
||||
MatRippleModule,
|
||||
],
|
||||
})
|
||||
export class TokensModule { }
|
||||
export class TokensModule {}
|
||||
|
@ -8,9 +8,8 @@ describe('TransactionDetailsComponent', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ TransactionDetailsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
declarations: [TransactionDetailsComponent],
|
||||
}).compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -1,15 +1,15 @@
|
||||
import {ChangeDetectionStrategy, Component, Input, OnInit} from '@angular/core';
|
||||
import {Router} from '@angular/router';
|
||||
import {TransactionService} from '@app/_services';
|
||||
import {copyToClipboard} from '@app/_helpers';
|
||||
import {MatSnackBar} from '@angular/material/snack-bar';
|
||||
import {strip0x} from '@src/assets/js/ethtx/dist/hex';
|
||||
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
|
||||
import { Router } from '@angular/router';
|
||||
import { TransactionService } from '@app/_services';
|
||||
import { copyToClipboard } from '@app/_helpers';
|
||||
import { MatSnackBar } from '@angular/material/snack-bar';
|
||||
import { strip0x } from '@src/assets/js/ethtx/dist/hex';
|
||||
|
||||
@Component({
|
||||
selector: 'app-transaction-details',
|
||||
templateUrl: './transaction-details.component.html',
|
||||
styleUrls: ['./transaction-details.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class TransactionDetailsComponent implements OnInit {
|
||||
@Input() transaction;
|
||||
@ -20,15 +20,18 @@ export class TransactionDetailsComponent implements OnInit {
|
||||
constructor(
|
||||
private router: Router,
|
||||
private transactionService: TransactionService,
|
||||
private snackBar: MatSnackBar,
|
||||
) { }
|
||||
private snackBar: MatSnackBar
|
||||
) {}
|
||||
|
||||
ngOnInit(): void {
|
||||
if (this.transaction?.type === 'conversion') {
|
||||
this.traderBloxbergLink = 'https://blockexplorer.bloxberg.org/address/' + this.transaction?.trader + '/transactions';
|
||||
this.traderBloxbergLink =
|
||||
'https://blockexplorer.bloxberg.org/address/' + this.transaction?.trader + '/transactions';
|
||||
} else {
|
||||
this.senderBloxbergLink = 'https://blockexplorer.bloxberg.org/address/' + this.transaction?.from + '/transactions';
|
||||
this.recipientBloxbergLink = 'https://blockexplorer.bloxberg.org/address/' + this.transaction?.to + '/transactions';
|
||||
this.senderBloxbergLink =
|
||||
'https://blockexplorer.bloxberg.org/address/' + this.transaction?.from + '/transactions';
|
||||
this.recipientBloxbergLink =
|
||||
'https://blockexplorer.bloxberg.org/address/' + this.transaction?.to + '/transactions';
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,6 @@ const routes: Routes = [{ path: '', component: TransactionsComponent }];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forChild(routes)],
|
||||
exports: [RouterModule]
|
||||
exports: [RouterModule],
|
||||
})
|
||||
export class TransactionsRoutingModule { }
|
||||
export class TransactionsRoutingModule {}
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TransactionsComponent } from '@pages/transactions/transactions.component';
|
||||
import {HttpClient} from '@angular/common/http';
|
||||
import {HttpClientTestingModule, HttpTestingController} from '@angular/common/http/testing';
|
||||
import {FooterStubComponent, SidebarStubComponent, TopbarStubComponent} from '@src/testing';
|
||||
import {TransactionsModule} from '@pages/transactions/transactions.module';
|
||||
import {AppModule} from '@app/app.module';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
|
||||
import { FooterStubComponent, SidebarStubComponent, TopbarStubComponent } from '@src/testing';
|
||||
import { TransactionsModule } from '@pages/transactions/transactions.module';
|
||||
import { AppModule } from '@app/app.module';
|
||||
|
||||
describe('TransactionsComponent', () => {
|
||||
let component: TransactionsComponent;
|
||||
@ -19,15 +19,10 @@ describe('TransactionsComponent', () => {
|
||||
TransactionsComponent,
|
||||
FooterStubComponent,
|
||||
SidebarStubComponent,
|
||||
TopbarStubComponent
|
||||
TopbarStubComponent,
|
||||
],
|
||||
imports: [
|
||||
AppModule,
|
||||
HttpClientTestingModule,
|
||||
TransactionsModule
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
imports: [AppModule, HttpClientTestingModule, TransactionsModule],
|
||||
}).compileComponents();
|
||||
httpClient = TestBed.inject(HttpClient);
|
||||
httpTestingController = TestBed.inject(HttpTestingController);
|
||||
});
|
||||
|
@ -1,17 +1,23 @@
|
||||
import {AfterViewInit, ChangeDetectionStrategy, Component, OnInit, ViewChild} from '@angular/core';
|
||||
import {BlockSyncService, TransactionService, UserService} from '@app/_services';
|
||||
import {MatTableDataSource} from '@angular/material/table';
|
||||
import {MatPaginator} from '@angular/material/paginator';
|
||||
import {MatSort} from '@angular/material/sort';
|
||||
import {exportCsv} from '@app/_helpers';
|
||||
import {first} from 'rxjs/operators';
|
||||
import {Transaction} from '@app/_models';
|
||||
import {
|
||||
AfterViewInit,
|
||||
ChangeDetectionStrategy,
|
||||
Component,
|
||||
OnInit,
|
||||
ViewChild,
|
||||
} from '@angular/core';
|
||||
import { BlockSyncService, TransactionService, UserService } from '@app/_services';
|
||||
import { MatTableDataSource } from '@angular/material/table';
|
||||
import { MatPaginator } from '@angular/material/paginator';
|
||||
import { MatSort } from '@angular/material/sort';
|
||||
import { exportCsv } from '@app/_helpers';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { Transaction } from '@app/_models';
|
||||
|
||||
@Component({
|
||||
selector: 'app-transactions',
|
||||
templateUrl: './transactions.component.html',
|
||||
styleUrls: ['./transactions.component.scss'],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
})
|
||||
export class TransactionsComponent implements OnInit, AfterViewInit {
|
||||
transactionDataSource: MatTableDataSource<any>;
|
||||
@ -35,13 +41,16 @@ export class TransactionsComponent implements OnInit, AfterViewInit {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.transactionService.transactionsSubject.subscribe(transactions => {
|
||||
this.transactionService.transactionsSubject.subscribe((transactions) => {
|
||||
this.transactionDataSource = new MatTableDataSource<any>(transactions);
|
||||
this.transactionDataSource.paginator = this.paginator;
|
||||
this.transactionDataSource.sort = this.sort;
|
||||
this.transactions = transactions;
|
||||
});
|
||||
this.userService.getTransactionTypes().pipe(first()).subscribe(res => this.transactionsTypes = res);
|
||||
this.userService
|
||||
.getTransactionTypes()
|
||||
.pipe(first())
|
||||
.subscribe((res) => (this.transactionsTypes = res));
|
||||
}
|
||||
|
||||
viewTransaction(transaction): void {
|
||||
@ -54,12 +63,14 @@ export class TransactionsComponent implements OnInit, AfterViewInit {
|
||||
|
||||
filterTransactions(): void {
|
||||
if (this.transactionsType === 'all') {
|
||||
this.transactionService.transactionsSubject.subscribe(transactions => {
|
||||
this.transactionService.transactionsSubject.subscribe((transactions) => {
|
||||
this.transactionDataSource.data = transactions;
|
||||
this.transactions = transactions;
|
||||
});
|
||||
} else {
|
||||
this.transactionDataSource.data = this.transactions.filter(transaction => transaction.type === this.transactionsType);
|
||||
this.transactionDataSource.data = this.transactions.filter(
|
||||
(transaction) => transaction.type === this.transactionsType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,27 +4,24 @@ import { CommonModule } from '@angular/common';
|
||||
import { TransactionsRoutingModule } from '@pages/transactions/transactions-routing.module';
|
||||
import { TransactionsComponent } from '@pages/transactions/transactions.component';
|
||||
import { TransactionDetailsComponent } from '@pages/transactions/transaction-details/transaction-details.component';
|
||||
import {DataTablesModule} from 'angular-datatables';
|
||||
import {SharedModule} from '@app/shared/shared.module';
|
||||
import {MatTableModule} from '@angular/material/table';
|
||||
import {MatCheckboxModule} from '@angular/material/checkbox';
|
||||
import {MatPaginatorModule} from '@angular/material/paginator';
|
||||
import {MatSortModule} from '@angular/material/sort';
|
||||
import {MatFormFieldModule} from '@angular/material/form-field';
|
||||
import {MatInputModule} from '@angular/material/input';
|
||||
import {MatButtonModule} from '@angular/material/button';
|
||||
import {MatIconModule} from '@angular/material/icon';
|
||||
import {MatSelectModule} from '@angular/material/select';
|
||||
import {MatCardModule} from '@angular/material/card';
|
||||
import {MatRippleModule} from '@angular/material/core';
|
||||
import {MatSnackBarModule} from '@angular/material/snack-bar';
|
||||
|
||||
import { DataTablesModule } from 'angular-datatables';
|
||||
import { SharedModule } from '@app/shared/shared.module';
|
||||
import { MatTableModule } from '@angular/material/table';
|
||||
import { MatCheckboxModule } from '@angular/material/checkbox';
|
||||
import { MatPaginatorModule } from '@angular/material/paginator';
|
||||
import { MatSortModule } from '@angular/material/sort';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatButtonModule } from '@angular/material/button';
|
||||
import { MatIconModule } from '@angular/material/icon';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatCardModule } from '@angular/material/card';
|
||||
import { MatRippleModule } from '@angular/material/core';
|
||||
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
||||
|
||||
@NgModule({
|
||||
declarations: [TransactionsComponent, TransactionDetailsComponent],
|
||||
exports: [
|
||||
TransactionDetailsComponent
|
||||
],
|
||||
exports: [TransactionDetailsComponent],
|
||||
imports: [
|
||||
CommonModule,
|
||||
TransactionsRoutingModule,
|
||||
@ -42,6 +39,6 @@ import {MatSnackBarModule} from '@angular/material/snack-bar';
|
||||
MatCardModule,
|
||||
MatRippleModule,
|
||||
MatSnackBarModule,
|
||||
]
|
||||
],
|
||||
})
|
||||
export class TransactionsModule { }
|
||||
export class TransactionsModule {}
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { MenuSelectionDirective } from '@app/shared/_directives/menu-selection.directive';
|
||||
import {ElementRef, Renderer2} from '@angular/core';
|
||||
import { ElementRef, Renderer2 } from '@angular/core';
|
||||
|
||||
describe('MenuSelectionDirective', () => {
|
||||
// tslint:disable-next-line:prefer-const
|
||||
let elementRef: ElementRef;
|
||||
// tslint:disable-next-line:prefer-const
|
||||
let renderer: Renderer2;
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -1,14 +1,10 @@
|
||||
import {Directive, ElementRef, Renderer2} from '@angular/core';
|
||||
import { Directive, ElementRef, Renderer2 } from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[appMenuSelection]'
|
||||
selector: '[appMenuSelection]',
|
||||
})
|
||||
export class MenuSelectionDirective {
|
||||
|
||||
constructor(
|
||||
private elementRef: ElementRef,
|
||||
private renderer: Renderer2
|
||||
) {
|
||||
constructor(private elementRef: ElementRef, private renderer: Renderer2) {
|
||||
this.renderer.listen(this.elementRef.nativeElement, 'click', () => {
|
||||
const mediaQuery = window.matchMedia('(max-width: 768px)');
|
||||
if (mediaQuery.matches) {
|
||||
|
@ -1,8 +1,10 @@
|
||||
import { MenuToggleDirective } from '@app/shared/_directives/menu-toggle.directive';
|
||||
import {ElementRef, Renderer2} from '@angular/core';
|
||||
import { ElementRef, Renderer2 } from '@angular/core';
|
||||
|
||||
describe('MenuToggleDirective', () => {
|
||||
// tslint:disable-next-line:prefer-const
|
||||
let elementRef: ElementRef;
|
||||
// tslint:disable-next-line:prefer-const
|
||||
let renderer: Renderer2;
|
||||
it('should create an instance', () => {
|
||||
const directive = new MenuToggleDirective(elementRef, renderer);
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user