import React, { useEffect, useState, useCallback } from 'react'; import { Box, Text, useInput } from 'ink'; import TextInput from 'ink-text-input'; import { useNavigation } from '../../state/navigation-context.js'; import { useProductionOrders } from '../../hooks/useProductionOrders.js'; import { useRecipeNameLookup } from '../../hooks/useRecipeNameLookup.js'; import { client } from '../../utils/api-client.js'; import type { BatchDTO } from '@effigenix/api-client'; import { LoadingSpinner } from '../shared/LoadingSpinner.js'; import { ErrorDisplay } from '../shared/ErrorDisplay.js'; import { SuccessDisplay } from '../shared/SuccessDisplay.js'; import { PRODUCTION_ORDER_STATUS_LABELS, PRIORITY_LABELS } from '@effigenix/api-client'; import type { ProductionOrderStatus, Priority } from '@effigenix/api-client'; const STATUS_COLORS: Record = { PLANNED: 'gray', RELEASED: 'yellow', IN_PROGRESS: 'blue', COMPLETED: 'green', CANCELLED: 'red', }; type Mode = 'view' | 'menu' | 'start-batch-input' | 'reschedule-input'; export function ProductionOrderDetailScreen() { const { params, back } = useNavigation(); const { productionOrder, loading, error, fetchProductionOrder, releaseProductionOrder, rescheduleProductionOrder, startProductionOrder, clearError, } = useProductionOrders(); const { recipeName } = useRecipeNameLookup(); const [mode, setMode] = useState('view'); const [menuIndex, setMenuIndex] = useState(0); const [batchId, setBatchId] = useState(''); const [newDate, setNewDate] = useState(''); const [success, setSuccess] = useState(null); const [batch, setBatch] = useState(null); const orderId = params.orderId ?? ''; useEffect(() => { if (orderId) void fetchProductionOrder(orderId); }, [fetchProductionOrder, orderId]); const loadBatch = useCallback(async (id: string) => { try { const b = await client.batches.getById(id); setBatch(b); } catch (err) { setBatch(null); } }, []); useEffect(() => { if (productionOrder?.batchId) void loadBatch(productionOrder.batchId); }, [loadBatch, productionOrder?.batchId]); const getMenuItems = () => { const items: { label: string; action: string }[] = []; const status = productionOrder?.status; if (status === 'PLANNED') { items.push({ label: 'Freigeben', action: 'release' }); items.push({ label: 'Umterminieren', action: 'reschedule' }); } if (status === 'RELEASED') { items.push({ label: 'Produktion starten', action: 'start' }); items.push({ label: 'Umterminieren', action: 'reschedule' }); } return items; }; const menuItems = getMenuItems(); const handleRelease = async () => { const result = await releaseProductionOrder(orderId); if (result) { setSuccess('Produktionsauftrag freigegeben.'); setMode('view'); } }; const handleStart = async () => { if (!batchId.trim()) return; const result = await startProductionOrder(orderId, { batchId: batchId.trim() }); if (result) { setSuccess('Produktion gestartet.'); setMode('view'); setBatchId(''); } }; const handleReschedule = async () => { if (!newDate.trim()) return; const result = await rescheduleProductionOrder(orderId, newDate.trim()); if (result) { setSuccess(`Umterminiert auf ${newDate.trim()}.`); setMode('view'); setNewDate(''); } }; useInput((_input, key) => { if (loading) return; if (mode === 'reschedule-input') { if (key.escape) setMode('menu'); return; } if (mode === 'start-batch-input') { if (key.escape) setMode('menu'); return; } if (mode === 'menu') { if (key.upArrow) setMenuIndex((i) => Math.max(0, i - 1)); if (key.downArrow) setMenuIndex((i) => Math.min(menuItems.length - 1, i + 1)); if (key.return && menuItems[menuIndex]) { const action = menuItems[menuIndex].action; if (action === 'release') void handleRelease(); if (action === 'reschedule') { setMode('reschedule-input'); setNewDate(''); } if (action === 'start') { setMode('start-batch-input'); setBatchId(''); } } if (key.escape) setMode('view'); return; } // view mode if (_input === 'm' && menuItems.length > 0) { setMode('menu'); setMenuIndex(0); } if (key.backspace || key.escape) back(); }); if (loading && !productionOrder) return ; if (!productionOrder) { return ( {error && } Produktionsauftrag nicht gefunden. ); } const statusLabel = PRODUCTION_ORDER_STATUS_LABELS[(productionOrder.status ?? '') as ProductionOrderStatus] ?? productionOrder.status; const statusColor = STATUS_COLORS[productionOrder.status ?? ''] ?? 'white'; const prioLabel = PRIORITY_LABELS[(productionOrder.priority ?? '') as Priority] ?? productionOrder.priority; const createdAt = productionOrder.createdAt ? new Date(productionOrder.createdAt).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short' }) : ''; const updatedAt = productionOrder.updatedAt ? new Date(productionOrder.updatedAt).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short' }) : ''; return ( Produktionsauftrag {loading && (aktualisiere...)} {error && } {success && setSuccess(null)} />} ID: {productionOrder.id} Rezept: {recipeName(productionOrder.recipeId ?? '') ?? productionOrder.recipeId} ({productionOrder.recipeId}) Status: {statusLabel} Menge: {productionOrder.plannedQuantity} {productionOrder.plannedQuantityUnit} Geplant am: {productionOrder.plannedDate} Priorität: {prioLabel} {productionOrder.batchId && ( Chargen-Nr: {batch?.batchNumber ?? productionOrder.batchId} )} {productionOrder.notes && ( Notizen: {productionOrder.notes} )} Erstellt: {createdAt} Aktualisiert: {updatedAt} {batch && productionOrder.batchId && ( Produktionsergebnis Soll-Menge: {batch.plannedQuantity} {batch.plannedQuantityUnit} {batch.actualQuantity && ( Ist-Menge: {batch.actualQuantity} {batch.actualQuantityUnit} )} {batch.waste && ( Ausschuss: {batch.waste} {batch.wasteUnit} )} {batch.remarks && ( Bemerkungen: {batch.remarks} )} {batch.completedAt && ( Abgeschl. am: {new Date(batch.completedAt).toLocaleString('de-DE', { dateStyle: 'medium', timeStyle: 'short' })} )} )} {mode === 'menu' && ( Aktionen {menuItems.map((item, i) => ( {i === menuIndex ? '▶ ' : ' '}{item.label} ))} ↑↓ nav · Enter ausführen · Escape zurück )} {mode === 'start-batch-input' && ( Chargen-ID eingeben: void handleStart()} focus={true} /> Enter bestätigen · Escape abbrechen )} {mode === 'reschedule-input' && ( Neues Datum (YYYY-MM-DD): void handleReschedule()} focus={true} /> Enter bestätigen · Escape abbrechen )} {menuItems.length > 0 ? '[m] Aktionsmenü · ' : ''}Backspace Zurück ); }