mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-08 07:01:40 +02:00
Add material mismatch check
This commit is contained in:
@@ -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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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 ? (
|
||||
|
||||
@@ -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...',
|
||||
|
||||
@@ -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...',
|
||||
|
||||
@@ -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...',
|
||||
|
||||
@@ -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...',
|
||||
|
||||
@@ -2414,6 +2414,9 @@ export default {
|
||||
unassignSpool: '割り当て解除',
|
||||
assignSuccess: 'スプールを割り当て、AMSスロットを設定しました',
|
||||
assignFailed: 'スプールの割り当てに失敗しました',
|
||||
assignMismatchTitle: '素材が一致しません',
|
||||
assignMismatchMessage: '選択したスプールの素材({{spoolMaterial}})が、{{location}} のAMSプロファイル({{trayMaterial}})と一致しません。割り当てを続行しますか?',
|
||||
assignMismatchConfirm: '続行する',
|
||||
selectSpool: 'このスロットに割り当てるスプールを選択',
|
||||
assigned: '割り当て済み',
|
||||
assigning: '割り当て中...',
|
||||
|
||||
Reference in New Issue
Block a user