1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 18:49:59 +01:00

feat(tui,seed): Seed-Testdaten und Bestandsbewegung-Detail verbessern

Seed-Daten SQL-File (099-seed-testdata.sql) mit realistischem Szenario
"Fleischerei & Feinkost" erstellt: 6 User, 6 Kategorien, 5 Lieferanten,
18 Artikel, 5 Kunden, 5 Lagerorte, Bestände, Rezepte, Chargen,
Produktionsaufträge und Bestandsbewegungen.

StockMovement-Detailseite: IDs durch lesbare Namen ersetzt (Lagerort,
Artikel, Benutzer) mit ausgegrautem ID dahinter, stockBatchId entfernt.
This commit is contained in:
Sebastian Frick 2026-02-25 22:33:35 +01:00
parent 6504d3a54e
commit d63ac899e7
3 changed files with 617 additions and 8 deletions

View file

@ -1,11 +1,12 @@
import React, { useEffect } from 'react';
import React, { useEffect, useState } from 'react';
import { Box, Text, useInput } from 'ink';
import { useNavigation } from '../../state/navigation-context.js';
import { useStockMovements } from '../../hooks/useStockMovements.js';
import { LoadingSpinner } from '../shared/LoadingSpinner.js';
import { ErrorDisplay } from '../shared/ErrorDisplay.js';
import { MOVEMENT_TYPE_LABELS, MOVEMENT_DIRECTION_LABELS } from '@effigenix/api-client';
import type { MovementType, MovementDirection } from '@effigenix/api-client';
import { MOVEMENT_TYPE_LABELS, MOVEMENT_DIRECTION_LABELS, STORAGE_TYPE_LABELS } from '@effigenix/api-client';
import type { MovementType, MovementDirection, StorageType } from '@effigenix/api-client';
import { client } from '../../utils/api-client.js';
const DIRECTION_COLORS: Record<string, string> = {
IN: 'green',
@ -16,12 +17,51 @@ export function StockMovementDetailScreen() {
const { params, back } = useNavigation();
const { movement, loading, error, fetchMovement, clearError } = useStockMovements();
const [storageLabel, setStorageLabel] = useState<string | null>(null);
const [articleLabel, setArticleLabel] = useState<string | null>(null);
const [userLabel, setUserLabel] = useState<string | null>(null);
const movementId = params.movementId ?? '';
useEffect(() => {
if (movementId) void fetchMovement(movementId);
}, [fetchMovement, movementId]);
// Resolve Storage Location via Stock → StorageLocation
useEffect(() => {
if (!movement?.stockId) return;
void (async () => {
try {
const stock = await client.stocks.getById(movement.stockId);
const loc = await client.storageLocations.getById(stock.storageLocationId);
const typeLabel = STORAGE_TYPE_LABELS[loc.storageType as StorageType] ?? loc.storageType;
setStorageLabel(`${loc.name} (${typeLabel})`);
} catch { setStorageLabel(null); }
})();
}, [movement?.stockId]);
// Resolve Article name + number
useEffect(() => {
if (!movement?.articleId) return;
void (async () => {
try {
const article = await client.articles.getById(movement.articleId);
setArticleLabel(`${article.name} (${article.articleNumber})`);
} catch { setArticleLabel(null); }
})();
}, [movement?.articleId]);
// Resolve User
useEffect(() => {
if (!movement?.performedBy) return;
void (async () => {
try {
const user = await client.users.getById(movement.performedBy);
setUserLabel(user.username);
} catch { setUserLabel(null); }
})();
}, [movement?.performedBy]);
useInput((_input, key) => {
if (key.backspace || key.escape) back();
});
@ -58,9 +98,8 @@ export function StockMovementDetailScreen() {
<Box><Text color="gray">Typ: </Text><Text>{typeLabel}</Text></Box>
<Box><Text color="gray">Richtung: </Text><Text color={dirColor}>{dirLabel}</Text></Box>
<Box><Text color="gray">Menge: </Text><Text>{movement.quantityAmount} {movement.quantityUnit}</Text></Box>
<Box><Text color="gray">Stock-ID: </Text><Text>{movement.stockId}</Text></Box>
<Box><Text color="gray">Artikel-ID: </Text><Text>{movement.articleId}</Text></Box>
<Box><Text color="gray">Chargen-Batch: </Text><Text>{movement.stockBatchId}</Text></Box>
<Box><Text color="gray">Lagerort: </Text><Text>{storageLabel ?? '...'}</Text><Text dimColor> {movement.stockId}</Text></Box>
<Box><Text color="gray">Artikel: </Text><Text>{articleLabel ?? '...'}</Text><Text dimColor> {movement.articleId}</Text></Box>
<Box><Text color="gray">Chargen-Nr.: </Text><Text>{movement.batchId}</Text></Box>
<Box><Text color="gray">Chargen-Typ: </Text><Text>{movement.batchType}</Text></Box>
{movement.reason && (
@ -69,7 +108,7 @@ export function StockMovementDetailScreen() {
{movement.referenceDocumentId && (
<Box><Text color="gray">Referenz-Dok.: </Text><Text>{movement.referenceDocumentId}</Text></Box>
)}
<Box><Text color="gray">Durchgeführt von:</Text><Text> {movement.performedBy}</Text></Box>
<Box><Text color="gray">Durchgeführt von:</Text><Text> {userLabel ?? '...'}</Text><Text dimColor> {movement.performedBy}</Text></Box>
<Box><Text color="gray">Zeitpunkt: </Text><Text>{dateStr}</Text></Box>
</Box>