1
0
Fork 0
mirror of https://github.com/s-frick/effigenix.git synced 2026-03-28 10:39:35 +01:00

feat(frontend): TUI-Screens für Rezept-Filter, Archivierung und Chargen-Einbuchung

Production: Rezeptliste mit Status-Filter (Draft/Active/Archived), Rezept
archivieren für aktive Rezepte, list() gibt RecipeSummaryDTO zurück.
Inventory: Charge einbuchen (AddBatch) mit neuem Stocks-Resource und Screens.
This commit is contained in:
Sebastian Frick 2026-02-19 22:46:38 +01:00
parent f2003a3093
commit ec736cf294
16 changed files with 553 additions and 16 deletions

View file

@ -9,8 +9,8 @@ import { SuccessDisplay } from '../shared/SuccessDisplay.js';
import { ConfirmDialog } from '../shared/ConfirmDialog.js';
import { client } from '../../utils/api-client.js';
type MenuAction = 'add-ingredient' | 'remove-ingredient' | 'add-step' | 'remove-step' | 'activate' | 'back';
type Mode = 'menu' | 'select-step-to-remove' | 'confirm-remove' | 'select-ingredient-to-remove' | 'confirm-remove-ingredient' | 'confirm-activate';
type MenuAction = 'add-ingredient' | 'remove-ingredient' | 'add-step' | 'remove-step' | 'activate' | 'archive' | 'back';
type Mode = 'menu' | 'select-step-to-remove' | 'confirm-remove' | 'select-ingredient-to-remove' | 'confirm-remove-ingredient' | 'confirm-activate' | 'confirm-archive';
function errorMessage(err: unknown): string {
return err instanceof Error ? err.message : 'Unbekannter Fehler';
@ -33,6 +33,7 @@ export function RecipeDetailScreen() {
const [ingredientToRemove, setIngredientToRemove] = useState<IngredientDTO | null>(null);
const isDraft = recipe?.status === 'DRAFT';
const isActive = recipe?.status === 'ACTIVE';
const menuItems: { id: MenuAction; label: string }[] = [
...(isDraft ? [
@ -42,6 +43,9 @@ export function RecipeDetailScreen() {
{ id: 'remove-step' as const, label: '[Schritt entfernen]' },
{ id: 'activate' as const, label: '[Rezept aktivieren]' },
] : []),
...(isActive ? [
{ id: 'archive' as const, label: '[Rezept archivieren]' },
] : []),
{ id: 'back' as const, label: '[Zurück]' },
];
@ -127,6 +131,9 @@ export function RecipeDetailScreen() {
case 'activate':
setMode('confirm-activate');
break;
case 'archive':
setMode('confirm-archive');
break;
case 'back':
back();
break;
@ -179,6 +186,20 @@ export function RecipeDetailScreen() {
setActionLoading(false);
}, [recipe]);
const handleArchive = useCallback(async () => {
if (!recipe) return;
setMode('menu');
setActionLoading(true);
try {
const updated = await client.recipes.archiveRecipe(recipe.id);
setRecipe(updated);
setSuccessMessage('Rezept wurde archiviert.');
} catch (err: unknown) {
setError(errorMessage(err));
}
setActionLoading(false);
}, [recipe]);
if (loading) return <LoadingSpinner label="Lade Rezept..." />;
if (error && !recipe) return <ErrorDisplay message={error} onDismiss={back} />;
if (!recipe) return <Text color="red">Rezept nicht gefunden.</Text>;
@ -307,6 +328,14 @@ export function RecipeDetailScreen() {
/>
)}
{mode === 'confirm-archive' && (
<ConfirmDialog
message="Rezept wirklich archivieren? Der Status wechselt zu ARCHIVED."
onConfirm={() => void handleArchive()}
onCancel={() => setMode('menu')}
/>
)}
{mode === 'menu' && (
<Box flexDirection="column">
<Text color="gray" bold>Aktionen:</Text>