From 5895792631533da440ac2b1b722bf1437fba869d Mon Sep 17 00:00:00 2001
From: Lucas Li <35748253+yzlucas@users.noreply.github.com>
Date: Mon, 6 Jan 2025 09:38:03 -0800
Subject: [PATCH] Api hookup (#395) (#396)
---
.../main/angular/src/app/app.component.html | 8 -
.../main/angular/src/app/app.component.scss | 27 --
.../angular/src/app/app.component.spec.ts | 113 ++++-----
.../src/main/angular/src/app/app.component.ts | 9 -
.../create-new-project-dialog.component.html | 10 +-
...reate-new-project-dialog.component.spec.ts | 237 +++++++++++-------
.../create-new-project-dialog.component.ts | 146 ++++++++---
.../projects-list.component.html | 31 +--
.../projects-list.component.scss | 28 +++
.../projects-list.component.spec.ts | 231 ++++++++---------
.../projects-list/projects-list.component.ts | 199 +++++++--------
.../src/app/components/list/list.component.ts | 1 -
.../src/app/components/map/map.component.scss | 3 +-
.../app/components/map/map.component.spec.ts | 112 ++++++---
.../src/app/components/map/map.component.ts | 2 +-
.../main/angular/src/app/components/models.ts | 36 +++
.../resizable-panel.component.spec.ts | 124 ++++++---
.../src/app/services/code-table-services.ts | 46 ++++
.../src/app/services/project-services.ts | 50 ++++
.../angular/src/assets/data/appConfig.json | 6 +-
.../src/assets/data/appConfig.local.json | 8 +-
.../src/assets/data/checktoken-user.json | 118 +++++++++
22 files changed, 972 insertions(+), 573 deletions(-)
create mode 100644 client/wfprev-war/src/main/angular/src/app/components/models.ts
create mode 100644 client/wfprev-war/src/main/angular/src/app/services/code-table-services.ts
create mode 100644 client/wfprev-war/src/main/angular/src/app/services/project-services.ts
create mode 100644 client/wfprev-war/src/main/angular/src/assets/data/checktoken-user.json
diff --git a/client/wfprev-war/src/main/angular/src/app/app.component.html b/client/wfprev-war/src/main/angular/src/app/app.component.html
index 70451db4f..80c99ab3c 100644
--- a/client/wfprev-war/src/main/angular/src/app/app.component.html
+++ b/client/wfprev-war/src/main/angular/src/app/app.component.html
@@ -1,13 +1,5 @@
-
@@ -38,28 +38,28 @@
diff --git a/client/wfprev-war/src/main/angular/src/app/components/create-new-project-dialog/create-new-project-dialog.component.spec.ts b/client/wfprev-war/src/main/angular/src/app/components/create-new-project-dialog/create-new-project-dialog.component.spec.ts
index ef50a94e4..347718275 100644
--- a/client/wfprev-war/src/main/angular/src/app/components/create-new-project-dialog/create-new-project-dialog.component.spec.ts
+++ b/client/wfprev-war/src/main/angular/src/app/components/create-new-project-dialog/create-new-project-dialog.component.spec.ts
@@ -1,28 +1,54 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ReactiveFormsModule } from '@angular/forms';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
-import { of } from 'rxjs';
+import { of, throwError } from 'rxjs';
import { CreateNewProjectDialogComponent } from './create-new-project-dialog.component';
import { ConfirmationDialogComponent } from '../confirmation-dialog/confirmation-dialog.component';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { Messages } from 'src/app/utils/messages';
-
+import { ProjectService } from 'src/app/services/project-services';
+import { CodeTableServices } from 'src/app/services/code-table-services';
+import { MatSnackBar } from '@angular/material/snack-bar';
describe('CreateNewProjectDialogComponent', () => {
let component: CreateNewProjectDialogComponent;
let fixture: ComponentFixture;
let mockDialog: jasmine.SpyObj;
let mockDialogRef: jasmine.SpyObj>;
+ let mockProjectService: jasmine.SpyObj;
+ let mockCodeTableService: jasmine.SpyObj;
+ let mockSnackbarService: jasmine.SpyObj;
beforeEach(async () => {
mockDialog = jasmine.createSpyObj('MatDialog', ['open']);
mockDialogRef = jasmine.createSpyObj('MatDialogRef', ['close']);
+ mockProjectService = jasmine.createSpyObj('ProjectService', ['createProject']);
+ mockCodeTableService = jasmine.createSpyObj('CodeTableServices', ['fetchCodeTable']);
+ mockSnackbarService = jasmine.createSpyObj('MatSnackBar', ['open']);
+
+ mockCodeTableService.fetchCodeTable.and.callFake((name: string) => {
+ switch (name) {
+ case 'programAreaCodes':
+ return of({ _embedded: { programArea: [{ name: 'Program Area 1' }] } });
+ case 'forestRegionCodes':
+ return of({ _embedded: { forestRegion: [{ name: 'Forest Region 1' }] } });
+ case 'bcParksRegionCodes':
+ return of({ _embedded: { bcParksRegionCode: [{ name: 'BC Parks Region 1' }] } });
+ case 'bcParksSectionCodes':
+ return of({ _embedded: { bcParksSectionCode: [{ parentOrgUnitId: 1, name: 'BC Section 1' }] } });
+ default:
+ return of({ _embedded: [] });
+ }
+ });
await TestBed.configureTestingModule({
- imports: [ReactiveFormsModule, CreateNewProjectDialogComponent, BrowserAnimationsModule ],
+ imports: [ReactiveFormsModule, CreateNewProjectDialogComponent, BrowserAnimationsModule],
providers: [
{ provide: MatDialog, useValue: mockDialog },
{ provide: MatDialogRef, useValue: mockDialogRef },
+ { provide: ProjectService, useValue: mockProjectService },
+ { provide: CodeTableServices, useValue: mockCodeTableService },
+ { provide: MatSnackBar, useValue: mockSnackbarService },
],
}).compileComponents();
@@ -58,139 +84,158 @@ describe('CreateNewProjectDialogComponent', () => {
});
it('should enable bcParksSection when a region is selected', () => {
- component.projectForm.get('bcParksRegion')?.setValue('Northern');
+ component.projectForm.get('bcParksRegion')?.setValue(1);
fixture.detectChanges();
- const bcParksSectionControl = component.projectForm.get('bcParksSection');
- expect(bcParksSectionControl?.enabled).toBeTrue();
- expect(component.bcParksSections).toEqual(['Omineca', 'Peace', 'Skeena']);
+ expect(component.projectForm.get('bcParksSection')?.enabled).toBeTrue();
});
it('should reset and disable bcParksSection when no region is selected', () => {
- component.projectForm.get('bcParksRegion')?.setValue(null);
+ component.projectForm.get('bcParksRegion')?.setValue(null); // Simulate no region selected
fixture.detectChanges();
- const bcParksSectionControl = component.projectForm.get('bcParksSection');
- expect(bcParksSectionControl?.disabled).toBeTrue();
+ expect(component.projectForm.get('bcParksSection')?.disabled).toBeTrue();
expect(component.bcParksSections).toEqual([]);
});
+ it('should fetch and set code tables on initialization', () => {
+ // Mocking the responses for fetchCodeTable
+ mockCodeTableService.fetchCodeTable.and.callFake((name: string) => {
+ switch (name) {
+ case 'programAreaCodes':
+ return of({ _embedded: { programArea: [{ name: 'Program Area 1' }] } });
+ case 'forestRegionCodes':
+ return of({ _embedded: { forestRegionCode: [{ name: 'Forest Region 1' }] } });
+ default:
+ return of({ _embedded: [] });
+ }
+ });
+
+ // Trigger the loadCodeTables method
+ component.loadCodeTables();
+ fixture.detectChanges();
+
+ // Verify that the service was called with correct table names
+ expect(mockCodeTableService.fetchCodeTable).toHaveBeenCalledWith('programAreaCodes');
+ expect(mockCodeTableService.fetchCodeTable).toHaveBeenCalledWith('forestRegionCodes');
+
+ // Verify the component's state is updated correctly
+ expect(component.businessAreas).toEqual([{ name: 'Program Area 1' }]);
+ expect(component.forestRegions).toEqual([{ name: 'Forest Region 1' }]);
+ });
+
+
+ it('should display correct error messages', () => {
+ component.projectForm.get('projectName')?.setErrors({ required: true });
+ expect(component.getErrorMessage('projectName')).toBe(Messages.requiredField);
+
+ component.projectForm.get('projectLeadEmail')?.setErrors({ email: true });
+ expect(component.getErrorMessage('projectLeadEmail')).toBe(Messages.invalidEmail);
+ });
+
+ it('should create a new project and close dialog on success', () => {
+ // Mock createProject to simulate a successful API response
+ mockProjectService.createProject.and.returnValue(of({}));
+
+ // Populate the form with valid values
+ component.projectForm.patchValue({
+ projectName: 'New Project', // Required field
+ businessArea: 'Area 1', // Required field
+ forestRegion: 1, // Required field
+ forestDistrict: 2, // Required field
+ bcParksRegion: 3, // Required field
+ bcParksSection: 4, // Required field
+ projectLead: 'John Doe', // Optional field
+ projectLeadEmail: 'john.doe@example.com', // Optional field
+ siteUnitName: 'Unit 1', // Optional field
+ closestCommunity: 'Community 1', // Required field
+ });
+
+ // Call the function to create a project
+ component.onCreate();
+
+ // Assertions
+ expect(mockProjectService.createProject).toHaveBeenCalled(); // Ensure createProject was called
+ expect(mockSnackbarService.open).toHaveBeenCalledWith(
+ Messages.projectCreatedSuccess,
+ 'OK',
+ { duration: 100000, panelClass: 'snackbar-success' }
+ ); // Ensure snackbar was called
+ expect(mockDialogRef.close).toHaveBeenCalledWith({ success: true }); // Ensure the dialog was closed
+ });
+
+ // Future task
+ // it('should handle duplicate project error during creation', () => {
+ // mockProjectService.createProject.and.returnValue(
+ // throwError({ status: 500, error: { message: 'duplicate' } })
+ // );
+
+ // component.onCreate();
+
+ // expect(mockDialog.open).toHaveBeenCalledWith(ConfirmationDialogComponent, {
+ // data: { indicator: 'duplicate-project', projectName: '' },
+ // width: '500px',
+ // });
+ // });
+
it('should open confirmation dialog on cancel', () => {
const mockAfterClosed = of(true);
- mockDialog.open.and.returnValue({
- afterClosed: () => mockAfterClosed,
- } as any);
+ mockDialog.open.and.returnValue({ afterClosed: () => mockAfterClosed } as any);
component.onCancel();
+
expect(mockDialog.open).toHaveBeenCalledWith(ConfirmationDialogComponent, {
data: { indicator: 'confirm-cancel' },
width: '500px',
});
- });
- it('should close the dialog if confirmation dialog returns true', () => {
- const mockAfterClosed = of(true);
- mockDialog.open.and.returnValue({
- afterClosed: () => mockAfterClosed,
- } as any);
-
- component.onCancel();
- expect(mockDialog.open).toHaveBeenCalled();
mockAfterClosed.subscribe(() => {
expect(mockDialogRef.close).toHaveBeenCalled();
});
});
- it('should not close the dialog if confirmation dialog returns false', () => {
+ it('should not close dialog if cancel confirmation returns false', () => {
const mockAfterClosed = of(false);
- mockDialog.open.and.returnValue({
- afterClosed: () => mockAfterClosed,
- } as any);
+ mockDialog.open.and.returnValue({ afterClosed: () => mockAfterClosed } as any);
component.onCancel();
+
expect(mockDialog.open).toHaveBeenCalled();
mockAfterClosed.subscribe(() => {
expect(mockDialogRef.close).not.toHaveBeenCalled();
});
});
- it('should close the dialog on successful form submission', () => {
- component.projectForm.patchValue({
- projectName: 'Test Project',
- latLong: '123.456',
- businessArea: 'Area 1',
- forestRegion: 'Region 1',
- forestDistrict: 'District 1',
- bcParksRegion: 'Northern',
- bcParksSection: 'Omineca',
- projectLead: 'John Doe',
- projectLeadEmail: 'john.doe@example.com',
- siteUnitName: 'Unit 1',
- closestCommunity: 'Community 1',
- });
-
- component.onCreate();
- expect(mockDialogRef.close).toHaveBeenCalledWith(component.projectForm.value);
- });
-
- it('should not close the dialog if the form is invalid', () => {
- component.projectForm.patchValue({
- projectName: '',
- });
-
- component.onCreate();
- expect(mockDialogRef.close).not.toHaveBeenCalled();
- });
- it('should return the correct error message for required fields', () => {
- component.projectForm.get('projectName')?.setErrors({ required: true });
- const errorMessage = component.getErrorMessage('projectName');
- expect(errorMessage).toBe(Messages.requiredField);
- });
+ it('should not create a new project if the form is invalid', () => {
+ component.projectForm.get('projectName')?.setValue(''); // Invalid since it's required
- it('should return the correct error message for maxlength errors', () => {
- component.projectForm.get('projectName')?.setErrors({ maxlength: true });
- const errorMessage = component.getErrorMessage('projectName');
- expect(errorMessage).toBe(Messages.maxLengthExceeded);
- });
+ component.onCreate();
- it('should return the correct error message for email format errors', () => {
- component.projectForm.get('projectLeadEmail')?.setErrors({ email: true });
- const errorMessage = component.getErrorMessage('projectLeadEmail');
- expect(errorMessage).toBe(Messages.invalidEmail);
+ expect(mockProjectService.createProject).not.toHaveBeenCalled();
+ expect(mockSnackbarService.open).not.toHaveBeenCalled();
+ expect(mockDialogRef.close).not.toHaveBeenCalled();
});
+
+ it('should update bcParksSections when a bcParksRegion is selected', () => {
+ // Mock data for allBcParksSections
+ component.allBcParksSections = [
+ { parentOrgUnitId: '1', name: 'Section 1' },
+ { parentOrgUnitId: '2', name: 'Section 2' },
+ ];
- it('should dynamically display error messages in the template', () => {
- const projectNameControl = component.projectForm.get('projectName');
- projectNameControl?.setErrors({ maxlength: true });
- projectNameControl?.markAsTouched();
+ // Set bcParksRegion value to 1
+ component.projectForm.get('bcParksRegion')?.setValue('1');
fixture.detectChanges();
- const errorElement = fixture.nativeElement.querySelector('.form-field .error');
- expect(errorElement.textContent.trim()).toBe(Messages.maxLengthExceeded);
- });
+ // Check if bcParksSections is updated correctly
+ expect(component.bcParksSections).toEqual([{ parentOrgUnitId: '1', name: 'Section 1' }]);
- it('should show a snackbar message on successful form submission', () => {
- const mockSnackbar = spyOn(component['snackbarService'], 'open');
- component.projectForm.patchValue({
- projectName: 'Test Project',
- latLong: '123.456',
- businessArea: 'Area 1',
- forestRegion: 'Region 1',
- forestDistrict: 'District 1',
- bcParksRegion: 'Northern',
- bcParksSection: 'Omineca',
- projectLead: 'John Doe',
- projectLeadEmail: 'john.doe@example.com',
- siteUnitName: 'Unit 1',
- closestCommunity: 'Community 1',
- });
-
- component.onCreate();
+ // Set bcParksRegion value to 2
+ component.projectForm.get('bcParksRegion')?.setValue('2');
+ fixture.detectChanges();
- expect(mockSnackbar).toHaveBeenCalledWith(
- Messages.projectCreatedSuccess,
- 'OK',
- { duration: 100000, panelClass: 'snackbar-success' }
- );
+ // Check if bcParksSections is updated correctly
+ expect(component.bcParksSections).toEqual([{ parentOrgUnitId: '2', name: 'Section 2' }]);
});
+
});
diff --git a/client/wfprev-war/src/main/angular/src/app/components/create-new-project-dialog/create-new-project-dialog.component.ts b/client/wfprev-war/src/main/angular/src/app/components/create-new-project-dialog/create-new-project-dialog.component.ts
index 0e9853a9d..71bbd7894 100644
--- a/client/wfprev-war/src/main/angular/src/app/components/create-new-project-dialog/create-new-project-dialog.component.ts
+++ b/client/wfprev-war/src/main/angular/src/app/components/create-new-project-dialog/create-new-project-dialog.component.ts
@@ -1,10 +1,13 @@
import { CommonModule } from '@angular/common';
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { MatDialog , MatDialogRef } from '@angular/material/dialog';
import { ConfirmationDialogComponent } from 'src/app/components/confirmation-dialog/confirmation-dialog.component';
import { MatSnackBar } from '@angular/material/snack-bar';
import { Messages } from 'src/app/utils/messages';
+import { ProjectService } from 'src/app/services/project-services';
+import { CodeTableServices } from 'src/app/services/code-table-services';
+import { Project } from 'src/app/components/models';
@Component({
selector: 'app-create-new-project-dialog',
standalone: true,
@@ -15,7 +18,8 @@ import { Messages } from 'src/app/utils/messages';
templateUrl: './create-new-project-dialog.component.html',
styleUrls: ['./create-new-project-dialog.component.scss']
})
-export class CreateNewProjectDialogComponent {
+export class CreateNewProjectDialogComponent implements OnInit {
+ [key: string]: any; // Add this line to allow dynamic properties
projectForm: FormGroup;
messages = Messages;
// Regions and sections mapping
@@ -27,17 +31,20 @@ export class CreateNewProjectDialogComponent {
'West Coast': ['Central Coast/North Island', 'Haida Gwaii/South Island']
};
- businessAreas = ['Area 1', 'Area 2', 'Area 3']; // Example data
- forestRegions = ['Region 1', 'Region 2', 'Region 3']; // Example data
- forestDistricts = ['District 1', 'District 2', 'District 3']; // Example data
- bcParksRegions = Object.keys(this.regionToSections);
- bcParksSections: string[] = []; // Dynamically updated based on the selected region
+ businessAreas : any[] = [];
+ forestRegions : any[] = [];
+ forestDistricts : any[] = [];
+ bcParksRegions : any[] = [];
+ bcParksSections: any[] = [];
+ allBcParksSections: any[] = []; // To hold all sections initially
constructor(
private readonly fb: FormBuilder,
private readonly dialog: MatDialog,
private readonly dialogRef: MatDialogRef,
private readonly snackbarService: MatSnackBar,
+ private readonly projectService: ProjectService,
+ private readonly codeTableService: CodeTableServices
) {
this.projectForm = this.fb.group({
@@ -55,21 +62,46 @@ export class CreateNewProjectDialogComponent {
});
// Dynamically enable/disable bcParksSection based on bcParksRegion selection
- this.projectForm.get('bcParksRegion')?.valueChanges.subscribe((region: string | number) => {
- if (region) {
- this.projectForm.get('bcParksSection')?.enable();
- this.bcParksSections = this.regionToSections[region] || [];
- } else {
- this.projectForm.get('bcParksSection')?.reset();
- this.projectForm.get('bcParksSection')?.disable();
- this.bcParksSections = [];
- }
- });
+ this.projectForm.get('bcParksRegion')?.valueChanges.subscribe((regionId: number) => {
+ if (regionId) {
+ this.projectForm.get('bcParksSection')?.enable();
+ this.bcParksSections = this.allBcParksSections.filter(
+ (section) => section.parentOrgUnitId === regionId.toString()
+ );
+ } else {
+ this.projectForm.get('bcParksSection')?.reset();
+ this.projectForm.get('bcParksSection')?.disable();
+ this.bcParksSections = [];
+ }
+ });
+ }
+ ngOnInit(): void {
+ this.loadCodeTables(); // Call the helper method to load code tables
}
+ loadCodeTables(): void {
+ const codeTables = [
+ { name: 'programAreaCodes', property: 'businessAreas', embeddedKey: 'programArea' },
+ { name: 'forestRegionCodes', property: 'forestRegions', embeddedKey: 'forestRegionCode' },
+ { name: 'forestDistrictCodes', property: 'forestDistricts', embeddedKey: 'forestDistrictCode' },
+ { name: 'bcParksRegionCodes', property: 'bcParksRegions', embeddedKey: 'bcParksRegionCode' },
+ { name: 'bcParksSectionCodes', property: 'allBcParksSections', embeddedKey: 'bcParksSectionCode' },
+ ];
+
+ codeTables.forEach((table) => {
+ this.codeTableService.fetchCodeTable(table.name).subscribe({
+ next: (data) => {
+ this[table.property] = data?._embedded?.[table.embeddedKey] || [];
+ },
+ error: (err) => {
+ console.error(`Error fetching ${table.name}`, err);
+ },
+ });
+ });
+ }
getErrorMessage(controlName: string): string | null {
const control = this.projectForm.get(controlName);
- if (!control || !control.errors) return null;
+ if (!control?.errors) return null;
if (control.hasError('required')) {
return this.messages.requiredField;
@@ -86,25 +118,65 @@ export class CreateNewProjectDialogComponent {
onCreate(): void {
if (this.projectForm.valid) {
- console.log(this.projectForm.value);
- //call POST endpoint,
- // if return 500 error with duplicate project name error message,
-
- // this.dialog.open(ConfirmationDialogComponent, {
- // data: {
- // indicator: 'duplicate-project',
- // projectName: '',
- // },
- // width: '500px',
- // });
-
- //OK will return the user to the Modal and allow further editing. just close the Modal for now
- this.snackbarService.open(
- this.messages.projectCreatedSuccess,
- 'OK',
- { duration: 100000, panelClass: 'snackbar-success' },
- )
- this.dialogRef.close(this.projectForm.value);
+ const newProject: Project = {
+ projectName: this.projectForm.get('projectName')?.value ?? '',
+ programAreaGuid: this.projectForm.get('businessArea')?.value ?? '',
+ forestRegionOrgUnitId: Number(this.projectForm.get('forestRegion')?.value) || 0,
+ forestDistrictOrgUnitId: Number(this.projectForm.get('forestDistrict')?.value) || 0,
+ bcParksRegionOrgUnitId: Number(this.projectForm.get('bcParksRegion')?.value) || 0,
+ bcParksSectionOrgUnitId: Number(this.projectForm.get('bcParksSection')?.value) || 0,
+ projectLead: this.projectForm.get('projectLead')?.value ?? '',
+ projectLeadEmailAddress: this.projectForm.get('projectLeadEmail')?.value ?? '',
+ siteUnitName: this.projectForm.get('siteUnitName')?.value ?? '',
+ closestCommunityName: this.projectForm.get('closestCommunity')?.value ?? '',
+ fireCentreOrgUnitId: this.projectForm.get('fireCentre')?.value ?? 0,
+ generalScopeCode: {
+ generalScopeCode: "SL_ACT"
+ },
+ projectTypeCode: {
+ projectTypeCode: "FUEL_MGMT"
+ },
+ projectDescription: this.projectForm.get('projectDescription')?.value ?? '',
+ projectNumber: this.projectForm.get('projectNumber')?.value ?? '',
+ totalFundingRequestAmount:
+ this.projectForm.get('totalFundingRequestAmount')?.value ?? '',
+ totalAllocatedAmount: this.projectForm.get('totalAllocatedAmount')?.value ?? '',
+ totalPlannedProjectSizeHa:
+ this.projectForm.get('totalPlannedProjectSizeHa')?.value ?? '',
+ totalPlannedCostPerHectare:
+ this.projectForm.get('totalPlannedCostPerHectare')?.value ?? '',
+ totalActualAmount: this.projectForm.get('totalActualAmount')?.value ?? 0,
+ isMultiFiscalYearProj: false,
+ };
+
+ this.projectService.createProject(newProject).subscribe({
+ next: (response) => {
+ this.snackbarService.open(
+ this.messages.projectCreatedSuccess,
+ 'OK',
+ { duration: 100000, panelClass: 'snackbar-success' },
+ );
+ this.dialogRef.close({ success: true });
+ },
+ error: (err) =>{
+ if (err.status === 500 && err.error.message.includes('duplicate')) {
+ this.dialog.open(ConfirmationDialogComponent, {
+ data: {
+ indicator: 'duplicate-project',
+ projectName: '',
+ },
+ width: '500px',
+ });
+ }
+ else{
+ this.snackbarService.open(
+ "Create project failed",
+ 'OK',
+ { duration: 5000, panelClass: 'snackbar-error' }
+ );
+ }
+ }
+ })
}
}
diff --git a/client/wfprev-war/src/main/angular/src/app/components/list-panel/projects-list/projects-list.component.html b/client/wfprev-war/src/main/angular/src/app/components/list-panel/projects-list/projects-list.component.html
index 28fb68bdc..fa11d2e33 100644
--- a/client/wfprev-war/src/main/angular/src/app/components/list-panel/projects-list/projects-list.component.html
+++ b/client/wfprev-war/src/main/angular/src/app/components/list-panel/projects-list/projects-list.component.html
@@ -7,23 +7,18 @@
-
-
-
-
-
-
-
-
-
- Off/On
-
+
+
+ {{ projectList?.length }} Results
-
-
-
-
{{ resultCount }} Results
+
@@ -63,15 +58,15 @@