supplier und address

This commit is contained in:
Tizian.Breuch
2025-10-10 12:13:09 +02:00
parent a38ab54119
commit 8813dc21ee
6 changed files with 235 additions and 23 deletions

View File

@@ -95,6 +95,13 @@ export const routes: Routes = [
'./features/components/shop-info/shop-info.routes' './features/components/shop-info/shop-info.routes'
).then((r) => r.SHOP_INFO_ROUTES), ).then((r) => r.SHOP_INFO_ROUTES),
}, },
{
path: 'supplier-list',
loadChildren: () =>
import(
'./features/components/suppliers/suppliers.routes'
).then((r) => r.SUPPLIERS_ROUTES),
},
], ],
}, },
{ {

View File

@@ -5,14 +5,29 @@
<h3> <h3>
{{ selectedSupplierId ? "Lieferant bearbeiten" : "Neuer Lieferant" }} {{ selectedSupplierId ? "Lieferant bearbeiten" : "Neuer Lieferant" }}
</h3> </h3>
<input type="text" formControlName="name" placeholder="Name" />
<input <fieldset>
type="text" <legend>Lieferanten-Daten</legend>
formControlName="contactPerson" <div><label>Name:</label><input type="text" formControlName="name" /></div>
placeholder="Ansprechpartner" <div><label>Ansprechpartner:</label><input type="text" formControlName="contactPerson" /></div>
/> <div><label>E-Mail:</label><input type="email" formControlName="email" /></div>
<input type="email" formControlName="email" placeholder="E-Mail" /> <div><label>Telefon:</label><input type="tel" formControlName="phoneNumber" /></div>
<input type="tel" formControlName="phoneNumber" placeholder="Telefon" /> <div><label>Notizen:</label><textarea formControlName="notes"></textarea></div>
</fieldset>
<!-- +++ NEUES ADRESS-FORMULAR +++ -->
<fieldset formGroupName="address">
<legend>Adresse (optional)</legend>
<div><label>Straße:</label><input type="text" formControlName="street" /></div>
<div><label>Hausnummer:</label><input type="text" formControlName="houseNumber" /></div>
<div><label>Stadt:</label><input type="text" formControlName="city" /></div>
<div><label>PLZ:</label><input type="text" formControlName="postalCode" /></div>
<div><label>Land:</label><input type="text" formControlName="country" /></div>
</fieldset>
<!-- +++ ENDE NEU +++ -->
<br>
<button type="submit" [disabled]="supplierForm.invalid"> <button type="submit" [disabled]="supplierForm.invalid">
{{ selectedSupplierId ? "Aktualisieren" : "Erstellen" }} {{ selectedSupplierId ? "Aktualisieren" : "Erstellen" }}
</button> </button>
@@ -26,7 +41,7 @@
<h2>Bestehende Lieferanten</h2> <h2>Bestehende Lieferanten</h2>
<ul> <ul>
<li *ngFor="let supplier of suppliers$ | async"> <li *ngFor="let supplier of suppliers$ | async">
{{ supplier.name }} ({{ supplier.contactPerson }}) {{ supplier.name }} ({{ supplier.contactPerson || 'Kein Ansprechpartner' }})
<button (click)="selectSupplier(supplier)">Bearbeiten</button> <button (click)="selectSupplier(supplier)">Bearbeiten</button>
<button (click)="onDelete(supplier.id)">Löschen</button> <button (click)="onDelete(supplier.id)">Löschen</button>
</li> </li>

View File

@@ -1,9 +1,27 @@
import { Component, OnInit, inject } from '@angular/core'; import { Component, OnInit, inject } from '@angular/core';
import { CommonModule } from '@angular/common'; import { CommonModule } from '@angular/common';
import { FormBuilder, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms'; import {
import { Observable } from 'rxjs'; FormBuilder,
FormGroup,
ReactiveFormsModule,
Validators,
} from '@angular/forms';
import { Observable, of } from 'rxjs';
import { switchMap, map } from 'rxjs/operators'; // <-- KORREKTUR: 'map' importiert
// Models
import { Supplier } from '../../../../core/models/supplier.model'; import { Supplier } from '../../../../core/models/supplier.model';
import {
Address,
CreateAddress,
UpdateAddress,
} from '../../../../core/models/address.model';
// Services
import { SupplierService } from '../../../services/supplier.service'; import { SupplierService } from '../../../services/supplier.service';
import { AddressService } from '../../../services/address.service';
// HINWEIS: Du hast den CustomerAddressService nicht mehr gebraucht, daher habe ich ihn entfernt.
// Falls du ihn doch an anderer Stelle brauchst, füge den Import wieder hinzu.
@Component({ @Component({
selector: 'app-supplier-list', selector: 'app-supplier-list',
@@ -13,9 +31,12 @@ import { SupplierService } from '../../../services/supplier.service';
}) })
export class SupplierListComponent implements OnInit { export class SupplierListComponent implements OnInit {
private supplierService = inject(SupplierService); private supplierService = inject(SupplierService);
private addressService = inject(AddressService); // Korrekten Service verwenden
private fb = inject(FormBuilder); private fb = inject(FormBuilder);
suppliers$!: Observable<Supplier[]>; suppliers$!: Observable<Supplier[]>;
// Das Dropdown für Adressen wird nicht mehr benötigt
// addresses$!: Observable<Address[]>;
supplierForm: FormGroup; supplierForm: FormGroup;
selectedSupplierId: string | null = null; selectedSupplierId: string | null = null;
@@ -24,16 +45,50 @@ export class SupplierListComponent implements OnInit {
name: ['', Validators.required], name: ['', Validators.required],
contactPerson: [''], contactPerson: [''],
email: ['', Validators.email], email: ['', Validators.email],
phoneNumber: [''] phoneNumber: [''],
addressId: [null],
notes: [''],
address: this.fb.group({
street: [''],
houseNumber: [''],
city: [''],
postalCode: [''],
country: [''],
}),
}); });
} }
ngOnInit(): void { this.loadSuppliers(); } get addressForm(): FormGroup {
loadSuppliers(): void { this.suppliers$ = this.supplierService.getAll(); } return this.supplierForm.get('address') as FormGroup;
}
private isAddressFormDirty(): boolean {
const address = this.addressForm.value;
return Object.values(address).some((value) => !!value);
}
ngOnInit(): void {
this.loadSuppliers();
// this.addresses$ = this.adminAddressService.getAllUnlinkedAddresses(); // Lade ungebundene Adressen
}
loadSuppliers(): void {
this.suppliers$ = this.supplierService.getAll();
}
selectSupplier(supplier: Supplier): void { selectSupplier(supplier: Supplier): void {
this.selectedSupplierId = supplier.id; this.selectedSupplierId = supplier.id;
// Reset address form before patching to avoid leftover values
this.addressForm.reset();
this.supplierForm.patchValue(supplier); this.supplierForm.patchValue(supplier);
if (supplier.addressId) {
this.addressService
.getAddressById(supplier.addressId)
.subscribe((address) => {
this.addressForm.patchValue(address);
});
}
} }
clearSelection(): void { clearSelection(): void {
@@ -44,17 +99,73 @@ export class SupplierListComponent implements OnInit {
onSubmit(): void { onSubmit(): void {
if (this.supplierForm.invalid) return; if (this.supplierForm.invalid) return;
const dataToSend = { ...this.supplierForm.value, id: this.selectedSupplierId || '0' }; const addressIdToSave$ = this.isAddressFormDirty()
? this.saveAddress()
: of(this.supplierForm.value.addressId || null);
addressIdToSave$
.pipe(
switchMap((addressId: string | null) => {
const supplierData: Supplier = {
...this.supplierForm.value,
id: this.selectedSupplierId || undefined,
addressId: addressId,
};
if (this.selectedSupplierId) { if (this.selectedSupplierId) {
this.supplierService.update(this.selectedSupplierId, dataToSend).subscribe(() => this.reset()); return this.supplierService.update(
this.selectedSupplierId,
supplierData
);
} else { } else {
this.supplierService.create(dataToSend).subscribe(() => this.reset()); return this.supplierService.create(supplierData);
}
})
)
.subscribe(() => this.reset());
}
private saveAddress(): Observable<string | null> {
const supplierName = this.supplierForm.get('name')?.value || 'Lieferant';
const contactPerson = this.supplierForm
.get('contactPerson')
?.value.split(' ') || [''];
const firstName = contactPerson[0] || supplierName;
const lastName = contactPerson.slice(1).join(' ') || '(Lieferant)';
const addressData = {
...this.addressForm.value,
firstName,
lastName,
type: 'Billing',
};
const existingAddressId = this.supplierForm.get('addressId')?.value;
if (existingAddressId) {
const updateData: UpdateAddress = {
...addressData,
id: existingAddressId,
};
return this.addressService
.updateAddress(existingAddressId, updateData)
.pipe(
map(() => existingAddressId) // Gibt die bestehende ID zurück
);
} else {
const createData: CreateAddress = addressData;
return this.addressService.createAddress(createData).pipe(
map((createdAddress: Address) => createdAddress.id) // Gibt die neue ID zurück
);
} }
} }
onDelete(id: string): void { onDelete(id: string): void {
if (confirm('Lieferant wirklich löschen?')) { if (
confirm(
'Lieferant wirklich löschen? (Zugehörige Adresse wird nicht gelöscht)'
)
) {
this.supplierService.delete(id).subscribe(() => this.loadSuppliers()); this.supplierService.delete(id).subscribe(() => this.loadSuppliers());
} }
} }

View File

@@ -0,0 +1,11 @@
import { Routes } from '@angular/router';
import { SupplierListComponent } from './supplier-list/supplier-list.component';
export const SUPPLIERS_ROUTES: Routes = [
{
path: '',
component: SupplierListComponent,
title: '',
},
];

View File

@@ -0,0 +1,59 @@
import { Injectable, inject } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { API_URL } from '../../core/tokens/api-url.token';
import { Address, CreateAddress, UpdateAddress } from '../../core/models/address.model';
@Injectable({
providedIn: 'root'
})
export class AddressService {
private http = inject(HttpClient);
private apiUrl = inject(API_URL);
private readonly endpoint = '/admin/AdminAddresses';
/**
* Retrieves all addresses that are not linked to a customer (e.g., for suppliers).
* @returns An Observable array of Address objects.
*/
getAllUnlinkedAddresses(): Observable<Address[]> {
return this.http.get<Address[]>(`${this.apiUrl}${this.endpoint}`);
}
/**
* Retrieves a single unlinked address by its ID.
* @param id The unique identifier of the address.
* @returns An Observable of a single Address object.
*/
getAddressById(id: string): Observable<Address> {
return this.http.get<Address>(`${this.apiUrl}${this.endpoint}/${id}`);
}
/**
* Creates a new unlinked address.
* @param data The data for the new address.
* @returns An Observable of the newly created Address object.
*/
createAddress(data: CreateAddress): Observable<Address> {
return this.http.post<Address>(`${this.apiUrl}${this.endpoint}`, data);
}
/**
* Updates an existing unlinked address.
* @param id The unique identifier of the address to update.
* @param data The updated address data.
* @returns An Observable that completes when the update is successful.
*/
updateAddress(id: string, data: UpdateAddress): Observable<void> {
return this.http.put<void>(`${this.apiUrl}${this.endpoint}/${id}`, data);
}
/**
* Deletes an unlinked address by its ID.
* @param id The unique identifier of the address to delete.
* @returns An Observable that completes when the deletion is successful.
*/
deleteAddress(id: string): Observable<void> {
return this.http.delete<void>(`${this.apiUrl}${this.endpoint}/${id}`);
}
}

View File

@@ -89,5 +89,14 @@
<span>shop-info</span> <span>shop-info</span>
</div> </div>
<div
class="nav-item"
[class.active]="activeRoute === 'supplier-list'"
(click)="setActive('supplier-list')"
>
<app-icon [iconName]="'placeholder'" [svgColor]="'#8e44ad'"></app-icon>
<span>supplier-list</span>
</div>
</nav> </nav>
</aside> </aside>