From 7e5939868b39568174f97f7dbb270aa6eb25baa4 Mon Sep 17 00:00:00 2001 From: "Tizian.Breuch" Date: Wed, 17 Sep 2025 09:43:45 +0200 Subject: [PATCH] Fertig- next ist dashboard --- src/app/app.routes.ts | 12 +- .../not-found/not-found.component.html | 24 ++-- .../not-found/not-found.component.ts | 14 +- src/app/features/auth/_auth-common.css | 85 ------------ src/app/features/auth/auth.routes.ts | 7 + .../auth-layout/auth-layout.component.css | 128 ++++++++++++++++-- .../auth-layout/auth-layout.component.html | 30 +--- .../auth-layout/auth-layout.component.ts | 6 +- .../forgot-password.component.css | 28 ---- .../forgot-password.component.html | 51 ++++--- .../forgot-password.component.ts | 15 +- .../components/login/login.component.html | 45 +++--- .../auth/components/login/login.component.ts | 2 + .../register/register.component.css | 8 -- .../register/register.component.html | 58 ++++---- .../components/register/register.component.ts | 4 +- .../reset-password.component.css | 5 - .../reset-password.component.html | 40 ++++-- .../reset-password.component.ts | 36 +++-- .../verify-email/verify-email.component.css | 22 --- .../verify-email/verify-email.component.html | 12 +- .../components/demo2/demo2.component.html | 49 ++++--- .../demo/components/demo2/demo2.component.ts | 31 ++++- src/app/features/demo/demo.routes.ts | 4 +- .../form/form-field/form-field.component.html | 14 +- .../form/form-field/form-field.component.ts | 63 ++++++--- 26 files changed, 424 insertions(+), 369 deletions(-) delete mode 100644 src/app/features/auth/_auth-common.css diff --git a/src/app/app.routes.ts b/src/app/app.routes.ts index 1bab5f2..cbbfbfc 100644 --- a/src/app/app.routes.ts +++ b/src/app/app.routes.ts @@ -9,14 +9,9 @@ export const routes: Routes = [ // wird sofort und ohne Umwege zur Login-Seite weitergeleitet. { path: '', - redirectTo: 'auth', // Leitet zur /auth-Route weiter - pathMatch: 'full', // Wichtig: Gilt nur für den exakt leeren Pfad + redirectTo: 'auth', + pathMatch: 'full', }, - - // Regel 2: Authentifizierungs-Feature - // Alle URLs, die mit "auth/" beginnen (z.B. "/auth/login", "/auth/register"), - // werden von dieser Regel abgefangen und an die auth.routes.ts zur - // weiteren Verarbeitung übergeben. { path: 'auth', loadChildren: () => @@ -27,14 +22,11 @@ export const routes: Routes = [ loadChildren: () => import('./features/demo/demo.routes').then((r) => r.DEMO_ROUTES), }, - { path: 'access-denied', component: AccessDeniedComponent, title: 'Zugriff verweigert', }, - - { path: '**', component: NotFoundComponent, diff --git a/src/app/core/components/not-found/not-found.component.html b/src/app/core/components/not-found/not-found.component.html index 4a982c3..6c5e243 100644 --- a/src/app/core/components/not-found/not-found.component.html +++ b/src/app/core/components/not-found/not-found.component.html @@ -1,25 +1,19 @@ - - -

404

\ No newline at end of file + diff --git a/src/app/core/components/not-found/not-found.component.ts b/src/app/core/components/not-found/not-found.component.ts index dcd9f8b..281545d 100644 --- a/src/app/core/components/not-found/not-found.component.ts +++ b/src/app/core/components/not-found/not-found.component.ts @@ -2,18 +2,16 @@ import { Component } from '@angular/core'; import { CommonModule, Location } from '@angular/common'; import { RouterLink } from '@angular/router'; +import { ButtonComponent } from '../../../shared/components/ui/button/button.component'; + @Component({ selector: 'app-not-found', - imports: [ - CommonModule - - ], + imports: [CommonModule, ButtonComponent,RouterLink], templateUrl: './not-found.component.html', - styleUrl: './not-found.component.css' + styleUrl: './not-found.component.css', }) export class NotFoundComponent { - - constructor(private location: Location) { } + constructor(private location: Location) {} /** * Navigiert den Benutzer eine Seite in der Browser-Historie zurück. @@ -21,4 +19,4 @@ export class NotFoundComponent { goBack(): void { this.location.back(); } -} \ No newline at end of file +} diff --git a/src/app/features/auth/_auth-common.css b/src/app/features/auth/_auth-common.css deleted file mode 100644 index 35d1dfb..0000000 --- a/src/app/features/auth/_auth-common.css +++ /dev/null @@ -1,85 +0,0 @@ -/* src\app\features\auth\_auth-common.css */ -/* ================================================================================= - * GEMEINSAME STILE FÜR DAS AUTH-FEATURE - * ================================================================================= */ - -/* -- Globale Layout-Elemente -- */ -.component-header { - text-align: center; - margin-bottom: 2rem; -} -.auth-title { - font-size: 1.75rem; - font-weight: 700; - color: var(--color-text); - margin-bottom: 0.5rem; -} -.auth-subtitle { - color: var(--color-text-light); - font-size: 1rem; -} - -/* -- Formular-Basiselemente & Helfer -- */ -form { - display: flex; - flex-direction: column; - gap: 1.25rem; -} -.btn-full-width { - width: 100%; -} -.btn-icon-left { - display: flex; - justify-content: center; - align-items: center; - gap: 0.75rem; -} -.error-text { - color: var(--color-danger); - font-size: 0.85rem; - padding-top: 0.25rem; -} -.info-text { - color: var(--color-text-light); - text-align: center; - line-height: 1.6; -} - -/* -- Links & Footer -- */ -.link { - color: var(--color-primary); - font-weight: 500; - text-decoration: none; - font-size: 0.9rem; - transition: color var(--transition-speed); -} -.link:hover { - text-decoration: underline; - color: var(--color-primary-dark); -} -.footer-link { - text-align: center; - margin-top: 1.5rem; - color: var(--color-text-light); - font-size: 0.95rem; -} - -/* -- Visueller Trenner ("oder") -- */ -.divider-or { - display: flex; - align-items: center; - text-align: center; - color: var(--color-text-light); - font-size: 0.85rem; - font-weight: 500; - margin: 0.5rem 0; -} -.divider-or::before, -.divider-or::after { - content: ''; - flex: 1; - border-bottom: 1px solid var(--color-border); -} -.divider-or:not(:empty)::before { margin-right: 1em; } -.divider-or:not(:empty)::after { margin-left: 1em; } - diff --git a/src/app/features/auth/auth.routes.ts b/src/app/features/auth/auth.routes.ts index 500bc7e..2a26115 100644 --- a/src/app/features/auth/auth.routes.ts +++ b/src/app/features/auth/auth.routes.ts @@ -6,6 +6,8 @@ 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'; export const AUTH_ROUTES: Routes = [ { @@ -26,6 +28,11 @@ export const AUTH_ROUTES: Routes = [ component: ResetPasswordComponent, title: 'Neues Passwort', }, + { + path: 'verify-email/:token', + component: VerifyEmailComponent, + title: 'Email bestätigen', + }, ], }, ]; diff --git a/src/app/features/auth/components/auth-layout/auth-layout.component.css b/src/app/features/auth/components/auth-layout/auth-layout.component.css index 090e40b..092e7c7 100644 --- a/src/app/features/auth/components/auth-layout/auth-layout.component.css +++ b/src/app/features/auth/components/auth-layout/auth-layout.component.css @@ -1,39 +1,56 @@ -/* src\app\features\auth\components\auth-layout\auth-layout.component.css */ -/* Stile NUR für den äußeren Rahmen aller Auth-Seiten */ +/* ================================================================================= + FINALES & ZENTRALISIERTES STYLING FÜR DAS AUTH-LAYOUT +================================================================================== */ + +:host ::ng-deep .form-content-wrapper { + flex-grow: 1; /* Sorgt dafür, dass der Inhalt den Platz füllt */ +} + +:host ::ng-deep .verify-content { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; +} +:host ::ng-deep .verify-content app-icon { + font-size: 64px; + color: var(--color-primary); + margin-bottom: 1.5rem; +} +:host ::ng-deep .verify-content .info-text.small { + font-size: 0.9rem; +} + +/* 1. Basis-Layout für den Container und die Karte */ :host { display: block; width: 100%; min-height: 100vh; background-color: var(--color-body-bg); } - .auth-container { display: grid; place-items: center; min-height: 100vh; padding: 1rem; } - .auth-card { - padding: 2rem; width: 100%; max-width: 450px; animation: fade-in 0.5s ease-out forwards; } - .auth-header { text-align: center; - margin-bottom: 0.5rem; + margin-bottom: 1.5rem; } - -.auth-logo { +.auth-header app-icon { + font-size: 48px; display: inline-block; padding: 0.75rem; background: var(--color-primary-gradient); color: #fff; border-radius: 50%; } - @keyframes fade-in { from { opacity: 0; @@ -44,3 +61,94 @@ transform: translateY(0); } } + +/* 2. Flexbox-Layout für den Karteninhalt (DER ENTSCHEIDENDE TEIL) */ +/* Wir verwenden ::ng-deep, damit diese Layout-Regeln auf die projizierten Inhalte wirken. */ +:host ::ng-deep .auth-content { + display: flex; + flex-direction: column; + height: 100%; +} + +/* 3. Stile für die gemeinsamen Elemente innerhalb der Auth-Seiten */ +:host ::ng-deep .component-header { + text-align: center; + margin-bottom: 2rem; +} +:host ::ng-deep .auth-title { + font-size: 1.75rem; + font-weight: 700; + color: var(--color-text); + margin-bottom: 0.5rem; +} +:host ::ng-deep .auth-subtitle { + color: var(--color-text-light); + font-size: 1rem; +} + +/* Der
-Tag oder ein anderer Inhalts-Wrapper wird anpassungsfähig */ +:host ::ng-deep form { + display: flex; + flex-direction: column; + gap: 1.25rem; + flex-grow: 1; /* WICHTIG: Nimmt den verfügbaren Platz ein */ +} + +/* Spezifische Formular-Elemente */ +:host ::ng-deep .form-actions { + text-align: right; + margin-top: -0.75rem; + margin-bottom: 0.5rem; +} +:host ::ng-deep .error-text { + color: var(--color-danger); + font-size: 0.85rem; + padding-top: 0.25rem; +} +:host ::ng-deep .info-text { + color: var(--color-text-light); + text-align: center; + line-height: 1.6; + margin-bottom: 1.5rem; +} + +/* Links & Footer */ +:host ::ng-deep .link { + color: var(--color-primary); + font-weight: 500; + text-decoration: none; + font-size: 0.9rem; + transition: color var(--transition-speed); +} +:host ::ng-deep .link:hover { + text-decoration: underline; + color: var(--color-primary-dark); +} +:host ::ng-deep .footer-link { + text-align: center; + margin-top: auto; /* WICHTIG: Schiebt den Footer immer nach ganz unten */ + padding-top: 1.5rem; /* Sorgt für Abstand zum Element darüber */ + color: var(--color-text-light); + font-size: 0.95rem; +} + +/* Visueller Trenner */ +:host ::ng-deep .divider-or { + display: flex; + align-items: center; + color: var(--color-text-light); + font-size: 0.85rem; + margin: 0.5rem 0; +} +:host ::ng-deep .divider-or::before, +:host ::ng-deep .divider-or::after { + content: ""; + flex: 1; + border-bottom: 1px solid var(--color-border); +} +:host ::ng-deep .divider-or:not(:empty)::before { + margin-right: 1em; +} +:host ::ng-deep .divider-or:not(:empty)::after { + margin-left: 1em; +} diff --git a/src/app/features/auth/components/auth-layout/auth-layout.component.html b/src/app/features/auth/components/auth-layout/auth-layout.component.html index 2e5b65f..7fe819d 100644 --- a/src/app/features/auth/components/auth-layout/auth-layout.component.html +++ b/src/app/features/auth/components/auth-layout/auth-layout.component.html @@ -1,29 +1,11 @@ - -
-
-
- -
- - + + +
-
+
diff --git a/src/app/features/auth/components/auth-layout/auth-layout.component.ts b/src/app/features/auth/components/auth-layout/auth-layout.component.ts index 4cd54aa..8fe6e5d 100644 --- a/src/app/features/auth/components/auth-layout/auth-layout.component.ts +++ b/src/app/features/auth/components/auth-layout/auth-layout.component.ts @@ -2,13 +2,11 @@ import { Component } from '@angular/core'; import { RouterOutlet } from '@angular/router'; import { CardComponent } from '../../../../shared/components/ui/card/card.component'; + @Component({ selector: 'app-auth-layout', imports: [RouterOutlet, CardComponent], templateUrl: './auth-layout.component.html', styleUrl: './auth-layout.component.css', }) -export class AuthLayoutComponent { - // Diese Komponente benötigt in der Regel keine eigene Logik. - // Sie dient nur als Hülle für die untergeordneten Routen. -} +export class AuthLayoutComponent {} diff --git a/src/app/features/auth/components/forgot-password/forgot-password.component.css b/src/app/features/auth/components/forgot-password/forgot-password.component.css index ba9f4a5..e69de29 100644 --- a/src/app/features/auth/components/forgot-password/forgot-password.component.css +++ b/src/app/features/auth/components/forgot-password/forgot-password.component.css @@ -1,28 +0,0 @@ -/* src\app\features\auth\components\forgot-password\forgot-password.component.css */ -@import '../../_auth-common.css'; - -/* Stile NUR für die Passwort-vergessen-Seite */ -:host { display: block; width: 100%; } - -form { - gap: 1.5rem; /* Etwas mehr Abstand als beim Login */ -} - -.info-text { - margin-bottom: 2rem; -} - -.success-message { - text-align: center; -} - -.success-title { - font-size: 1.25rem; - font-weight: 600; - color: var(--color-text); - margin-bottom: 0.75rem; -} - -.footer-link { - margin-top: 2rem; -} \ No newline at end of file diff --git a/src/app/features/auth/components/forgot-password/forgot-password.component.html b/src/app/features/auth/components/forgot-password/forgot-password.component.html index 945255b..b9ecaf7 100644 --- a/src/app/features/auth/components/forgot-password/forgot-password.component.html +++ b/src/app/features/auth/components/forgot-password/forgot-password.component.html @@ -2,28 +2,39 @@

Passwort vergessen?

-
-

- Geben Sie Ihre E-Mail-Adresse ein. Wir senden Ihnen einen Link, mit dem Sie Ihr Passwort zurücksetzen können. -

- -
- - -
- - -
+ +
+ +
+

+ Geben Sie Ihre E-Mail ein, wir senden Ihnen einen Link zum Zurücksetzen. +

+
+ + Link anfordern +
+
-
-

Prüfen Sie Ihr Postfach

-

- Wenn ein Konto mit {{ forgotPasswordForm.value.email }} existiert, wurde ein Link versendet. -

+ +
+

Prüfen Sie Ihr Postfach

+

+ Wenn ein Konto existiert, wurde ein Link an + {{ form.value.email }} gesendet. +

+
\ No newline at end of file +
diff --git a/src/app/features/auth/components/forgot-password/forgot-password.component.ts b/src/app/features/auth/components/forgot-password/forgot-password.component.ts index 1e544b6..33b44e1 100644 --- a/src/app/features/auth/components/forgot-password/forgot-password.component.ts +++ b/src/app/features/auth/components/forgot-password/forgot-password.component.ts @@ -2,38 +2,39 @@ import { Component } from '@angular/core'; import { CommonModule } from '@angular/common'; import { ReactiveFormsModule, FormBuilder, Validators, FormGroup } from '@angular/forms'; import { RouterLink } from '@angular/router'; +import { ButtonComponent } from '../../../../shared/components/ui/button/button.component'; +import { FormFieldComponent } from '../../../../shared/components/form/form-field/form-field.component'; @Component({ selector: 'app-forgot-password', imports: [ CommonModule, ReactiveFormsModule, - RouterLink + RouterLink,ButtonComponent,FormFieldComponent ], templateUrl: './forgot-password.component.html', styleUrl: './forgot-password.component.css' }) export class ForgotPasswordComponent { - forgotPasswordForm: FormGroup; - // Optional: Eine Eigenschaft, um eine Erfolgsmeldung anzuzeigen + form: FormGroup; emailSent = false; constructor(private fb: FormBuilder) { - this.forgotPasswordForm = this.fb.group({ + this.form = this.fb.group({ email: ['', [Validators.required, Validators.email]] }); } onSubmit() { - if (this.forgotPasswordForm.valid) { + if (this.form.valid) { // Hier würde die Logik zum Senden der E-Mail an das Backend stehen - console.log('Anforderung zum Zurücksetzen des Passworts für:', this.forgotPasswordForm.value.email); + console.log('Anforderung zum Zurücksetzen des Passworts für:', this.form.value.email); // z.B. this.authService.sendPasswordResetEmail(this.forgotPasswordForm.value.email); // Setze den Zustand, um die Erfolgsmeldung anzuzeigen this.emailSent = true; } else { - this.forgotPasswordForm.markAllAsTouched(); + this.form.markAllAsTouched(); } } } \ No newline at end of file diff --git a/src/app/features/auth/components/login/login.component.html b/src/app/features/auth/components/login/login.component.html index c8a5ade..64e98f2 100644 --- a/src/app/features/auth/components/login/login.component.html +++ b/src/app/features/auth/components/login/login.component.html @@ -1,36 +1,43 @@ -

Anmelden

Bitte geben Sie Ihre Daten ein, um fortzufahren.

-
- - -
+ + -
- - -
+ + - +
oder
- + + Mit Google anmelden + +
- - \ No newline at end of file + \ No newline at end of file diff --git a/src/app/features/auth/components/login/login.component.ts b/src/app/features/auth/components/login/login.component.ts index ab8f6cb..1354bf3 100644 --- a/src/app/features/auth/components/login/login.component.ts +++ b/src/app/features/auth/components/login/login.component.ts @@ -28,6 +28,8 @@ import { FormFieldComponent } from '../../../../shared/components/form/form-fiel export class LoginComponent { loginForm: FormGroup; + + constructor(private fb: FormBuilder) { this.loginForm = this.fb.group({ email: ['', [Validators.required, Validators.email]], diff --git a/src/app/features/auth/components/register/register.component.css b/src/app/features/auth/components/register/register.component.css index 50996d4..e69de29 100644 --- a/src/app/features/auth/components/register/register.component.css +++ b/src/app/features/auth/components/register/register.component.css @@ -1,8 +0,0 @@ -/* src\app\features\auth\components\register\register.component.css */ -@import '../../_auth-common.css'; - -/* Stile NUR für die Registrierungs-Seite */ -:host { display: block; width: 100%; } - -/* Diese Komponente hat momentan keine spezifischen Extra-Stile, - aber die Datei ist da, falls welche benötigt werden. */ \ No newline at end of file diff --git a/src/app/features/auth/components/register/register.component.html b/src/app/features/auth/components/register/register.component.html index 88edad2..504d7c3 100644 --- a/src/app/features/auth/components/register/register.component.html +++ b/src/app/features/auth/components/register/register.component.html @@ -4,34 +4,44 @@
-
- - -
- -
- - -
- -
- - + + + + + +
+ Die Passwörter stimmen nicht überein.
-
- - -
- Die Passwörter stimmen nicht überein. -
-
- - + \ No newline at end of file +
diff --git a/src/app/features/auth/components/register/register.component.ts b/src/app/features/auth/components/register/register.component.ts index f6c351c..5736561 100644 --- a/src/app/features/auth/components/register/register.component.ts +++ b/src/app/features/auth/components/register/register.component.ts @@ -4,10 +4,12 @@ import { ReactiveFormsModule, FormBuilder, Validators, FormGroup } from '@angula import { RouterLink } from '@angular/router'; // Optional: Ein Custom Validator für den Passwort-Vergleich import { passwordMatchValidator } from '../../../../shared/validators/password-match.validator'; +import { ButtonComponent } from '../../../../shared/components/ui/button/button.component'; +import { FormFieldComponent } from '../../../../shared/components/form/form-field/form-field.component'; @Component({ selector: 'app-register', - imports: [CommonModule, ReactiveFormsModule, RouterLink], + imports: [CommonModule, ReactiveFormsModule, RouterLink,ButtonComponent,FormFieldComponent], templateUrl: './register.component.html', styleUrl: './register.component.css' }) diff --git a/src/app/features/auth/components/reset-password/reset-password.component.css b/src/app/features/auth/components/reset-password/reset-password.component.css index 933c3e5..e69de29 100644 --- a/src/app/features/auth/components/reset-password/reset-password.component.css +++ b/src/app/features/auth/components/reset-password/reset-password.component.css @@ -1,5 +0,0 @@ -/* src\app\features\auth\components\reset-password\reset-password.component.css */ -@import '../../_auth-common.css'; - -/* Stile NUR für die Passwort-zurücksetzen-Seite */ -:host { display: block; width: 100%; } \ No newline at end of file diff --git a/src/app/features/auth/components/reset-password/reset-password.component.html b/src/app/features/auth/components/reset-password/reset-password.component.html index d2ae5d0..53c1bcb 100644 --- a/src/app/features/auth/components/reset-password/reset-password.component.html +++ b/src/app/features/auth/components/reset-password/reset-password.component.html @@ -4,18 +4,32 @@
-
- - + + + +
+ Die Passwörter stimmen nicht überein.
-
- - -
- Die Passwörter stimmen nicht überein. -
-
- - \ No newline at end of file + + + + diff --git a/src/app/features/auth/components/reset-password/reset-password.component.ts b/src/app/features/auth/components/reset-password/reset-password.component.ts index 4096b3d..c56fbdd 100644 --- a/src/app/features/auth/components/reset-password/reset-password.component.ts +++ b/src/app/features/auth/components/reset-password/reset-password.component.ts @@ -1,29 +1,43 @@ import { Component, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; -import { ReactiveFormsModule, FormBuilder, Validators, FormGroup } from '@angular/forms'; +import { + ReactiveFormsModule, + FormBuilder, + Validators, + FormGroup, +} from '@angular/forms'; import { Router, ActivatedRoute } from '@angular/router'; import { passwordMatchValidator } from '../../../../shared/validators/password-match.validator'; - +import { ButtonComponent } from '../../../../shared/components/ui/button/button.component'; +import { FormFieldComponent } from '../../../../shared/components/form/form-field/form-field.component'; @Component({ selector: 'app-reset-password', - - imports: [CommonModule, ReactiveFormsModule], + + imports: [ + CommonModule, + ReactiveFormsModule, + ButtonComponent, + FormFieldComponent, + ], templateUrl: './reset-password.component.html', - styleUrl: './reset-password.component.css' + styleUrl: './reset-password.component.css', }) export class ResetPasswordComponent implements OnInit { resetPasswordForm: FormGroup; token: string | null = null; - + constructor( private fb: FormBuilder, private route: ActivatedRoute, private router: Router ) { - this.resetPasswordForm = this.fb.group({ - password: ['', [Validators.required, Validators.minLength(8)]], - confirmPassword: ['', [Validators.required]] - }, { validators: passwordMatchValidator }); + this.resetPasswordForm = this.fb.group( + { + password: ['', [Validators.required, Validators.minLength(8)]], + confirmPassword: ['', [Validators.required]], + }, + { validators: passwordMatchValidator } + ); } ngOnInit(): void { @@ -45,4 +59,4 @@ export class ResetPasswordComponent implements OnInit { this.resetPasswordForm.markAllAsTouched(); } } -} \ No newline at end of file +} diff --git a/src/app/features/auth/components/verify-email/verify-email.component.css b/src/app/features/auth/components/verify-email/verify-email.component.css index fe46f8a..e69de29 100644 --- a/src/app/features/auth/components/verify-email/verify-email.component.css +++ b/src/app/features/auth/components/verify-email/verify-email.component.css @@ -1,22 +0,0 @@ -/* src\app\features\auth\components\register\register.component.css */ -@import '../../_auth-common.css'; - -/* Stile NUR für die E-Mail-verifizieren-Seite */ -:host { display: block; width: 100%; } - -.verify-content { - text-align: center; -} -.icon { - color: var(--color-primary); - margin-bottom: 1.5rem; -} -.info-text { - margin-bottom: 1rem; -} -.info-text.small { - font-size: 0.9rem; -} -.footer-link { - margin-top: 2rem; -} \ No newline at end of file diff --git a/src/app/features/auth/components/verify-email/verify-email.component.html b/src/app/features/auth/components/verify-email/verify-email.component.html index acc16de..07a6f22 100644 --- a/src/app/features/auth/components/verify-email/verify-email.component.html +++ b/src/app/features/auth/components/verify-email/verify-email.component.html @@ -1,12 +1,12 @@

Bestätigen Sie Ihre E-Mail

+

+ Ein Bestätigungslink wurde an Ihre E-Mail gesendet. Bitte aktivieren Sie Ihr + Konto. +

-
- -

- Ein Bestätigungslink wurde an Ihre E-Mail-Adresse gesendet. Bitte klicken Sie darauf, um Ihr Konto zu aktivieren. -

+

Keine E-Mail erhalten? Erneut senden.

@@ -14,4 +14,4 @@ \ No newline at end of file +
diff --git a/src/app/features/demo/components/demo2/demo2.component.html b/src/app/features/demo/components/demo2/demo2.component.html index 69518f9..cbc8560 100644 --- a/src/app/features/demo/components/demo2/demo2.component.html +++ b/src/app/features/demo/components/demo2/demo2.component.html @@ -26,26 +26,43 @@
- - + - + + - - + + + + + + + - - - - - + + +
diff --git a/src/app/features/demo/components/demo2/demo2.component.ts b/src/app/features/demo/components/demo2/demo2.component.ts index 07e50a5..d2bdd0a 100644 --- a/src/app/features/demo/components/demo2/demo2.component.ts +++ b/src/app/features/demo/components/demo2/demo2.component.ts @@ -24,7 +24,12 @@ import { SelectOption, } from '../../../../shared/components/form/form-select/form-select.component'; -import { FormsModule } from '@angular/forms'; +import { + FormBuilder, + FormGroup, + FormsModule, + Validators, +} from '@angular/forms'; import { FormTextareaComponent } from '../../../../shared/components/form/form-textarea/form-textarea.component'; @@ -40,6 +45,8 @@ import { ButtonComponent } from '../../../../shared/components/ui/button/button. import { ChipComponent } from '../../../../shared/components/ui/chip/chip.component'; +import { ReactiveFormsModule } from '@angular/forms'; + // Wir definieren ein Interface für unsere KPI-Daten für Typsicherheit interface Kpi { value: string; @@ -65,11 +72,16 @@ interface Kpi { ExpansionPanelComponent, PageHeaderComponent, ButtonComponent, - ChipComponent + ChipComponent, + ReactiveFormsModule ], templateUrl: './demo2.component.html', }) export class Demo2Component { + + demoForm: FormGroup; + + kpiData: Kpi[] = [ { value: '€ 14.750', @@ -199,8 +211,6 @@ export class Demo2Component { }, ]; - benutzername: string = ''; - email: string = ''; aktuellesPasswort: any = ''; neuesPasswort: any = ''; @@ -221,11 +231,20 @@ export class Demo2Component { private readonly darkModeKey = 'app-dark-mode-setting'; darkModeAktiv: boolean = false; + constructor( private renderer: Renderer2, @Inject(DOCUMENT) private document: Document, - @Inject(PLATFORM_ID) private platformId: Object - ) {} + @Inject(PLATFORM_ID) private platformId: Object, + + private fb: FormBuilder + ) { + this.demoForm = this.fb.group({ + username: [''], // Entspricht dem [(value)]="benutzername" + email: ['', [Validators.required, Validators.email]], + password: ['', [Validators.required]] + }); + } ngOnInit(): void { this.loadThemeSetting(); diff --git a/src/app/features/demo/demo.routes.ts b/src/app/features/demo/demo.routes.ts index 0f0bd58..68ae8cc 100644 --- a/src/app/features/demo/demo.routes.ts +++ b/src/app/features/demo/demo.routes.ts @@ -15,13 +15,13 @@ export const DEMO_ROUTES: Routes = [ // Diese Route passt auf '/demo/1' und lädt die Komponente genau einmal. path: '1', component: Demo1Component, - title: 'Demo', + title: 'Demo1', }, { // Diese Route passt auf '/demo/1' und lädt die Komponente genau einmal. path: '2', component: Demo2Component, - title: 'Demo', + title: 'Demo2', }, // Hier könntest du weitere Routen wie '2', '3' etc. hinzufügen, // die andere Komponenten laden. diff --git a/src/app/shared/components/form/form-field/form-field.component.html b/src/app/shared/components/form/form-field/form-field.component.html index 8d8f568..f7f9baf 100644 --- a/src/app/shared/components/form/form-field/form-field.component.html +++ b/src/app/shared/components/form/form-field/form-field.component.html @@ -1,11 +1,13 @@
- - + [disabled]="disabled" + [(ngModel)]="value" + (ngModelChange)="onChange($event)" + (blur)="onTouched()"> + +
\ No newline at end of file diff --git a/src/app/shared/components/form/form-field/form-field.component.ts b/src/app/shared/components/form/form-field/form-field.component.ts index 08f6529..c98fe57 100644 --- a/src/app/shared/components/form/form-field/form-field.component.ts +++ b/src/app/shared/components/form/form-field/form-field.component.ts @@ -1,32 +1,57 @@ -import { Component, Input, Output, EventEmitter } from '@angular/core'; +import { Component, Input, forwardRef } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { + FormsModule, + ControlValueAccessor, + NG_VALUE_ACCESSOR, +} from '@angular/forms'; @Component({ selector: 'app-form-field', standalone: true, - imports: [CommonModule], // Kein FormsModule mehr nötig + imports: [ + CommonModule, + FormsModule, // Wichtig für [(ngModel)] im Template + ], templateUrl: './form-field.component.html', styleUrl: './form-field.component.css', - // Kein 'providers'-Block für ControlValueAccessor mehr + providers: [ + { + // Stellt diese Komponente als "Value Accessor" zur Verfügung + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => FormFieldComponent), + multi: true, + }, + ], }) -export class FormFieldComponent { - // EINGÄNGE: Werte, die von außen gesetzt werden +// Die Komponente implementiert die ControlValueAccessor-Schnittstelle +export class FormFieldComponent implements ControlValueAccessor { @Input() label: string = ''; @Input() type: 'text' | 'email' | 'password' = 'text'; - @Input() value: string = ''; // Der aktuelle Wert des Feldes - @Input() disabled: boolean = false; - // AUSGANG: Ein Event, das ausgelöst wird, wenn sich der Wert ändert - // WICHTIG: Der Name muss `[InputName]Change` sein, also `valueChange` - @Output() valueChange = new EventEmitter(); + controlId = `form-field-${Math.random().toString(36)}`; - /** - * Diese Methode wird bei jeder Tastatureingabe im Input-Feld aufgerufen. - */ - onInput(event: Event): void { - // 1. Hole den neuen Wert aus dem HTML-Input-Element - const newValue = (event.target as HTMLInputElement).value; - // 2. Sende den neuen Wert über den EventEmitter nach außen - this.valueChange.emit(newValue); + // --- Eigenschaften für ControlValueAccessor --- + value: string = ''; + onChange: (value: any) => void = () => {}; + onTouched: () => void = () => {}; + disabled = false; + + // --- Methoden, die von Angular Forms aufgerufen werden --- + + writeValue(value: any): void { + this.value = value; } -} \ No newline at end of file + + registerOnChange(fn: any): void { + this.onChange = fn; + } + + registerOnTouched(fn: any): void { + this.onTouched = fn; + } + + setDisabledState?(isDisabled: boolean): void { + this.disabled = isDisabled; + } +}