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'
).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>
{{ 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" />
<fieldset>
<legend>Lieferanten-Daten</legend>
<div><label>Name:</label><input type="text" formControlName="name" /></div>
<div><label>Ansprechpartner:</label><input type="text" formControlName="contactPerson" /></div>
<div><label>E-Mail:</label><input type="email" formControlName="email" /></div>
<div><label>Telefon:</label><input type="tel" formControlName="phoneNumber" /></div>
<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">
{{ selectedSupplierId ? "Aktualisieren" : "Erstellen" }}
</button>
@@ -26,9 +41,9 @@
<h2>Bestehende Lieferanten</h2>
<ul>
<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)="onDelete(supplier.id)">Löschen</button>
</li>
</ul>
</div>
</div>

View File

@@ -1,9 +1,27 @@
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 {
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 {
Address,
CreateAddress,
UpdateAddress,
} from '../../../../core/models/address.model';
// Services
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({
selector: 'app-supplier-list',
@@ -13,9 +31,12 @@ import { SupplierService } from '../../../services/supplier.service';
})
export class SupplierListComponent implements OnInit {
private supplierService = inject(SupplierService);
private addressService = inject(AddressService); // Korrekten Service verwenden
private fb = inject(FormBuilder);
suppliers$!: Observable<Supplier[]>;
// Das Dropdown für Adressen wird nicht mehr benötigt
// addresses$!: Observable<Address[]>;
supplierForm: FormGroup;
selectedSupplierId: string | null = null;
@@ -24,16 +45,50 @@ export class SupplierListComponent implements OnInit {
name: ['', Validators.required],
contactPerson: [''],
email: ['', Validators.email],
phoneNumber: ['']
phoneNumber: [''],
addressId: [null],
notes: [''],
address: this.fb.group({
street: [''],
houseNumber: [''],
city: [''],
postalCode: [''],
country: [''],
}),
});
}
ngOnInit(): void { this.loadSuppliers(); }
loadSuppliers(): void { this.suppliers$ = this.supplierService.getAll(); }
get addressForm(): FormGroup {
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 {
this.selectedSupplierId = supplier.id;
// Reset address form before patching to avoid leftover values
this.addressForm.reset();
this.supplierForm.patchValue(supplier);
if (supplier.addressId) {
this.addressService
.getAddressById(supplier.addressId)
.subscribe((address) => {
this.addressForm.patchValue(address);
});
}
}
clearSelection(): void {
@@ -43,24 +98,80 @@ export class SupplierListComponent implements OnInit {
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());
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) {
return this.supplierService.update(
this.selectedSupplierId,
supplierData
);
} else {
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 {
this.supplierService.create(dataToSend).subscribe(() => this.reset());
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 {
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());
}
}
private reset(): void {
this.loadSuppliers();
this.clearSelection();
}
}
}

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>
</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>
</aside>