diff --git a/bun.lockb b/bun.lockb index 505d200..a60e455 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 39047c9..788cc2a 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "keycloak-angular": "^16.1.0", "rxjs": "~7.8.1", "tailwind": "4.0.0", + "tailwindcss": "^3.4.17", "tslib": "^2.8.1", "zone.js": "~0.15.0" }, @@ -31,12 +32,14 @@ "@angular/cli": "^19.0.5", "@angular/compiler-cli": "^19.0.4", "@types/jasmine": "~5.1.5", + "autoprefixer": "^10.4.20", "jasmine-core": "~5.2.0", "karma": "~6.4.4", "karma-chrome-launcher": "~3.2.0", "karma-coverage": "~2.2.1", "karma-jasmine": "~5.1.0", "karma-jasmine-html-reporter": "~2.1.0", + "postcss": "^8.4.49", "typescript": "~5.5.4" } } 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 index e69de29..7563a61 100644 --- a/src/app/employee-list/employee-list.component.css +++ b/src/app/employee-list/employee-list.component.css @@ -0,0 +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; + } + } + } +} diff --git a/src/app/employee-list/employee-list.component.html b/src/app/employee-list/employee-list.component.html index 217b9d0..ff1fa54 100644 --- a/src/app/employee-list/employee-list.component.html +++ b/src/app/employee-list/employee-list.component.html @@ -1,9 +1,94 @@ -

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! - + diff --git a/src/app/employee-list/employee-list.component.ts b/src/app/employee-list/employee-list.component.ts index 8c167b8..fa346a2 100644 --- a/src/app/employee-list/employee-list.component.ts +++ b/src/app/employee-list/employee-list.component.ts @@ -1,25 +1,74 @@ 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, of, retry } from 'rxjs'; +import { HttpClient, HttpErrorResponse } from '@angular/common/http'; +import { Employee } from '../Employee'; + +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 } from '@angular/material/sort'; @Component({ selector: 'app-employee-list', - imports: [CommonModule], - templateUrl: './employee-list.component.html', standalone: true, + imports: [ + CommonModule, + MatCardModule, + MatButtonModule, + MatIconModule, + MatProgressSpinnerModule, + MatSnackBarModule, + MatDividerModule, + MatTooltipModule, + MatMenuModule, + MatTableModule, + MatSortModule + ], + templateUrl: './employee-list.component.html', + host: { + class: 'block w-full p-6' + }, styleUrl: './employee-list.component.css' }) export class EmployeeListComponent { - employees$: Observable; + private static readonly EMPLOYEES_ENDPOINT = 'http://localhost:8089/employees'; + private static readonly MAX_RETRIES = 3; + public employees$: Observable; + public readonly displayedColumns: string[] = ['name', 'actions']; - constructor(private http: HttpClient) { - this.employees$ = of([]); - this.fetchData(); + constructor( + private readonly httpClient: HttpClient, + private readonly snackBar: MatSnackBar + ) { + this.employees$ = this.fetchEmployees(); } - fetchData() { - this.employees$ = this.http.get('http://localhost:8089/employees'); + private fetchEmployees(): Observable { + return this.httpClient.get( + EmployeeListComponent.EMPLOYEES_ENDPOINT, + ).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 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/app/services/auth-guard.service.ts b/src/app/services/auth-guard.service.ts index 2d65472..eded0c4 100644 --- a/src/app/services/auth-guard.service.ts +++ b/src/app/services/auth-guard.service.ts @@ -16,6 +16,7 @@ export class AuthGuardService { return false; } + return true; } } diff --git a/src/app/services/auth.service.spec.ts b/src/app/services/auth.service.spec.ts new file mode 100644 index 0000000..f1251ca --- /dev/null +++ b/src/app/services/auth.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { AuthService } from './auth.service'; + +describe('AuthService', () => { + let service: AuthService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(AuthService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/styles.css b/src/styles.css index 7e7239a..6df3509 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,4 +1,6 @@ -/* 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 new file mode 100644 index 0000000..03231e9 --- /dev/null +++ b/tailwind.config.js @@ -0,0 +1,11 @@ +/** @type {import('tailwindcss').Config} */ +module.exports = { + content: [ + './src/**/*.{html,ts,css,scss,sass,less,styl}', + ], + theme: { + extend: {}, + }, + plugins: [], +} +