mirror of
https://github.com/s-frick/effigenix.git
synced 2026-03-28 17:49:57 +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
183
frontend/apps/cli/src/hooks/useArticles.ts
Normal file
183
frontend/apps/cli/src/hooks/useArticles.ts
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
import { useState, useCallback } from 'react';
|
||||
import type {
|
||||
ArticleDTO,
|
||||
CreateArticleRequest,
|
||||
UpdateArticleRequest,
|
||||
AddSalesUnitRequest,
|
||||
UpdateSalesUnitPriceRequest,
|
||||
} from '@effigenix/api-client';
|
||||
import { client } from '../utils/api-client.js';
|
||||
|
||||
interface ArticlesState {
|
||||
articles: ArticleDTO[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
function errorMessage(err: unknown): string {
|
||||
return err instanceof Error ? err.message : 'Unbekannter Fehler';
|
||||
}
|
||||
|
||||
export function useArticles() {
|
||||
const [state, setState] = useState<ArticlesState>({
|
||||
articles: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
const fetchArticles = useCallback(async () => {
|
||||
setState((s) => ({ ...s, loading: true, error: null }));
|
||||
try {
|
||||
const articles = await client.articles.list();
|
||||
setState({ articles, loading: false, error: null });
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const createArticle = useCallback(async (request: CreateArticleRequest) => {
|
||||
setState((s) => ({ ...s, loading: true, error: null }));
|
||||
try {
|
||||
const article = await client.articles.create(request);
|
||||
setState((s) => ({ articles: [...s.articles, article], loading: false, error: null }));
|
||||
return article;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateArticle = useCallback(async (id: string, request: UpdateArticleRequest) => {
|
||||
try {
|
||||
const updated = await client.articles.update(id, request);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
articles: s.articles.map((a) => (a.id === id ? updated : a)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const activateArticle = useCallback(async (id: string) => {
|
||||
try {
|
||||
const updated = await client.articles.activate(id);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
articles: s.articles.map((a) => (a.id === id ? updated : a)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const deactivateArticle = useCallback(async (id: string) => {
|
||||
try {
|
||||
const updated = await client.articles.deactivate(id);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
articles: s.articles.map((a) => (a.id === id ? updated : a)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const addSalesUnit = useCallback(async (id: string, request: AddSalesUnitRequest) => {
|
||||
try {
|
||||
const updated = await client.articles.addSalesUnit(id, request);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
articles: s.articles.map((a) => (a.id === id ? updated : a)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const removeSalesUnit = useCallback(async (articleId: string, salesUnitId: string) => {
|
||||
try {
|
||||
const updated = await client.articles.removeSalesUnit(articleId, salesUnitId);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
articles: s.articles.map((a) => (a.id === articleId ? updated : a)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateSalesUnitPrice = useCallback(
|
||||
async (articleId: string, salesUnitId: string, request: UpdateSalesUnitPriceRequest) => {
|
||||
try {
|
||||
const updated = await client.articles.updateSalesUnitPrice(articleId, salesUnitId, request);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
articles: s.articles.map((a) => (a.id === articleId ? updated : a)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const assignSupplier = useCallback(async (articleId: string, supplierId: string) => {
|
||||
try {
|
||||
const updated = await client.articles.assignSupplier(articleId, supplierId);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
articles: s.articles.map((a) => (a.id === articleId ? updated : a)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const removeSupplier = useCallback(async (articleId: string, supplierId: string) => {
|
||||
try {
|
||||
const updated = await client.articles.removeSupplier(articleId, supplierId);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
articles: s.articles.map((a) => (a.id === articleId ? updated : a)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const clearError = useCallback(() => {
|
||||
setState((s) => ({ ...s, error: null }));
|
||||
}, []);
|
||||
|
||||
return {
|
||||
...state,
|
||||
fetchArticles,
|
||||
createArticle,
|
||||
updateArticle,
|
||||
activateArticle,
|
||||
deactivateArticle,
|
||||
addSalesUnit,
|
||||
removeSalesUnit,
|
||||
updateSalesUnitPrice,
|
||||
assignSupplier,
|
||||
removeSupplier,
|
||||
clearError,
|
||||
};
|
||||
}
|
||||
88
frontend/apps/cli/src/hooks/useCategories.ts
Normal file
88
frontend/apps/cli/src/hooks/useCategories.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { useState, useCallback } from 'react';
|
||||
import type { ProductCategoryDTO } from '@effigenix/api-client';
|
||||
import { client } from '../utils/api-client.js';
|
||||
|
||||
interface CategoriesState {
|
||||
categories: ProductCategoryDTO[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
function errorMessage(err: unknown): string {
|
||||
return err instanceof Error ? err.message : 'Unbekannter Fehler';
|
||||
}
|
||||
|
||||
export function useCategories() {
|
||||
const [state, setState] = useState<CategoriesState>({
|
||||
categories: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
const fetchCategories = useCallback(async () => {
|
||||
setState((s) => ({ ...s, loading: true, error: null }));
|
||||
try {
|
||||
const categories = await client.categories.list();
|
||||
setState({ categories, loading: false, error: null });
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const createCategory = useCallback(async (name: string, description?: string) => {
|
||||
setState((s) => ({ ...s, loading: true, error: null }));
|
||||
try {
|
||||
const req = description ? { name, description } : { name };
|
||||
const cat = await client.categories.create(req);
|
||||
setState((s) => ({ categories: [...s.categories, cat], loading: false, error: null }));
|
||||
return cat;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateCategory = useCallback(
|
||||
async (id: string, name: string, description: string | null) => {
|
||||
try {
|
||||
const updated = await client.categories.update(id, { name, description });
|
||||
setState((s) => ({
|
||||
...s,
|
||||
categories: s.categories.map((c) => (c.id === id ? updated : c)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const deleteCategory = useCallback(async (id: string) => {
|
||||
try {
|
||||
await client.categories.delete(id);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
categories: s.categories.filter((c) => c.id !== id),
|
||||
}));
|
||||
return true;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return false;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const clearError = useCallback(() => {
|
||||
setState((s) => ({ ...s, error: null }));
|
||||
}, []);
|
||||
|
||||
return {
|
||||
...state,
|
||||
fetchCategories,
|
||||
createCategory,
|
||||
updateCategory,
|
||||
deleteCategory,
|
||||
clearError,
|
||||
};
|
||||
}
|
||||
150
frontend/apps/cli/src/hooks/useCustomers.ts
Normal file
150
frontend/apps/cli/src/hooks/useCustomers.ts
Normal file
|
|
@ -0,0 +1,150 @@
|
|||
import { useState, useCallback } from 'react';
|
||||
import type {
|
||||
CustomerDTO,
|
||||
CustomerPreference,
|
||||
CreateCustomerRequest,
|
||||
UpdateCustomerRequest,
|
||||
AddDeliveryAddressRequest,
|
||||
} from '@effigenix/api-client';
|
||||
import { client } from '../utils/api-client.js';
|
||||
|
||||
interface CustomersState {
|
||||
customers: CustomerDTO[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
function errorMessage(err: unknown): string {
|
||||
return err instanceof Error ? err.message : 'Unbekannter Fehler';
|
||||
}
|
||||
|
||||
export function useCustomers() {
|
||||
const [state, setState] = useState<CustomersState>({
|
||||
customers: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
const fetchCustomers = useCallback(async () => {
|
||||
setState((s) => ({ ...s, loading: true, error: null }));
|
||||
try {
|
||||
const customers = await client.customers.list();
|
||||
setState({ customers, loading: false, error: null });
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const createCustomer = useCallback(async (request: CreateCustomerRequest) => {
|
||||
setState((s) => ({ ...s, loading: true, error: null }));
|
||||
try {
|
||||
const customer = await client.customers.create(request);
|
||||
setState((s) => ({ customers: [...s.customers, customer], loading: false, error: null }));
|
||||
return customer;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateCustomer = useCallback(async (id: string, request: UpdateCustomerRequest) => {
|
||||
try {
|
||||
const updated = await client.customers.update(id, request);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
customers: s.customers.map((c) => (c.id === id ? updated : c)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const activateCustomer = useCallback(async (id: string) => {
|
||||
try {
|
||||
const updated = await client.customers.activate(id);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
customers: s.customers.map((c) => (c.id === id ? updated : c)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const deactivateCustomer = useCallback(async (id: string) => {
|
||||
try {
|
||||
const updated = await client.customers.deactivate(id);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
customers: s.customers.map((c) => (c.id === id ? updated : c)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const addDeliveryAddress = useCallback(async (id: string, request: AddDeliveryAddressRequest) => {
|
||||
try {
|
||||
const updated = await client.customers.addDeliveryAddress(id, request);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
customers: s.customers.map((c) => (c.id === id ? updated : c)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const removeDeliveryAddress = useCallback(async (id: string, label: string) => {
|
||||
try {
|
||||
const updated = await client.customers.removeDeliveryAddress(id, label);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
customers: s.customers.map((c) => (c.id === id ? updated : c)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const setPreferences = useCallback(async (id: string, preferences: CustomerPreference[]) => {
|
||||
try {
|
||||
const updated = await client.customers.setPreferences(id, preferences);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
customers: s.customers.map((c) => (c.id === id ? updated : c)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const clearError = useCallback(() => {
|
||||
setState((s) => ({ ...s, error: null }));
|
||||
}, []);
|
||||
|
||||
return {
|
||||
...state,
|
||||
fetchCustomers,
|
||||
createCustomer,
|
||||
updateCustomer,
|
||||
activateCustomer,
|
||||
deactivateCustomer,
|
||||
addDeliveryAddress,
|
||||
removeDeliveryAddress,
|
||||
setPreferences,
|
||||
clearError,
|
||||
};
|
||||
}
|
||||
151
frontend/apps/cli/src/hooks/useSuppliers.ts
Normal file
151
frontend/apps/cli/src/hooks/useSuppliers.ts
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
import { useState, useCallback } from 'react';
|
||||
import type {
|
||||
SupplierDTO,
|
||||
CreateSupplierRequest,
|
||||
UpdateSupplierRequest,
|
||||
RateSupplierRequest,
|
||||
AddCertificateRequest,
|
||||
RemoveCertificateRequest,
|
||||
} from '@effigenix/api-client';
|
||||
import { client } from '../utils/api-client.js';
|
||||
|
||||
interface SuppliersState {
|
||||
suppliers: SupplierDTO[];
|
||||
loading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
function errorMessage(err: unknown): string {
|
||||
return err instanceof Error ? err.message : 'Unbekannter Fehler';
|
||||
}
|
||||
|
||||
export function useSuppliers() {
|
||||
const [state, setState] = useState<SuppliersState>({
|
||||
suppliers: [],
|
||||
loading: false,
|
||||
error: null,
|
||||
});
|
||||
|
||||
const fetchSuppliers = useCallback(async () => {
|
||||
setState((s) => ({ ...s, loading: true, error: null }));
|
||||
try {
|
||||
const suppliers = await client.suppliers.list();
|
||||
setState({ suppliers, loading: false, error: null });
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
|
||||
}
|
||||
}, []);
|
||||
|
||||
const createSupplier = useCallback(async (request: CreateSupplierRequest) => {
|
||||
setState((s) => ({ ...s, loading: true, error: null }));
|
||||
try {
|
||||
const supplier = await client.suppliers.create(request);
|
||||
setState((s) => ({ suppliers: [...s.suppliers, supplier], loading: false, error: null }));
|
||||
return supplier;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, loading: false, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const updateSupplier = useCallback(async (id: string, request: UpdateSupplierRequest) => {
|
||||
try {
|
||||
const updated = await client.suppliers.update(id, request);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
suppliers: s.suppliers.map((sup) => (sup.id === id ? updated : sup)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const activateSupplier = useCallback(async (id: string) => {
|
||||
try {
|
||||
const updated = await client.suppliers.activate(id);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
suppliers: s.suppliers.map((sup) => (sup.id === id ? updated : sup)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const deactivateSupplier = useCallback(async (id: string) => {
|
||||
try {
|
||||
const updated = await client.suppliers.deactivate(id);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
suppliers: s.suppliers.map((sup) => (sup.id === id ? updated : sup)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const rateSupplier = useCallback(async (id: string, request: RateSupplierRequest) => {
|
||||
try {
|
||||
const updated = await client.suppliers.rate(id, request);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
suppliers: s.suppliers.map((sup) => (sup.id === id ? updated : sup)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const addCertificate = useCallback(async (id: string, request: AddCertificateRequest) => {
|
||||
try {
|
||||
const updated = await client.suppliers.addCertificate(id, request);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
suppliers: s.suppliers.map((sup) => (sup.id === id ? updated : sup)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const removeCertificate = useCallback(async (id: string, request: RemoveCertificateRequest) => {
|
||||
try {
|
||||
const updated = await client.suppliers.removeCertificate(id, request);
|
||||
setState((s) => ({
|
||||
...s,
|
||||
suppliers: s.suppliers.map((sup) => (sup.id === id ? updated : sup)),
|
||||
}));
|
||||
return updated;
|
||||
} catch (err) {
|
||||
setState((s) => ({ ...s, error: errorMessage(err) }));
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
|
||||
const clearError = useCallback(() => {
|
||||
setState((s) => ({ ...s, error: null }));
|
||||
}, []);
|
||||
|
||||
return {
|
||||
...state,
|
||||
fetchSuppliers,
|
||||
createSupplier,
|
||||
updateSupplier,
|
||||
activateSupplier,
|
||||
deactivateSupplier,
|
||||
rateSupplier,
|
||||
addCertificate,
|
||||
removeCertificate,
|
||||
clearError,
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue