components

This commit is contained in:
Tizian.Breuch
2025-09-30 11:30:53 +02:00
parent fbca5558ec
commit eff6d8d8aa
43 changed files with 1035 additions and 14 deletions

View File

@@ -1,9 +1,9 @@
import { KpiColor } from "../types/dashboard"; // import { KpiColor } from "../types/dashboard";
export interface Kpi { export interface Kpi {
value: string; value: string;
label: string; label: string;
color: KpiColor; // <-- Hier verwenden wir den importierten Typ color: any; // <-- Hier verwenden wir den importierten Typ
iconName: string; iconName: string;
} }

View File

@@ -1 +0,0 @@
export type KpiColor = 'blue' | 'green' | 'orange' | 'purple';

View File

@@ -1 +0,0 @@
export type OrderStatus = 'completed' | 'processing' | 'cancelled' | 'info';

View File

@@ -1 +0,0 @@
export type PillStatus = 'success' | 'warning' | 'danger' | 'info';

View File

@@ -0,0 +1,29 @@
<div>
<h1>Kategorien verwalten</h1>
<!-- Formular für Erstellen/Bearbeiten -->
<form [formGroup]="categoryForm" (ngSubmit)="onSubmit()">
<h3>{{ selectedCategoryId ? 'Kategorie bearbeiten' : 'Neue Kategorie erstellen' }}</h3>
<input type="text" formControlName="name" placeholder="Name">
<input type="text" formControlName="slug" placeholder="Slug">
<textarea formControlName="description" placeholder="Beschreibung"></textarea>
<label>
<input type="checkbox" formControlName="isActive"> Aktiv
</label>
<input type="file" (change)="onFileChange($event)">
<button type="submit" [disabled]="categoryForm.invalid">{{ selectedCategoryId ? 'Aktualisieren' : 'Erstellen' }}</button>
<button type="button" *ngIf="selectedCategoryId" (click)="clearSelection()">Abbrechen</button>
</form>
<hr>
<!-- Liste der Kategorien -->
<h2>Bestehende Kategorien</h2>
<ul>
<li *ngFor="let category of categories$ | async">
{{ category.name }} (Slug: {{ category.slug }}) - Aktiv: {{ category.isActive }}
<button (click)="selectCategory(category)">Bearbeiten</button>
<button (click)="onDelete(category.id)">Löschen</button>
</li>
</ul>
</div>

View File

@@ -0,0 +1,91 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Observable } from 'rxjs';
import { Category } from '../../../../core/models/category.model';
import { CategoryService } from '../../../services/category.service';
@Component({
selector: 'app-category-list',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
templateUrl: './category-list.component.html',
})
export class CategoryListComponent implements OnInit {
private categoryService = inject(CategoryService);
private fb = inject(FormBuilder);
categories$!: Observable<Category[]>;
categoryForm: FormGroup;
selectedCategoryId: string | null = null;
selectedFile: File | null = null;
constructor() {
this.categoryForm = this.fb.group({
name: ['', Validators.required],
slug: ['', Validators.required],
description: [''],
isActive: [true],
});
}
ngOnInit(): void {
this.loadCategories();
}
loadCategories(): void {
this.categories$ = this.categoryService.getAll();
}
onFileChange(event: Event): void {
const element = event.currentTarget as HTMLInputElement;
let fileList: FileList | null = element.files;
if (fileList) {
this.selectedFile = fileList[0];
}
}
selectCategory(category: Category): void {
this.selectedCategoryId = category.id;
this.categoryForm.patchValue(category);
}
clearSelection(): void {
this.selectedCategoryId = null;
this.categoryForm.reset({ isActive: true });
this.selectedFile = null;
}
onSubmit(): void {
if (this.categoryForm.invalid) return;
const formData = new FormData();
Object.keys(this.categoryForm.value).forEach(key => {
formData.append(key, this.categoryForm.value[key]);
});
if (this.selectedFile) {
formData.append('ImageFile', this.selectedFile, this.selectedFile.name);
}
if (this.selectedCategoryId) {
// Update
formData.append('Id', this.selectedCategoryId);
this.categoryService.update(this.selectedCategoryId, formData).subscribe(() => this.reset());
} else {
// Create
this.categoryService.create(formData).subscribe(() => this.reset());
}
}
onDelete(id: string): void {
if (confirm('Möchten Sie diese Kategorie wirklich löschen?')) {
this.categoryService.delete(id).subscribe(() => this.loadCategories());
}
}
private reset(): void {
this.loadCategories();
this.clearSelection();
}
}

View File

@@ -0,0 +1,19 @@
<div>
<h1>Admin Dashboard</h1>
<div>
<button (click)="loadAnalytics('Last7Days')">Letzte 7 Tage</button>
<button (click)="loadAnalytics('Last30Days')">Letzte 30 Tage</button>
<button (click)="loadAnalytics('AllTime')">Gesamt</button>
</div>
<div *ngIf="analytics$ | async as data">
<h2>KPIs</h2>
<pre>{{ data.kpiSummary | json }}</pre>
<h2>Verkäufe</h2>
<pre>{{ data.salesOverTime | json }}</pre>
<h2>Top Produkte</h2>
<pre>{{ data.topPerformingProducts | json }}</pre>
</div>
</div>

View File

@@ -0,0 +1,25 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Observable } from 'rxjs';
import { Analytics } from '../../../../core/models/analytics.model';
import { AnalyticsService } from '../../../services/analytics.service';
import { AnalyticsPeriod } from '../../../../core/enums/shared.enum';
@Component({
selector: 'app-admin-dashboard',
standalone: true,
imports: [CommonModule],
templateUrl: './dashboard.component.html',
})
export class AdminDashboardComponent implements OnInit {
private analyticsService = inject(AnalyticsService);
analytics$!: Observable<Analytics>;
ngOnInit(): void {
this.loadAnalytics('Last30Days');
}
loadAnalytics(period: AnalyticsPeriod): void {
this.analytics$ = this.analyticsService.get(period);
}
}

View File

@@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common';
import { IconComponent } from '../../../../shared/components/ui/icon/icon.component'; import { IconComponent } from '../../../../shared/components/ui/icon/icon.component';
import { CardComponent } from '../../../../shared/components/ui/card/card.component'; import { CardComponent } from '../../../../shared/components/ui/card/card.component';
import { KpiColor } from '../../../../core/types/dashboard'; // import { KpiColor } from '../../../../core/types/dashboard';
@Component({ @Component({
selector: 'app-kpi-card', selector: 'app-kpi-card',
@@ -15,7 +15,7 @@ import { KpiColor } from '../../../../core/types/dashboard';
export class KpiCardComponent { export class KpiCardComponent {
@Input() value: string = ''; @Input() value: string = '';
@Input() label: string = ''; @Input() label: string = '';
@Input() color: KpiColor = 'blue'; @Input() color: any = 'blue';
@Input() iconName: string | null = null; @Input() iconName: string | null = null;
@Input() svgColor: string | null = null; @Input() svgColor: string | null = null;
} }

View File

@@ -0,0 +1,47 @@
<div>
<h1>Rabatte verwalten</h1>
<form [formGroup]="discountForm" (ngSubmit)="onSubmit()">
<h3>{{ selectedDiscountId ? "Rabatt bearbeiten" : "Neuer Rabatt" }}</h3>
<input type="text" formControlName="name" placeholder="Name des Rabatts" />
<select formControlName="discountType">
<option *ngFor="let type of discountTypes" [value]="type">
{{ type }}
</option>
</select>
<input
type="number"
formControlName="discountValue"
placeholder="Wert (z.B. 10 für 10% oder 10€)"
/>
<input
type="text"
formControlName="couponCode"
placeholder="Gutscheincode (optional)"
/>
<label
><input type="checkbox" formControlName="requiresCouponCode" /> Code
erforderlich</label
>
<label><input type="checkbox" formControlName="isActive" /> Aktiv</label>
<input type="date" formControlName="startDate" />
<button type="submit" [disabled]="discountForm.invalid">
{{ selectedDiscountId ? "Aktualisieren" : "Erstellen" }}
</button>
<button type="button" *ngIf="selectedDiscountId" (click)="clearSelection()">
Abbrechen
</button>
</form>
<hr />
<h2>Bestehende Rabatte</h2>
<ul>
<li *ngFor="let discount of discounts$ | async">
{{ discount.name }} ({{ discount.discountValue
}}{{ discount.discountType === "Percentage" ? "%" : "€" }})
<button (click)="selectDiscount(discount)">Bearbeiten</button>
<button (click)="onDelete(discount.id)">Löschen</button>
</li>
</ul>
</div>

View File

@@ -0,0 +1,86 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Observable } from 'rxjs';
import { Discount } from '../../../../core/models/discount.model';
import { DiscountService } from '../../../services/discount.service';
import { DiscountType } from '../../../../core/enums/shared.enum';
@Component({
selector: 'app-discount-list',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
templateUrl: './discount-list.component.html',
})
export class DiscountListComponent implements OnInit {
private discountService = inject(DiscountService);
private fb = inject(FormBuilder);
discounts$!: Observable<Discount[]>;
discountForm: FormGroup;
selectedDiscountId: string | null = null;
discountTypes: DiscountType[] = ['Percentage', 'FixedAmount'];
constructor() {
this.discountForm = this.fb.group({
name: ['', Validators.required],
discountType: ['Percentage', Validators.required],
discountValue: [0, [Validators.required, Validators.min(0)]],
couponCode: [''],
requiresCouponCode: [false],
isActive: [true],
startDate: [new Date().toISOString().split('T')[0], Validators.required]
});
}
ngOnInit(): void { this.loadDiscounts(); }
loadDiscounts(): void { this.discounts$ = this.discountService.getAll(); }
selectDiscount(discount: Discount): void {
this.selectedDiscountId = discount.id;
// Format date for the input[type=date]
const formattedDiscount = {
...discount,
startDate: new Date(discount.startDate).toISOString().split('T')[0]
};
this.discountForm.patchValue(formattedDiscount);
}
clearSelection(): void {
this.selectedDiscountId = null;
this.discountForm.reset({
discountType: 'Percentage',
isActive: true,
requiresCouponCode: false,
startDate: new Date().toISOString().split('T')[0]
});
}
onSubmit(): void {
if (this.discountForm.invalid) return;
const formValue = this.discountForm.value;
const dataToSend: Discount = {
...formValue,
id: this.selectedDiscountId || '00000000-0000-0000-0000-000000000000', // Dummy ID for create
startDate: new Date(formValue.startDate).toISOString()
};
if (this.selectedDiscountId) {
this.discountService.update(this.selectedDiscountId, dataToSend).subscribe(() => this.reset());
} else {
this.discountService.create(dataToSend).subscribe(() => this.reset());
}
}
onDelete(id: string): void {
if (confirm('Rabatt wirklich löschen?')) {
this.discountService.delete(id).subscribe(() => this.loadDiscounts());
}
}
private reset(): void {
this.loadDiscounts();
this.clearSelection();
}
}

View File

@@ -0,0 +1,46 @@
<div>
<h1>Bestellungen verwalten</h1>
<!-- Liste -->
<table>
<thead>
<tr>
<th>Bestellnr.</th>
<th>Datum</th>
<th>Kunde</th>
<th>Betrag</th>
<th>Status</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let order of orders$ | async">
<td>{{ order.orderNumber }}</td>
<td>{{ order.orderDate | date : "short" }}</td>
<td>{{ order.customerName }}</td>
<td>{{ order.totalAmount | currency : "EUR" }}</td>
<td>
<select (change)="updateStatus(order.id, $event)">
<option
*ngFor="let status of orderStatuses"
[value]="status"
[selected]="status === order.status"
>
{{ status }}
</option>
</select>
</td>
<td><button (click)="viewDetails(order.id)">Details</button></td>
</tr>
</tbody>
</table>
<hr />
<!-- Detailansicht -->
<div *ngIf="selectedOrder">
<h2>Details für Bestellung {{ selectedOrder.orderNumber }}</h2>
<pre>{{ selectedOrder | json }}</pre>
<button (click)="selectedOrder = null">Schließen</button>
</div>
</div>

View File

@@ -0,0 +1,49 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Observable } from 'rxjs';
import { OrderDetail, OrderSummary } from '../../../../core/models/order.model';
import { OrderService } from '../../../services/order.service';
import { OrderStatus } from '../../../../core/enums/shared.enum';
@Component({
selector: 'app-order-list',
standalone: true,
imports: [CommonModule],
templateUrl: './order-list.component.html',
})
export class OrderListComponent implements OnInit {
private orderService = inject(OrderService);
orders$!: Observable<OrderSummary[]>;
selectedOrder: OrderDetail | null = null;
orderStatuses: OrderStatus[] = [
'Pending',
'Processing',
'Shipped',
'Delivered',
'Cancelled',
'Refunded',
];
ngOnInit(): void {
this.loadOrders();
}
loadOrders(): void {
this.orders$ = this.orderService.getAll();
this.selectedOrder = null;
}
viewDetails(id: string): void {
this.orderService.getById(id).subscribe((details) => {
this.selectedOrder = details;
});
}
updateStatus(id: string, event: Event): void {
const newStatus = (event.target as HTMLSelectElement).value as OrderStatus;
this.orderService.updateStatus(id, { newStatus }).subscribe(() => {
this.loadOrders(); // Lade alles neu, um die Änderungen zu sehen
});
}
}

View File

@@ -0,0 +1,23 @@
<div>
<h1>Zahlungsmethoden verwalten</h1>
<form [formGroup]="methodForm" (ngSubmit)="onSubmit()">
<h3>{{ selectedMethodId ? 'Methode bearbeiten' : 'Neue Methode' }}</h3>
<input type="text" formControlName="name" placeholder="Name">
<textarea formControlName="description" placeholder="Beschreibung"></textarea>
<select formControlName="paymentGatewayType">
<option *ngFor="let type of gatewayTypes" [value]="type">{{ type }}</option>
</select>
<label><input type="checkbox" formControlName="isActive"> Aktiv</label>
<button type="submit" [disabled]="methodForm.invalid">{{ selectedMethodId ? 'Aktualisieren' : 'Erstellen' }}</button>
<button type="button" *ngIf="selectedMethodId" (click)="clearSelection()">Abbrechen</button>
</form>
<hr>
<h2>Bestehende Methoden</h2>
<ul>
<li *ngFor="let method of methods$ | async">
{{ method.name }} (Typ: {{ method.paymentGatewayType }}) - Aktiv: {{ method.isActive }}
<button (click)="selectMethod(method)">Bearbeiten</button>
<button (click)="onDelete(method.id)">Löschen</button>
</li>
</ul>
</div>

View File

@@ -0,0 +1,67 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Observable } from 'rxjs';
import { AdminPaymentMethod } from '../../../../core/models/payment.model';
import { PaymentGatewayType } from '../../../../core/enums/shared.enum';
import { PaymentMethodService } from '../../../services/payment-method.service';
@Component({
selector: 'app-payment-method-list',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
templateUrl: './payment-method-list.component.html',
})
export class PaymentMethodListComponent implements OnInit {
private paymentMethodService = inject(PaymentMethodService);
private fb = inject(FormBuilder);
methods$!: Observable<AdminPaymentMethod[]>;
methodForm: FormGroup;
selectedMethodId: string | null = null;
gatewayTypes: PaymentGatewayType[] = ['BankTransfer', 'Invoice', 'PayPal', 'Stripe', 'CashOnDelivery'];
constructor() {
this.methodForm = this.fb.group({
name: ['', Validators.required],
description: [''],
isActive: [true],
paymentGatewayType: ['Invoice', Validators.required]
});
}
ngOnInit(): void { this.loadMethods(); }
loadMethods(): void { this.methods$ = this.paymentMethodService.getAll(); }
selectMethod(method: AdminPaymentMethod): void {
this.selectedMethodId = method.id;
this.methodForm.patchValue(method);
}
clearSelection(): void {
this.selectedMethodId = null;
this.methodForm.reset({ isActive: true, paymentGatewayType: 'Invoice' });
}
onSubmit(): void {
if (this.methodForm.invalid) return;
const dataToSend = { ...this.methodForm.value, id: this.selectedMethodId || '0' };
if (this.selectedMethodId) {
this.paymentMethodService.update(this.selectedMethodId, dataToSend).subscribe(() => this.reset());
} else {
this.paymentMethodService.create(dataToSend).subscribe(() => this.reset());
}
}
onDelete(id: string): void {
if (confirm('Zahlungsmethode wirklich löschen?')) {
this.paymentMethodService.delete(id).subscribe(() => this.loadMethods());
}
}
private reset(): void {
this.loadMethods();
this.clearSelection();
}
}

View File

@@ -0,0 +1,29 @@
<div>
<h1>Produkte verwalten</h1>
<!-- Formular -->
<form [formGroup]="productForm" (ngSubmit)="onSubmit()">
<h3>{{ selectedProductId ? 'Produkt bearbeiten' : 'Neues Produkt' }}</h3>
<input type="text" formControlName="name" placeholder="Produktname">
<input type="text" formControlName="slug" placeholder="Slug">
<input type="text" formControlName="sku" placeholder="SKU">
<input type="number" formControlName="price" placeholder="Preis">
<input type="number" formControlName="stockQuantity" placeholder="Lagerbestand">
<label><input type="checkbox" formControlName="isActive"> Aktiv</label>
<label><input type="checkbox" formControlName="isFeatured"> Hervorgehoben</label>
<button type="submit" [disabled]="productForm.invalid">{{ selectedProductId ? 'Aktualisieren' : 'Erstellen' }}</button>
<button type="button" *ngIf="selectedProductId" (click)="clearSelection()">Abbrechen</button>
</form>
<hr>
<!-- Liste -->
<h2>Bestehende Produkte</h2>
<ul>
<li *ngFor="let product of products$ | async">
{{ product.name }} (SKU: {{ product.sku }}) - Preis: {{ product.price | number:'1.2-2' }} €
<button (click)="selectProduct(product)">Bearbeiten</button>
<button (click)="onDelete(product.id)">Löschen</button>
</li>
</ul>
</div>

View File

@@ -0,0 +1,74 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Observable } from 'rxjs';
import { AdminProduct } from '../../../../core/models/product.model';
import { ProductService } from '../../../services/product.service';
@Component({
selector: 'app-product-list',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
templateUrl: './product-list.component.html',
})
export class ProductListComponent implements OnInit {
private productService = inject(ProductService);
private fb = inject(FormBuilder);
products$!: Observable<AdminProduct[]>;
productForm: FormGroup;
selectedProductId: string | null = null;
constructor() {
this.productForm = this.fb.group({
name: ['', Validators.required],
slug: ['', Validators.required],
sku: ['', Validators.required],
description: [''],
price: [0, [Validators.required, Validators.min(0)]],
stockQuantity: [0, [Validators.required, Validators.min(0)]],
isActive: [true],
isFeatured: [false]
});
}
ngOnInit(): void { this.loadProducts(); }
loadProducts(): void { this.products$ = this.productService.getAll(); }
selectProduct(product: AdminProduct): void {
this.selectedProductId = product.id;
this.productForm.patchValue(product);
}
clearSelection(): void {
this.selectedProductId = null;
this.productForm.reset({ isActive: true, isFeatured: false });
}
onSubmit(): void {
if (this.productForm.invalid) return;
const formData = new FormData();
Object.keys(this.productForm.value).forEach(key => {
formData.append(key, this.productForm.value[key]);
});
if (this.selectedProductId) {
formData.append('Id', this.selectedProductId);
this.productService.update(this.selectedProductId, formData).subscribe(() => this.reset());
} else {
this.productService.create(formData).subscribe(() => this.reset());
}
}
onDelete(id: string): void {
if (confirm('Produkt wirklich löschen?')) {
this.productService.delete(id).subscribe(() => this.loadProducts());
}
}
private reset(): void {
this.loadProducts();
this.clearSelection();
}
}

View File

@@ -0,0 +1,30 @@
<div>
<h1>Bewertungen verwalten</h1>
<table>
<thead>
<tr>
<th>Kunde</th>
<th>Produkt</th>
<th>Bewertung</th>
<th>Titel</th>
<th>Status</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let review of reviews$ | async">
<td>{{ review.customerName }}</td>
<td>{{ review.productName }}</td>
<td>{{ review.rating }}/5</td>
<td>{{ review.title }}</td>
<td>{{ review.isApproved ? "Freigegeben" : "Ausstehend" }}</td>
<td>
<button *ngIf="!review.isApproved" (click)="onApprove(review.id)">
Freigeben
</button>
<button (click)="onDelete(review.id)">Löschen</button>
</td>
</tr>
</tbody>
</table>
</div>

View File

@@ -0,0 +1,34 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Observable } from 'rxjs';
import { Review } from '../../../../core/models/review.model';
import { ReviewService } from '../../../services/review.service';
@Component({
selector: 'app-review-list',
standalone: true,
imports: [CommonModule],
templateUrl: './review-list.component.html',
})
export class ReviewListComponent implements OnInit {
private reviewService = inject(ReviewService);
reviews$!: Observable<Review[]>;
ngOnInit(): void {
this.loadReviews();
}
loadReviews(): void {
this.reviews$ = this.reviewService.getAll();
}
onApprove(id: string): void {
this.reviewService.approve(id).subscribe(() => this.loadReviews());
}
onDelete(id: string): void {
if (confirm('Bewertung wirklich löschen?')) {
this.reviewService.delete(id).subscribe(() => this.loadReviews());
}
}
}

View File

@@ -0,0 +1,29 @@
<div>
<h1>Einstellungen verwalten</h1>
<form [formGroup]="settingsForm" (ngSubmit)="onSubmit()">
<div formArrayName="settings">
<div *ngFor="let group of settingGroups">
<h3>{{ group.groupName }}</h3>
<div *ngFor="let setting of group.settings; let i = index">
<!-- Find the correct form group in the FormArray by key -->
<ng-container
*ngFor="let control of settingsArray.controls; let j = index"
>
<div
*ngIf="control.get('key')?.value === setting.key"
[formGroupName]="j"
>
<label>{{ setting.description || setting.key }}</label>
<input type="text" formControlName="value" />
<label
><input type="checkbox" formControlName="isActive" />
Aktiv</label
>
</div>
</ng-container>
</div>
</div>
</div>
<button type="submit">Einstellungen speichern</button>
</form>
</div>

View File

@@ -0,0 +1,54 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, FormArray, ReactiveFormsModule } from '@angular/forms';
import { Setting } from '../../../../core/models/setting.model';
import { SettingService } from '../../../services/setting.service';
@Component({
selector: 'app-settings',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
templateUrl: './settings.component.html',
})
export class SettingsComponent implements OnInit {
private settingService = inject(SettingService);
private fb = inject(FormBuilder);
settingsForm: FormGroup;
settingGroups: { groupName: string, settings: Setting[] }[] = [];
constructor() {
this.settingsForm = this.fb.group({
settings: this.fb.array([])
});
}
get settingsArray(): FormArray {
return this.settingsForm.get('settings') as FormArray;
}
ngOnInit(): void {
this.settingService.getAllGrouped().subscribe(groupedSettings => {
this.settingsArray.clear();
this.settingGroups = [];
Object.keys(groupedSettings).forEach(groupName => {
const settings = groupedSettings[groupName];
this.settingGroups.push({ groupName, settings });
settings.forEach(setting => {
this.settingsArray.push(this.fb.group({
key: [setting.key],
value: [setting.value],
isActive: [setting.isActive]
}));
});
});
});
}
onSubmit(): void {
if (this.settingsForm.invalid) return;
this.settingService.update(this.settingsForm.value.settings).subscribe(() => {
alert('Einstellungen gespeichert!');
});
}
}

View File

@@ -0,0 +1,21 @@
<div>
<h1>Versandmethoden verwalten</h1>
<form [formGroup]="methodForm" (ngSubmit)="onSubmit()">
<h3>{{ selectedMethodId ? 'Methode bearbeiten' : 'Neue Methode' }}</h3>
<input type="text" formControlName="name" placeholder="Name">
<textarea formControlName="description" placeholder="Beschreibung"></textarea>
<input type="number" formControlName="cost" placeholder="Kosten">
<label><input type="checkbox" formControlName="isActive"> Aktiv</label>
<button type="submit" [disabled]="methodForm.invalid">{{ selectedMethodId ? 'Aktualisieren' : 'Erstellen' }}</button>
<button type="button" *ngIf="selectedMethodId" (click)="clearSelection()">Abbrechen</button>
</form>
<hr>
<h2>Bestehende Methoden</h2>
<ul>
<li *ngFor="let method of methods$ | async">
{{ method.name }} ({{ method.cost | currency:'EUR' }}) - Aktiv: {{ method.isActive }}
<button (click)="selectMethod(method)">Bearbeiten</button>
<button (click)="onDelete(method.id)">Löschen</button>
</li>
</ul>
</div>

View File

@@ -0,0 +1,65 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Observable } from 'rxjs';
import { ShippingMethod } from '../../../../core/models/shipping.model';
import { ShippingMethodService } from '../../../services/shipping-method.service';
@Component({
selector: 'app-shipping-method-list',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
templateUrl: './shipping-method-list.component.html',
})
export class ShippingMethodListComponent implements OnInit {
private shippingMethodService = inject(ShippingMethodService);
private fb = inject(FormBuilder);
methods$!: Observable<ShippingMethod[]>;
methodForm: FormGroup;
selectedMethodId: string | null = null;
constructor() {
this.methodForm = this.fb.group({
name: ['', Validators.required],
description: [''],
cost: [0, [Validators.required, Validators.min(0)]],
isActive: [true]
});
}
ngOnInit(): void { this.loadMethods(); }
loadMethods(): void { this.methods$ = this.shippingMethodService.getAll(); }
selectMethod(method: ShippingMethod): void {
this.selectedMethodId = method.id;
this.methodForm.patchValue(method);
}
clearSelection(): void {
this.selectedMethodId = null;
this.methodForm.reset({ isActive: true });
}
onSubmit(): void {
if (this.methodForm.invalid) return;
const dataToSend = { ...this.methodForm.value, id: this.selectedMethodId || '0' };
if (this.selectedMethodId) {
this.shippingMethodService.update(this.selectedMethodId, dataToSend).subscribe(() => this.reset());
} else {
this.shippingMethodService.create(dataToSend).subscribe(() => this.reset());
}
}
onDelete(id: string): void {
if (confirm('Versandmethode wirklich löschen?')) {
this.shippingMethodService.delete(id).subscribe(() => this.loadMethods());
}
}
private reset(): void {
this.loadMethods();
this.clearSelection();
}
}

View File

@@ -0,0 +1,15 @@
<div>
<h1>Shop-Stammdaten verwalten</h1>
<form *ngIf="shopInfoForm" [formGroup]="shopInfoForm" (ngSubmit)="onSubmit()">
<input type="text" formControlName="shopName" placeholder="Shop-Name">
<input type="text" formControlName="slogan" placeholder="Slogan">
<input type="email" formControlName="contactEmail" placeholder="Kontakt E-Mail">
<input type="tel" formControlName="phoneNumber" placeholder="Telefonnummer">
<hr>
<input type="text" formControlName="street" placeholder="Straße & Hausnummer">
<input type="text" formControlName="city" placeholder="Stadt">
<input type="text" formControlName="postalCode" placeholder="PLZ">
<input type="text" formControlName="country" placeholder="Land">
<button type="submit" [disabled]="shopInfoForm.invalid">Speichern</button>
</form>
</div>

View File

@@ -0,0 +1,42 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { AdminShopInfo } from '../../../../core/models/shop.model';
import { ShopInfoService } from '../../../services/shop-info.service';
@Component({
selector: 'app-shop-info',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
templateUrl: './shop-info.component.html',
})
export class ShopInfoComponent implements OnInit {
private shopInfoService = inject(ShopInfoService);
private fb = inject(FormBuilder);
shopInfoForm!: FormGroup;
ngOnInit(): void {
this.shopInfoForm = this.fb.group({
shopName: ['', Validators.required],
slogan: [''],
contactEmail: ['', [Validators.required, Validators.email]],
phoneNumber: [''],
street: [''],
city: [''],
postalCode: [''],
country: ['']
});
this.shopInfoService.get().subscribe(data => {
this.shopInfoForm.patchValue(data);
});
}
onSubmit(): void {
if (this.shopInfoForm.invalid) return;
this.shopInfoService.update(this.shopInfoForm.value).subscribe(() => {
alert('Shop-Informationen gespeichert!');
});
}
}

View File

@@ -0,0 +1,34 @@
<div>
<h1>Lieferanten verwalten</h1>
<form [formGroup]="supplierForm" (ngSubmit)="onSubmit()">
<h3>
{{ selectedSupplierId ? "Lieferant bearbeiten" : "Neuer Lieferant" }}
</h3>
<input type="text" formControlName="name" placeholder="Name" />
<input
type="text"
formControlName="contactPerson"
placeholder="Ansprechpartner"
/>
<input type="email" formControlName="email" placeholder="E-Mail" />
<input type="tel" formControlName="phoneNumber" placeholder="Telefon" />
<button type="submit" [disabled]="supplierForm.invalid">
{{ selectedSupplierId ? "Aktualisieren" : "Erstellen" }}
</button>
<button type="button" *ngIf="selectedSupplierId" (click)="clearSelection()">
Abbrechen
</button>
</form>
<hr />
<h2>Bestehende Lieferanten</h2>
<ul>
<li *ngFor="let supplier of suppliers$ | async">
{{ supplier.name }} ({{ supplier.contactPerson }})
<button (click)="selectSupplier(supplier)">Bearbeiten</button>
<button (click)="onDelete(supplier.id)">Löschen</button>
</li>
</ul>
</div>

View File

@@ -0,0 +1,66 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
import { Observable } from 'rxjs';
import { Supplier } from '../../../../core/models/supplier.model';
import { SupplierService } from '../../../services/supplier.service';
@Component({
selector: 'app-supplier-list',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
templateUrl: './supplier-list.component.html',
})
export class SupplierListComponent implements OnInit {
private supplierService = inject(SupplierService);
private fb = inject(FormBuilder);
suppliers$!: Observable<Supplier[]>;
supplierForm: FormGroup;
selectedSupplierId: string | null = null;
constructor() {
this.supplierForm = this.fb.group({
name: ['', Validators.required],
contactPerson: [''],
email: ['', Validators.email],
phoneNumber: ['']
});
}
ngOnInit(): void { this.loadSuppliers(); }
loadSuppliers(): void { this.suppliers$ = this.supplierService.getAll(); }
selectSupplier(supplier: Supplier): void {
this.selectedSupplierId = supplier.id;
this.supplierForm.patchValue(supplier);
}
clearSelection(): void {
this.selectedSupplierId = null;
this.supplierForm.reset();
}
onSubmit(): void {
if (this.supplierForm.invalid) return;
const dataToSend = { ...this.supplierForm.value, id: this.selectedSupplierId || '0' };
if (this.selectedSupplierId) {
this.supplierService.update(this.selectedSupplierId, dataToSend).subscribe(() => this.reset());
} else {
this.supplierService.create(dataToSend).subscribe(() => this.reset());
}
}
onDelete(id: string): void {
if (confirm('Lieferant wirklich löschen?')) {
this.supplierService.delete(id).subscribe(() => this.loadSuppliers());
}
}
private reset(): void {
this.loadSuppliers();
this.clearSelection();
}
}

View File

@@ -0,0 +1,24 @@
<div>
<h1>Benutzer verwalten</h1>
<table>
<thead>
<tr>
<th>Email</th>
<th>Name</th>
<th>Rollen</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let user of users$ | async">
<td>{{ user.email }}</td>
<td>{{ user.firstName }} {{ user.lastName }}</td>
<td>{{ user.roles?.join(', ') }}</td>
<td>
<!-- Hier könnte ein "Rollen bearbeiten"-Button hin -->
<button (click)="onDelete(user)">Löschen</button>
</td>
</tr>
</tbody>
</table>
</div>

View File

@@ -0,0 +1,25 @@
import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { Observable } from 'rxjs';
import { User } from '../../../../core/models/user.model';
import { UserService } from '../../../services/user.service';
@Component({
selector: 'app-user-list',
standalone: true,
imports: [CommonModule],
templateUrl: './user-list.component.html',
})
export class UserListComponent implements OnInit {
private userService = inject(UserService);
users$!: Observable<User[]>;
ngOnInit(): void { this.loadUsers(); }
loadUsers(): void { this.users$ = this.userService.getAll(); }
onDelete(user: User): void {
if (confirm(`Benutzer ${user.email} wirklich löschen?`)) {
this.userService.delete(user.id!).subscribe(() => this.loadUsers());
}
}
}

View File

@@ -11,7 +11,7 @@ import { ButtonComponent } from '../../ui/button/button.component';
import { PaginatorComponent } from '../paginator/paginator.component'; import { PaginatorComponent } from '../paginator/paginator.component';
import { OrderStatus } from '../../../../core/types/order'; // import { any } from '../../../../core/types/order';
import { SearchBarComponent } from '../../layout/search-bar/search-bar.component'; import { SearchBarComponent } from '../../layout/search-bar/search-bar.component';
@Component({ @Component({
@@ -38,7 +38,7 @@ export class OrdersTableComponent {
@Output() delete = new EventEmitter<string>(); @Output() delete = new EventEmitter<string>();
public searchTerm = ''; public searchTerm = '';
public selectedStatus: OrderStatus | 'all' = 'all'; public selectedStatus: any | 'all' = 'all';
public statusOptions: any[] = [ public statusOptions: any[] = [
{ value: 'all', label: 'Alle' }, { value: 'all', label: 'Alle' },
@@ -52,7 +52,7 @@ export class OrdersTableComponent {
public displayedOrders: any[] = []; public displayedOrders: any[] = [];
public currentPage = 1; public currentPage = 1;
private statusTextMap = new Map<OrderStatus, string>([ private statusTextMap = new Map<any, string>([
['completed', 'Abgeschlossen'], ['completed', 'Abgeschlossen'],
['processing', 'In Bearbeitung'], ['processing', 'In Bearbeitung'],
['cancelled', 'Storniert'], ['cancelled', 'Storniert'],
@@ -76,7 +76,7 @@ export class OrdersTableComponent {
} }
// Called when a status pill is clicked // Called when a status pill is clicked
onStatusChange(status: OrderStatus | 'all'): void { onStatusChange(status: any | 'all'): void {
this.selectedStatus = status; this.selectedStatus = status;
this.applyFiltersAndPagination(); this.applyFiltersAndPagination();
} }

View File

@@ -1,6 +1,6 @@
import { Component, Input, OnChanges } from '@angular/core'; import { Component, Input, OnChanges } from '@angular/core';
import { CommonModule, NgClass } from '@angular/common'; import { CommonModule, NgClass } from '@angular/common';
import { OrderStatus } from '../../../../core/types/order'; // import { OrderStatus } from '../../../../core/types/order';
@Component({ @Component({
selector: 'app-status-pill', selector: 'app-status-pill',
@@ -11,14 +11,14 @@ import { OrderStatus } from '../../../../core/types/order';
}) })
export class StatusPillComponent implements OnChanges { export class StatusPillComponent implements OnChanges {
// Nimmt jetzt den neuen, sprechenden Status entgegen // Nimmt jetzt den neuen, sprechenden Status entgegen
@Input() status: OrderStatus = 'info'; @Input() status: any = 'info';
// Diese Eigenschaften werden vom Template verwendet // Diese Eigenschaften werden vom Template verwendet
public displayText = ''; public displayText = '';
public cssClass = ''; public cssClass = '';
// Eine Map, die Statusnamen auf Text und CSS-Klasse abbildet // Eine Map, die Statusnamen auf Text und CSS-Klasse abbildet
private statusMap = new Map<OrderStatus, { text: string, css: string }>([ private statusMap = new Map<any, { text: string, css: string }>([
['completed', { text: 'Abgeschlossen', css: 'pill-success' }], ['completed', { text: 'Abgeschlossen', css: 'pill-success' }],
['processing', { text: 'In Bearbeitung', css: 'pill-warning' }], ['processing', { text: 'In Bearbeitung', css: 'pill-warning' }],
['cancelled', { text: 'Storniert', css: 'pill-danger' }], ['cancelled', { text: 'Storniert', css: 'pill-danger' }],