komponents erstellen

This commit is contained in:
Tizian.Breuch
2025-09-08 18:17:32 +02:00
parent 27cfa1e925
commit ee8974e2ff
71 changed files with 1627 additions and 27 deletions

View File

@@ -1,3 +1,5 @@
/* src\app\app.component.css */
@import "tailwindcss"; @import "tailwindcss";
/* /*

View File

@@ -1,2 +1,5 @@
<router-outlet></router-outlet> <main>
<app-snackbar-container></app-snackbar-container> <router-outlet></router-outlet>
</main>
<app-snackbar-container></app-snackbar-container>
<app-cookie-consent></app-cookie-consent>

View File

@@ -1,12 +1,13 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router'; import { RouterOutlet } from '@angular/router';
import { SnackbarContainerComponent } from './shared/snackbar/components/snackbar-container/snackbar-container.component'; import { SnackbarContainerComponent } from './shared/components/snackbar-container/snackbar-container.component';
import { CookieConsentComponent } from './core/components/cookie-consent/cookie-consent.component';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
imports: [RouterOutlet,SnackbarContainerComponent], imports: [RouterOutlet, SnackbarContainerComponent, CookieConsentComponent],
templateUrl: './app.component.html', templateUrl: './app.component.html',
styleUrl: './app.component.css' styleUrl: './app.component.css',
}) })
export class AppComponent { export class AppComponent {
title = 'frontend'; title = 'frontend';

View File

@@ -1,5 +1,8 @@
import { Routes } from '@angular/router'; import { Routes } from '@angular/router';
import { AccessDeniedComponent } from './core/components/access-denied/access-denied.component';
import { NotFoundComponent } from './core/components/not-found/not-found.component';
export const routes: Routes = [ export const routes: Routes = [
// Regel 1: Leerer Pfad (Startseite der Anwendung) // Regel 1: Leerer Pfad (Startseite der Anwendung)
// Ein Benutzer, der die Haupt-URL Ihrer Seite aufruft (z.B. "http://localhost:4200/"), // Ein Benutzer, der die Haupt-URL Ihrer Seite aufruft (z.B. "http://localhost:4200/"),
@@ -7,20 +10,28 @@ export const routes: Routes = [
{ {
path: '', path: '',
redirectTo: 'auth', // Leitet zur /auth-Route weiter redirectTo: 'auth', // Leitet zur /auth-Route weiter
pathMatch: 'full' // Wichtig: Gilt nur für den exakt leeren Pfad pathMatch: 'full', // Wichtig: Gilt nur für den exakt leeren Pfad
}, },
// Regel 2: Authentifizierungs-Feature // Regel 2: Authentifizierungs-Feature
// Alle URLs, die mit "auth/" beginnen (z.B. "/auth/login", "/auth/register"), // Alle URLs, die mit "auth/" beginnen (z.B. "/auth/login", "/auth/register"),
// werden von dieser Regel abgefangen und an die auth.routes.ts zur // werden von dieser Regel abgefangen und an die auth.routes.ts zur
// weiteren Verarbeitung übergeben. // weiteren Verarbeitung übergeben.
{ {
path: 'auth', path: 'auth',
loadChildren: () => import('./features/auth/auth.routes').then(r => r.AUTH_ROUTES) loadChildren: () =>
import('./features/auth/auth.routes').then((r) => r.AUTH_ROUTES),
}, },
{ {
path: 'demo', path: 'demo',
loadChildren: () => import('./features/demo/demo.routes').then(r => r.DEMO_ROUTES) loadChildren: () =>
import('./features/demo/demo.routes').then((r) => r.DEMO_ROUTES),
},
{
path: 'access-denied',
component: AccessDeniedComponent,
title: 'Zugriff verweigert',
}, },
// Regel 3: Fallback-Route (Wildcard) // Regel 3: Fallback-Route (Wildcard)
@@ -29,6 +40,7 @@ export const routes: Routes = [
// Das stellt sicher, dass Benutzer immer auf einer validen Seite landen. // Das stellt sicher, dass Benutzer immer auf einer validen Seite landen.
{ {
path: '**', path: '**',
redirectTo: 'auth' component: NotFoundComponent,
} title: '404 - Seite nicht gefunden',
]; },
];

View File

@@ -0,0 +1,52 @@
/* src\app\core\components\access-denied\access-denied.component.css */
:host {
display: block;
background-color: var(--color-body-bg);
}
.denied-container {
display: grid;
place-items: center;
min-height: 100vh;
padding: 1rem;
}
.denied-box {
max-width: 550px;
text-align: center;
padding: 2rem;
background-color: var(--color-surface);
border-radius: var(--border-radius-md);
border: 1px solid var(--color-border);
animation: fade-in 0.5s ease-out;
}
.icon {
color: var(--color-danger);
margin-bottom: 1.5rem;
}
.title {
font-size: 2rem;
font-weight: 700;
color: var(--color-text);
margin-bottom: 0.75rem;
}
.subtitle {
color: var(--color-text-light);
line-height: 1.7;
margin-bottom: 2.5rem;
}
.actions {
display: flex;
justify-content: center;
gap: 1rem;
flex-wrap: wrap; /* Sorgt für Umbruch auf kleinen Bildschirmen */
}
@keyframes fade-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}

View File

@@ -1 +1,32 @@
<p>access-denied works!</p> <div class="denied-container">
<div class="denied-box">
<!-- Visuelles Icon (ein Schild symbolisiert Schutz/Berechtigung) -->
<svg
class="icon"
xmlns="http://www.w3.org/2000/svg"
width="64"
height="64"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path>
</svg>
<h1 class="title">Zugriff verweigert</h1>
<p class="subtitle">
Ihnen fehlen die erforderlichen Berechtigungen, um auf diese Seite
zuzugreifen. Bitte wenden Sie sich an einen Administrator, wenn Sie
glauben, dass dies ein Fehler ist.
</p>
<div class="actions">
<button class="btn btn-stroked" (click)="goBack()">
Zurück zur vorherigen Seite
</button>
</div>
</div>
</div>

View File

@@ -1,11 +1,22 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { CommonModule, Location } from '@angular/common';
import { RouterLink } from '@angular/router';
@Component({ @Component({
selector: 'app-access-denied', selector: 'app-access-denied',
imports: [], imports: [CommonModule],
templateUrl: './access-denied.component.html', templateUrl: './access-denied.component.html',
styleUrl: './access-denied.component.css' styleUrl: './access-denied.component.css',
}) })
export class AccessDeniedComponent { export class AccessDeniedComponent {
// Wir injizieren den 'Location'-Service von Angular,
// um sicher in der Browser-Historie zurückgehen zu können.
constructor(private location: Location) {}
/**
* Navigiert den Benutzer zur vorherigen Seite in der Browser-Historie.
*/
goBack(): void {
this.location.back();
}
} }

View File

@@ -0,0 +1,75 @@
/* src\app\core\components\cookie-consent\cookie-consent.component.css */
@keyframes slide-up-fade-in {
from {
opacity: 0;
transform: translateY(100%);
}
to {
opacity: 1;
transform: translateY(0);
}
}
:host {
/* Stellt sicher, dass der Container keine Positionierungsprobleme verursacht */
display: contents;
}
.consent-banner-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
z-index: 1100; /* Muss über allen anderen Inhalten liegen */
padding: 1rem;
display: flex;
justify-content: center;
/* Animiert das Erscheinen des Banners */
animation: slide-up-fade-in 0.5s ease-out;
}
.consent-banner {
width: 100%;
max-width: 1200px;
display: flex;
justify-content: space-between;
align-items: center;
gap: 1.5rem;
padding: 1.25rem 1.5rem;
background-color: var(--color-surface);
color: var(--color-text);
border: 1px solid var(--color-border);
border-radius: var(--border-radius-md);
box-shadow: var(--box-shadow-md);
}
.consent-text p {
margin: 0;
color: var(--color-text-light);
font-size: 0.95rem;
line-height: 1.6;
}
.consent-actions {
display: flex;
gap: 0.75rem;
flex-shrink: 0; /* Verhindert, dass die Buttons schrumpfen */
}
/* Responsivität für mobile Geräte */
@media (max-width: 768px) {
.consent-banner {
flex-direction: column;
text-align: center;
}
.consent-actions {
width: 100%;
justify-content: center;
}
.consent-actions .btn {
flex-grow: 1; /* Buttons nehmen die volle Breite ein */
}
}

View File

@@ -1 +1,18 @@
<p>cookie-consent works!</p> <!-- Der Container wird nur angezeigt, wenn isVisible true ist -->
<div class="consent-banner-container" *ngIf="isVisible">
<div class="consent-banner">
<div class="consent-text">
<p>
Wir verwenden Cookies, um Ihr Surferlebnis zu verbessern und die Nutzung der Website zu analysieren.
Indem Sie auf "Akzeptieren" klicken, stimmen Sie der Verwendung von Cookies zu.
</p>
</div>
<div class="consent-actions">
<button class="btn btn-stroked" (click)="decline()">Ablehnen</button>
<button class="btn btn-primary" (click)="accept()">Akzeptieren</button>
</div>
</div>
</div>

View File

@@ -1,11 +1,53 @@
import { Component } from '@angular/core'; import { Component, OnInit, Inject, PLATFORM_ID } from '@angular/core';
import { isPlatformBrowser, CommonModule } from '@angular/common'; // isPlatformBrowser importieren
@Component({ @Component({
selector: 'app-cookie-consent', selector: 'app-cookie-consent',
imports: [], imports: [CommonModule],
templateUrl: './cookie-consent.component.html', templateUrl: './cookie-consent.component.html',
styleUrl: './cookie-consent.component.css' styleUrl: './cookie-consent.component.css'
}) })
export class CookieConsentComponent { export class CookieConsentComponent implements OnInit {
} isVisible = false;
private readonly consentKey = 'cookie_consent_status';
private isBrowser: boolean;
// Wir injizieren PLATFORM_ID, um die aktuelle Plattform (Browser/Server) zu ermitteln.
constructor(@Inject(PLATFORM_ID) private platformId: Object) {
// Speichere das Ergebnis in einer Eigenschaft, um es wiederverwenden zu können.
this.isBrowser = isPlatformBrowser(this.platformId);
}
ngOnInit(): void {
// Wir führen die Prüfung nur aus, wenn der Code im Browser läuft.
if (this.isBrowser) {
this.checkConsent();
}
}
private checkConsent(): void {
const consent = localStorage.getItem(this.consentKey);
if (!consent) {
this.isVisible = true;
}
}
accept(): void {
// Wir stellen sicher, dass wir localStorage nur im Browser schreiben.
if (this.isBrowser) {
localStorage.setItem(this.consentKey, 'accepted');
this.isVisible = false;
console.log('Cookies wurden akzeptiert.');
}
}
decline(): void {
// Wir stellen sicher, dass wir localStorage nur im Browser schreiben.
if (this.isBrowser) {
localStorage.setItem(this.consentKey, 'declined');
this.isVisible = false;
console.log('Cookies wurden abgelehnt.');
}
}
}

View File

@@ -0,0 +1,77 @@
/* src\app\core\components\not-found\not-found.component.css */
:host {
display: block;
background-color: var(--color-body-bg);
}
.not-found-container {
display: grid;
place-items: center;
min-height: 100vh;
padding: 1rem;
}
.not-found-box {
position: relative;
max-width: 550px;
text-align: center;
padding: 2rem;
background-color: var(--color-surface);
border-radius: var(--border-radius-md);
border: 1px solid var(--color-border);
animation: fade-in 0.5s ease-out;
}
.status-code {
position: absolute;
top: 1.5rem;
left: 50%;
transform: translateX(-50%);
font-size: 8rem;
font-weight: 900;
color: var(--color-border);
z-index: 0;
user-select: none; /* Text kann nicht markiert werden */
}
body.dark-theme .status-code {
color: rgba(255, 255, 255, 0.05);
}
.title, .subtitle, .actions {
/* Positioniert die Inhalte VOR dem 404-Text */
position: relative;
z-index: 1;
}
.title {
padding-top: 6rem; /* Platz für den 404-Text schaffen */
font-size: 2rem;
font-weight: 700;
color: var(--color-text);
margin-bottom: 0.75rem;
}
.subtitle {
color: var(--color-text-light);
line-height: 1.7;
margin-bottom: 2.5rem;
}
.actions {
display: flex;
justify-content: center;
gap: 1rem;
flex-wrap: wrap;
}
.actions .btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
}
@keyframes fade-in {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}

View File

@@ -1 +1,23 @@
<p>not-found works!</p> <div class="not-found-container">
<div class="not-found-box">
<div class="status-code">404</div>
<h1 class="title">Seite nicht gefunden</h1>
<p class="subtitle">
Die von Ihnen angeforderte Seite konnte leider nicht gefunden werden.
Möglicherweise wurde sie verschoben, gelöscht oder die URL ist falsch.
</p>
<div class="actions">
<button class="btn btn-stroked" (click)="goBack()">
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="19" y1="12" x2="5" y2="12"></line><polyline points="12 19 5 12 12 5"></polyline></svg>
<span>Zurück</span>
</button>
<button class="btn btn-primary" routerLink="/"> <!-- Link zur Startseite der App -->
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"></path><polyline points="9 22 9 12 15 12 15 22"></polyline></svg>
<span>Zur Startseite</span>
</button>
</div>
</div>
</div>

View File

@@ -1,11 +1,24 @@
import { Component } from '@angular/core'; import { Component } from '@angular/core';
import { CommonModule, Location } from '@angular/common';
import { RouterLink } from '@angular/router';
@Component({ @Component({
selector: 'app-not-found', selector: 'app-not-found',
imports: [], imports: [
CommonModule
],
templateUrl: './not-found.component.html', templateUrl: './not-found.component.html',
styleUrl: './not-found.component.css' styleUrl: './not-found.component.css'
}) })
export class NotFoundComponent { export class NotFoundComponent {
} constructor(private location: Location) { }
/**
* Navigiert den Benutzer eine Seite in der Browser-Historie zurück.
*/
goBack(): void {
this.location.back();
}
}

View File

@@ -1,3 +1,4 @@
/* src\app\features\auth\_auth-common.css */
/* ================================================================================= /* =================================================================================
* GEMEINSAME STILE FÜR DAS AUTH-FEATURE * GEMEINSAME STILE FÜR DAS AUTH-FEATURE
* ================================================================================= */ * ================================================================================= */

View File

@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common';
import { ReactiveFormsModule, FormBuilder, Validators, FormGroup } from '@angular/forms'; import { ReactiveFormsModule, FormBuilder, Validators, FormGroup } from '@angular/forms';
import { RouterLink } from '@angular/router'; import { RouterLink } from '@angular/router';
// Optional: Ein Custom Validator für den Passwort-Vergleich // Optional: Ein Custom Validator für den Passwort-Vergleich
import { passwordMatchValidator } from '../../../../core/validators/password-match.validator'; import { passwordMatchValidator } from '../../../../shared/validators/password-match.validator';
@Component({ @Component({
selector: 'app-register', selector: 'app-register',

View File

@@ -1,4 +1,4 @@
/* src\app\features\auth\components\register\register.component.css */ /* src\app\features\auth\components\reset-password\reset-password.component.css */
@import '../../_auth-common.css'; @import '../../_auth-common.css';
/* Stile NUR für die Passwort-zurücksetzen-Seite */ /* Stile NUR für die Passwort-zurücksetzen-Seite */

View File

@@ -2,7 +2,7 @@ import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common'; 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 { Router, ActivatedRoute } from '@angular/router';
import { passwordMatchValidator } from '../../../../core/validators/password-match.validator'; import { passwordMatchValidator } from '../../../../shared/validators/password-match.validator';
@Component({ @Component({
selector: 'app-reset-password', selector: 'app-reset-password',

View File

@@ -0,0 +1 @@
/* src\app\features\demo\components\demo\demo.component.css */

View File

@@ -2,7 +2,7 @@ import { Component, Renderer2, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { trigger, transition, style, animate } from '@angular/animations'; import { trigger, transition, style, animate } from '@angular/animations';
import { SnackbarService } from '../../../../shared/snackbar/services/snackbar.service'; import { SnackbarService } from '../../../../shared/services/snackbar.service';
@Component({ @Component({
selector: 'app-demo', selector: 'app-demo',

View File

@@ -0,0 +1 @@
<p>kpi-card works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-kpi-card',
imports: [],
templateUrl: './kpi-card.component.html',
styleUrl: './kpi-card.component.css'
})
export class KpiCardComponent {
}

View File

@@ -0,0 +1 @@
<p>form-field works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-form-field',
imports: [],
templateUrl: './form-field.component.html',
styleUrl: './form-field.component.css'
})
export class FormFieldComponent {
}

View File

@@ -0,0 +1 @@
<p>slide-toggle works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-slide-toggle',
imports: [],
templateUrl: './slide-toggle.component.html',
styleUrl: './slide-toggle.component.css'
})
export class SlideToggleComponent {
}

View File

@@ -0,0 +1 @@
<p>page-header works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-page-header',
imports: [],
templateUrl: './page-header.component.html',
styleUrl: './page-header.component.css'
})
export class PageHeaderComponent {
}

View File

@@ -0,0 +1 @@
<p>search-bar works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-search-bar',
imports: [],
templateUrl: './search-bar.component.html',
styleUrl: './search-bar.component.css'
})
export class SearchBarComponent {
}

View File

@@ -0,0 +1 @@
<p>user-profile works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-user-profile',
imports: [],
templateUrl: './user-profile.component.html',
styleUrl: './user-profile.component.css'
})
export class UserProfileComponent {
}

View File

@@ -0,0 +1 @@
<p>dialog works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-dialog',
imports: [],
templateUrl: './dialog.component.html',
styleUrl: './dialog.component.css'
})
export class DialogComponent {
}

View File

@@ -0,0 +1 @@
<p>menu works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-menu',
imports: [],
templateUrl: './menu.component.html',
styleUrl: './menu.component.css'
})
export class MenuComponent {
}

View File

@@ -1,3 +1,4 @@
/* src\app\shared\snackbar\components\snackbar-container\snackbar-container.component.css */
/* Stile, die NUR für den Container gelten */ /* Stile, die NUR für den Container gelten */
.snackbar-container-wrapper { .snackbar-container-wrapper {
position: fixed; position: fixed;

View File

@@ -1,3 +1,4 @@
/* src\app\shared\snackbar\components\snackbar\snackbar.component.css */
/* Stile, die NUR für eine einzelne Snackbar gelten */ /* Stile, die NUR für eine einzelne Snackbar gelten */
:host { :host {
position: absolute; position: absolute;

View File

@@ -0,0 +1 @@
<p>alert works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-alert',
imports: [],
templateUrl: './alert.component.html',
styleUrl: './alert.component.css'
})
export class AlertComponent {
}

View File

@@ -0,0 +1 @@
<p>button works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-button',
imports: [],
templateUrl: './button.component.html',
styleUrl: './button.component.css'
})
export class ButtonComponent {
}

View File

@@ -0,0 +1 @@
<p>card works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-card',
imports: [],
templateUrl: './card.component.html',
styleUrl: './card.component.css'
})
export class CardComponent {
}

View File

@@ -0,0 +1 @@
<p>chip works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-chip',
imports: [],
templateUrl: './chip.component.html',
styleUrl: './chip.component.css'
})
export class ChipComponent {
}

View File

@@ -0,0 +1 @@
<p>icon works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-icon',
imports: [],
templateUrl: './icon.component.html',
styleUrl: './icon.component.css'
})
export class IconComponent {
}

View File

@@ -0,0 +1 @@
<p>status-pill works!</p>

View File

@@ -0,0 +1,11 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-status-pill',
imports: [],
templateUrl: './status-pill.component.html',
styleUrl: './status-pill.component.css'
})
export class StatusPillComponent {
}

View File

@@ -1,3 +1,5 @@
/* src\styles.css */
/* ================================================================================= /* =================================================================================
FINALES & PRODUKTIONSREIFES STYLING-SYSTEM (VERSION 5.0) FINALES & PRODUKTIONSREIFES STYLING-SYSTEM (VERSION 5.0)
================================================================================== */ ================================================================================== */

1068
src/styles_OLD.css Normal file

File diff suppressed because it is too large Load Diff