From 407a386bd08f06323c2975a04c5324409e4611e1 Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Wed, 15 Jan 2025 14:12:24 +0100 Subject: [PATCH] style(table): format HTML and CSS for employee table --- src/app/employee/table/table.component.css | 146 ++++++------ src/app/employee/table/table.component.html | 210 +++++++++--------- src/app/employee/table/table.component.ts | 59 +++-- .../qualification/delete/delete.component.ts | 1 - .../qualification/table/table.component.html | 19 +- .../qualification/table/table.component.ts | 130 +++++++---- 6 files changed, 322 insertions(+), 243 deletions(-) diff --git a/src/app/employee/table/table.component.css b/src/app/employee/table/table.component.css index 7563a61..40a34c3 100644 --- a/src/app/employee/table/table.component.css +++ b/src/app/employee/table/table.component.css @@ -1,73 +1,73 @@ -:host ::ng-deep { - .mat-mdc-card { - --mdc-elevated-card-container-color: transparent; - @apply !shadow-none !rounded-xl; - } - - .mat-mdc-button-base { - --mat-mdc-button-persistent-ripple-color: currentColor; - @apply !rounded-lg; - } - - .mat-mdc-progress-spinner { - --mdc-circular-progress-active-indicator-color: #2563eb; - } - - .mdc-data-table__header-cell { - @apply !text-gray-600 !font-semibold !text-sm !py-4 !px-6; - } - - .mat-mdc-table { - @apply !bg-transparent !border-separate !border-spacing-y-2; - - .mat-mdc-row { - @apply !bg-white !rounded-xl !shadow-sm !transition-all !duration-200; - - &:hover { - @apply !bg-gray-50 !shadow-md !transform !scale-[1.01]; - } - - .mat-mdc-cell { - @apply !border-b-0 !py-4 !px-6 first:!rounded-l-xl last:!rounded-r-xl; - } - } - - .mat-mdc-header-row { - @apply !bg-transparent; - - .mat-mdc-header-cell { - @apply !border-b-0; - } - } - } - - .mat-mdc-menu-panel { - @apply !rounded-xl !shadow-lg; - } - - .mat-mdc-menu-item { - @apply !rounded-lg !mx-1 !my-0.5; - } - - .mdc-button { - @apply !font-medium; - - &.mat-primary { - @apply !bg-blue-600 !text-white hover:!bg-blue-700; - } - - &.mat-warn { - @apply !bg-red-600 !text-white hover:!bg-red-700; - } - } - - .mat-mdc-snack-bar-container { - &.error-snackbar { - @apply !rounded-xl; - - .mdc-snackbar__surface { - @apply !bg-red-50 !text-red-900 !border !border-red-100; - } - } - } -} +:host ::ng-deep { + .mat-mdc-card { + --mdc-elevated-card-container-color: transparent; + @apply !shadow-none !rounded-xl; + } + + .mat-mdc-button-base { + --mat-mdc-button-persistent-ripple-color: currentColor; + @apply !rounded-lg; + } + + .mat-mdc-progress-spinner { + --mdc-circular-progress-active-indicator-color: #2563eb; + } + + .mdc-data-table__header-cell { + @apply !text-gray-600 !font-semibold !text-sm !py-4 !px-6; + } + + .mat-mdc-table { + @apply !bg-transparent !border-separate !border-spacing-y-2; + + .mat-mdc-row { + @apply !bg-white !rounded-xl !shadow-sm !transition-all !duration-200; + + &:hover { + @apply !bg-gray-50 !shadow-md !transform !scale-[1.01]; + } + + .mat-mdc-cell { + @apply !border-b-0 !py-4 !px-6 first:!rounded-l-xl last:!rounded-r-xl; + } + } + + .mat-mdc-header-row { + @apply !bg-transparent; + + .mat-mdc-header-cell { + @apply !border-b-0; + } + } + } + + .mat-mdc-menu-panel { + @apply !rounded-xl !shadow-lg; + } + + .mat-mdc-menu-item { + @apply !rounded-lg !mx-1 !my-0.5; + } + + .mdc-button { + @apply !font-medium; + + &.mat-primary { + @apply !bg-blue-600 !text-white hover:!bg-blue-700; + } + + &.mat-warn { + @apply !bg-red-600 !text-white hover:!bg-red-700; + } + } + + .mat-mdc-snack-bar-container { + &.error-snackbar { + @apply !rounded-xl; + + .mdc-snackbar__surface { + @apply !bg-red-50 !text-red-900 !border !border-red-100; + } + } + } +} diff --git a/src/app/employee/table/table.component.html b/src/app/employee/table/table.component.html index a8139b4..d60787b 100644 --- a/src/app/employee/table/table.component.html +++ b/src/app/employee/table/table.component.html @@ -1,109 +1,113 @@
- - - @defer { - @if (employees$ | async; as employees) { -
-
-

Employee Directory

- -
- - @if (employees) { -
- - - - - - - - - - - - - -
Name -
-
- - {{ employee.firstName[0] }}{{ employee.lastName[0] }} - -
- -
-
Actions -
- - -
-
+ @if (employees$ | async; as employees) { +
+
+
+

Employee Directory

+ + search + +
+
- } @else { - - - people -

No employees found

-
-
- } +
+ +
+ + @if (employees) { +
+ + + + + + + + + + + + + +
Name +
+
+ + {{ employee.firstName[0] }}{{ employee.lastName[0] }} + +
+ +
+
Actions +
+ + +
+
+
+ } @else { + + + people +

No employees found

+
+
} - } @placeholder { -
-
-
-
-
-
-
-
-
- @for (i of [1, 2, 3]; track i) { -
- } -
-
-
-
- } @error { -
-
- error -
-

There was an error loading the employees.

-

Please try refreshing the page.

-
-
-
- } @loading { -
- -
+
} -
+ } @placeholder { +
+
+
+
+
+
+
+
+
+ @for (i of [1, 2, 3]; track i) { +
+ } +
+
+
+
+ } @error { +
+
+ error +
+

There was an error loading the employees.

+

Please try refreshing the page.

+
+
+
+ } @loading { +
+ +
+ } + \ No newline at end of file diff --git a/src/app/employee/table/table.component.ts b/src/app/employee/table/table.component.ts index 5927afe..7104b73 100644 --- a/src/app/employee/table/table.component.ts +++ b/src/app/employee/table/table.component.ts @@ -1,6 +1,6 @@ import {Component, inject, OnInit} from '@angular/core'; import {CommonModule} from '@angular/common'; -import {catchError, filter, map, Observable, of, retry} from 'rxjs'; +import {catchError, debounceTime, distinctUntilChanged, filter, map, Observable, of, retry, Subject} from 'rxjs'; import {HttpErrorResponse} from '@angular/common/http'; import {Employee} from '../Employee'; @@ -20,7 +20,8 @@ import EmployeeApiService from "../../services/employee-api.service"; import {CreateComponent} from "../create/create.component"; import {EditComponent} from "../edit/edit.component"; import {DetailsComponent} from "../details/details.component"; -import {MatFormField, MatInput} from "@angular/material/input"; +import {MatFormFieldModule} from "@angular/material/form-field"; +import {MatInputModule} from "@angular/material/input"; import {Qualification} from "../../qualification/Qualification"; @Component({ @@ -38,8 +39,8 @@ import {Qualification} from "../../qualification/Qualification"; MatMenuModule, MatTableModule, MatSortModule, - MatInput, - MatFormField + MatFormFieldModule, + MatInputModule, ], templateUrl: './table.component.html', styleUrl: './table.component.css' @@ -50,11 +51,40 @@ export class TableComponent implements OnInit { private readonly matDialog: MatDialog = inject(MatDialog); private static readonly MAX_RETRIES = 3; + + private allEmployees: Employee[] = []; + private searchSubject = new Subject(); public employees$: Observable = of([]); + public isSearching = false; public readonly displayedColumns: string[] = ['name', 'actions']; public ngOnInit(): void { - this.employees$ = this.fetchEmployees(); + this.loadEmployees(); + this.setupSearch(); + } + + private loadEmployees(): void { + this.fetchEmployees().subscribe(employees => { + this.allEmployees = employees; + this.employees$ = of(employees); + }); + } + + private setupSearch(): void { + this.searchSubject.pipe( + debounceTime(300), + distinctUntilChanged() + ).subscribe(searchTerm => { + this.isSearching = true; + setTimeout(() => { + const filteredEmployees = this.allEmployees.filter(employee => + employee.firstName?.toLowerCase().includes(searchTerm) || + employee.lastName?.toLowerCase().includes(searchTerm) + ); + this.employees$ = of(filteredEmployees); + this.isSearching = false; + }, 150); + }); } private fetchEmployees(): Observable { @@ -111,21 +141,8 @@ export class TableComponent implements OnInit { this.matDialog.open(DetailsComponent, {data: employee}); } - filterEmployees($event: Event) { - const input = $event.target as HTMLInputElement; - const filterValue = input.value.trim().toLowerCase(); - - this.employees$ = this.employees$.pipe( - map((employees: Employee[]) => employees.filter((employee: Employee) => { - const filterValue = input.value.trim().toLowerCase(); - return employee.firstName?.toLowerCase().includes(filterValue) - || employee.lastName?.toLowerCase().includes(filterValue) - || employee.street?.toLowerCase().includes(filterValue) - || employee.phone?.toLowerCase().includes(filterValue) - || employee.postcode?.toString().toLowerCase().includes(filterValue) - || employee.city?.toString().toLowerCase().includes(filterValue) - || employee.skillSet?.some((skill: Qualification) => skill.skill?.toLowerCase().includes(filterValue)); - })) - ); + protected filterEmployees(event: Event): void { + const searchTerm = (event.target as HTMLInputElement).value.toLowerCase(); + this.searchSubject.next(searchTerm); } } diff --git a/src/app/qualification/delete/delete.component.ts b/src/app/qualification/delete/delete.component.ts index 478cf51..eb16805 100644 --- a/src/app/qualification/delete/delete.component.ts +++ b/src/app/qualification/delete/delete.component.ts @@ -22,7 +22,6 @@ import {MatIcon} from "@angular/material/icon"; ReactiveFormsModule, MatDialogActions, MatButton, - MatError, MatIcon ], templateUrl: './delete.component.html', diff --git a/src/app/qualification/table/table.component.html b/src/app/qualification/table/table.component.html index ce358e1..f45ab0a 100644 --- a/src/app/qualification/table/table.component.html +++ b/src/app/qualification/table/table.component.html @@ -3,8 +3,23 @@ @if (qualifications$ | async; as qualifications) {
-

Qualifications

-
+