diff --git a/src/app/features/demo/components/demo1/demo1.component.css b/src/app/.demo/demo/components/demo1/demo1.component.css similarity index 100% rename from src/app/features/demo/components/demo1/demo1.component.css rename to src/app/.demo/demo/components/demo1/demo1.component.css diff --git a/src/app/features/demo/components/demo1/demo1.component.html b/src/app/.demo/demo/components/demo1/demo1.component.html similarity index 100% rename from src/app/features/demo/components/demo1/demo1.component.html rename to src/app/.demo/demo/components/demo1/demo1.component.html diff --git a/src/app/features/demo/components/demo1/demo1.component.ts b/src/app/.demo/demo/components/demo1/demo1.component.ts similarity index 100% rename from src/app/features/demo/components/demo1/demo1.component.ts rename to src/app/.demo/demo/components/demo1/demo1.component.ts diff --git a/src/app/features/demo/components/demo2/demo2.component.txt b/src/app/.demo/demo/components/demo2/demo2.component.html similarity index 98% rename from src/app/features/demo/components/demo2/demo2.component.txt rename to src/app/.demo/demo/components/demo2/demo2.component.html index 7e5143e..14b8e2d 100644 --- a/src/app/features/demo/components/demo2/demo2.component.txt +++ b/src/app/.demo/demo/components/demo2/demo2.component.html @@ -3,17 +3,17 @@ - - + -->
- +
diff --git a/src/app/features/demo/components/demo2/demo2.component.2.txt b/src/app/.demo/demo/components/demo2/demo2.component.ts similarity index 63% rename from src/app/features/demo/components/demo2/demo2.component.2.txt rename to src/app/.demo/demo/components/demo2/demo2.component.ts index d5504c7..c278328 100644 --- a/src/app/features/demo/components/demo2/demo2.component.2.txt +++ b/src/app/.demo/demo/components/demo2/demo2.component.ts @@ -9,7 +9,7 @@ import { DOCUMENT } from '@angular/common'; import { CommonModule } from '@angular/common'; import { CardComponent } from '../../../../shared/components/ui/card/card.component'; // Wir müssen KpiColor hier importieren, um es als Typ verwenden zu können -import { KpiCardComponent } from '../../../dashboard/components/kpi-card/kpi-card.component'; +import { KpiCardComponent } from '../../../components/dashboard/kpi-card/kpi-card.component'; import { KpiColor } from '../../../../core/types/dashboard'; @@ -86,134 +86,134 @@ export class Demo2Component { demoForm: FormGroup; - kpiData: Kpi[] = [ - { - value: '€ 14.750', - label: 'Umsatz', - color: 'green', - iconName: 'placeholder', - }, - { - value: '1.284', - label: 'Neue Nutzer', - color: 'blue', - iconName: 'placeholder', - }, - { - value: '312', - label: 'Bestellungen', - color: 'orange', - iconName: 'placeholder', - }, - { - value: '99.8%', - label: 'Verfügbarkeit', - color: 'purple', - iconName: 'placeholder', - }, - ]; + // kpiData: Kpi[] = [ + // { + // value: '€ 14.750', + // label: 'Umsatz', + // color: 'green', + // iconName: 'placeholder', + // }, + // { + // value: '1.284', + // label: 'Neue Nutzer', + // color: 'blue', + // iconName: 'placeholder', + // }, + // { + // value: '312', + // label: 'Bestellungen', + // color: 'orange', + // iconName: 'placeholder', + // }, + // { + // value: '99.8%', + // label: 'Verfügbarkeit', + // color: 'purple', + // iconName: 'placeholder', + // }, + // ]; - ordersData: Order[] = [ - { - id: '10543', - user: { - name: 'Max Mustermann', - email: 'max.m@example.com', - avatarUrl: 'https://i.pravatar.cc/40?u=max', - }, - amount: '€ 129,99', - status: 'success', - statusText: 'Abgeschlossen', - }, - { - id: '10542', - user: { - name: 'Erika Mustermann', - email: 'erika.m@example.com', - avatarUrl: 'https://i.pravatar.cc/40?u=erika', - }, - amount: '€ 49,50', - status: 'warning', - statusText: 'In Bearbeitung', - }, - { - id: '10541', - user: { - name: 'Peter Pan', - email: 'peter.p@example.com', - avatarUrl: 'https://i.pravatar.cc/40?u=peter', - }, - amount: '€ 87,00', - status: 'danger', - statusText: 'Storniert', - }, - { - id: '10543', - user: { - name: 'Max Mustermann', - email: 'max.m@example.com', - avatarUrl: 'https://i.pravatar.cc/40?u=max', - }, - amount: '€ 129,99', - status: 'success', - statusText: 'Abgeschlossen', - }, - { - id: '10542', - user: { - name: 'Erika Mustermann', - email: 'erika.m@example.com', - avatarUrl: 'https://i.pravatar.cc/40?u=erika', - }, - amount: '€ 49,50', - status: 'warning', - statusText: 'In Bearbeitung', - }, - { - id: '10541', - user: { - name: 'Peter Pan', - email: 'peter.p@example.com', - avatarUrl: 'https://i.pravatar.cc/40?u=peter', - }, - amount: '€ 87,00', - status: 'danger', - statusText: 'Storniert', - }, - { - id: '10543', - user: { - name: 'Max Mustermann', - email: 'max.m@example.com', - avatarUrl: 'https://i.pravatar.cc/40?u=max', - }, - amount: '€ 129,99', - status: 'success', - statusText: 'Abgeschlossen', - }, - { - id: '10542', - user: { - name: 'Erika Mustermann', - email: 'erika.m@example.com', - avatarUrl: 'https://i.pravatar.cc/40?u=erika', - }, - amount: '€ 49,50', - status: 'warning', - statusText: 'In Bearbeitung', - }, - { - id: '10541', - user: { - name: 'Peter Pan', - email: 'peter.p@example.com', - avatarUrl: 'https://i.pravatar.cc/40?u=peter', - }, - amount: '€ 87,00', - status: 'danger', - statusText: 'Storniert', - }, - ]; + // ordersData: Order[] = [ + // { + // id: '10543', + // user: { + // name: 'Max Mustermann', + // email: 'max.m@example.com', + // avatarUrl: 'https://i.pravatar.cc/40?u=max', + // }, + // amount: '€ 129,99', + // status: 'success', + // statusText: 'Abgeschlossen', + // }, + // { + // id: '10542', + // user: { + // name: 'Erika Mustermann', + // email: 'erika.m@example.com', + // avatarUrl: 'https://i.pravatar.cc/40?u=erika', + // }, + // amount: '€ 49,50', + // status: 'warning', + // statusText: 'In Bearbeitung', + // }, + // { + // id: '10541', + // user: { + // name: 'Peter Pan', + // email: 'peter.p@example.com', + // avatarUrl: 'https://i.pravatar.cc/40?u=peter', + // }, + // amount: '€ 87,00', + // status: 'danger', + // statusText: 'Storniert', + // }, + // { + // id: '10543', + // user: { + // name: 'Max Mustermann', + // email: 'max.m@example.com', + // avatarUrl: 'https://i.pravatar.cc/40?u=max', + // }, + // amount: '€ 129,99', + // status: 'success', + // statusText: 'Abgeschlossen', + // }, + // { + // id: '10542', + // user: { + // name: 'Erika Mustermann', + // email: 'erika.m@example.com', + // avatarUrl: 'https://i.pravatar.cc/40?u=erika', + // }, + // amount: '€ 49,50', + // status: 'warning', + // statusText: 'In Bearbeitung', + // }, + // { + // id: '10541', + // user: { + // name: 'Peter Pan', + // email: 'peter.p@example.com', + // avatarUrl: 'https://i.pravatar.cc/40?u=peter', + // }, + // amount: '€ 87,00', + // status: 'danger', + // statusText: 'Storniert', + // }, + // { + // id: '10543', + // user: { + // name: 'Max Mustermann', + // email: 'max.m@example.com', + // avatarUrl: 'https://i.pravatar.cc/40?u=max', + // }, + // amount: '€ 129,99', + // status: 'success', + // statusText: 'Abgeschlossen', + // }, + // { + // id: '10542', + // user: { + // name: 'Erika Mustermann', + // email: 'erika.m@example.com', + // avatarUrl: 'https://i.pravatar.cc/40?u=erika', + // }, + // amount: '€ 49,50', + // status: 'warning', + // statusText: 'In Bearbeitung', + // }, + // { + // id: '10541', + // user: { + // name: 'Peter Pan', + // email: 'peter.p@example.com', + // avatarUrl: 'https://i.pravatar.cc/40?u=peter', + // }, + // amount: '€ 87,00', + // status: 'danger', + // statusText: 'Storniert', + // }, + // ]; aktuellesPasswort: any = ''; neuesPasswort: any = ''; diff --git a/src/app/features/demo/demo.module.ts b/src/app/.demo/demo/demo.module.ts similarity index 100% rename from src/app/features/demo/demo.module.ts rename to src/app/.demo/demo/demo.module.ts diff --git a/src/app/features/demo/demo.routes.ts b/src/app/.demo/demo/demo.routes.ts similarity index 72% rename from src/app/features/demo/demo.routes.ts rename to src/app/.demo/demo/demo.routes.ts index 9bfc4a9..68ae8cc 100644 --- a/src/app/features/demo/demo.routes.ts +++ b/src/app/.demo/demo/demo.routes.ts @@ -1,7 +1,7 @@ import { Routes } from '@angular/router'; import { Demo1Component } from './components/demo1/demo1.component'; -// import { Demo2Component } from './components/demo2/demo2.component'; +import { Demo2Component } from './components/demo2/demo2.component'; export const DEMO_ROUTES: Routes = [ { @@ -17,12 +17,12 @@ export const DEMO_ROUTES: Routes = [ component: Demo1Component, title: 'Demo1', }, - // { - // // Diese Route passt auf '/demo/1' und lädt die Komponente genau einmal. - // path: '2', - // component: Demo2Component, - // title: 'Demo2', - // }, + { + // Diese Route passt auf '/demo/1' und lädt die Komponente genau einmal. + path: '2', + component: Demo2Component, + title: 'Demo2', + }, // Hier könntest du weitere Routen wie '2', '3' etc. hinzufügen, // die andere Komponenten laden. // { path: '2', component: AnotherDemoComponent }, diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index ccae308..fc8c9a8 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -5,7 +5,8 @@ import { NotFoundComponent } from './core/components/not-found/not-found.compone import { DefaultLayoutComponent } from './core/components/default-layout/default-layout.component'; -import { DashboardPageComponent } from './features/dashboard/components/dashboard-page/dashboard-page.component'; +import { DashboardPageComponent } from './features/components/dashboard/dashboard-page/dashboard-page.component'; +import { authGuard } from './core/guards/auth.guard'; export const routes: Routes = [ { @@ -17,11 +18,13 @@ export const routes: Routes = [ { path: 'dashboard', component: DefaultLayoutComponent, + // canActivate: [authGuard], + // data: { requiredRole: 'Admin' }, children: [ { path: '', loadChildren: () => - import('./features/dashboard/dashboard.routes').then( + import('./features/components/dashboard/dashboard.routes').then( (r) => r.DASHBOARD_ROUTES ), }, @@ -30,12 +33,7 @@ export const routes: Routes = [ { path: 'auth', loadChildren: () => - import('./features/auth/auth.routes').then((r) => r.AUTH_ROUTES), - }, - { - path: 'demo', - loadChildren: () => - import('./features/demo/demo.routes').then((r) => r.DEMO_ROUTES), + import('./features/components/auth/auth.routes').then((r) => r.AUTH_ROUTES), }, { path: 'access-denied', diff --git a/src/app/core/guards/auth.guard.ts b/src/app/core/guards/auth.guard.ts index 543fa0e..39678ca 100644 --- a/src/app/core/guards/auth.guard.ts +++ b/src/app/core/guards/auth.guard.ts @@ -1,5 +1,48 @@ -import { CanActivateFn } from '@angular/router'; +import { inject } from '@angular/core'; +import { CanActivateFn, Router, ActivatedRouteSnapshot } from '@angular/router'; +import { AuthService } from '../services/auth.service'; -export const authGuard: CanActivateFn = (route, state) => { +/** + * Ein funktionaler Guard, der Routen schützt. + * + * Verwendung in der Routing-Konfiguration: + * { + * path: 'admin', + * component: AdminDashboardComponent, + * canActivate: [authGuard], + * data: { requiredRole: 'Admin' } // Die erforderliche Rolle hier übergeben + * } + * { + * path: 'profile', + * component: UserProfileComponent, + * canActivate: [authGuard] // Schützt nur vor nicht angemeldeten Benutzern + * } + */ +export const authGuard: CanActivateFn = (route: ActivatedRouteSnapshot) => { + const authService = inject(AuthService); + const router = inject(Router); + + // 1. Prüfen, ob der Benutzer überhaupt angemeldet ist + if (!authService.getToken()) { + // Nicht angemeldet -> zum Login umleiten + router.navigate(['/login']); + return false; + } + + // 2. Prüfen, ob die Route eine bestimmte Rolle erfordert + const requiredRole = route.data['requiredRole'] as string; + if (requiredRole) { + // Rolle ist erforderlich -> prüfen, ob der Benutzer sie hat + if (authService.hasRole(requiredRole)) { + // Benutzer hat die Rolle -> Zugriff erlaubt + return true; + } else { + // Benutzer hat die Rolle nicht -> zu einer "Zugriff verweigert"-Seite umleiten + router.navigate(['/access-denied']); + return false; + } + } + + // 3. Wenn die Route nur eine Anmeldung, aber keine spezielle Rolle erfordert return true; -}; +}; \ No newline at end of file diff --git a/src/app/core/models/auth.models.ts b/src/app/core/models/auth.models.ts new file mode 100644 index 0000000..f49fba0 --- /dev/null +++ b/src/app/core/models/auth.models.ts @@ -0,0 +1,15 @@ +// src/app/core/models/auth.models.ts + +export interface LoginRequestDto { + email: string; + password: string; +} + +export interface AuthResponseDto { + isAuthSuccessful: boolean; + errorMessage?: string; + token?: string; + userId?: string; + email?: string; + roles?: string[]; +} \ No newline at end of file diff --git a/src/app/core/services/api.service.ts b/src/app/core/services/api.service.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/core/services/auth.service.ts b/src/app/core/services/auth.service.ts new file mode 100644 index 0000000..33bf0c8 --- /dev/null +++ b/src/app/core/services/auth.service.ts @@ -0,0 +1,121 @@ +import { Injectable, Inject, PLATFORM_ID } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { isPlatformBrowser } from '@angular/common'; +import { Router } from '@angular/router'; +import { Observable, BehaviorSubject, of } from 'rxjs'; +import { tap, catchError } from 'rxjs/operators'; + +import { LoginRequestDto } from '../models/auth.models'; +import { AuthResponseDto } from '../models/auth.models'; + +@Injectable({ + providedIn: 'root' +}) +export class AuthService { + // Basis-URL Ihrer API + private apiUrl = '/api/v1/Auth'; + + // Keys für die Speicherung im localStorage + private readonly tokenKey = 'auth-token'; + private readonly userRolesKey = 'auth-user-roles'; + + // Ein BehaviorSubject, um den Login-Status reaktiv in der App zu teilen + private loggedInStatus = new BehaviorSubject(this.hasToken()); + public isLoggedIn$ = this.loggedInStatus.asObservable(); + + constructor( + private http: HttpClient, + private router: Router, + @Inject(PLATFORM_ID) private platformId: Object + ) {} + + /** + * Meldet einen Admin-Benutzer an. + */ + loginAdmin(credentials: LoginRequestDto): Observable { + return this.http.post(`${this.apiUrl}/login/admin`, credentials).pipe( + tap(response => { + if (response.isAuthSuccessful && response.token) { + this.setSession(response); + } + }), + catchError(error => { + // Fehlerbehandlung, z.B. das Token löschen, falls eines vorhanden war + this.logout(); + throw error; + }) + ); + } + + /** + * Meldet einen normalen Kunden an. + */ + loginCustomer(credentials: LoginRequestDto): Observable { + return this.http.post(`${this.apiUrl}/login/customer`, credentials).pipe( + tap(response => { + if (response.isAuthSuccessful && response.token) { + this.setSession(response); + } + }) + ); + } + + /** + * Meldet den Benutzer ab, entfernt die Daten aus dem Speicher und leitet um. + */ + logout(): void { + if (isPlatformBrowser(this.platformId)) { + localStorage.removeItem(this.tokenKey); + localStorage.removeItem(this.userRolesKey); + } + this.loggedInStatus.next(false); + this.router.navigate(['/login']); // oder wohin auch immer Sie umleiten möchten + } + + /** + * Gibt das gespeicherte JWT-Token zurück. + */ + getToken(): string | null { + if (isPlatformBrowser(this.platformId)) { + return localStorage.getItem(this.tokenKey); + } + return null; + } + + /** + * Gibt die Rollen des angemeldeten Benutzers zurück. + */ + getUserRoles(): string[] { + if (isPlatformBrowser(this.platformId)) { + const roles = localStorage.getItem(this.userRolesKey); + return roles ? JSON.parse(roles) : []; + } + return []; + } + + /** + * Überprüft, ob der Benutzer eine bestimmte Rolle hat. + */ + hasRole(requiredRole: string): boolean { + const userRoles = this.getUserRoles(); + return userRoles.includes(requiredRole); + } + + /** + * Private Methode zum Speichern der Sitzungsdaten. + */ + private setSession(authResponse: AuthResponseDto): void { + if (isPlatformBrowser(this.platformId) && authResponse.token && authResponse.roles) { + localStorage.setItem(this.tokenKey, authResponse.token); + localStorage.setItem(this.userRolesKey, JSON.stringify(authResponse.roles)); + this.loggedInStatus.next(true); + } + } + + /** + * Private Methode, die prüft, ob ein Token vorhanden ist (nützlich beim App-Start). + */ + private hasToken(): boolean { + return !!this.getToken(); + } +} \ No newline at end of file diff --git a/src/app/core/services/notification.service.ts b/src/app/core/services/notification.service.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/core/tokens/api-url.token.ts b/src/app/core/tokens/api-url.token.ts new file mode 100644 index 0000000..83129fe --- /dev/null +++ b/src/app/core/tokens/api-url.token.ts @@ -0,0 +1,5 @@ +// /src/app/core/tokens/api-url.token.ts + +import { InjectionToken } from '@angular/core'; + +export const API_URL = new InjectionToken('api.url'); \ No newline at end of file diff --git a/src/app/environment.ts b/src/app/environment.ts new file mode 100644 index 0000000..9153267 --- /dev/null +++ b/src/app/environment.ts @@ -0,0 +1,5 @@ +// /src/environments/environment.ts +export const environment = { + production: false, + apiUrl: 'https://shopsolution-backend.tzbre.dev/api/v1', +}; diff --git a/src/app/features/auth/auth.module.ts b/src/app/features/auth/auth.module.ts deleted file mode 100644 index 7be5213..0000000 --- a/src/app/features/auth/auth.module.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; -import { AuthLayoutComponent } from './components/auth-layout/auth-layout.component'; -import { LoginComponent } from './components/login/login.component'; - -@NgModule({ - declarations: [], - imports: [CommonModule, AuthLayoutComponent, LoginComponent], -}) -export class AuthModule {} diff --git a/src/app/features/auth/services/auth.service.ts b/src/app/features/auth/services/auth.service.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/app/features/auth/components/auth-layout/auth-layout.component.css b/src/app/features/components/auth/auth-layout/auth-layout.component.css similarity index 100% rename from src/app/features/auth/components/auth-layout/auth-layout.component.css rename to src/app/features/components/auth/auth-layout/auth-layout.component.css diff --git a/src/app/features/auth/components/auth-layout/auth-layout.component.html b/src/app/features/components/auth/auth-layout/auth-layout.component.html similarity index 100% rename from src/app/features/auth/components/auth-layout/auth-layout.component.html rename to src/app/features/components/auth/auth-layout/auth-layout.component.html diff --git a/src/app/features/auth/components/auth-layout/auth-layout.component.ts b/src/app/features/components/auth/auth-layout/auth-layout.component.ts similarity index 100% rename from src/app/features/auth/components/auth-layout/auth-layout.component.ts rename to src/app/features/components/auth/auth-layout/auth-layout.component.ts diff --git a/src/app/features/auth/auth.routes.ts b/src/app/features/components/auth/auth.routes.ts similarity index 58% rename from src/app/features/auth/auth.routes.ts rename to src/app/features/components/auth/auth.routes.ts index 2a26115..ea7e91e 100644 --- a/src/app/features/auth/auth.routes.ts +++ b/src/app/features/components/auth/auth.routes.ts @@ -1,13 +1,13 @@ import { Routes } from '@angular/router'; // Importiere dein spezielles Layout für Auth-Seiten und alle Komponenten -import { AuthLayoutComponent } from './components/auth-layout/auth-layout.component'; -import { LoginComponent } from './components/login/login.component'; -import { RegisterComponent } from './components/register/register.component'; -import { ForgotPasswordComponent } from './components/forgot-password/forgot-password.component'; -import { ResetPasswordComponent } from './components/reset-password/reset-password.component'; -import { VerifyEmailComponent } from './components/verify-email/verify-email.component'; -import { NotFoundComponent } from '../../core/components/not-found/not-found.component'; +import { AuthLayoutComponent } from './auth-layout/auth-layout.component'; +import { LoginComponent } from './login/login.component'; +import { RegisterComponent } from './register/register.component'; +import { ForgotPasswordComponent } from './forgot-password/forgot-password.component'; +import { ResetPasswordComponent } from './reset-password/reset-password.component'; +import { VerifyEmailComponent } from './verify-email/verify-email.component'; +import { NotFoundComponent } from '../../../core/components/not-found/not-found.component'; export const AUTH_ROUTES: Routes = [ { diff --git a/src/app/features/auth/components/forgot-password/forgot-password.component.css b/src/app/features/components/auth/forgot-password/forgot-password.component.css similarity index 100% rename from src/app/features/auth/components/forgot-password/forgot-password.component.css rename to src/app/features/components/auth/forgot-password/forgot-password.component.css diff --git a/src/app/features/auth/components/forgot-password/forgot-password.component.html b/src/app/features/components/auth/forgot-password/forgot-password.component.html similarity index 100% rename from src/app/features/auth/components/forgot-password/forgot-password.component.html rename to src/app/features/components/auth/forgot-password/forgot-password.component.html diff --git a/src/app/features/auth/components/forgot-password/forgot-password.component.ts b/src/app/features/components/auth/forgot-password/forgot-password.component.ts similarity index 100% rename from src/app/features/auth/components/forgot-password/forgot-password.component.ts rename to src/app/features/components/auth/forgot-password/forgot-password.component.ts diff --git a/src/app/features/auth/components/login/login.component.css b/src/app/features/components/auth/login/login.component.css similarity index 100% rename from src/app/features/auth/components/login/login.component.css rename to src/app/features/components/auth/login/login.component.css diff --git a/src/app/features/auth/components/login/login.component.html b/src/app/features/components/auth/login/login.component.html similarity index 100% rename from src/app/features/auth/components/login/login.component.html rename to src/app/features/components/auth/login/login.component.html diff --git a/src/app/features/auth/components/login/login.component.ts b/src/app/features/components/auth/login/login.component.ts similarity index 100% rename from src/app/features/auth/components/login/login.component.ts rename to src/app/features/components/auth/login/login.component.ts diff --git a/src/app/features/auth/components/register/register.component.css b/src/app/features/components/auth/register/register.component.css similarity index 100% rename from src/app/features/auth/components/register/register.component.css rename to src/app/features/components/auth/register/register.component.css diff --git a/src/app/features/auth/components/register/register.component.html b/src/app/features/components/auth/register/register.component.html similarity index 100% rename from src/app/features/auth/components/register/register.component.html rename to src/app/features/components/auth/register/register.component.html diff --git a/src/app/features/auth/components/register/register.component.ts b/src/app/features/components/auth/register/register.component.ts similarity index 100% rename from src/app/features/auth/components/register/register.component.ts rename to src/app/features/components/auth/register/register.component.ts diff --git a/src/app/features/auth/components/reset-password/reset-password.component.css b/src/app/features/components/auth/reset-password/reset-password.component.css similarity index 100% rename from src/app/features/auth/components/reset-password/reset-password.component.css rename to src/app/features/components/auth/reset-password/reset-password.component.css diff --git a/src/app/features/auth/components/reset-password/reset-password.component.html b/src/app/features/components/auth/reset-password/reset-password.component.html similarity index 100% rename from src/app/features/auth/components/reset-password/reset-password.component.html rename to src/app/features/components/auth/reset-password/reset-password.component.html diff --git a/src/app/features/auth/components/reset-password/reset-password.component.ts b/src/app/features/components/auth/reset-password/reset-password.component.ts similarity index 100% rename from src/app/features/auth/components/reset-password/reset-password.component.ts rename to src/app/features/components/auth/reset-password/reset-password.component.ts diff --git a/src/app/features/auth/components/verify-email/verify-email.component.css b/src/app/features/components/auth/verify-email/verify-email.component.css similarity index 100% rename from src/app/features/auth/components/verify-email/verify-email.component.css rename to src/app/features/components/auth/verify-email/verify-email.component.css diff --git a/src/app/features/auth/components/verify-email/verify-email.component.html b/src/app/features/components/auth/verify-email/verify-email.component.html similarity index 100% rename from src/app/features/auth/components/verify-email/verify-email.component.html rename to src/app/features/components/auth/verify-email/verify-email.component.html diff --git a/src/app/features/auth/components/verify-email/verify-email.component.ts b/src/app/features/components/auth/verify-email/verify-email.component.ts similarity index 100% rename from src/app/features/auth/components/verify-email/verify-email.component.ts rename to src/app/features/components/auth/verify-email/verify-email.component.ts diff --git a/src/app/features/dashboard/components/dashboard-page/dashboard-page.component.css b/src/app/features/components/dashboard/dashboard-page/dashboard-page.component.css similarity index 100% rename from src/app/features/dashboard/components/dashboard-page/dashboard-page.component.css rename to src/app/features/components/dashboard/dashboard-page/dashboard-page.component.css diff --git a/src/app/features/components/dashboard/dashboard-page/dashboard-page.component.html b/src/app/features/components/dashboard/dashboard-page/dashboard-page.component.html new file mode 100644 index 0000000..8f97e8a --- /dev/null +++ b/src/app/features/components/dashboard/dashboard-page/dashboard-page.component.html @@ -0,0 +1,157 @@ +
+
+ + +
+
+ + +
+
+ +
+

Übersicht Admin-Dashboard

+ +
+

Kategorie 1: Handlungsbedarf – "Was muss ich jetzt tun?"

+ +
+

Offene Bestellungen

+

Anzahl und Liste der letzten 3-5 Bestellungen mit Status "Pending" oder "Processing".

+

Jede Zeile sollte direkt zur Detailseite der Bestellung verlinken.

+
    +
  • Bestellung #12345 - Kunde: Max Mustermann - Status: Pending [Details]
  • +
  • Bestellung #12346 - Kunde: Anna Schmidt - Status: Processing [Details]
  • +
  • Bestellung #12347 - Kunde: Peter Müller - Status: Pending [Details]
  • +
+
+ Datenquelle: GET /api/v1/admin/AdminOrders (clientseitig filtern) +
+
+ +
+

Niedriger Lagerbestand

+

Warnung und Liste der Produkte mit stockQuantity unter einem kritischen Schwellenwert.

+
    +
  • Produkt A - Aktueller Bestand: 5
  • +
  • Produkt B - Aktueller Bestand: 2
  • +
  • Produkt C - Aktueller Bestand: 1
  • +
+
+ Datenquelle: GET /api/v1/admin/AdminAnalytics -> inventoryStatus.productsWithLowStock +
+
+ +
+

Zu genehmigende Bewertungen

+

Anzahl der Kundenbewertungen, die auf Freigabe warten.

+

Anzahl: 7

+
+ Datenquelle: GET /api/v1/admin/AdminReviews (clientseitig nach isApproved === false filtern) +
+
+
+ +
+

Kategorie 2: Key Performance Indicators (KPIs) – "Wie läuft mein Geschäft?"

+ +
+

Umsatz (letzte 30 Tage)

+

Wichtigste Kennzahl.

+

Betrag: 15.450,00 €

+
+ Datenquelle: GET /api/v1/admin/AdminAnalytics -> kpiSummary.totalRevenue +
+
+ +
+

Anzahl Bestellungen (letzte 30 Tage)

+

Zeigt die allgemeine Aktivität.

+

Anzahl: 320

+
+ Datenquelle: GET /api/v1/admin/AdminAnalytics -> kpiSummary.totalOrders +
+
+ +
+

Durchschnittlicher Bestellwert

+

Gibt Aufschluss über das Kaufverhalten.

+

Wert: 48,28 €

+
+ Datenquelle: GET /api/v1/admin/AdminAnalytics -> kpiSummary.averageOrderValue +
+
+ +
+

Neukunden (letzte 30 Tage)

+

Wichtige Metrik für das Wachstum.

+

Anzahl: 55

+
+ Datenquelle: GET /api/v1/admin/AdminAnalytics -> kpiSummary.newCustomersThisPeriod +
+
+
+ +
+

Kategorie 3: Jüngste Aktivitäten & Trends – "Was passiert gerade?"

+ +
+

Umsatzverlauf (Diagramm)

+

Einfaches Linien- oder Balkendiagramm, das den Umsatz der letzten 7 oder 30 Tage anzeigt.

+ +
+ [Platzhalter für Umsatzdiagramm] +
+
+ Datenquelle: GET /api/v1/admin/AdminAnalytics -> salesOverTime +
+
+ +
+

Letzte Bestellungen (Feed)

+

Kurze, scrollbare Liste der letzten 10 Bestellungen (unabhängig vom Status) mit Kundenname und Bestellwert.

+
    +
  • Bestellung #12348 - Kunde: Lisa Meier - Wert: 75,50 €
  • +
  • Bestellung #12349 - Kunde: Tom Weber - Wert: 120,00 €
  • +
  • Bestellung #12350 - Kunde: Sarah Bauer - Wert: 35,20 €
  • +
  • Bestellung #12351 - Kunde: Tim Schulz - Wert: 90,00 €
  • + +
+
+ Datenquelle: GET /api/v1/admin/AdminOrders +
+
+
+ +
+

Kategorie 4: Strategische Einblicke – "Was funktioniert gut?"

+ +
+

Top 5 Produkte

+

Einfache Liste der meistverkauften Produkte im aktuellen Zeitraum (nach Umsatz oder verkauften Einheiten).

+
    +
  1. Produkt X (Umsatz: 5000 €)
  2. +
  3. Produkt Y (Umsatz: 4500 €)
  4. +
  5. Produkt Z (Umsatz: 4000 €)
  6. +
  7. Produkt A (Umsatz: 3800 €)
  8. +
  9. Produkt B (Umsatz: 3500 €)
  10. +
+
+ Datenquelle: GET /api/v1/admin/AdminAnalytics -> topPerformingProducts +
+
+
+
+ diff --git a/src/app/features/dashboard/components/dashboard-page/dashboard-page.component.ts b/src/app/features/components/dashboard/dashboard-page/dashboard-page.component.ts similarity index 100% rename from src/app/features/dashboard/components/dashboard-page/dashboard-page.component.ts rename to src/app/features/components/dashboard/dashboard-page/dashboard-page.component.ts diff --git a/src/app/features/dashboard/dashboard.routes.ts b/src/app/features/components/dashboard/dashboard.routes.ts similarity index 77% rename from src/app/features/dashboard/dashboard.routes.ts rename to src/app/features/components/dashboard/dashboard.routes.ts index 646d156..4c95727 100644 --- a/src/app/features/dashboard/dashboard.routes.ts +++ b/src/app/features/components/dashboard/dashboard.routes.ts @@ -1,5 +1,5 @@ import { Routes } from '@angular/router'; -import { DashboardPageComponent } from './components/dashboard-page/dashboard-page.component'; +import { DashboardPageComponent } from './dashboard-page/dashboard-page.component'; // Importiere dein spezielles Layout für Auth-Seiten und alle Komponenten export const DASHBOARD_ROUTES: Routes = [ diff --git a/src/app/features/dashboard/components/kpi-card/kpi-card.component.css b/src/app/features/components/dashboard/kpi-card/kpi-card.component.css similarity index 100% rename from src/app/features/dashboard/components/kpi-card/kpi-card.component.css rename to src/app/features/components/dashboard/kpi-card/kpi-card.component.css diff --git a/src/app/features/dashboard/components/kpi-card/kpi-card.component.html b/src/app/features/components/dashboard/kpi-card/kpi-card.component.html similarity index 100% rename from src/app/features/dashboard/components/kpi-card/kpi-card.component.html rename to src/app/features/components/dashboard/kpi-card/kpi-card.component.html diff --git a/src/app/features/dashboard/components/kpi-card/kpi-card.component.ts b/src/app/features/components/dashboard/kpi-card/kpi-card.component.ts similarity index 100% rename from src/app/features/dashboard/components/kpi-card/kpi-card.component.ts rename to src/app/features/components/dashboard/kpi-card/kpi-card.component.ts diff --git a/src/app/features/dashboard/components/dashboard-page/dashboard-page.component.html b/src/app/features/dashboard/components/dashboard-page/dashboard-page.component.html deleted file mode 100644 index e4c3664..0000000 --- a/src/app/features/dashboard/components/dashboard-page/dashboard-page.component.html +++ /dev/null @@ -1,75 +0,0 @@ -
-
- - -
-
- - -
-
-Exzellente Frage! Ein gutes Dashboard ist der Schlüssel zu einem effizienten -Admin-Panel. Es sollte auf einen Blick die wichtigsten Fragen beantworten: "Wie -läuft mein Geschäft?" und "Was muss ich jetzt tun?". Basierend auf den -Endpunkten und Datenmodellen, die in Ihrer Swagger-Datei verfügbar sind -(insbesondere AdminAnalytics, AdminOrders etc.), hier eine Gliederung der -Kerninformationen, von der höchsten zur niedrigsten Priorität: Kategorie 1: -Handlungsbedarf – "Was muss ich jetzt tun?" Diese Sektion ist die wichtigste, -denn sie treibt die tägliche Arbeit an. Sie sollte prominent platziert sein. -Offene Bestellungen: Eine Zählung und eine Liste der letzten 3-5 Bestellungen -mit dem Status Pending oder Processing. Jede Zeile sollte direkt zur Detailseite -der Bestellung verlinken. Warum? Dies ist die primäre Aufgabe des -Shop-Betreibers: Bestellungen bearbeiten und versenden. Datenquelle: GET -/api/v1/admin/AdminOrders (clientseitig filtern). Niedriger Lagerbestand: Eine -Warnung und eine Liste der Produkte, deren stockQuantity unter einen kritischen -Schwellenwert gefallen ist. Warum? Verhindert "Out of Stock"-Situationen und -Umsatzverluste. Ermöglicht rechtzeitige Nachbestellungen. Datenquelle: GET -/api/v1/admin/AdminAnalytics -> inventoryStatus.productsWithLowStock. Zu -genehmigende Bewertungen: Eine Zählung der Kundenbewertungen, die auf Freigabe -warten. Warum? Fördert die Interaktion mit Kunden und stellt die Qualität der -Inhalte sicher. Datenquelle: GET /api/v1/admin/AdminReviews (clientseitig nach -isApproved === false filtern). Kategorie 2: Key Performance Indicators (KPIs) – -"Wie läuft mein Geschäft?" Dies ist der "Auf einen Blick"-Überblick über die -Gesundheit des Shops. Ideal als große, klare Zahlen am oberen Rand des -Dashboards. Umsatz (letzte 30 Tage): Die wichtigste Kennzahl. Datenquelle: GET -/api/v1/admin/AdminAnalytics -> kpiSummary.totalRevenue. Anzahl Bestellungen -(letzte 30 Tage): Zeigt die allgemeine Aktivität. Datenquelle: GET -/api/v1/admin/AdminAnalytics -> kpiSummary.totalOrders. Durchschnittlicher -Bestellwert: Gibt Aufschluss über das Kaufverhalten. Datenquelle: GET -/api/v1/admin/AdminAnalytics -> kpiSummary.averageOrderValue. Neukunden (letzte -30 Tage): Eine wichtige Metrik für das Wachstum. Datenquelle: GET -/api/v1/admin/AdminAnalytics -> kpiSummary.newCustomersThisPeriod. Kategorie 3: -Jüngste Aktivitäten & Trends – "Was passiert gerade?" Diese Sektion gibt ein -Gefühl für die aktuelle Dynamik und hilft, Muster zu erkennen. Umsatzverlauf -(Diagramm): Ein einfaches Linien- oder Balkendiagramm, das den Umsatz der -letzten 7 oder 30 Tage anzeigt. Warum? Visualisiert Trends, Hochs und Tiefs -(z.B. an Wochenenden). Datenquelle: GET /api/v1/admin/AdminAnalytics -> -salesOverTime. Letzte Bestellungen (Feed): Eine kurze, scrollbare Liste der -letzten 10 Bestellungen (unabhängig vom Status) mit Kundenname und Bestellwert. -Warum? Gibt dem Dashboard ein "lebendiges" Gefühl. Datenquelle: GET -/api/v1/admin/AdminOrders. Kategorie 4: Strategische Einblicke – "Was -funktioniert gut?" Diese Informationen helfen bei Marketing- und -Bestandsentscheidungen. Top 5 Produkte: Eine einfache Liste der meistverkauften -Produkte im aktuellen Zeitraum (nach Umsatz oder verkauften Einheiten). Warum? -Zeigt, welche Produkte beworben werden sollten und wo der Lagerbestand immer -hoch sein muss. Datenquelle: GET /api/v1/admin/AdminAnalytics -> -topPerformingProducts. Vorschlag für ein Dashboard-Layout: Ein bewährtes Layout -könnte so aussehen: Obere Reihe: Die 4 großen KPI-Karten (Umsatz, Bestellungen, -Ø-Wert, Neukunden). Linke (breitere) Spalte: Ganz oben das -Umsatzverlauf-Diagramm. Darunter die Liste der "Letzten Bestellungen". Rechte -(schmalere) Spalte: Ganz oben die "Handlungsbedarf"-Box mit den offenen -Bestellungen und dem niedrigen Lagerbestand. Darunter die "Top 5 -Produkte"-Liste. Mit dieser Auswahl an Informationen hat der Admin alles -Wichtige im Blick und kann direkt die notwendigen Aktionen ausführen. diff --git a/src/app/features/dashboard/dashboard.module.ts b/src/app/features/dashboard/dashboard.module.ts deleted file mode 100644 index c57e32a..0000000 --- a/src/app/features/dashboard/dashboard.module.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { NgModule } from '@angular/core'; -import { CommonModule } from '@angular/common'; - - -@NgModule({ - declarations: [], - imports: [CommonModule], -}) -export class DashboardModule {} diff --git a/src/app/modules.ts b/src/app/modules.ts index 7cd79bf..88a8b07 100644 --- a/src/app/modules.ts +++ b/src/app/modules.ts @@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'; -export const MATERIAL_MODULES = [ +export const MODULES = [ CommonModule, ]; \ No newline at end of file diff --git a/src/app/shared/shared.module.ts b/src/app/shared/shared.module.ts deleted file mode 100644 index e69de29..0000000