From c159429ca710dd62efec9f91e5681e85115dd07d Mon Sep 17 00:00:00 2001 From: Jan-Marlon Leibl Date: Wed, 18 Dec 2024 12:14:51 +0100 Subject: [PATCH] update table styling --- src/app/app.component.html | 3 + src/app/app.component.ts | 2 +- .../employee-list/employee-list.component.css | 0 .../employee-list.component.html | 103 ++++++++++- .../employee-list/employee-list.component.ts | 170 ++++++++++++++++-- src/styles.css | 3 + tailwind.config.js | 2 +- 7 files changed, 261 insertions(+), 22 deletions(-) delete mode 100644 src/app/employee-list/employee-list.component.css diff --git a/src/app/app.component.html b/src/app/app.component.html index 6a164d6..e824d2e 100644 --- a/src/app/app.component.html +++ b/src/app/app.component.html @@ -1,2 +1,5 @@ +
+

{{ title }}

+
diff --git a/src/app/app.component.ts b/src/app/app.component.ts index fdeeb79..db3e8f5 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -10,5 +10,5 @@ import {RouterOutlet} from '@angular/router'; styleUrl: './app.component.css' }) export class AppComponent { - title = 'lf10StarterNew'; + title = 'Employee Management System'; } diff --git a/src/app/employee-list/employee-list.component.css b/src/app/employee-list/employee-list.component.css deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/employee-list/employee-list.component.html b/src/app/employee-list/employee-list.component.html index 217b9d0..dafe1fc 100644 --- a/src/app/employee-list/employee-list.component.html +++ b/src/app/employee-list/employee-list.component.html @@ -1,9 +1,96 @@ -

LF10-Starter

-Wenn Sie in der EmployeeListComponent.ts ein gültiges Bearer-Token eintragen, sollten hier die Namen der in der Datenbank gespeicherten Mitarbeiter angezeigt werden! - + \ No newline at end of file diff --git a/src/app/employee-list/employee-list.component.ts b/src/app/employee-list/employee-list.component.ts index 8c167b8..4c2f99a 100644 --- a/src/app/employee-list/employee-list.component.ts +++ b/src/app/employee-list/employee-list.component.ts @@ -1,25 +1,171 @@ import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; -import {Observable, of} from "rxjs"; -import {HttpClient, HttpHeaders} from "@angular/common/http"; -import {Employee} from "../Employee"; +import { Observable, catchError, map, of, retry, switchMap } from 'rxjs'; +import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http'; +import { Employee } from '../Employee'; +import { AuthService } from '../services/auth.service'; + +import { MatCardModule } from '@angular/material/card'; +import { MatButtonModule } from '@angular/material/button'; +import { MatIconModule } from '@angular/material/icon'; +import { MatProgressSpinnerModule } from '@angular/material/progress-spinner'; +import { MatSnackBar, MatSnackBarModule } from '@angular/material/snack-bar'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatTooltipModule } from '@angular/material/tooltip'; +import { MatMenuModule } from '@angular/material/menu'; +import { MatTableModule } from '@angular/material/table'; +import { MatSortModule, Sort } from '@angular/material/sort'; @Component({ selector: 'app-employee-list', - imports: [CommonModule], - templateUrl: './employee-list.component.html', standalone: true, - styleUrl: './employee-list.component.css' + imports: [ + CommonModule, + MatCardModule, + MatButtonModule, + MatIconModule, + MatProgressSpinnerModule, + MatSnackBarModule, + MatDividerModule, + MatTooltipModule, + MatMenuModule, + MatTableModule, + MatSortModule + ], + templateUrl: './employee-list.component.html', + host: { + class: 'block w-full p-6' + }, + styles: [` + :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; + } + } + } + } + `] }) export class EmployeeListComponent { - employees$: Observable; + private static readonly EMPLOYEES_ENDPOINT = '/backend/employees'; + private static readonly MAX_RETRIES = 3; + public employees$: Observable; + public readonly displayedColumns: string[] = ['name', 'actions']; + bearer = 'eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICIzUFQ0dldiNno5MnlQWk1EWnBqT1U0RjFVN0lwNi1ELUlqQWVGczJPbGU0In0.eyJleHAiOjE3MzM5MTQ5MjgsImlhdCI6MTczMzkxMTMyOCwianRpIjoiMjNhYzMwMmUtYmYxNS00OTRmLWJhYTItNjIzODllYWZkMmZhIiwiaXNzIjoiaHR0cHM6Ly9rZXljbG9hay5zenV0LmRldi9hdXRoL3JlYWxtcy9zenV0IiwiYXVkIjoiYWNjb3VudCIsInN1YiI6IjU1NDZjZDIxLTk4NTQtNDMyZi1hNDY3LTRkZTNlZWRmNTg4OSIsInR5cCI6IkJlYXJlciIsImF6cCI6ImVtcGxveWVlLW1hbmFnZW1lbnQtc2VydmljZSIsInNlc3Npb25fc3RhdGUiOiI2ODdiMTEwYS00NTRjLTQwMzgtYjBkMS1kZDAzZGQ1N2JiNjEiLCJhY3IiOiIxIiwiYWxsb3dlZC1vcmlnaW5zIjpbImh0dHA6Ly9sb2NhbGhvc3Q6NDIwMCJdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsicHJvZHVjdF9vd25lciIsIm9mZmxpbmVfYWNjZXNzIiwiZGVmYXVsdC1yb2xlcy1zenV0IiwidW1hX2F1dGhvcml6YXRpb24iLCJ1c2VyIl19LCJyZXNvdXJjZV9hY2Nlc3MiOnsiYWNjb3VudCI6eyJyb2xlcyI6WyJtYW5hZ2UtYWNjb3VudCIsIm1hbmFnZS1hY2NvdW50LWxpbmtzIiwidmlldy1wcm9maWxlIl19fSwic2NvcGUiOiJlbWFpbCBwcm9maWxlIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsInByZWZlcnJlZF91c2VybmFtZSI6InVzZXIifQ.E5ir1Z-POpUU_jvTh8CzoMYO74qo_7uQXw7QQBUvXB2_37pT3_tutAq6sM4V5cNBu--fWar5bltlNcOAWd_7Kdb66Qc23i0RR9vPneoSduJAzoD8gtFbx8c7ltNR4pG-c6tdnkGhLLqM621DShaSlH8Shp-Z0-y4Iq3GFdQrAFH1CrRVYlW0qFv1EZsE9BmhW3hJwrR1S2IPiEN6MwhehLflLa_ZgLcF417ocIfK-6gbbRNAwXA-JajFVOZAEVXs-52Ta9Kb_EEQFpRsjXorfflmbizQmgrbhBUB7MTiPYIcRruZSYdfmjcE008PHnut52cTcVYEuOrUCUqY4VmhoQ'; - constructor(private http: HttpClient) { - this.employees$ = of([]); - this.fetchData(); + constructor( + private readonly httpClient: HttpClient, + private readonly authService: AuthService, + private readonly snackBar: MatSnackBar + ) { + this.employees$ = this.initializeEmployeesStream(); } - fetchData() { - this.employees$ = this.http.get('http://localhost:8089/employees'); + private initializeEmployeesStream(): Observable { + return of(this.bearer).pipe( + retry(EmployeeListComponent.MAX_RETRIES), + catchError((error: HttpErrorResponse) => { + console.error('Error fetching auth token:', error); + this.showErrorMessage('Failed to authenticate. Please try again.'); + return of([]); + }), + switchMap(token => this.fetchEmployees(token as string)) + ); + } + + private fetchEmployees(token: string): Observable { + const headers = this.createAuthenticatedHeaders(token); + return this.httpClient.get( + EmployeeListComponent.EMPLOYEES_ENDPOINT, + { headers } + ).pipe( + retry(EmployeeListComponent.MAX_RETRIES), + catchError((error: HttpErrorResponse) => { + console.error('Error fetching employees:', error); + this.showErrorMessage('Failed to load employees. Please try again.'); + return of([]); + }) + ); + } + + private createAuthenticatedHeaders(token: string): HttpHeaders { + return new HttpHeaders() + .set('Content-Type', 'application/json') + .set('Authorization', `Bearer ${token}`); + } + + private showErrorMessage(message: string): void { + this.snackBar.open(message, 'Close', { + duration: 5000, + horizontalPosition: 'end', + verticalPosition: 'bottom', + panelClass: ['!bg-red-50', '!text-red-900', '!border', '!border-red-100'] + }); } } diff --git a/src/styles.css b/src/styles.css index 7e7239a..9201eba 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,4 +1,7 @@ /* You can add global styles to this file, and also import other style files */ +@tailwind base; +@tailwind components; +@tailwind utilities; html, body { height: 100%; } body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; } diff --git a/tailwind.config.js b/tailwind.config.js index cf32239..03231e9 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -1,7 +1,7 @@ /** @type {import('tailwindcss').Config} */ module.exports = { content: [ - './src/**/*.{html,ts,css,scss,sass,less,styl,.component.html}', + './src/**/*.{html,ts,css,scss,sass,less,styl}', ], theme: { extend: {},