| 作者 | SHA1 | 備註 | 提交日期 |
|---|---|---|---|
|
|
481c37b21a | Add other name registration endpoints | 3 年之前 |
|
|
890271cd70 | Add a register name service | 3 年之前 |
|
|
e1877048c1 | Make notifications responsive | 3 年之前 |
| @@ -8,8 +8,11 @@ | |||||
| "watch": "ng build --watch --configuration development", | "watch": "ng build --watch --configuration development", | ||||
| "test": "ng test", | "test": "ng test", | ||||
| "docker-build": "docker build -t b2rs-multi-stage-image .", | |||||
| "docker-create": "docker run --name b2rs-app-container -d -p 80:80 b2rs-multi-stage-image", | |||||
| "docker-build": "docker build -t b2rs-front-end-image .", | |||||
| "docker-tag": "docker tag b2rs-front-end-image:latest 834628752744.dkr.ecr.ap-southeast-1.amazonaws.com/b2rs-front-end-image:latest", | |||||
| "docker-push": "docker push 834628752744.dkr.ecr.ap-southeast-1.amazonaws.com/b2rs-front-end-image:latest", | |||||
| "docker-create": "docker run --name b2rs-app-container -d -p 80:80 b2rs-front-end-image", | |||||
| "docker-destroy": "docker container rm b2rs-app-container", | "docker-destroy": "docker container rm b2rs-app-container", | ||||
| "docker-start": "docker start b2rs-app-container", | "docker-start": "docker start b2rs-app-container", | ||||
| @@ -34,6 +34,7 @@ import { DashboardComponent } from './pages/dashboard/dashboard.component'; | |||||
| import { FilterViewCardComponent } from './pages/dashboard/filter-view-card/filter-view-card.component'; | import { FilterViewCardComponent } from './pages/dashboard/filter-view-card/filter-view-card.component'; | ||||
| import { NavbarComponent } from './layout/navbar/navbar.component'; | import { NavbarComponent } from './layout/navbar/navbar.component'; | ||||
| import { NotificationsListComponent } from './layout/notifications/notifications-list/notifications-list.component'; | import { NotificationsListComponent } from './layout/notifications/notifications-list/notifications-list.component'; | ||||
| import { HttpClientModule } from '@angular/common/http'; | |||||
| @NgModule({ | @NgModule({ | ||||
| declarations: [ | declarations: [ | ||||
| @@ -73,6 +74,7 @@ import { NotificationsListComponent } from './layout/notifications/notifications | |||||
| AppRoutingModule, | AppRoutingModule, | ||||
| FormsModule, | FormsModule, | ||||
| FileUploadModule, | FileUploadModule, | ||||
| HttpClientModule, | |||||
| ], | ], | ||||
| providers: [], | providers: [], | ||||
| bootstrap: [AppComponent] | bootstrap: [AppComponent] | ||||
| @@ -13,12 +13,16 @@ export class NavbarComponent implements OnInit, OnDestroy { | |||||
| showLogout: boolean = false; | showLogout: boolean = false; | ||||
| isShowingNotificationsSubscription: Subscription; | isShowingNotificationsSubscription: Subscription; | ||||
| loginRoleSubscription: Subscription; | |||||
| loginName: string = ''; | loginName: string = ''; | ||||
| notificationsCount: number = 0; | notificationsCount: number = 0; | ||||
| constructor(loginService: LoginService, private notificationService: NotificationService) { | constructor(loginService: LoginService, private notificationService: NotificationService) { | ||||
| this.loginName = loginService.getLoginName(); | |||||
| this.loginRoleSubscription = loginService.getLoginRoleObservable().subscribe(loginRole => { | |||||
| this.loginName = loginService.getLoginName(); | |||||
| }); | |||||
| this.notificationsCount = notificationService.getAllowedNotifications().length; | this.notificationsCount = notificationService.getAllowedNotifications().length; | ||||
| this.isShowingNotificationsSubscription = this.notificationService.getIsShowingNotificationsObservable().subscribe(isShowingNotifications => this.isShowingNotifications = isShowingNotifications); | this.isShowingNotificationsSubscription = this.notificationService.getIsShowingNotificationsObservable().subscribe(isShowingNotifications => this.isShowingNotifications = isShowingNotifications); | ||||
| @@ -35,6 +39,10 @@ export class NavbarComponent implements OnInit, OnDestroy { | |||||
| if (this.isShowingNotificationsSubscription) { | if (this.isShowingNotificationsSubscription) { | ||||
| this.isShowingNotificationsSubscription.unsubscribe(); | this.isShowingNotificationsSubscription.unsubscribe(); | ||||
| } | } | ||||
| if (this.loginRoleSubscription) { | |||||
| this.loginRoleSubscription.unsubscribe(); | |||||
| } | |||||
| } | } | ||||
| } | } | ||||
| @@ -1,6 +1,6 @@ | |||||
| <ul> | <ul> | ||||
| <li *ngFor="let notification of allowedNotifications"> | <li *ngFor="let notification of allowedNotifications"> | ||||
| <a *ngIf="notification.redirectionUrl" [routerLink]="notification.redirectionUrl"> | |||||
| <a *ngIf="notification.redirectionUrl" (click)="closeNotifications()" [routerLink]="notification.redirectionUrl"> | |||||
| <h5> {{ notification.text }} </h5> | <h5> {{ notification.text }} </h5> | ||||
| <p *ngIf="notification.description"> {{ notification.description }} </p> | <p *ngIf="notification.description"> {{ notification.description }} </p> | ||||
| <span class="time-stamp"> {{ notification.timeStamp }} </span> | <span class="time-stamp"> {{ notification.timeStamp }} </span> | ||||
| @@ -9,11 +9,15 @@ import { Notification, NotificationService } from 'src/app/services/notification | |||||
| export class NotificationsListComponent implements OnInit { | export class NotificationsListComponent implements OnInit { | ||||
| allowedNotifications: Array<Notification> = []; | allowedNotifications: Array<Notification> = []; | ||||
| constructor(notificationService: NotificationService) { | |||||
| constructor(private notificationService: NotificationService) { | |||||
| this.allowedNotifications = notificationService.getAllowedNotifications(); | this.allowedNotifications = notificationService.getAllowedNotifications(); | ||||
| } | } | ||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| } | } | ||||
| closeNotifications() { | |||||
| this.notificationService.setIsShowingNotifications(false); | |||||
| } | |||||
| } | } | ||||
| @@ -1,4 +1,5 @@ | |||||
| import { Component, OnInit } from '@angular/core'; | |||||
| import { Component, OnDestroy, OnInit } from '@angular/core'; | |||||
| import { Subscription } from 'rxjs'; | |||||
| import { LoginService } from 'src/app/services/login.service'; | import { LoginService } from 'src/app/services/login.service'; | ||||
| import { NotificationService } from 'src/app/services/notification.service'; | import { NotificationService } from 'src/app/services/notification.service'; | ||||
| @@ -7,19 +8,30 @@ import { NotificationService } from 'src/app/services/notification.service'; | |||||
| templateUrl: './tabs.component.html', | templateUrl: './tabs.component.html', | ||||
| styleUrls: ['./tabs.component.scss'] | styleUrls: ['./tabs.component.scss'] | ||||
| }) | }) | ||||
| export class TabsComponent implements OnInit { | |||||
| export class TabsComponent implements OnInit, OnDestroy { | |||||
| showNotifications: boolean = false; | showNotifications: boolean = false; | ||||
| showLogout: boolean = false; | showLogout: boolean = false; | ||||
| loginRoleSubscription: Subscription; | |||||
| loginName: string = ''; | loginName: string = ''; | ||||
| notificationsCount: number = 0; | notificationsCount: number = 0; | ||||
| constructor(loginService: LoginService, notificationService: NotificationService) { | constructor(loginService: LoginService, notificationService: NotificationService) { | ||||
| this.loginName = loginService.getLoginName(); | |||||
| this.loginRoleSubscription = loginService.getLoginRoleObservable().subscribe(loginRole => { | |||||
| this.loginName = loginService.getLoginName(); | |||||
| }); | |||||
| this.notificationsCount = notificationService.getAllowedNotifications().length; | this.notificationsCount = notificationService.getAllowedNotifications().length; | ||||
| } | } | ||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| } | } | ||||
| ngOnDestroy(): void { | |||||
| if (this.loginRoleSubscription) { | |||||
| this.loginRoleSubscription.unsubscribe(); | |||||
| } | |||||
| } | |||||
| } | } | ||||
| @@ -1,7 +1,7 @@ | |||||
| import { Component, OnInit } from '@angular/core'; | import { Component, OnInit } from '@angular/core'; | ||||
| type State = 'VIEW INITIAL DETAILS'|'ENTER MORE DETAILS'|'ASSIGN COMMITTEE'|'REVIEW NON COMPLIANCE'|'CONCUR COMPLIANCE REVIEW'|'CLOSING REMARKS'|'PREPARE PRELIMINARY LETTER'|'RESPOND TO PRELIMINARY LETTER'; | type State = 'VIEW INITIAL DETAILS'|'ENTER MORE DETAILS'|'ASSIGN COMMITTEE'|'REVIEW NON COMPLIANCE'|'CONCUR COMPLIANCE REVIEW'|'CLOSING REMARKS'|'PREPARE PRELIMINARY LETTER'|'RESPOND TO PRELIMINARY LETTER'; | ||||
| type Role = 'Investigator'|'Hod'|'Panel'|'Customer'; | |||||
| type Role = 'Investigator'|'Hod'|'Panel'|'Customer'|'Officer'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-investigate-business-entities-and-individuals', | selector: 'app-investigate-business-entities-and-individuals', | ||||
| @@ -41,6 +41,8 @@ export class InvestigateBusinessEntitiesAndIndividualsComponent implements OnIni | |||||
| this.loginRole = 'Panel'; | this.loginRole = 'Panel'; | ||||
| } else if (loginEmail && loginEmail.toLocaleLowerCase().startsWith('customer')) { | } else if (loginEmail && loginEmail.toLocaleLowerCase().startsWith('customer')) { | ||||
| this.loginRole = 'Customer'; | this.loginRole = 'Customer'; | ||||
| } else if (loginEmail && loginEmail.toLocaleLowerCase().startsWith('officer')) { | |||||
| this.loginRole = 'Officer'; | |||||
| } else { | } else { | ||||
| this.loginRole = 'Investigator'; | this.loginRole = 'Investigator'; | ||||
| } | } | ||||
| @@ -1,4 +1,5 @@ | |||||
| import { Component, OnInit } from '@angular/core'; | import { Component, OnInit } from '@angular/core'; | ||||
| import { LoginService } from 'src/app/services/login.service'; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-login', | selector: 'app-login', | ||||
| @@ -8,7 +9,7 @@ import { Component, OnInit } from '@angular/core'; | |||||
| export class LoginComponent implements OnInit { | export class LoginComponent implements OnInit { | ||||
| email = ''; | email = ''; | ||||
| constructor() { } | |||||
| constructor(private loginService: LoginService) { } | |||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| } | } | ||||
| @@ -17,6 +18,7 @@ export class LoginComponent implements OnInit { | |||||
| this.email = email; | this.email = email; | ||||
| localStorage.setItem('loginEmail', email); | localStorage.setItem('loginEmail', email); | ||||
| this.loginService.updateLoginRole(); | |||||
| } | } | ||||
| } | } | ||||
| @@ -77,7 +77,7 @@ | |||||
| <button class="common-button neutral" (click)="formState = 'INIT_REGISTER'"> | <button class="common-button neutral" (click)="formState = 'INIT_REGISTER'"> | ||||
| Back | Back | ||||
| </button> | </button> | ||||
| <button class="common-button" (click)="formState = 'SELECT_PLAN'"> | |||||
| <button class="common-button" (click)="registerName()"> | |||||
| Proceed | Proceed | ||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| @@ -150,7 +150,7 @@ | |||||
| <button class="common-button outline" (click)="print()"> | <button class="common-button outline" (click)="print()"> | ||||
| </button> | </button> | ||||
| <button class="common-button" (click)="formState = 'RECEIPT'"> | |||||
| <button class="common-button" (click)="goToReceipt()"> | |||||
| View Receipt | View Receipt | ||||
| </button> | </button> | ||||
| </div> | </div> | ||||
| @@ -1,7 +1,17 @@ | |||||
| import { Component, OnInit } from '@angular/core'; | import { Component, OnInit } from '@angular/core'; | ||||
| import { DateInputProperties } from 'src/app/widgets/form/date-input/date-input.component'; | |||||
| import { GenericInputProperties } from 'src/app/widgets/form/generic-input/generic-input.component'; | |||||
| import { SelectInputProperties } from 'src/app/widgets/form/select-input/select-input.component'; | |||||
| import { RegisterBusinessNameService } from 'src/app/services/register-business-name.service'; | |||||
| export interface AcknowledgementRow { | |||||
| name: string, | |||||
| value: string | number, | |||||
| amount?: number, | |||||
| highlight?: boolean, | |||||
| }; | |||||
| export interface ReceiptRow { | |||||
| name: string, | |||||
| value: string | number, | |||||
| }; | |||||
| @Component({ | @Component({ | ||||
| selector: 'app-register-business', | selector: 'app-register-business', | ||||
| @@ -50,64 +60,11 @@ export class RegisterBusinessComponent implements OnInit { | |||||
| dropSuffixOptions = ['YES', 'NO']; | dropSuffixOptions = ['YES', 'NO']; | ||||
| acknowledgementDetails: Array<{ | |||||
| name: string, | |||||
| value: string | number, | |||||
| amount?: number, | |||||
| highlight?: boolean, | |||||
| }> = [{ | |||||
| name: 'UEM', | |||||
| value: '---' | |||||
| }, { | |||||
| name: 'Entity Name', | |||||
| value: this.nameToCheck | |||||
| }, { | |||||
| name: 'Transaction Number', | |||||
| value: '39047729362923293', | |||||
| highlight: true, | |||||
| }, { | |||||
| name: 'Receipt Number', | |||||
| value: 'ACRA38293', | |||||
| highlight: true | |||||
| }, { | |||||
| name: 'EP Reference No.', | |||||
| value: '20910038829384470' | |||||
| }, { | |||||
| name: 'Payment Date', | |||||
| value: '06/11/2021 11:28:01' | |||||
| }, { | |||||
| name: 'Description', | |||||
| value: 'Application for Business Name', | |||||
| amount: 15 | |||||
| }]; | |||||
| receiptDetails: Array<{ | |||||
| name: string, | |||||
| value: string | number, | |||||
| }> = [{ | |||||
| name: 'Receipt Number', | |||||
| value: 'ACRA38293', | |||||
| }, { | |||||
| name: 'ARN', | |||||
| value: 'ARN2021110294038', | |||||
| }, { | |||||
| name: 'EP Reference No.', | |||||
| value: '20910038829384470' | |||||
| }, { | |||||
| name: 'Tax ID', | |||||
| value: 'M9-0C038921', | |||||
| }, { | |||||
| name: 'Paid By', | |||||
| value: 'KOH YA TING', | |||||
| }, { | |||||
| name: 'Payment Date', | |||||
| value: '06/11/2021 11:28:01' | |||||
| }, { | |||||
| name: 'Paid Via', | |||||
| value: 'Net Banking', | |||||
| }]; | |||||
| constructor() { } | |||||
| acknowledgementDetails: Array<AcknowledgementRow> = []; | |||||
| receiptDetails: Array<ReceiptRow> = []; | |||||
| constructor(private registerBusinessNameService: RegisterBusinessNameService) { } | |||||
| ngOnInit(): void { | ngOnInit(): void { | ||||
| } | } | ||||
| @@ -151,33 +108,52 @@ export class RegisterBusinessComponent implements OnInit { | |||||
| payForReservation() { | payForReservation() { | ||||
| this.paymentChild = window.open('/mock', '_blank', 'toolbar=0,status=0,width=626,height=436'); | this.paymentChild = window.open('/mock', '_blank', 'toolbar=0,status=0,width=626,height=436'); | ||||
| this.childCheck = window.setInterval(() => { | |||||
| this.childCheck = window.setInterval(async () => { | |||||
| if (this.paymentChild && this.paymentChild.closed) { | if (this.paymentChild && this.paymentChild.closed) { | ||||
| this.formState = 'ACKNOWLEDGEMENT'; | this.formState = 'ACKNOWLEDGEMENT'; | ||||
| window.clearInterval(this.childCheck); | window.clearInterval(this.childCheck); | ||||
| this.childCheck = undefined; | this.childCheck = undefined; | ||||
| this.acknowledgementDetails = await this.registerBusinessNameService.getAcknowledgement(); | |||||
| } | } | ||||
| }, 1000); | }, 1000); | ||||
| } | } | ||||
| checkName() { | |||||
| async goToReceipt() { | |||||
| this.formState = 'RECEIPT'; | |||||
| this.receiptDetails = await this.registerBusinessNameService.getReceipt(); | |||||
| } | |||||
| async checkName() { | |||||
| this.formState = 'INIT_REGISTER'; | this.formState = 'INIT_REGISTER'; | ||||
| if (this.nameToCheck === `A'DROIT MANAGEMENT SERVICES`) { | |||||
| this.error = { | |||||
| message: this.nameToCheck + ' is unavailable. Entity with identical name exists', | |||||
| isUnique: false, | |||||
| isInvalid: false, | |||||
| }; | |||||
| } else { | |||||
| const nameCheckResponse = await this.registerBusinessNameService.checkName(this.nameToCheck); | |||||
| if (nameCheckResponse.status === `success`) { | |||||
| this.error = { | this.error = { | ||||
| message: this.nameToCheck + ' is available', | message: this.nameToCheck + ' is available', | ||||
| isUnique: true, | isUnique: true, | ||||
| isInvalid: false | isInvalid: false | ||||
| }; | }; | ||||
| } else { | |||||
| this.error = { | |||||
| message: this.nameToCheck + ' is unavailable. ' + nameCheckResponse.status, | |||||
| isUnique: false, | |||||
| isInvalid: false, | |||||
| }; | |||||
| } | } | ||||
| } | } | ||||
| async registerName() { | |||||
| await this.registerBusinessNameService.registerName( | |||||
| this.nameToCheck, | |||||
| this.selectedEntityType, | |||||
| this.selectedCompanyCategory, | |||||
| this.selectedCompanySuffix, | |||||
| this.selectedDropSuffix, | |||||
| ); | |||||
| this.formState = 'SELECT_PLAN'; | |||||
| } | |||||
| print() { | print() { | ||||
| window.print(); | window.print(); | ||||
| } | } | ||||
| @@ -1,4 +1,5 @@ | |||||
| import { Injectable } from '@angular/core'; | import { Injectable } from '@angular/core'; | ||||
| import { BehaviorSubject } from 'rxjs'; | |||||
| export type Role = 'Officer'|'Investigator'|'Hod'|'Panel'|'Customer'; | export type Role = 'Officer'|'Investigator'|'Hod'|'Panel'|'Customer'; | ||||
| @@ -7,8 +8,15 @@ export type Role = 'Officer'|'Investigator'|'Hod'|'Panel'|'Customer'; | |||||
| }) | }) | ||||
| export class LoginService { | export class LoginService { | ||||
| private loginRole: Role; | private loginRole: Role; | ||||
| private loginRoleSubject: BehaviorSubject<string>; | |||||
| constructor() { | constructor() { | ||||
| this.loginRole = 'Customer'; | |||||
| this.loginRoleSubject = new BehaviorSubject(''); | |||||
| this.updateLoginRole(); | |||||
| } | |||||
| updateLoginRole() { | |||||
| const loginEmail = localStorage.getItem('loginEmail'); | const loginEmail = localStorage.getItem('loginEmail'); | ||||
| if (loginEmail && loginEmail.toLocaleLowerCase().startsWith('hod')) { | if (loginEmail && loginEmail.toLocaleLowerCase().startsWith('hod')) { | ||||
| @@ -22,6 +30,8 @@ export class LoginService { | |||||
| } else { | } else { | ||||
| this.loginRole = 'Officer'; | this.loginRole = 'Officer'; | ||||
| } | } | ||||
| this.loginRoleSubject.next(this.loginRole); | |||||
| } | } | ||||
| getLoginName() { | getLoginName() { | ||||
| @@ -34,6 +44,10 @@ export class LoginService { | |||||
| } | } | ||||
| } | } | ||||
| getLoginRoleObservable() { | |||||
| return this.loginRoleSubject.asObservable(); | |||||
| } | |||||
| getLoginRole() { | getLoginRole() { | ||||
| return this.loginRole; | return this.loginRole; | ||||
| } | } | ||||
| @@ -71,8 +71,10 @@ export class NotificationService { | |||||
| private allowedNotifications: Array<Notification> = []; | private allowedNotifications: Array<Notification> = []; | ||||
| constructor(loginService: LoginService) { | constructor(loginService: LoginService) { | ||||
| const loginRole = loginService.getLoginRole(); | |||||
| this.allowedNotifications = this.notifications.filter(notification => notification.roles.includes(loginRole)); | |||||
| loginService.getLoginRoleObservable().subscribe(loginRoleObserved => { | |||||
| const loginRole = loginService.getLoginRole(); | |||||
| this.allowedNotifications = this.notifications.filter(notification => notification.roles.includes(loginRole)); | |||||
| }); | |||||
| } | } | ||||
| getAllowedNotifications() { | getAllowedNotifications() { | ||||
| @@ -0,0 +1,16 @@ | |||||
| import { TestBed } from '@angular/core/testing'; | |||||
| import { RegisterBusinessNameService } from './register-business-name.service'; | |||||
| describe('RegisterBusinessNameService', () => { | |||||
| let service: RegisterBusinessNameService; | |||||
| beforeEach(() => { | |||||
| TestBed.configureTestingModule({}); | |||||
| service = TestBed.inject(RegisterBusinessNameService); | |||||
| }); | |||||
| it('should be created', () => { | |||||
| expect(service).toBeTruthy(); | |||||
| }); | |||||
| }); | |||||
| @@ -0,0 +1,73 @@ | |||||
| import { Injectable } from '@angular/core'; | |||||
| import { HttpClient, HttpHeaders } from '@angular/common/http'; | |||||
| import { Observable, throwError } from 'rxjs'; | |||||
| import { catchError, retry } from 'rxjs/operators'; | |||||
| import { AcknowledgementRow, ReceiptRow } from '../pages/register-business/register-business.component'; | |||||
| export interface NameCheckRequest { | |||||
| name: string; | |||||
| } | |||||
| export interface NameCheckResponse { | |||||
| status: string; | |||||
| } | |||||
| export interface NameRegisterRequest { | |||||
| name: string; | |||||
| entityType: string; | |||||
| companyCategory: string; | |||||
| companySuffix: string; | |||||
| dropSuffix: string; | |||||
| } | |||||
| export interface NameRegisterResponse { | |||||
| status: string; | |||||
| } | |||||
| @Injectable({ | |||||
| providedIn: 'root' | |||||
| }) | |||||
| export class RegisterBusinessNameService { | |||||
| constructor(private http: HttpClient) { | |||||
| } | |||||
| checkName(name: string) { | |||||
| const nameCheckRequest: NameCheckRequest = { | |||||
| name, | |||||
| }; | |||||
| return new Promise<NameCheckResponse>((resolve, reject) => resolve({status: 'success'})); | |||||
| return this.http.post<NameCheckResponse>('http://localhost:10000/name-check/', nameCheckRequest).toPromise(); | |||||
| } | |||||
| registerName( | |||||
| name: string, | |||||
| entityType: string, | |||||
| companyCategory: string, | |||||
| companySuffix: string, | |||||
| dropSuffix: string, | |||||
| ) { | |||||
| const nameRegisterRequest: NameRegisterRequest = { | |||||
| name, | |||||
| entityType, | |||||
| companyCategory, | |||||
| companySuffix, | |||||
| dropSuffix, | |||||
| }; | |||||
| return new Promise<NameCheckResponse>((resolve, reject) => resolve({status: 'success'})); | |||||
| return this.http.post<NameRegisterResponse>('http://localhost:8080/name-register/', nameRegisterRequest).toPromise(); | |||||
| } | |||||
| getAcknowledgement() { | |||||
| return this.http.get<Array<AcknowledgementRow>>('http://localhost:8080/acknowledgement/').toPromise(); | |||||
| } | |||||
| getReceipt() { | |||||
| return this.http.get<Array<ReceiptRow>>('http://localhost:8080/receipt/').toPromise(); | |||||
| } | |||||
| } | |||||