From eff6d8d8aa762b3d088e48348021144c2ef26aef Mon Sep 17 00:00:00 2001 From: "Tizian.Breuch" Date: Tue, 30 Sep 2025 11:30:53 +0200 Subject: [PATCH] components --- src/app/core/models/dashboard.model.ts | 4 +- src/app/core/types/dashboard.ts | 1 - src/app/core/types/order.ts | 1 - src/app/core/types/status-pill.ts | 1 - .../category-list/category-list.component.css | 0 .../category-list.component.html | 29 ++++++ .../category-list/category-list.component.ts | 91 +++++++++++++++++++ .../dashboard/dashboard.component.css | 0 .../dashboard/dashboard.component.html | 19 ++++ .../dashboard/dashboard.component.ts | 25 +++++ .../dashboard/kpi-card/kpi-card.component.ts | 4 +- .../discount-list/discount-list.component.css | 0 .../discount-list.component.html | 47 ++++++++++ .../discount-list/discount-list.component.ts | 86 ++++++++++++++++++ .../order-list/order-list.component.css | 0 .../order-list/order-list.component.html | 46 ++++++++++ .../orders/order-list/order-list.component.ts | 49 ++++++++++ .../payment-method-list.component.css | 0 .../payment-method-list.component.html | 23 +++++ .../payment-method-list.component.ts | 67 ++++++++++++++ .../product-list/product-list.component.css | 0 .../product-list/product-list.component.html | 29 ++++++ .../product-list/product-list.component.ts | 74 +++++++++++++++ .../review-list/review-list.component.css | 0 .../review-list/review-list.component.html | 30 ++++++ .../review-list/review-list.component.ts | 34 +++++++ .../settings/settings/settings.component.css | 0 .../settings/settings/settings.component.html | 29 ++++++ .../settings/settings/settings.component.ts | 54 +++++++++++ .../shipping-method-list.component.css | 0 .../shipping-method-list.component.html | 21 +++++ .../shipping-method-list.component.ts | 65 +++++++++++++ .../shop-info/shop-info.component.css | 0 .../shop-info/shop-info.component.html | 15 +++ .../shop-info/shop-info.component.ts | 42 +++++++++ .../supplier-list/supplier-list.component.css | 0 .../supplier-list.component.html | 34 +++++++ .../supplier-list/supplier-list.component.ts | 66 ++++++++++++++ .../users/user-list/user-list.component.css | 0 .../users/user-list/user-list.component.html | 24 +++++ .../users/user-list/user-list.component.ts | 25 +++++ .../orders-table/orders-table.component.ts | 8 +- .../ui/status-pill/status-pill.component.ts | 6 +- 43 files changed, 1035 insertions(+), 14 deletions(-) delete mode 100644 src/app/core/types/dashboard.ts delete mode 100644 src/app/core/types/order.ts delete mode 100644 src/app/core/types/status-pill.ts create mode 100644 src/app/features/components/categories/category-list/category-list.component.css create mode 100644 src/app/features/components/categories/category-list/category-list.component.html create mode 100644 src/app/features/components/categories/category-list/category-list.component.ts create mode 100644 src/app/features/components/dashboard/dashboard/dashboard.component.css create mode 100644 src/app/features/components/dashboard/dashboard/dashboard.component.html create mode 100644 src/app/features/components/dashboard/dashboard/dashboard.component.ts create mode 100644 src/app/features/components/discounts/discount-list/discount-list.component.css create mode 100644 src/app/features/components/discounts/discount-list/discount-list.component.html create mode 100644 src/app/features/components/discounts/discount-list/discount-list.component.ts create mode 100644 src/app/features/components/orders/order-list/order-list.component.css create mode 100644 src/app/features/components/orders/order-list/order-list.component.html create mode 100644 src/app/features/components/orders/order-list/order-list.component.ts create mode 100644 src/app/features/components/payment-methods/payment-method-list/payment-method-list.component.css create mode 100644 src/app/features/components/payment-methods/payment-method-list/payment-method-list.component.html create mode 100644 src/app/features/components/payment-methods/payment-method-list/payment-method-list.component.ts create mode 100644 src/app/features/components/products/product-list/product-list.component.css create mode 100644 src/app/features/components/products/product-list/product-list.component.html create mode 100644 src/app/features/components/products/product-list/product-list.component.ts create mode 100644 src/app/features/components/reviews/review-list/review-list.component.css create mode 100644 src/app/features/components/reviews/review-list/review-list.component.html create mode 100644 src/app/features/components/reviews/review-list/review-list.component.ts create mode 100644 src/app/features/components/settings/settings/settings.component.css create mode 100644 src/app/features/components/settings/settings/settings.component.html create mode 100644 src/app/features/components/settings/settings/settings.component.ts create mode 100644 src/app/features/components/shipping-methods/shipping-method-list/shipping-method-list.component.css create mode 100644 src/app/features/components/shipping-methods/shipping-method-list/shipping-method-list.component.html create mode 100644 src/app/features/components/shipping-methods/shipping-method-list/shipping-method-list.component.ts create mode 100644 src/app/features/components/shop-info/shop-info/shop-info.component.css create mode 100644 src/app/features/components/shop-info/shop-info/shop-info.component.html create mode 100644 src/app/features/components/shop-info/shop-info/shop-info.component.ts create mode 100644 src/app/features/components/suppliers/supplier-list/supplier-list.component.css create mode 100644 src/app/features/components/suppliers/supplier-list/supplier-list.component.html create mode 100644 src/app/features/components/suppliers/supplier-list/supplier-list.component.ts create mode 100644 src/app/features/components/users/user-list/user-list.component.css create mode 100644 src/app/features/components/users/user-list/user-list.component.html create mode 100644 src/app/features/components/users/user-list/user-list.component.ts diff --git a/src/app/core/models/dashboard.model.ts b/src/app/core/models/dashboard.model.ts index 393f23b..2a687eb 100644 --- a/src/app/core/models/dashboard.model.ts +++ b/src/app/core/models/dashboard.model.ts @@ -1,9 +1,9 @@ -import { KpiColor } from "../types/dashboard"; +// import { KpiColor } from "../types/dashboard"; export interface Kpi { value: string; label: string; - color: KpiColor; // <-- Hier verwenden wir den importierten Typ + color: any; // <-- Hier verwenden wir den importierten Typ iconName: string; } \ No newline at end of file diff --git a/src/app/core/types/dashboard.ts b/src/app/core/types/dashboard.ts deleted file mode 100644 index 0dfe432..0000000 --- a/src/app/core/types/dashboard.ts +++ /dev/null @@ -1 +0,0 @@ -export type KpiColor = 'blue' | 'green' | 'orange' | 'purple'; \ No newline at end of file diff --git a/src/app/core/types/order.ts b/src/app/core/types/order.ts deleted file mode 100644 index d57aad1..0000000 --- a/src/app/core/types/order.ts +++ /dev/null @@ -1 +0,0 @@ -export type OrderStatus = 'completed' | 'processing' | 'cancelled' | 'info'; \ No newline at end of file diff --git a/src/app/core/types/status-pill.ts b/src/app/core/types/status-pill.ts deleted file mode 100644 index 8a59cfe..0000000 --- a/src/app/core/types/status-pill.ts +++ /dev/null @@ -1 +0,0 @@ -export type PillStatus = 'success' | 'warning' | 'danger' | 'info'; \ No newline at end of file diff --git a/src/app/features/components/categories/category-list/category-list.component.css b/src/app/features/components/categories/category-list/category-list.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/components/categories/category-list/category-list.component.html b/src/app/features/components/categories/category-list/category-list.component.html new file mode 100644 index 0000000..b49570c --- /dev/null +++ b/src/app/features/components/categories/category-list/category-list.component.html @@ -0,0 +1,29 @@ +
+

Kategorien verwalten

+ + +
+

{{ selectedCategoryId ? 'Kategorie bearbeiten' : 'Neue Kategorie erstellen' }}

+ + + + + + + +
+ +
+ + +

Bestehende Kategorien

+ +
\ No newline at end of file diff --git a/src/app/features/components/categories/category-list/category-list.component.ts b/src/app/features/components/categories/category-list/category-list.component.ts new file mode 100644 index 0000000..fc8bce1 --- /dev/null +++ b/src/app/features/components/categories/category-list/category-list.component.ts @@ -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; + 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(); + } +} \ No newline at end of file diff --git a/src/app/features/components/dashboard/dashboard/dashboard.component.css b/src/app/features/components/dashboard/dashboard/dashboard.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/components/dashboard/dashboard/dashboard.component.html b/src/app/features/components/dashboard/dashboard/dashboard.component.html new file mode 100644 index 0000000..cf3861f --- /dev/null +++ b/src/app/features/components/dashboard/dashboard/dashboard.component.html @@ -0,0 +1,19 @@ +
+

Admin Dashboard

+
+ + + +
+ +
+

KPIs

+
{{ data.kpiSummary | json }}
+ +

Verkäufe

+
{{ data.salesOverTime | json }}
+ +

Top Produkte

+
{{ data.topPerformingProducts | json }}
+
+
\ No newline at end of file diff --git a/src/app/features/components/dashboard/dashboard/dashboard.component.ts b/src/app/features/components/dashboard/dashboard/dashboard.component.ts new file mode 100644 index 0000000..54617a9 --- /dev/null +++ b/src/app/features/components/dashboard/dashboard/dashboard.component.ts @@ -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; + + ngOnInit(): void { + this.loadAnalytics('Last30Days'); + } + + loadAnalytics(period: AnalyticsPeriod): void { + this.analytics$ = this.analyticsService.get(period); + } +} diff --git a/src/app/features/components/dashboard/kpi-card/kpi-card.component.ts b/src/app/features/components/dashboard/kpi-card/kpi-card.component.ts index b4b680a..79bac58 100644 --- a/src/app/features/components/dashboard/kpi-card/kpi-card.component.ts +++ b/src/app/features/components/dashboard/kpi-card/kpi-card.component.ts @@ -3,7 +3,7 @@ import { CommonModule } from '@angular/common'; import { IconComponent } from '../../../../shared/components/ui/icon/icon.component'; import { CardComponent } from '../../../../shared/components/ui/card/card.component'; -import { KpiColor } from '../../../../core/types/dashboard'; +// import { KpiColor } from '../../../../core/types/dashboard'; @Component({ selector: 'app-kpi-card', @@ -15,7 +15,7 @@ import { KpiColor } from '../../../../core/types/dashboard'; export class KpiCardComponent { @Input() value: string = ''; @Input() label: string = ''; - @Input() color: KpiColor = 'blue'; + @Input() color: any = 'blue'; @Input() iconName: string | null = null; @Input() svgColor: string | null = null; } diff --git a/src/app/features/components/discounts/discount-list/discount-list.component.css b/src/app/features/components/discounts/discount-list/discount-list.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/components/discounts/discount-list/discount-list.component.html b/src/app/features/components/discounts/discount-list/discount-list.component.html new file mode 100644 index 0000000..f8bb460 --- /dev/null +++ b/src/app/features/components/discounts/discount-list/discount-list.component.html @@ -0,0 +1,47 @@ +
+

Rabatte verwalten

+ +
+

{{ selectedDiscountId ? "Rabatt bearbeiten" : "Neuer Rabatt" }}

+ + + + + + + + + +
+ +
+ +

Bestehende Rabatte

+
    +
  • + {{ discount.name }} ({{ discount.discountValue + }}{{ discount.discountType === "Percentage" ? "%" : "€" }}) + + +
  • +
+
diff --git a/src/app/features/components/discounts/discount-list/discount-list.component.ts b/src/app/features/components/discounts/discount-list/discount-list.component.ts new file mode 100644 index 0000000..525d066 --- /dev/null +++ b/src/app/features/components/discounts/discount-list/discount-list.component.ts @@ -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; + 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(); + } +} \ No newline at end of file diff --git a/src/app/features/components/orders/order-list/order-list.component.css b/src/app/features/components/orders/order-list/order-list.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/components/orders/order-list/order-list.component.html b/src/app/features/components/orders/order-list/order-list.component.html new file mode 100644 index 0000000..f63ae6a --- /dev/null +++ b/src/app/features/components/orders/order-list/order-list.component.html @@ -0,0 +1,46 @@ +
+

Bestellungen verwalten

+ + + + + + + + + + + + + + + + + + + + + + + +
Bestellnr.DatumKundeBetragStatusAktionen
{{ order.orderNumber }}{{ order.orderDate | date : "short" }}{{ order.customerName }}{{ order.totalAmount | currency : "EUR" }} + +
+ +
+ + +
+

Details für Bestellung {{ selectedOrder.orderNumber }}

+
{{ selectedOrder | json }}
+ +
+
diff --git a/src/app/features/components/orders/order-list/order-list.component.ts b/src/app/features/components/orders/order-list/order-list.component.ts new file mode 100644 index 0000000..464a16e --- /dev/null +++ b/src/app/features/components/orders/order-list/order-list.component.ts @@ -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; + 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 + }); + } +} diff --git a/src/app/features/components/payment-methods/payment-method-list/payment-method-list.component.css b/src/app/features/components/payment-methods/payment-method-list/payment-method-list.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/components/payment-methods/payment-method-list/payment-method-list.component.html b/src/app/features/components/payment-methods/payment-method-list/payment-method-list.component.html new file mode 100644 index 0000000..8618b2e --- /dev/null +++ b/src/app/features/components/payment-methods/payment-method-list/payment-method-list.component.html @@ -0,0 +1,23 @@ +
+

Zahlungsmethoden verwalten

+
+

{{ selectedMethodId ? 'Methode bearbeiten' : 'Neue Methode' }}

+ + + + + + +
+
+

Bestehende Methoden

+
    +
  • + {{ method.name }} (Typ: {{ method.paymentGatewayType }}) - Aktiv: {{ method.isActive }} + + +
  • +
+
\ No newline at end of file diff --git a/src/app/features/components/payment-methods/payment-method-list/payment-method-list.component.ts b/src/app/features/components/payment-methods/payment-method-list/payment-method-list.component.ts new file mode 100644 index 0000000..3d32b1d --- /dev/null +++ b/src/app/features/components/payment-methods/payment-method-list/payment-method-list.component.ts @@ -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; + 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(); + } +} \ No newline at end of file diff --git a/src/app/features/components/products/product-list/product-list.component.css b/src/app/features/components/products/product-list/product-list.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/components/products/product-list/product-list.component.html b/src/app/features/components/products/product-list/product-list.component.html new file mode 100644 index 0000000..8c5c9f6 --- /dev/null +++ b/src/app/features/components/products/product-list/product-list.component.html @@ -0,0 +1,29 @@ +
+

Produkte verwalten

+ + +
+

{{ selectedProductId ? 'Produkt bearbeiten' : 'Neues Produkt' }}

+ + + + + + + + + +
+ +
+ + +

Bestehende Produkte

+
    +
  • + {{ product.name }} (SKU: {{ product.sku }}) - Preis: {{ product.price | number:'1.2-2' }} € + + +
  • +
+
\ No newline at end of file diff --git a/src/app/features/components/products/product-list/product-list.component.ts b/src/app/features/components/products/product-list/product-list.component.ts new file mode 100644 index 0000000..28cbb6e --- /dev/null +++ b/src/app/features/components/products/product-list/product-list.component.ts @@ -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; + 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(); + } +} \ No newline at end of file diff --git a/src/app/features/components/reviews/review-list/review-list.component.css b/src/app/features/components/reviews/review-list/review-list.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/components/reviews/review-list/review-list.component.html b/src/app/features/components/reviews/review-list/review-list.component.html new file mode 100644 index 0000000..8c12577 --- /dev/null +++ b/src/app/features/components/reviews/review-list/review-list.component.html @@ -0,0 +1,30 @@ +
+

Bewertungen verwalten

+ + + + + + + + + + + + + + + + + + + + + +
KundeProduktBewertungTitelStatusAktionen
{{ review.customerName }}{{ review.productName }}{{ review.rating }}/5{{ review.title }}{{ review.isApproved ? "Freigegeben" : "Ausstehend" }} + + +
+
diff --git a/src/app/features/components/reviews/review-list/review-list.component.ts b/src/app/features/components/reviews/review-list/review-list.component.ts new file mode 100644 index 0000000..1707846 --- /dev/null +++ b/src/app/features/components/reviews/review-list/review-list.component.ts @@ -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; + + 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()); + } + } +} \ No newline at end of file diff --git a/src/app/features/components/settings/settings/settings.component.css b/src/app/features/components/settings/settings/settings.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/components/settings/settings/settings.component.html b/src/app/features/components/settings/settings/settings.component.html new file mode 100644 index 0000000..9055778 --- /dev/null +++ b/src/app/features/components/settings/settings/settings.component.html @@ -0,0 +1,29 @@ +
+

Einstellungen verwalten

+
+
+
+

{{ group.groupName }}

+
+ + +
+ + + +
+
+
+
+
+ +
+
diff --git a/src/app/features/components/settings/settings/settings.component.ts b/src/app/features/components/settings/settings/settings.component.ts new file mode 100644 index 0000000..4fcc5ef --- /dev/null +++ b/src/app/features/components/settings/settings/settings.component.ts @@ -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!'); + }); + } +} \ No newline at end of file diff --git a/src/app/features/components/shipping-methods/shipping-method-list/shipping-method-list.component.css b/src/app/features/components/shipping-methods/shipping-method-list/shipping-method-list.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/components/shipping-methods/shipping-method-list/shipping-method-list.component.html b/src/app/features/components/shipping-methods/shipping-method-list/shipping-method-list.component.html new file mode 100644 index 0000000..e28253b --- /dev/null +++ b/src/app/features/components/shipping-methods/shipping-method-list/shipping-method-list.component.html @@ -0,0 +1,21 @@ +
+

Versandmethoden verwalten

+
+

{{ selectedMethodId ? 'Methode bearbeiten' : 'Neue Methode' }}

+ + + + + + +
+
+

Bestehende Methoden

+
    +
  • + {{ method.name }} ({{ method.cost | currency:'EUR' }}) - Aktiv: {{ method.isActive }} + + +
  • +
+
\ No newline at end of file diff --git a/src/app/features/components/shipping-methods/shipping-method-list/shipping-method-list.component.ts b/src/app/features/components/shipping-methods/shipping-method-list/shipping-method-list.component.ts new file mode 100644 index 0000000..6e70faa --- /dev/null +++ b/src/app/features/components/shipping-methods/shipping-method-list/shipping-method-list.component.ts @@ -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; + 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(); + } +} \ No newline at end of file diff --git a/src/app/features/components/shop-info/shop-info/shop-info.component.css b/src/app/features/components/shop-info/shop-info/shop-info.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/components/shop-info/shop-info/shop-info.component.html b/src/app/features/components/shop-info/shop-info/shop-info.component.html new file mode 100644 index 0000000..592c5d2 --- /dev/null +++ b/src/app/features/components/shop-info/shop-info/shop-info.component.html @@ -0,0 +1,15 @@ +
+

Shop-Stammdaten verwalten

+
+ + + + +
+ + + + + +
+
\ No newline at end of file diff --git a/src/app/features/components/shop-info/shop-info/shop-info.component.ts b/src/app/features/components/shop-info/shop-info/shop-info.component.ts new file mode 100644 index 0000000..395f97b --- /dev/null +++ b/src/app/features/components/shop-info/shop-info/shop-info.component.ts @@ -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!'); + }); + } +} \ No newline at end of file diff --git a/src/app/features/components/suppliers/supplier-list/supplier-list.component.css b/src/app/features/components/suppliers/supplier-list/supplier-list.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/components/suppliers/supplier-list/supplier-list.component.html b/src/app/features/components/suppliers/supplier-list/supplier-list.component.html new file mode 100644 index 0000000..374f425 --- /dev/null +++ b/src/app/features/components/suppliers/supplier-list/supplier-list.component.html @@ -0,0 +1,34 @@ +
+

Lieferanten verwalten

+ +
+

+ {{ selectedSupplierId ? "Lieferant bearbeiten" : "Neuer Lieferant" }} +

+ + + + + + +
+ +
+ +

Bestehende Lieferanten

+
    +
  • + {{ supplier.name }} ({{ supplier.contactPerson }}) + + +
  • +
+
diff --git a/src/app/features/components/suppliers/supplier-list/supplier-list.component.ts b/src/app/features/components/suppliers/supplier-list/supplier-list.component.ts new file mode 100644 index 0000000..c8bd402 --- /dev/null +++ b/src/app/features/components/suppliers/supplier-list/supplier-list.component.ts @@ -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; + 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(); + } +} \ No newline at end of file diff --git a/src/app/features/components/users/user-list/user-list.component.css b/src/app/features/components/users/user-list/user-list.component.css new file mode 100644 index 0000000..e69de29 diff --git a/src/app/features/components/users/user-list/user-list.component.html b/src/app/features/components/users/user-list/user-list.component.html new file mode 100644 index 0000000..6d8f50b --- /dev/null +++ b/src/app/features/components/users/user-list/user-list.component.html @@ -0,0 +1,24 @@ +
+

Benutzer verwalten

+ + + + + + + + + + + + + + + + + +
EmailNameRollenAktionen
{{ user.email }}{{ user.firstName }} {{ user.lastName }}{{ user.roles?.join(', ') }} + + +
+
\ No newline at end of file diff --git a/src/app/features/components/users/user-list/user-list.component.ts b/src/app/features/components/users/user-list/user-list.component.ts new file mode 100644 index 0000000..16d62e8 --- /dev/null +++ b/src/app/features/components/users/user-list/user-list.component.ts @@ -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; + + 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()); + } + } +} \ No newline at end of file diff --git a/src/app/shared/components/data-display/orders-table/orders-table.component.ts b/src/app/shared/components/data-display/orders-table/orders-table.component.ts index e37d68e..38434d8 100644 --- a/src/app/shared/components/data-display/orders-table/orders-table.component.ts +++ b/src/app/shared/components/data-display/orders-table/orders-table.component.ts @@ -11,7 +11,7 @@ import { ButtonComponent } from '../../ui/button/button.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'; @Component({ @@ -38,7 +38,7 @@ export class OrdersTableComponent { @Output() delete = new EventEmitter(); public searchTerm = ''; - public selectedStatus: OrderStatus | 'all' = 'all'; + public selectedStatus: any | 'all' = 'all'; public statusOptions: any[] = [ { value: 'all', label: 'Alle' }, @@ -52,7 +52,7 @@ export class OrdersTableComponent { public displayedOrders: any[] = []; public currentPage = 1; - private statusTextMap = new Map([ + private statusTextMap = new Map([ ['completed', 'Abgeschlossen'], ['processing', 'In Bearbeitung'], ['cancelled', 'Storniert'], @@ -76,7 +76,7 @@ export class OrdersTableComponent { } // Called when a status pill is clicked - onStatusChange(status: OrderStatus | 'all'): void { + onStatusChange(status: any | 'all'): void { this.selectedStatus = status; this.applyFiltersAndPagination(); } diff --git a/src/app/shared/components/ui/status-pill/status-pill.component.ts b/src/app/shared/components/ui/status-pill/status-pill.component.ts index d5cbc2a..56cc86a 100644 --- a/src/app/shared/components/ui/status-pill/status-pill.component.ts +++ b/src/app/shared/components/ui/status-pill/status-pill.component.ts @@ -1,6 +1,6 @@ import { Component, Input, OnChanges } from '@angular/core'; import { CommonModule, NgClass } from '@angular/common'; -import { OrderStatus } from '../../../../core/types/order'; +// import { OrderStatus } from '../../../../core/types/order'; @Component({ selector: 'app-status-pill', @@ -11,14 +11,14 @@ import { OrderStatus } from '../../../../core/types/order'; }) export class StatusPillComponent implements OnChanges { // Nimmt jetzt den neuen, sprechenden Status entgegen - @Input() status: OrderStatus = 'info'; + @Input() status: any = 'info'; // Diese Eigenschaften werden vom Template verwendet public displayText = ''; public cssClass = ''; // Eine Map, die Statusnamen auf Text und CSS-Klasse abbildet - private statusMap = new Map([ + private statusMap = new Map([ ['completed', { text: 'Abgeschlossen', css: 'pill-success' }], ['processing', { text: 'In Bearbeitung', css: 'pill-warning' }], ['cancelled', { text: 'Storniert', css: 'pill-danger' }],