Add material mismatch check

This commit is contained in:
Matteo Parenti
2026-02-13 17:01:16 +01:00
parent 30b6190163
commit 51d3a92263
7 changed files with 74 additions and 3 deletions
+56 -2
View File
@@ -5,6 +5,7 @@ import { X, Loader2, Package, Check, Search } from 'lucide-react';
import { api } from '../api/client';
import type { InventorySpool, SpoolAssignment } from '../api/client';
import { Button } from './Button';
import { ConfirmModal } from './ConfirmModal';
import { useToast } from '../contexts/ToastContext';
interface AssignSpoolModalProps {
@@ -26,6 +27,8 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
const { showToast } = useToast();
const [selectedSpoolId, setSelectedSpoolId] = useState<number | null>(null);
const [searchFilter, setSearchFilter] = useState('');
const [pendingAssignId, setPendingAssignId] = useState<number | null>(null);
const [showMismatchConfirm, setShowMismatchConfirm] = useState(false);
const { data: spools, isLoading } = useQuery({
queryKey: ['inventory-spools'],
@@ -53,6 +56,8 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
});
queryClient.invalidateQueries({ queryKey: ['spool-assignments'] });
showToast(t('inventory.assignSuccess'), 'success');
setShowMismatchConfirm(false);
setPendingAssignId(null);
onClose();
},
onError: (error: Error) => {
@@ -84,10 +89,37 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
);
});
const normalizeMaterial = (value: string | undefined | null) =>
(value ?? '').trim().toUpperCase();
const materialsMatch = (spoolMaterial: string, trayMaterial: string) => {
const normalizedSpool = normalizeMaterial(spoolMaterial);
const normalizedTray = normalizeMaterial(trayMaterial);
if (!normalizedSpool || !normalizedTray) return true;
if (normalizedTray.includes(normalizedSpool)) return true;
if (normalizedSpool.includes(normalizedTray)) return true;
return false;
};
const handleAssign = () => {
if (selectedSpoolId) {
assignMutation.mutate(selectedSpoolId);
if (!selectedSpoolId) return;
const selectedSpool = spools?.find((spool: InventorySpool) => spool.id === selectedSpoolId);
if (selectedSpool && trayInfo?.type) {
const mismatch = !materialsMatch(selectedSpool.material, trayInfo.type);
if (mismatch) {
setPendingAssignId(selectedSpoolId);
setShowMismatchConfirm(true);
return;
}
}
assignMutation.mutate(selectedSpoolId);
};
const handleConfirmMismatch = () => {
if (!pendingAssignId) return;
assignMutation.mutate(pendingAssignId);
setShowMismatchConfirm(false);
setPendingAssignId(null);
};
return (
@@ -226,6 +258,28 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
</div>
)}
</div>
{showMismatchConfirm && trayInfo && selectedSpoolId && (
<ConfirmModal
title={t('inventory.assignMismatchTitle')}
message={t('inventory.assignMismatchMessage', {
spoolMaterial: spools?.find((spool: InventorySpool) => spool.id === selectedSpoolId)?.material ?? '',
trayMaterial: trayInfo.type,
location: trayInfo.location,
})}
confirmText={t('inventory.assignMismatchConfirm')}
confirmClassName="!text-black"
variant="warning"
isLoading={assignMutation.isPending}
onConfirm={handleConfirmMismatch}
onCancel={() => {
if (!assignMutation.isPending) {
setShowMismatchConfirm(false);
setPendingAssignId(null);
}
}}
/>
)}
</div>
);
}
+3 -1
View File
@@ -11,6 +11,7 @@ interface ConfirmModalProps {
cancelText?: string;
cancelVariant?: 'primary' | 'secondary' | 'danger' | 'ghost';
cardClassName?: string;
confirmClassName?: string;
variant?: 'danger' | 'warning' | 'default';
isLoading?: boolean;
loadingText?: string;
@@ -25,6 +26,7 @@ export function ConfirmModal({
cancelText,
cancelVariant,
cardClassName,
confirmClassName,
variant = 'default',
isLoading = false,
loadingText,
@@ -91,7 +93,7 @@ export function ConfirmModal({
</Button>
<Button
onClick={onConfirm}
className={`flex-1 ${styles.button}`}
className={`flex-1 ${styles.button}${confirmClassName ? ` ${confirmClassName}` : ''}`}
disabled={isLoading}
>
{isLoading ? (
+3
View File
@@ -2483,6 +2483,9 @@ export default {
unassignSpool: 'Zuweisung aufheben',
assignSuccess: 'Spule zugewiesen und AMS-Slot konfiguriert',
assignFailed: 'Spulenzuweisung fehlgeschlagen',
assignMismatchTitle: 'Material stimmt nicht überein',
assignMismatchMessage: 'Das Material der ausgewählten Spule ({{spoolMaterial}}) stimmt nicht mit dem AMS-Profil ({{trayMaterial}}) für {{location}} überein. Trotzdem zuweisen?',
assignMismatchConfirm: 'Trotzdem zuweisen',
selectSpool: 'Wählen Sie eine Spule für diesen Slot',
assigned: 'Zugewiesen',
assigning: 'Wird zugewiesen...',
+3
View File
@@ -2483,6 +2483,9 @@ export default {
unassignSpool: 'Unassign',
assignSuccess: 'Spool assigned and AMS slot configured',
assignFailed: 'Failed to assign spool',
assignMismatchTitle: 'Material mismatch',
assignMismatchMessage: 'The selected spool material ({{spoolMaterial}}) does not match the AMS profile ({{trayMaterial}}) for {{location}}. Assign anyway?',
assignMismatchConfirm: 'Assign Anyway',
selectSpool: 'Select a spool to assign to this slot',
assigned: 'Assigned',
assigning: 'Assigning...',
+3
View File
@@ -2479,6 +2479,9 @@ export default {
unassignSpool: 'Désassigner',
assignSuccess: 'Bobine assignée et slot AMS configuré',
assignFailed: 'Échec assignation',
assignMismatchTitle: 'Matériau non correspondant',
assignMismatchMessage: 'Le matériau de la bobine sélectionnée ({{spoolMaterial}}) ne correspond pas au profil AMS ({{trayMaterial}}) pour {{location}}. Assigner quand même ?',
assignMismatchConfirm: 'Assigner quand même',
selectSpool: 'Choisir une bobine pour ce slot',
assigned: 'Assigné',
assigning: 'Assignation...',
+3
View File
@@ -2303,6 +2303,9 @@ export default {
unassignSpool: 'Deassegna',
assignSuccess: 'Bobina assegnata e slot AMS configurato',
assignFailed: 'Assegnazione bobina fallita',
assignMismatchTitle: 'Materiale non corrispondente',
assignMismatchMessage: 'Il materiale della bobina selezionata ({{spoolMaterial}}) non corrisponde al profilo AMS ({{trayMaterial}}) per {{location}}. Assegnare comunque?',
assignMismatchConfirm: 'Assegna comunque',
selectSpool: 'Seleziona una bobina da assegnare a questo slot',
assigned: 'Assegnato',
assigning: 'Assegnazione...',
+3
View File
@@ -2414,6 +2414,9 @@ export default {
unassignSpool: '割り当て解除',
assignSuccess: 'スプールを割り当て、AMSスロットを設定しました',
assignFailed: 'スプールの割り当てに失敗しました',
assignMismatchTitle: '素材が一致しません',
assignMismatchMessage: '選択したスプールの素材({{spoolMaterial}})が、{{location}} のAMSプロファイル({{trayMaterial}})と一致しません。割り当てを続行しますか?',
assignMismatchConfirm: '続行する',
selectSpool: 'このスロットに割り当てるスプールを選択',
assigned: '割り当て済み',
assigning: '割り当て中...',