mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 17:19:56 +01:00
feat(cli): Stammdaten-TUI mit Master Data API-Anbindung
- Neue Screens: Kategorien, Lieferanten, Artikel, Kunden (jeweils Liste, Detail, Anlegen + Detailaktionen wie Bewertung, Zertifikate, Verkaufseinheiten, Lieferadressen, Präferenzen) - API-Client: Resources für alle 4 Stammdaten-Aggregate implementiert (categories, suppliers, articles, customers) mit Mapping von verschachtelten Domain-VOs auf flache DTOs - Lieferant, Artikel, Kategorie: echte HTTP-Calls gegen Backend (/api/suppliers, /api/articles, /api/categories, /api/customers) - 204-No-Content-Endpoints (removeSalesUnit, removeSupplier, removeCertificate, removeDeliveryAddress, removeFrameContract) lösen Re-Fetch des Aggregats aus - MasterdataMenu, Navigation-Erweiterung, App.tsx-Routing
This commit is contained in:
parent
797f435a49
commit
d27dbaa843
30 changed files with 3882 additions and 1 deletions
236
frontend/packages/api-client/src/resources/suppliers.ts
Normal file
236
frontend/packages/api-client/src/resources/suppliers.ts
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
/**
|
||||
* Suppliers resource – Real HTTP implementation.
|
||||
* Endpoints: GET/POST /api/suppliers, GET/PUT /api/suppliers/{id},
|
||||
* POST /api/suppliers/{id}/activate|deactivate,
|
||||
* POST /api/suppliers/{id}/rating,
|
||||
* POST /api/suppliers/{id}/certificates,
|
||||
* DELETE /api/suppliers/{id}/certificates (with body)
|
||||
*
|
||||
* NOTE: Backend returns domain objects with nested VOs:
|
||||
* { "id": {"value":"uuid"}, "name": {"value":"string"},
|
||||
* "address": {"street":"...","houseNumber":"...","postalCode":"...","city":"...","country":"DE"},
|
||||
* "contactInfo": {"phone":"...","email":"...","contactPerson":"..."},
|
||||
* "paymentTerms": {"paymentDueDays":30,"description":"..."},
|
||||
* "certificates": [{"certificateType":"...","issuer":"...","validFrom":"2024-01-01","validUntil":"2026-12-31"}],
|
||||
* "rating": {"qualityScore":4,"deliveryScore":4,"priceScore":5},
|
||||
* "status": "ACTIVE", "createdAt":"...", "updatedAt":"..." }
|
||||
* DELETE /api/suppliers/{id}/certificates returns 204 No Content → re-fetch.
|
||||
*/
|
||||
|
||||
import type { AxiosInstance } from 'axios';
|
||||
|
||||
export interface AddressDTO {
|
||||
street: string;
|
||||
houseNumber: string | null;
|
||||
postalCode: string;
|
||||
city: string;
|
||||
country: string;
|
||||
}
|
||||
|
||||
export interface ContactInfoDTO {
|
||||
phone: string;
|
||||
email: string | null;
|
||||
contactPerson: string | null;
|
||||
}
|
||||
|
||||
export interface PaymentTermsDTO {
|
||||
paymentDueDays: number;
|
||||
paymentDescription: string | null;
|
||||
}
|
||||
|
||||
export interface QualityCertificateDTO {
|
||||
certificateType: string;
|
||||
issuer: string;
|
||||
validFrom: string;
|
||||
validUntil: string;
|
||||
}
|
||||
|
||||
export interface SupplierRatingDTO {
|
||||
qualityScore: number;
|
||||
deliveryScore: number;
|
||||
priceScore: number;
|
||||
}
|
||||
|
||||
export type SupplierStatus = 'ACTIVE' | 'INACTIVE';
|
||||
|
||||
export interface SupplierDTO {
|
||||
id: string;
|
||||
name: string;
|
||||
status: SupplierStatus;
|
||||
address: AddressDTO | null;
|
||||
contactInfo: ContactInfoDTO;
|
||||
paymentTerms: PaymentTermsDTO | null;
|
||||
certificates: QualityCertificateDTO[];
|
||||
rating: SupplierRatingDTO | null;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface CreateSupplierRequest {
|
||||
name: string;
|
||||
phone: string;
|
||||
email?: string;
|
||||
contactPerson?: string;
|
||||
street?: string;
|
||||
houseNumber?: string;
|
||||
postalCode?: string;
|
||||
city?: string;
|
||||
country?: string;
|
||||
paymentDueDays?: number;
|
||||
paymentDescription?: string;
|
||||
}
|
||||
|
||||
export interface UpdateSupplierRequest {
|
||||
name?: string;
|
||||
phone?: string;
|
||||
email?: string | null;
|
||||
contactPerson?: string | null;
|
||||
street?: string | null;
|
||||
houseNumber?: string | null;
|
||||
postalCode?: string | null;
|
||||
city?: string | null;
|
||||
country?: string | null;
|
||||
paymentDueDays?: number | null;
|
||||
paymentDescription?: string | null;
|
||||
}
|
||||
|
||||
export interface RateSupplierRequest {
|
||||
qualityScore: number;
|
||||
deliveryScore: number;
|
||||
priceScore: number;
|
||||
}
|
||||
|
||||
export interface AddCertificateRequest {
|
||||
certificateType: string;
|
||||
issuer: string;
|
||||
validFrom: string;
|
||||
validUntil: string;
|
||||
}
|
||||
|
||||
export interface RemoveCertificateRequest {
|
||||
certificateType: string;
|
||||
issuer: string;
|
||||
validFrom: string;
|
||||
}
|
||||
|
||||
// ── Backend response shapes (domain objects with nested VOs) ─────────────────
|
||||
|
||||
interface BackendAddress {
|
||||
street: string;
|
||||
houseNumber: string | null;
|
||||
postalCode: string;
|
||||
city: string;
|
||||
country: string;
|
||||
}
|
||||
|
||||
interface BackendContactInfo {
|
||||
phone: string;
|
||||
email: string | null;
|
||||
contactPerson: string | null;
|
||||
}
|
||||
|
||||
interface BackendPaymentTerms {
|
||||
paymentDueDays: number;
|
||||
description: string | null; // Note: backend field is "description", not "paymentDescription"
|
||||
}
|
||||
|
||||
interface BackendQualityCertificate {
|
||||
certificateType: string;
|
||||
issuer: string;
|
||||
validFrom: string; // LocalDate → "2024-01-01"
|
||||
validUntil: string;
|
||||
}
|
||||
|
||||
interface BackendSupplierRating {
|
||||
qualityScore: number;
|
||||
deliveryScore: number;
|
||||
priceScore: number;
|
||||
}
|
||||
|
||||
interface BackendSupplier {
|
||||
id: { value: string };
|
||||
name: { value: string };
|
||||
address: BackendAddress | null;
|
||||
contactInfo: BackendContactInfo;
|
||||
paymentTerms: BackendPaymentTerms | null;
|
||||
certificates: BackendQualityCertificate[];
|
||||
rating: BackendSupplierRating | null;
|
||||
status: SupplierStatus;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
function mapSupplier(bs: BackendSupplier): SupplierDTO {
|
||||
return {
|
||||
id: bs.id.value,
|
||||
name: bs.name.value,
|
||||
status: bs.status,
|
||||
address: bs.address,
|
||||
contactInfo: bs.contactInfo,
|
||||
paymentTerms: bs.paymentTerms
|
||||
? {
|
||||
paymentDueDays: bs.paymentTerms.paymentDueDays,
|
||||
paymentDescription: bs.paymentTerms.description,
|
||||
}
|
||||
: null,
|
||||
certificates: bs.certificates,
|
||||
rating: bs.rating,
|
||||
createdAt: bs.createdAt,
|
||||
updatedAt: bs.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
// ── Resource factory ─────────────────────────────────────────────────────────
|
||||
|
||||
export function createSuppliersResource(client: AxiosInstance) {
|
||||
return {
|
||||
async list(): Promise<SupplierDTO[]> {
|
||||
const res = await client.get<BackendSupplier[]>('/api/suppliers');
|
||||
return res.data.map(mapSupplier);
|
||||
},
|
||||
|
||||
async getById(id: string): Promise<SupplierDTO> {
|
||||
const res = await client.get<BackendSupplier>(`/api/suppliers/${id}`);
|
||||
return mapSupplier(res.data);
|
||||
},
|
||||
|
||||
async create(request: CreateSupplierRequest): Promise<SupplierDTO> {
|
||||
const res = await client.post<BackendSupplier>('/api/suppliers', request);
|
||||
return mapSupplier(res.data);
|
||||
},
|
||||
|
||||
async update(id: string, request: UpdateSupplierRequest): Promise<SupplierDTO> {
|
||||
const res = await client.put<BackendSupplier>(`/api/suppliers/${id}`, request);
|
||||
return mapSupplier(res.data);
|
||||
},
|
||||
|
||||
async activate(id: string): Promise<SupplierDTO> {
|
||||
const res = await client.post<BackendSupplier>(`/api/suppliers/${id}/activate`);
|
||||
return mapSupplier(res.data);
|
||||
},
|
||||
|
||||
async deactivate(id: string): Promise<SupplierDTO> {
|
||||
const res = await client.post<BackendSupplier>(`/api/suppliers/${id}/deactivate`);
|
||||
return mapSupplier(res.data);
|
||||
},
|
||||
|
||||
async rate(id: string, request: RateSupplierRequest): Promise<SupplierDTO> {
|
||||
const res = await client.post<BackendSupplier>(`/api/suppliers/${id}/rating`, request);
|
||||
return mapSupplier(res.data);
|
||||
},
|
||||
|
||||
async addCertificate(id: string, request: AddCertificateRequest): Promise<SupplierDTO> {
|
||||
const res = await client.post<BackendSupplier>(`/api/suppliers/${id}/certificates`, request);
|
||||
return mapSupplier(res.data);
|
||||
},
|
||||
|
||||
// Returns 204 No Content → re-fetch supplier
|
||||
async removeCertificate(id: string, request: RemoveCertificateRequest): Promise<SupplierDTO> {
|
||||
await client.delete(`/api/suppliers/${id}/certificates`, { data: request });
|
||||
const res = await client.get<BackendSupplier>(`/api/suppliers/${id}`);
|
||||
return mapSupplier(res.data);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export type SuppliersResource = ReturnType<typeof createSuppliersResource>;
|
||||
Loading…
Add table
Add a link
Reference in a new issue