diff --git a/front-end/src/app/app.module.ts b/front-end/src/app/app.module.ts index 1676efe6a9..d16f8a1341 100644 --- a/front-end/src/app/app.module.ts +++ b/front-end/src/app/app.module.ts @@ -55,6 +55,7 @@ import { LoginService } from './shared/services/login.service'; import { SharedModule } from './shared/shared.module'; import { UsersModule } from './users/users.module'; import { SchedulerAction, asyncScheduler } from 'rxjs'; +import { ReportsModule } from './reports/reports.module'; // Save ngrx store to localStorage dynamically function localStorageSyncReducer(reducer: ActionReducer): ActionReducer { @@ -125,6 +126,7 @@ const metaReducers: Array> = [localStorageSyncRedu InputTextareaModule, OverlayPanelModule, SharedModule, + ReportsModule, ], providers: [ CookieService, diff --git a/front-end/src/app/dashboard/dashboard.component.html b/front-end/src/app/dashboard/dashboard.component.html index 7fb67cd834..6867d8e2c2 100644 --- a/front-end/src/app/dashboard/dashboard.component.html +++ b/front-end/src/app/dashboard/dashboard.component.html @@ -1,55 +1,43 @@ -
-
- +
+
+
+
+

In-progress reports

+
+

{{ report.report_code_label }}

+
+
+ +
+
+
+

Cash on-hand

+

+
+ +
-
-
-
- -
-
- -
-
-
-
- - - -
- -
- - - +
+
+
+

Recently submitted reports

+

+
+ +
+ + diff --git a/front-end/src/app/dashboard/dashboard.component.scss b/front-end/src/app/dashboard/dashboard.component.scss new file mode 100644 index 0000000000..3d3ff9fd32 --- /dev/null +++ b/front-end/src/app/dashboard/dashboard.component.scss @@ -0,0 +1,145 @@ +a { + font-family: karla, sans-serif; + font-weight: bold; + font-size: 18px; +} + +.container { + display: flex; + flex-direction: column; + align-items: center; + margin-left: 0; + margin-top: 32px; + padding-left: 0; +} + +.card { + margin-bottom: 20px; +} + +.card-body { + background-color: #e8e8e8; + border-top-left-radius: 4px; + border-top-right-radius: 4px; + padding: 8px 20px 0 20px; + display: flex; + flex-direction: column; +} + +.card-text { + background-color: white; + border-radius: 4px; + align-self: center; + padding: 0 8px; +} + +.card-footer { + display: flex; + justify-content: end; + background-color: #e8e8e8; + border: 0; + border-bottom-left-radius: 4px; + border-bottom-right-radius: 4px; + padding: 0 20px 16px 20px; +} + +#in-progress-card { + height: 404px; +} + +#report-container { + height: 308px; + max-height: 308px; + overflow: auto; + + .card-text { + height: 144px; + min-height: 100px; + max-height: 308px; + + &:not(:last-child) { + margin-bottom: 20px; + } + } +} + +#cash-on-hand-card { + height: 154px; + + .card-text { + height: 86px !important; + } +} + +#recently-submitted-card { + height: 578px; + + .card-text { + height: 482px !important; + } +} + +.card-title { + font-size: 24px; + font-family: 'gandhi-bold', serif; + color: #212121; + margin: 0; + padding-bottom: 8px; +} + +a { + border-bottom: none; + + span { + border-bottom: 2px dotted #112e51; + } + img { + padding-left: 5px; + } +} + +.card, +.card-body, +.card-footer { + width: 512px; +} + +.side { + width: 100%; +} + +@media screen and (min-width: 992px) { + .card, + .card-body, + .card-footer { + width: 704px; + } +} + +@media screen and (min-width: 1200px) { + .side { + width: fit-content; + } + + .container { + flex-direction: row; + } + + #recently-submitted-card { + margin-left: 20px; + } + + .card, + .card-body, + .card-footer { + width: 454px; + } +} + +@media screen and (min-width: 1400px) { + .card, + .card-body, + .card-footer { + width: 558px; + } +} diff --git a/front-end/src/app/dashboard/dashboard.component.spec.ts b/front-end/src/app/dashboard/dashboard.component.spec.ts index 02b234dfc1..6cd27ae33c 100644 --- a/front-end/src/app/dashboard/dashboard.component.spec.ts +++ b/front-end/src/app/dashboard/dashboard.component.spec.ts @@ -1,29 +1,72 @@ -import { HttpClientTestingModule } from '@angular/common/http/testing'; -import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; -import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; -import { RouterTestingModule } from '@angular/router/testing'; -import { PanelModule } from 'primeng/panel'; +import { ComponentFixture, TestBed } from '@angular/core/testing'; import { DashboardComponent } from './dashboard.component'; +import { ReportService } from 'app/shared/services/report.service'; +import { testActiveReport } from 'app/shared/utils/unit-test.utils'; +import { ElementRef, QueryList } from '@angular/core'; +import { Subject } from 'rxjs'; describe('DashboardComponent', () => { let component: DashboardComponent; let fixture: ComponentFixture; + let reportService: ReportService; - beforeEach(waitForAsync(() => { - TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, RouterTestingModule, PanelModule, BrowserAnimationsModule], + beforeEach(async () => { + await TestBed.configureTestingModule({ declarations: [DashboardComponent], - providers: [], + providers: [{ provide: ReportService, useValue: jasmine.createSpyObj('ReportService', ['getAllReports']) }], }).compileComponents(); - })); + + reportService = TestBed.inject(ReportService); + }); beforeEach(() => { fixture = TestBed.createComponent(DashboardComponent); component = fixture.componentInstance; - fixture.detectChanges(); }); it('should create', () => { expect(component).toBeTruthy(); }); + + it('should call getAllReports on ngOnInit', async () => { + const reports = [testActiveReport]; + (reportService.getAllReports as jasmine.Spy).and.returnValue(Promise.resolve(reports)); + await component.ngOnInit(); + expect(reportService.getAllReports).toHaveBeenCalled(); + expect(component.reports).toEqual(reports); + }); + + it('should adjust height on ngAfterViewInit', () => { + spyOn(component, 'adjustHeight'); + const changes = new Subject(); + const elements = { + changes: changes.asObservable(), + }; + component.elements = elements as unknown as QueryList; + component.ngAfterViewInit(); + + changes.next({}); + expect(component.adjustHeight).toHaveBeenCalled(); + }); + + it('should adjust height of elements', () => { + const elements = [createElement()]; + component.adjustHeight(elements); + expect(elements[0].nativeElement.style.height).toEqual('308px'); + + elements.push(createElement()); + component.adjustHeight(elements); + expect(elements[0].nativeElement.style.height).toEqual('144px'); + + elements.push(createElement()); + component.adjustHeight(elements); + expect(elements[0].nativeElement.style.height).toEqual('100px'); + }); }); + +function createElement(): ElementRef { + const p = document.createElement('p'); + p.className = 'card-text'; + p.textContent = 'APRIL 15 QUARTERLY REPORT (Q1)'; + return new ElementRef(p); +} diff --git a/front-end/src/app/dashboard/dashboard.component.ts b/front-end/src/app/dashboard/dashboard.component.ts index 7c07ad89a4..f57d31113d 100644 --- a/front-end/src/app/dashboard/dashboard.component.ts +++ b/front-end/src/app/dashboard/dashboard.component.ts @@ -1,8 +1,60 @@ -import { Component } from '@angular/core'; +import { + AfterViewInit, + ChangeDetectorRef, + Component, + ElementRef, + OnInit, + QueryList, + ViewChildren, +} from '@angular/core'; +import { ReportService } from 'app/shared/services/report.service'; +import { Report } from 'app/shared/models/report.model'; @Component({ selector: 'app-dashboard', templateUrl: './dashboard.component.html', - // styleUrls: ['./dashboard.component.scss'], + styleUrls: ['./dashboard.component.scss'], }) -export class DashboardComponent {} +export class DashboardComponent implements AfterViewInit, OnInit { + reports: Report[] = []; + dialogVisible = false; + @ViewChildren('reportText') elements?: QueryList>; + + constructor( + private reportService: ReportService, + private cdr: ChangeDetectorRef, + ) {} + + async ngOnInit() { + this.reports = (await this.reportService.getAllReports()).filter((r) => r.report_status === 'In progress'); + this.cdr.detectChanges(); + } + + ngAfterViewInit() { + this.elements?.changes.subscribe(() => { + this.adjustHeight(this.elements); + }); + } + + adjustHeight(elements: QueryList> | ElementRef[] | undefined) { + if (!elements) return; + + let height: string; + switch (elements.length) { + case 0: + case 1: + height = '308px'; + break; + case 2: + height = '144px'; + break; + default: + height = '100px'; + break; + } + + elements.forEach((element) => { + element.nativeElement.style.height = height; + }); + } +} diff --git a/front-end/src/app/layout/layout.component.scss b/front-end/src/app/layout/layout.component.scss index eb7f5aeed9..2d42f71463 100644 --- a/front-end/src/app/layout/layout.component.scss +++ b/front-end/src/app/layout/layout.component.scss @@ -3,8 +3,9 @@ } .main-content { - margin: 0; - padding: 0 32px; + margin-left: auto; + margin-right: auto; + width: fit-content; } .header-container { diff --git a/front-end/src/app/reports/report-list/report-list.component.spec.ts b/front-end/src/app/reports/report-list/report-list.component.spec.ts index 3999fc7e50..0929aaa7f3 100644 --- a/front-end/src/app/reports/report-list/report-list.component.spec.ts +++ b/front-end/src/app/reports/report-list/report-list.component.spec.ts @@ -9,8 +9,7 @@ import { ApiService } from 'app/shared/services/api.service'; import { ReportListComponent } from './report-list.component'; import { F3xFormTypes, Form3X } from '../../shared/models/form-3x.model'; import { Report, ReportTypes } from '../../shared/models/report.model'; -import { RouterTestingModule } from '@angular/router/testing'; -import { Router } from '@angular/router'; +import { ActivatedRoute, Router } from '@angular/router'; import { UploadSubmission } from 'app/shared/models/upload-submission.model'; import { TableAction } from 'app/shared/components/table-list-base/table-list-base.component'; import { FormTypeDialogComponent } from '../form-type-dialog/form-type-dialog.component'; @@ -30,14 +29,21 @@ describe('ReportListComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [HttpClientTestingModule, TableModule, ToolbarModule, RouterTestingModule.withRoutes([]), DialogModule], + imports: [HttpClientTestingModule, TableModule, ToolbarModule, DialogModule], declarations: [ReportListComponent, FormTypeDialogComponent, Dialog], providers: [ + ReportService, ConfirmationService, MessageService, ApiService, provideMockStore(testMockStore), { provide: Actions, useValue: actions$ }, + { + provide: ActivatedRoute, + useValue: { + queryParams: of({}), + }, + }, ], }).compileComponents(); }); diff --git a/front-end/src/app/reports/reports.module.ts b/front-end/src/app/reports/reports.module.ts index 33974757bd..3004fbb3db 100644 --- a/front-end/src/app/reports/reports.module.ts +++ b/front-end/src/app/reports/reports.module.ts @@ -81,5 +81,6 @@ import { PasswordModule } from 'primeng/password'; NgOptimizedImage, PasswordModule, ], + exports: [FormTypeDialogComponent], }) export class ReportsModule {} diff --git a/front-end/src/app/shared/utils/unit-test.utils.ts b/front-end/src/app/shared/utils/unit-test.utils.ts index c2701193ab..5d6adf9401 100644 --- a/front-end/src/app/shared/utils/unit-test.utils.ts +++ b/front-end/src/app/shared/utils/unit-test.utils.ts @@ -92,6 +92,7 @@ export const testActiveReport: Form3X = Form3X.fromJSON({ form_type: 'F3XN', report_type: 'F3X', report_code: 'Q1', + report_status: 'In progress', report_code_label: 'APRIL 15 QUARTERLY REPORT (Q1)', upload_submission: UploadSubmission.fromJSON({}), webprint_submission: {