소스 검색

Refactor notifications to be more responsive

master
Adwaith Rao 3 년 전
부모
커밋
02b92dc638
23개의 변경된 파일791개의 추가작업 그리고 697개의 파일을 삭제
  1. +7
    -1
      src/app/app.module.ts
  2. +38
    -0
      src/app/layout/navbar/navbar.component.html
  3. +200
    -0
      src/app/layout/navbar/navbar.component.scss
  4. +25
    -0
      src/app/layout/navbar/navbar.component.spec.ts
  5. +40
    -0
      src/app/layout/navbar/navbar.component.ts
  6. +14
    -0
      src/app/layout/notifications/notifications-list/notifications-list.component.html
  7. +44
    -0
      src/app/layout/notifications/notifications-list/notifications-list.component.scss
  8. +25
    -0
      src/app/layout/notifications/notifications-list/notifications-list.component.spec.ts
  9. +19
    -0
      src/app/layout/notifications/notifications-list/notifications-list.component.ts
  10. +8
    -14
      src/app/layout/notifications/notifications.component.html
  11. +44
    -34
      src/app/layout/notifications/notifications.component.scss
  12. +17
    -5
      src/app/layout/notifications/notifications.component.ts
  13. +2
    -47
      src/app/layout/tabs/tabs.component.html
  14. +0
    -199
      src/app/layout/tabs/tabs.component.scss
  15. +157
    -76
      src/app/pages/dashboard/dashboard.component.html
  16. +23
    -1
      src/app/pages/dashboard/dashboard.component.scss
  17. +15
    -0
      src/app/pages/dashboard/dashboard.component.ts
  18. +6
    -0
      src/app/pages/dashboard/filter-view-card/filter-view-card.component.html
  19. +52
    -0
      src/app/pages/dashboard/filter-view-card/filter-view-card.component.scss
  20. +25
    -0
      src/app/pages/dashboard/filter-view-card/filter-view-card.component.spec.ts
  21. +18
    -0
      src/app/pages/dashboard/filter-view-card/filter-view-card.component.ts
  22. +0
    -319
      src/app/pages/investigate-business-entities-and-individuals/investigate-business-entities-and-individuals.component.scss
  23. +12
    -1
      src/app/services/notification.service.ts

+ 7
- 1
src/app/app.module.ts 파일 보기

@@ -31,6 +31,9 @@ import { ClosingRemarksComponent } from './pages/investigate-business-entities-a
import { PreparePreliminaryLetterComponent } from './pages/investigate-business-entities-and-individuals/prepare-preliminary-letter/prepare-preliminary-letter.component';
import { RespondToPreliminaryLetterComponent } from './pages/investigate-business-entities-and-individuals/respond-to-preliminary-letter/respond-to-preliminary-letter.component';
import { DashboardComponent } from './pages/dashboard/dashboard.component';
import { FilterViewCardComponent } from './pages/dashboard/filter-view-card/filter-view-card.component';
import { NavbarComponent } from './layout/navbar/navbar.component';
import { NotificationsListComponent } from './layout/notifications/notifications-list/notifications-list.component';

@NgModule({
declarations: [
@@ -60,7 +63,10 @@ import { DashboardComponent } from './pages/dashboard/dashboard.component';
ClosingRemarksComponent,
PreparePreliminaryLetterComponent,
RespondToPreliminaryLetterComponent,
DashboardComponent
DashboardComponent,
FilterViewCardComponent,
NavbarComponent,
NotificationsListComponent
],
imports: [
BrowserModule,


+ 38
- 0
src/app/layout/navbar/navbar.component.html 파일 보기

@@ -0,0 +1,38 @@
<section class="navbar">
<figure class="logo">
<img src="assets/icons/logo.svg" alt="logo image">
</figure>
<nav>
<a *ngIf="loginName === 'Officer'" [routerLink]="['/tabs/dashboard']"> Dashboard </a>
<a [routerLink]="['/tabs/e-services']"> E-Services </a>
</nav>

<div class="search-input">
<input type="text" placeholder="Search for Business Entities">
<img src="assets/icons/search.svg" alt="search icon">
</div>

<button class="notification-button" (click)="showNotifications()"
[ngClass]="{'active' : isShowingNotifications}">
<img src="assets/icons/bell.svg" alt="bell icon">

<span class="badge"> {{ notificationsCount }} </span>
</button>

<section class="profile-actions" (mouseover)="showLogout=true" (mouseout)="showLogout=false">
<ng-container *ngIf="!showLogout">
<img src="assets/icons/user.svg" alt="user icon">
<span> Hi, {{ loginName }} </span>
<img src="assets/icons/chevron-down.svg" alt="chevron down image">
</ng-container>
<ng-container *ngIf="showLogout">
<a [routerLink]="['/login']">
<img src="assets/icons/logout.svg" class="logout-icon" alt="logout icon">
<span> Log me out </span>
<img src="assets/icons/chevron-down.svg" alt="chevron down image">
</a>
</ng-container>
</section>

</section>

+ 200
- 0
src/app/layout/navbar/navbar.component.scss 파일 보기

@@ -0,0 +1,200 @@
$header-height: 10rem;

.navbar {
display: flex;
width: 100%;
height: $header-height;
align-items: center;
justify-content: flex-start;

.logo {
width: 12rem;
margin: 0 2rem;

img {
width: 100%;
height: 100%;
}
}

nav {
margin: 0 2rem;
@media print {
display: none;
}

a {
font-size: 1.6rem;
letter-spacing: 0.5px;
color: var(--dark-grey);
margin-right: 2rem;

&.active {
text-decoration: underline;
color: var(--highlight);
}

&:hover {
text-decoration: underline;
}
}
}

.search-input {
position: relative;
margin-left: auto;

@media print {
display: none;
}

input {
display: block;
height: 5rem;
border: 1px solid var(--border-grey);
border-radius: 4rem;
padding: 0 2.5rem;
width: 35rem;
font-size: 1.5rem;
font-weight: 300;

&::placeholder {
color: var(--dark-grey);
opacity: 0.6;
}
}

img {
position: absolute;
width: 1.8rem;
top: 1.5rem;
right: 1.8rem;
}
}


.notification-button {
width: 5rem;
height: 5rem;
border-radius: 50%;
border: none;
position: relative;
background-color: white;
margin-left: 2rem;

@media print {
display: none;
}

&.active {
box-shadow: 0px 0px 10px -3px var(--primary);
img {
filter: brightness(500%);
}

.badge {
transform: scale(0);
}

&::before {
background-color: var(--highlight);
opacity: 1;
filter: none;
}
}

&:hover {
box-shadow: 0px 0px 10px -3px var(--primary);
}

.badge {
background-color: var(--highlight);
color: white;
font-size: 1.1rem;
border-radius: 7px;
position: absolute;
right: 0;
top: -0.5rem;
width: 2rem;
height: 2rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
transition: transform 0.1s;
}

&::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: var(--secondary);
opacity: 0.3;
filter: brightness(110%);
border-radius: 50%;
}

& > * {
position: relative;
}

img {
width: 40%;
height: 40%;
}
}

.profile-actions {
margin: 0 2rem;
display: flex;
align-items: center;
justify-content: center;
height: 5rem;
border-radius: 4rem;
padding: 0 2rem;
position: relative;
overflow: hidden;
width: 20rem;
cursor: pointer;

@media print {
display: none;
}
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: var(--secondary);
opacity: 0.3;
filter: brightness(110%);
}

& > * {
position: relative;
}

img {
width: 1.5rem;

&.logout-icon {
transform: scale(1.5);
}
}

span {
margin: 0 auto;
color: var(--primary);
font-size: 1.4rem;
}
}
}

+ 25
- 0
src/app/layout/navbar/navbar.component.spec.ts 파일 보기

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { NavbarComponent } from './navbar.component';

describe('NavbarComponent', () => {
let component: NavbarComponent;
let fixture: ComponentFixture<NavbarComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NavbarComponent ]
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(NavbarComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});

+ 40
- 0
src/app/layout/navbar/navbar.component.ts 파일 보기

@@ -0,0 +1,40 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { LoginService } from 'src/app/services/login.service';
import { NotificationService } from 'src/app/services/notification.service';

@Component({
selector: 'app-navbar',
templateUrl: './navbar.component.html',
styleUrls: ['./navbar.component.scss']
})
export class NavbarComponent implements OnInit, OnDestroy {
isShowingNotifications = false;
showLogout: boolean = false;

isShowingNotificationsSubscription: Subscription;

loginName: string = '';
notificationsCount: number = 0;

constructor(loginService: LoginService, private notificationService: NotificationService) {
this.loginName = loginService.getLoginName();
this.notificationsCount = notificationService.getAllowedNotifications().length;

this.isShowingNotificationsSubscription = this.notificationService.getIsShowingNotificationsObservable().subscribe(isShowingNotifications => this.isShowingNotifications = isShowingNotifications);
}

showNotifications() {
this.notificationService.setIsShowingNotifications(true);
}

ngOnInit(): void {
}

ngOnDestroy(): void {
if (this.isShowingNotificationsSubscription) {
this.isShowingNotificationsSubscription.unsubscribe();
}
}

}

+ 14
- 0
src/app/layout/notifications/notifications-list/notifications-list.component.html 파일 보기

@@ -0,0 +1,14 @@
<ul>
<li *ngFor="let notification of allowedNotifications">
<a *ngIf="notification.redirectionUrl" [routerLink]="notification.redirectionUrl">
<h5> {{ notification.text }} </h5>
<p *ngIf="notification.description"> {{ notification.description }} </p>
<span class="time-stamp"> {{ notification.timeStamp }} </span>
</a>
<ng-container *ngIf="!notification.redirectionUrl">
<h5> {{ notification.text }} </h5>
<p *ngIf="notification.description"> {{ notification.description }} </p>
<span class="time-stamp"> {{ notification.timeStamp }} </span>
</ng-container>
</li>
</ul>

+ 44
- 0
src/app/layout/notifications/notifications-list/notifications-list.component.scss 파일 보기

@@ -0,0 +1,44 @@
ul {
list-style: none;
height: 100%;
overflow: auto;
min-height: 10rem;
max-height: 40rem;

li {
display: block;
width: calc(100% - 2rem);
padding: 1.5rem;
cursor: pointer;
position: relative;
margin: 1rem auto;
border: 1px solid var(--border-grey);
border-radius: 1rem;
line-height: 1.5;

&:hover {
box-shadow: 0px 0px 10px -4px var(--dark-grey);
}

h5 {
font-size: 1.4rem;
margin: 0 0 0.5rem;
font-weight: 500;
color: var(--highlight);
filter: brightness(80%);
}

p {
color: var(--dark-grey);
font-size: 1.3rem;
}

.time-stamp {
display: block;
margin: 1rem 0 0;
font-size: 3rem;
font-size: 1.3rem;
color: var(--dark-grey);
}
}
}

+ 25
- 0
src/app/layout/notifications/notifications-list/notifications-list.component.spec.ts 파일 보기

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { NotificationsListComponent } from './notifications-list.component';

describe('NotificationsListComponent', () => {
let component: NotificationsListComponent;
let fixture: ComponentFixture<NotificationsListComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ NotificationsListComponent ]
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(NotificationsListComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});

+ 19
- 0
src/app/layout/notifications/notifications-list/notifications-list.component.ts 파일 보기

@@ -0,0 +1,19 @@
import { Component, OnInit } from '@angular/core';
import { Notification, NotificationService } from 'src/app/services/notification.service';

@Component({
selector: 'app-notifications-list',
templateUrl: './notifications-list.component.html',
styleUrls: ['./notifications-list.component.scss']
})
export class NotificationsListComponent implements OnInit {
allowedNotifications: Array<Notification> = [];

constructor(notificationService: NotificationService) {
this.allowedNotifications = notificationService.getAllowedNotifications();
}

ngOnInit(): void {
}

}

+ 8
- 14
src/app/layout/notifications/notifications.component.html 파일 보기

@@ -1,14 +1,8 @@
<ul>
<li *ngFor="let notification of allowedNotifications">
<a *ngIf="notification.redirectionUrl" [routerLink]="notification.redirectionUrl">
<h5> {{ notification.text }} </h5>
<p *ngIf="notification.description"> {{ notification.description }} </p>
<span class="time-stamp"> {{ notification.timeStamp }} </span>
</a>
<ng-container *ngIf="!notification.redirectionUrl">
<h5> {{ notification.text }} </h5>
<p *ngIf="notification.description"> {{ notification.description }} </p>
<span class="time-stamp"> {{ notification.timeStamp }} </span>
</ng-container>
</li>
</ul>
<div class="notifications-window" *ngIf="isShowingNotifications">
<div class="backdrop" (click)="hideNotifications()"></div>
<header>
<h4> Notifications </h4>
</header>

<app-notifications-list></app-notifications-list>
</div>

+ 44
- 34
src/app/layout/notifications/notifications.component.scss 파일 보기

@@ -1,44 +1,54 @@
ul {
list-style: none;
height: 100%;
overflow: auto;
min-height: 10rem;
max-height: 40rem;
$header-height: 10rem;

li {
display: block;
.notifications-window {
position: fixed;
top: calc(#{$header-height} - 1rem);
background-color: white;
width: 40rem;
box-shadow: 0px 0px 15px -3px var(--dark-grey);
z-index: 2;
right: 26rem;
border-radius: 1.5rem;
overflow: hidden;


header {
height: 5rem;
padding: 0 2rem;
display: flex;
align-items: center;
justify-content: flex-start;
width: calc(100% - 2rem);
padding: 1.5rem;
cursor: pointer;
position: relative;
margin: 1rem auto;
border: 1px solid var(--border-grey);
border-radius: 1rem;
line-height: 1.5;
margin: 1rem auto 0;
position: relative;
overflow: hidden;

&:hover {
box-shadow: 0px 0px 10px -4px var(--dark-grey);
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: var(--border-grey);
opacity: 0.7;
}

h5 {
font-size: 1.4rem;
margin: 0 0 0.5rem;
h4{
position: relative;
font-size: 1.7rem;
color: var(--primary);
font-weight: 500;
color: var(--highlight);
filter: brightness(80%);
}

p {
color: var(--dark-grey);
font-size: 1.3rem;
}
}

.time-stamp {
display: block;
margin: 1rem 0 0;
font-size: 3rem;
font-size: 1.3rem;
color: var(--dark-grey);
}
.backdrop {
position: fixed;
left: 0;
top: 0;
width: 100vw;
height: 100vw;
z-index: 0;
}
}

+ 17
- 5
src/app/layout/notifications/notifications.component.ts 파일 보기

@@ -1,4 +1,5 @@
import { Component, OnInit } from '@angular/core';
import { Component, OnDestroy, OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { Notification, NotificationService } from 'src/app/services/notification.service';

@Component({
@@ -6,14 +7,25 @@ import { Notification, NotificationService } from 'src/app/services/notification
templateUrl: './notifications.component.html',
styleUrls: ['./notifications.component.scss']
})
export class NotificationsComponent implements OnInit {
allowedNotifications: Array<Notification> = [];
export class NotificationsComponent implements OnInit, OnDestroy {
isShowingNotifications = false;
isShowingNotificationsSubscription: Subscription;

constructor(notificationService: NotificationService) {
this.allowedNotifications = notificationService.getAllowedNotifications();
constructor(private notificationService: NotificationService) {
this.isShowingNotificationsSubscription = this.notificationService.getIsShowingNotificationsObservable().subscribe(isShowingNotifications => this.isShowingNotifications = isShowingNotifications);
}

hideNotifications() {
this.notificationService.setIsShowingNotifications(false);
}

ngOnInit(): void {
}

ngOnDestroy(): void {
if (this.isShowingNotificationsSubscription) {
this.isShowingNotificationsSubscription.unsubscribe();
}
}

}

+ 2
- 47
src/app/layout/tabs/tabs.component.html 파일 보기

@@ -1,50 +1,5 @@
<section class="navbar">
<figure class="logo">
<img src="assets/icons/logo.svg" alt="logo image">
</figure>
<nav>
<a *ngIf="loginName === 'Officer'" [routerLink]="['/tabs/dashboard']"> Dashboard </a>
<a [routerLink]="['/tabs/e-services']"> E-Services </a>
</nav>

<div class="search-input">
<input type="text" placeholder="Search for Business Entities">
<img src="assets/icons/search.svg" alt="search icon">
</div>

<button class="notification-button" (click)="showNotifications = true"
[ngClass]="{'active' : showNotifications}">
<img src="assets/icons/bell.svg" alt="bell icon">

<span class="badge"> {{ notificationsCount }} </span>
</button>

<section class="profile-actions" (mouseover)="showLogout=true" (mouseout)="showLogout=false">
<ng-container *ngIf="!showLogout">
<img src="assets/icons/user.svg" alt="user icon">
<span> Hi, {{ loginName }} </span>
<img src="assets/icons/chevron-down.svg" alt="chevron down image">
</ng-container>
<ng-container *ngIf="showLogout">
<a [routerLink]="['/login']">
<img src="assets/icons/logout.svg" class="logout-icon" alt="logout icon">
<span> Log me out </span>
<img src="assets/icons/chevron-down.svg" alt="chevron down image">
</a>
</ng-container>
</section>

</section>

<div class="notifications-window" *ngIf="showNotifications">
<div class="backdrop" (click)="showNotifications = false"></div>
<header>
<h4> Notifications </h4>
</header>

<app-notifications></app-notifications>
</div>
<app-navbar></app-navbar>
<app-notifications></app-notifications>

<div class="page" [ngClass]="{'blur' : showNotifications}">
<router-outlet></router-outlet>

+ 0
- 199
src/app/layout/tabs/tabs.component.scss 파일 보기

@@ -1,204 +1,5 @@
$header-height: 10rem;

.navbar {
display: flex;
width: 100%;
height: $header-height;
align-items: center;
justify-content: flex-start;

.logo {
width: 12rem;
margin: 0 2rem;

img {
width: 100%;
height: 100%;
}
}

nav {
margin: 0 2rem;
@media print {
display: none;
}

a {
font-size: 1.6rem;
letter-spacing: 0.5px;
color: var(--dark-grey);
margin-right: 2rem;

&.active {
text-decoration: underline;
color: var(--highlight);
}

&:hover {
text-decoration: underline;
}
}
}

.search-input {
position: relative;
margin-left: auto;

@media print {
display: none;
}

input {
display: block;
height: 5rem;
border: 1px solid var(--border-grey);
border-radius: 4rem;
padding: 0 2.5rem;
width: 35rem;
font-size: 1.5rem;
font-weight: 300;

&::placeholder {
color: var(--dark-grey);
opacity: 0.6;
}
}

img {
position: absolute;
width: 1.8rem;
top: 1.5rem;
right: 1.8rem;
}
}


.notification-button {
width: 5rem;
height: 5rem;
border-radius: 50%;
border: none;
position: relative;
background-color: white;
margin-left: 2rem;

@media print {
display: none;
}

&.active {
box-shadow: 0px 0px 10px -3px var(--primary);
img {
filter: brightness(500%);
}

.badge {
transform: scale(0);
}

&::before {
background-color: var(--highlight);
opacity: 1;
filter: none;
}
}

&:hover {
box-shadow: 0px 0px 10px -3px var(--primary);
}

.badge {
background-color: var(--highlight);
color: white;
font-size: 1.1rem;
border-radius: 7px;
position: absolute;
right: 0;
top: -0.5rem;
width: 2rem;
height: 2rem;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
transition: transform 0.1s;
}

&::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: var(--secondary);
opacity: 0.3;
filter: brightness(110%);
border-radius: 50%;
}

& > * {
position: relative;
}

img {
width: 40%;
height: 40%;
}
}

.profile-actions {
margin: 0 2rem;
display: flex;
align-items: center;
justify-content: center;
height: 5rem;
border-radius: 4rem;
padding: 0 2rem;
position: relative;
overflow: hidden;
width: 20rem;
cursor: pointer;

@media print {
display: none;
}
&::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: var(--secondary);
opacity: 0.3;
filter: brightness(110%);
}

& > * {
position: relative;
}

img {
width: 1.5rem;

&.logout-icon {
transform: scale(1.5);
}
}

span {
margin: 0 auto;
color: var(--primary);
font-size: 1.4rem;
}
}
}

.notifications-window {
position: fixed;
top: calc(#{$header-height} - 1rem);


+ 157
- 76
src/app/pages/dashboard/dashboard.component.html 파일 보기

@@ -4,83 +4,164 @@
</h2>
</header>

<header class="page-heading">
<h1> Non compliance - COM/LLPs </h1>
<p> A search module to filter down non compliant entities </p>
</header>
<div class="content-holder">
<section class="sidebar">
<div class="filter-card-holder">
<app-filter-view-card
text="1 outstanding AR and no late filings for the past 5 years"
[count]="10"
(click)="updateSearchOptions('Green', 'All')"
></app-filter-view-card>
</div>
<div class="filter-card-holder">
<app-filter-view-card
text="1st Enforcement Letter generated"
[count]="5"
(click)="updateSearchOptions('Green', '1st Enforcement Letter')"
></app-filter-view-card>
</div>
<div class="filter-card-holder">
<app-filter-view-card
text="2nd Enforcement Letter generated"
[count]="5"
(click)="updateSearchOptions('Green', '2nd Enforcement Letter')"
></app-filter-view-card>
</div>

<div class="form-holder">
<div class="two-column-holder">
<app-select-input
label="Entity type"
[options]="entityTypeOptions"
></app-select-input>
<app-select-input
label="Category"
[value]="selectedCategory"
[options]="categoryOptions"
<div class="filter-card-holder">
<app-filter-view-card
text="2 outstanding AR and at least 1 late filings"
[count]="3"
type="AMBER"
(click)="updateSearchOptions('Amber', 'All')"
></app-filter-view-card>
</div>
<div class="filter-card-holder">
<app-filter-view-card
text="1st Enforcement Letter generated"
[count]="2"
type="AMBER"
(click)="updateSearchOptions('Amber', '1st Enforcement Letter')"
></app-filter-view-card>
</div>
<div class="filter-card-holder">
<app-filter-view-card
text="2nd Enforcement Letter generated"
[count]="2"
type="AMBER"
(click)="updateSearchOptions('Amber', '2nd Enforcement Letter')"
></app-filter-view-card>
</div>

(onChange)="updateSelectedCategory($event)"
></app-select-input>
<app-generic-input
type="text"
label="UEN"
></app-generic-input>
<app-select-input
label="Enforcment status"
[options]="enforcementStatusOptions"
></app-select-input>
<app-date-input
label="AR/AD From date"
></app-date-input>
<app-date-input
label="AR/AD To date"
></app-date-input>
</div>
<div class="form-action-buttons">
<button class="common-button" (click)="submitSearch()">
Submit
</button>
<button class="common-button neutral" (click)="resetSearch()">
Reset
</button>
</div>
<div class="filter-card-holder">
<app-filter-view-card
text="2 or more outstanding AR"
[count]="4"
type="RED"
(click)="updateSearchOptions('Red', 'All')"
></app-filter-view-card>
</div>
<div class="filter-card-holder">
<app-filter-view-card
text="1st Enforcement Letter generated"
[count]="1"
type="RED"
(click)="updateSearchOptions('Red', '1st Enforcement Letter')"
></app-filter-view-card>
</div>
<div class="filter-card-holder">
<app-filter-view-card
text="2nd Enforcement Letter generated"
[count]="2"
type="RED"
(click)="updateSearchOptions('Red', '2nd Enforcement Letter')"
></app-filter-view-card>
</div>
</section>
<main>

<section class="table" *ngIf="isShowingResults">
<header>
<div class="cell"> S/N </div>
<div class="cell"> UEN </div>
<div class="cell"> Compliance ID </div>
<div class="cell"> Entity name </div>
<div class="cell"> Last FYE </div>
<div class="cell"> Enforcment status </div>
<div class="cell"> Actions </div>
<header class="page-heading">
<h1> Non compliance - COM/LLPs </h1>
<p> A search module to filter down non compliant entities </p>
</header>
<ul>
<li>
<div class="cell"> 1 </div>
<div class="cell"> 001 </div>
<div class="cell"> 20190027N </div>
<div class="cell"> ABC PTE.LTD </div>
<div class="cell"> 01/01/2021 </div>
<div class="cell"> Closed </div>
<div class="cell">
<button [routerLink]="['/tabs/investigate-business-entities-and-individuals']"> Initiate Enforcment </button>
</div>
</li>
<li>
<div class="cell"> 2 </div>
<div class="cell"> 002 </div>
<div class="cell"> 20190027M </div>
<div class="cell"> DEF PTE.LTD </div>
<div class="cell"> 01/01/2020 </div>
<div class="cell"> 1st EF letter </div>
<div class="cell">
<button> Supress </button>
</div>
</li>
</ul>
</section>
</div>
<div class="form-holder">
<div class="two-column-holder">
<app-select-input
label="Entity type"
[options]="entityTypeOptions"
></app-select-input>
<app-select-input
label="Category"
[value]="selectedCategory"
[options]="categoryOptions"
(onChange)="updateSelectedCategory($event)"
></app-select-input>
<app-generic-input
type="text"
label="UEN"
></app-generic-input>
<app-select-input
label="Enforcment status"
[value]="selectedEnforcementStatus"
[options]="enforcementStatusOptions"
(onChange)="updateSelectedEnforcementStatus($event)"
></app-select-input>
<app-date-input
label="AR/AD From date"
></app-date-input>
<app-date-input
label="AR/AD To date"
></app-date-input>
</div>
<div class="form-action-buttons">
<button class="common-button" (click)="submitSearch()">
Submit
</button>
<button class="common-button neutral" (click)="resetSearch()">
Reset
</button>
</div>
<section class="table" *ngIf="isShowingResults">
<header>
<div class="cell"> S/N </div>
<div class="cell"> UEN </div>
<div class="cell"> Compliance ID </div>
<div class="cell"> Entity name </div>
<div class="cell"> Last FYE </div>
<div class="cell"> Enforcment status </div>
<div class="cell"> Actions </div>
</header>
<ul>
<li>
<div class="cell"> 1 </div>
<div class="cell"> 001 </div>
<div class="cell"> 20190027N </div>
<div class="cell"> ABC PTE.LTD </div>
<div class="cell"> 01/01/2021 </div>
<div class="cell"> Closed </div>
<div class="cell">
<button [routerLink]="['/tabs/investigate-business-entities-and-individuals']"> Initiate Enforcment </button>
</div>
</li>
<li>
<div class="cell"> 2 </div>
<div class="cell"> 002 </div>
<div class="cell"> 20190027M </div>
<div class="cell"> DEF PTE.LTD </div>
<div class="cell"> 01/01/2020 </div>
<div class="cell"> 1st EF letter </div>
<div class="cell">
<button> Supress </button>
</div>
</li>
</ul>
</section>
</div>
</main>
</div>

+ 23
- 1
src/app/pages/dashboard/dashboard.component.scss 파일 보기

@@ -1,3 +1,26 @@
.content-holder {
position: relative;
display: flex;
width: calc(80% - 2rem);
margin: 0 auto;

.sidebar {
position: sticky;
top: 30px;
flex-basis: 250px;
margin-right: 30px;
height: 80vh;
}

main {
flex-grow: 1;
}
}

.filter-card-holder {
margin-bottom: 5px;
}

.page-heading {
text-align: center;
line-height: 1.5;
@@ -21,7 +44,6 @@
}

.form-holder {
width: calc(70% - 2rem);
padding: 4rem;
margin: 0 auto;
}


+ 15
- 0
src/app/pages/dashboard/dashboard.component.ts 파일 보기

@@ -15,18 +15,21 @@ export class DashboardComponent implements OnInit {
];

categoryOptions = [
'All',
'Green',
'Amber',
'Red',
];

enforcementStatusOptions = [
'All',
'1st Enforcement Letter',
'2nd Enforcement Letter',
'Suppressed',
];

selectedCategory = '';
selectedEnforcementStatus = '';

constructor() { }

@@ -38,6 +41,8 @@ export class DashboardComponent implements OnInit {
}

resetSearch() {
this.selectedCategory = '';
this.selectedEnforcementStatus = '';
this.isShowingResults = false;
}

@@ -45,4 +50,14 @@ export class DashboardComponent implements OnInit {
this.selectedCategory = selectedCategory;
}

updateSelectedEnforcementStatus(selectedEnforcementStatus: string) {
this.selectedEnforcementStatus = selectedEnforcementStatus;
}

updateSearchOptions(selectedCategory: string, selectedEnforcementStatus: string) {
this.selectedCategory = selectedCategory;
this.selectedEnforcementStatus = selectedEnforcementStatus;
this.isShowingResults = true;
}

}

+ 6
- 0
src/app/pages/dashboard/filter-view-card/filter-view-card.component.html 파일 보기

@@ -0,0 +1,6 @@
<section class="card" [ngClass]="{'green': type === 'GREEN', 'amber': type === 'AMBER', 'red': type === 'RED'}">
<div class="content">
<div class="text">{{ text }}</div>
<div class="count">{{ count }}</div>
</div>
</section>

+ 52
- 0
src/app/pages/dashboard/filter-view-card/filter-view-card.component.scss 파일 보기

@@ -0,0 +1,52 @@
$red: hsl(0, 100%, 73%);
$amber: hsl(55, 60%, 73%);
$green: hsl(120, 100%, 73%);

.card {
padding: 15px;
background-color: #e7e7e7;

&.green {
background-color: lighten($green, 22%);

.content {
border-color: $green;
}
}

&.amber {
background-color: lighten($amber, 22%);

.content {
border-color: $amber;
}
}

&.red {
background-color: lighten($red, 22%);

.content {
border-color: $red;
}
}
}

.content {
display: flex;
border-left: 3px solid black;
padding: 2px 0 2px 10px;
font-weight: 300;
line-height: 1.4;
align-items: center;

.text {
font-size: 1.3rem;
flex-grow: 1;
margin-right: 20px;
}

.count {
font-size: 1.4rem;
font-weight: bold;
}
}

+ 25
- 0
src/app/pages/dashboard/filter-view-card/filter-view-card.component.spec.ts 파일 보기

@@ -0,0 +1,25 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';

import { FilterViewCardComponent } from './filter-view-card.component';

describe('FilterViewCardComponent', () => {
let component: FilterViewCardComponent;
let fixture: ComponentFixture<FilterViewCardComponent>;

beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [ FilterViewCardComponent ]
})
.compileComponents();
});

beforeEach(() => {
fixture = TestBed.createComponent(FilterViewCardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});

it('should create', () => {
expect(component).toBeTruthy();
});
});

+ 18
- 0
src/app/pages/dashboard/filter-view-card/filter-view-card.component.ts 파일 보기

@@ -0,0 +1,18 @@
import { Component, Input, OnInit } from '@angular/core';

@Component({
selector: 'app-filter-view-card',
templateUrl: './filter-view-card.component.html',
styleUrls: ['./filter-view-card.component.scss']
})
export class FilterViewCardComponent implements OnInit {
@Input() type: 'GREEN'|'AMBER'|'RED' = 'GREEN';
@Input() text = '';
@Input() count = 0;

constructor() { }

ngOnInit(): void {
}

}

+ 0
- 319
src/app/pages/investigate-business-entities-and-individuals/investigate-business-entities-and-individuals.component.scss 파일 보기

@@ -7,323 +7,4 @@
margin-right: 2rem;
}
}
}

.form {
display: grid;
grid-template-columns: 1fr 1fr;
width: calc(70% - 2rem);
padding: 4rem;
margin: 0 auto;

.input-holder {
width: calc(100% - 2rem);
}
}

.acknowledgement {
width: 100%;
margin: 0 auto;
h2 {
font-size: 2rem;
color: var(--dark-grey);
filter: brightness(80%);
font-weight: 500;
margin: 2rem 2rem 3rem;
}

.container {
width: calc(70% - 2rem);
margin: 2rem auto;

@media print {
width: calc(100% - 2rem);
}
}

.bill-container {
width: 70%;
box-shadow: 0 0 10px var(--shadow-grey);
border-radius: 1rem;
overflow: hidden;
margin: 0 auto 5rem;

@media print {
width: 100%;
}
}

.check-icon {
display: block;
width: 58px;
height: auto;
margin: 3rem auto 2rem;
}

h3 {
font-size: 3rem;
font-weight: normal;
text-align: center;
color: var(--success);
}

.bill-breakup {
margin-top: 3rem;
display: grid;
grid-template-columns: 1fr 1fr;
list-style: none;

li {
margin: 1.8rem;
width: 100%;
}

label {
display: block;
color: var(--dark-grey);
font-size: 1.6rem;
filter: brightness(80%);
}

.value {
display: block;
color: var(--dark-grey);
opacity: 0.8;
font-size: 1.6rem;
letter-spacing: 0.5px;
line-height: 1.6;
margin-top: 1rem;

&.active {
color: var(--highlight);
font-weight: normal;
}
}
}

.form-action-buttons {
margin: 2rem 1.5rem;

@media print {
display: none;
}
}

.total-amount {
text-align: center;
overflow: hidden;
padding: 1rem;
position: relative;

&::before {
content: '';
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: var(--footer-grey);
opacity: 0.4;
}

& > * {
position: relative;
}

label {
font-size: 2rem;
color: var(--dark-grey);
}

span {
margin-left: 1rem;
font-size: 2.4rem;
color: var(--highlight);
}
}

.message-board {
padding: 0 2rem;
list-style: none;

@media print {
display: none;
}
h5 {
font-size: 1.6rem;
color: var(--dark-grey);
filter: brightness(80%);
font-weight: 500;
}

li {
margin: 1.5rem 0;
line-height: 1.7;
letter-spacing: 0.5px;
font-size: 1.4rem;
color: var(--dark-grey);
opacity: 0.8;

a {
color: var(--highlight);
font-weight: 500;
}
}
}
}

.receipt {
width: calc(100% - 4rem);
margin: 0 auto;
h2 {
font-size: 2rem;
color: var(--dark-grey);
filter: brightness(80%);
font-weight: 500;
margin: 2rem 0;
}

.bill-breakup {
display: grid;
grid-template-columns: 1fr 1fr;
list-style: none;

li {
margin-bottom: 1rem;
width: 100%;
display: flex;
align-items: center;
justify-content: flex-start;
}

label {
display: block;
color: var(--dark-grey);
font-size: 1.4rem;
}

.value {
display: block;
color: var(--dark-grey);
opacity: 0.8;
font-size: 1.6rem;
letter-spacing: 0.5px;
line-height: 1.6;
margin-left: 1rem;
}
}

.table {
margin: 2rem auto;
width: 100%;
border-radius: 1rem;
overflow: auto;
border: 2px solid var(--border-grey);
min-height: 10rem;
header {
background: linear-gradient(90deg, var(--secondary) 0%, var(--primary));
color: white;
font-size: 1.6rem;
display: flex;
align-items: center;
justify-content: flex-start;
height: 5.5rem;
font-weight: 300;
letter-spacing: 0.5px;
}
.cell {
width: calc(100% / 6);

@media print {
word-break: break-all;
}

&:nth-child(1) {
text-align: center;
width: 10rem;
}

&:nth-child(2) {
width: calc(20% + (10% - 10rem));
}

&:nth-child(3) {
width: 20%;
}

&:nth-child(4) {
width: 20%;
}

&:nth-child(5) {
width: 10%;
}

&:nth-child(3) {
width: 20%;
}
}
ul {
list-style: none;
}
li {
display: flex;
align-items: flex-start;
justify-content: flex-start;
padding: 2rem 0;
&:nth-child(even) {
background-color: var(--border-grey);
}
.cell {
font-size: 1.4rem;
color: var(--dark-grey);
line-height: 1.7;
}
}
}

.message-board {
width: 100%;
margin: 4rem 0;
list-style: none;
h5 {
font-size: 1.6rem;
color: var(--dark-grey);
filter: brightness(80%);
font-weight: 500;
margin-bottom: 1rem;
}

li {
margin: 0.5rem 0;
line-height: 1.7;
letter-spacing: 0.5px;
font-size: 1.4rem;
color: var(--dark-grey);
opacity: 0.8;

a {
color: var(--highlight);
font-weight: 500;
}
}
}

.form-action-buttons {
text-align: left;

@media print {
display: none;
}
}
}

+ 12
- 1
src/app/services/notification.service.ts 파일 보기

@@ -1,4 +1,5 @@
import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs';
import { LoginService, Role } from './login.service';

export interface Notification {
@@ -14,6 +15,8 @@ export interface Notification {
})
export class NotificationService {

private isShowingNotificationsSubject = new BehaviorSubject(false);

private notifications: Array<Notification> = [{
text: 'New name application has been submitted',
description: 'A new Applicant in the company name Kimao has applied for an appeal',
@@ -64,7 +67,7 @@ export class NotificationService {
timeStamp: '1 hour ago',
redirectionUrl: '/tabs/investigate-business-entities-and-individuals',
}];
private allowedNotifications: Array<Notification> = [];

constructor(loginService: LoginService) {
@@ -75,4 +78,12 @@ export class NotificationService {
getAllowedNotifications() {
return this.allowedNotifications;
}

getIsShowingNotificationsObservable() {
return this.isShowingNotificationsSubject.asObservable();
}

setIsShowingNotifications(isShowingNotifications: boolean) {
this.isShowingNotificationsSubject.next(isShowingNotifications);
}
}

불러오는 중...
취소
저장