mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 03:01:21 +02:00
fix(inventory): drop profile-only mismatch popup and clarify reconfigure intent (#1552)
Reporter assigned a spool to a slot whose stored slicer profile differed
from the new spool's, got the Cancel / Assign Anyway popup, and was under
the impression that confirming the popup just linked the spool in
Bambuddy's DB without pushing the new profile to the AMS — i.e. that he
then had to manually open Configure AMS Slot to fix it. The auto-push has
actually been in place since the assign route existed:
backend/app/api/routes/inventory.py::assign_spool calls
apply_spool_to_slot_via_mqtt after upserting the SpoolAssignment row,
which publishes ams_filament_setting + extrusion_cali_sel over MQTT, and
backend/app/api/routes/spoolman_inventory.py::assign_spoolman_slot does
the same for the Spoolman backend. The only short-circuit is when
firmware reports the slot explicitly empty (tray_state in {9, 10}), in
which case main.py::on_ams_change deferred-replays the configure once a
spool appears. So the popup was friction without revealing what it did.
Two changes:
- frontend/src/components/AssignSpoolModal.tsx and
spoolbuddy/AssignToAmsModal.tsx: profile-only mismatch no longer
fires the popup. The condition becomes
`if (materialMatchResult !== 'exact')` instead of
`materialMatchResult !== 'exact' || !profileMatches`. The 'profile'
member is dropped from the mismatchType union and its standalone
branch in each popup render body is removed as dead code. Material
mismatch still warns — Bambu firmware can refuse the print when the
type is wrong.
- Every firing warning (material, partial, material+profile,
partial+profile) now appends one line via a new
inventory.assignReconfigureNote i18n key:
"The AMS slot will be reconfigured to use the spool's profile."
Real translations across all 9 locales per
feedback_translate_dont_fallback; parity script clean at 4999
leaves per locale.
This commit is contained in:
@@ -40,8 +40,16 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
|
||||
const [searchFilter, setSearchFilter] = useState('');
|
||||
const [pendingAssignId, setPendingAssignId] = useState<number | null>(null);
|
||||
const [showMismatchConfirm, setShowMismatchConfirm] = useState(false);
|
||||
// Profile-only mismatch no longer triggers the popup — the backend's
|
||||
// `apply_spool_to_slot_via_mqtt` pushes the spool's slicer profile to the
|
||||
// AMS slot on every assign anyway, so warning the user about a profile
|
||||
// delta then "fixing" it during the same action was friction without
|
||||
// benefit (#1552). Material mismatch still warns because the firmware can
|
||||
// refuse a print when type doesn't match; combined material+profile
|
||||
// mismatches keep the profile detail in the same popup as the material
|
||||
// warning.
|
||||
const [mismatchDetails, setMismatchDetails] = useState<{
|
||||
type: 'material' | 'partial' | 'profile' | 'material_profile' | 'partial_profile';
|
||||
type: 'material' | 'partial' | 'material_profile' | 'partial_profile';
|
||||
spoolMaterial: string;
|
||||
trayMaterial: string;
|
||||
spoolProfile?: string;
|
||||
@@ -291,17 +299,18 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
|
||||
const trayProfile = trayInfo.profile || trayInfo.type;
|
||||
const profileMatches = checkProfileMatch(spoolProfile, trayProfile);
|
||||
|
||||
// Always evaluate both checks; if both fail, show a combined warning.
|
||||
if (materialMatchResult !== 'exact' || !profileMatches) {
|
||||
let mismatchType: 'material' | 'partial' | 'profile' | 'material_profile' | 'partial_profile' = 'profile';
|
||||
|
||||
// Only material-bearing mismatches warn — profile-only deltas are
|
||||
// silently resolved by the backend's AMS reconfigure on every assign
|
||||
// (#1552).
|
||||
if (materialMatchResult !== 'exact') {
|
||||
let mismatchType: 'material' | 'partial' | 'material_profile' | 'partial_profile';
|
||||
if (materialMatchResult === 'none' && !profileMatches) {
|
||||
mismatchType = 'material_profile';
|
||||
} else if (materialMatchResult === 'partial' && !profileMatches) {
|
||||
mismatchType = 'partial_profile';
|
||||
} else if (materialMatchResult === 'none') {
|
||||
mismatchType = 'material';
|
||||
} else if (materialMatchResult === 'partial') {
|
||||
} else {
|
||||
mismatchType = 'partial';
|
||||
}
|
||||
|
||||
@@ -584,14 +593,14 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
|
||||
trayProfile: mismatchDetails.trayProfile || t('common.unknown'),
|
||||
location: trayInfo.location,
|
||||
})}`;
|
||||
} else if (mismatchDetails.type === 'profile') {
|
||||
message = t('inventory.assignProfileMismatchMessage', {
|
||||
spoolProfile: mismatchDetails.spoolProfile || t('common.unknown'),
|
||||
trayProfile: mismatchDetails.trayProfile || t('common.unknown'),
|
||||
location: trayInfo.location,
|
||||
});
|
||||
}
|
||||
|
||||
// Always tell the user the AMS slot is going to be reconfigured —
|
||||
// the existing wording made "Assign Anyway" sound like the popup was
|
||||
// a no-op confirmation, when the backend in fact pushes the spool's
|
||||
// profile to the slot on every assign (#1552).
|
||||
message = `${message}\n\n${t('inventory.assignReconfigureNote')}`;
|
||||
|
||||
return (
|
||||
<ConfirmModal
|
||||
title={t('inventory.assignMismatchTitle')}
|
||||
|
||||
@@ -66,8 +66,13 @@ export function AssignToAmsModal({ isOpen, onClose, spool, printerId, spoolmanMo
|
||||
const [statusMessage, setStatusMessage] = useState<string | null>(null);
|
||||
const [statusType, setStatusType] = useState<'info' | 'success' | 'error' | null>(null);
|
||||
const [showMismatchConfirm, setShowMismatchConfirm] = useState(false);
|
||||
// Profile-only mismatches no longer trigger the popup — the backend
|
||||
// pushes the spool's slicer profile to the AMS slot on every assign
|
||||
// anyway, so the warning was friction without benefit (#1552). Material
|
||||
// mismatch still warns because firmware can refuse a print when type
|
||||
// doesn't match.
|
||||
const [mismatchDetails, setMismatchDetails] = useState<{
|
||||
type: 'material' | 'partial' | 'profile' | 'material_profile' | 'partial_profile';
|
||||
type: 'material' | 'partial' | 'material_profile' | 'partial_profile';
|
||||
spoolMaterial: string;
|
||||
trayMaterial: string;
|
||||
spoolProfile?: string;
|
||||
@@ -266,15 +271,15 @@ export function AssignToAmsModal({ isOpen, onClose, spool, printerId, spoolmanMo
|
||||
const trayProfile = tray.tray_type || '';
|
||||
const profileMatches = checkProfileMatch(spoolProfile, trayProfile);
|
||||
|
||||
if (materialMatchResult !== 'exact' || !profileMatches) {
|
||||
let mismatchType: 'material' | 'partial' | 'profile' | 'material_profile' | 'partial_profile' = 'profile';
|
||||
if (materialMatchResult !== 'exact') {
|
||||
let mismatchType: 'material' | 'partial' | 'material_profile' | 'partial_profile';
|
||||
if (materialMatchResult === 'none' && !profileMatches) {
|
||||
mismatchType = 'material_profile';
|
||||
} else if (materialMatchResult === 'partial' && !profileMatches) {
|
||||
mismatchType = 'partial_profile';
|
||||
} else if (materialMatchResult === 'none') {
|
||||
mismatchType = 'material';
|
||||
} else if (materialMatchResult === 'partial') {
|
||||
} else {
|
||||
mismatchType = 'partial';
|
||||
}
|
||||
|
||||
@@ -542,14 +547,13 @@ export function AssignToAmsModal({ isOpen, onClose, spool, printerId, spoolmanMo
|
||||
trayProfile: mismatchDetails.trayProfile || t('common.unknown'),
|
||||
location: mismatchDetails.location,
|
||||
})}`;
|
||||
} else if (mismatchDetails.type === 'profile') {
|
||||
message = t('inventory.assignProfileMismatchMessage', {
|
||||
spoolProfile: mismatchDetails.spoolProfile || t('common.unknown'),
|
||||
trayProfile: mismatchDetails.trayProfile || t('common.unknown'),
|
||||
location: mismatchDetails.location,
|
||||
});
|
||||
}
|
||||
|
||||
// Always tell the user the AMS slot will be reconfigured — without
|
||||
// this, "Assign Anyway" reads as a no-op confirmation when the
|
||||
// backend in fact pushes the spool profile on every assign (#1552).
|
||||
message = `${message}\n\n${t('inventory.assignReconfigureNote')}`;
|
||||
|
||||
return (
|
||||
<ConfirmModal
|
||||
title={t('inventory.assignMismatchTitle')}
|
||||
|
||||
@@ -3831,6 +3831,7 @@ export default {
|
||||
assignMismatchConfirm: 'Trotzdem zuweisen',
|
||||
assignPartialMismatchMessage: 'Das Spulenmaterial "{{spoolMaterial}}" ist ähnlich, stimmt aber nicht genau mit "{{trayMaterial}}" in {{location}} überein. Möchten Sie fortfahren?',
|
||||
assignProfileMismatchMessage: 'Das Spulenprofil "{{spoolProfile}}" stimmt nicht mit dem Fachprofil "{{trayProfile}}" in {{location}} überein. Möchten Sie fortfahren?',
|
||||
assignReconfigureNote: 'Der AMS-Slot wird mit dem Profil der Spule neu konfiguriert.',
|
||||
// Spoolman filament catalog picker
|
||||
spoolmanFilamentCatalog: 'Spoolman-Filamentkatalog',
|
||||
pickFromSpoolmanCatalog: 'Aus Spoolman-Katalog wählen…',
|
||||
|
||||
@@ -3843,6 +3843,7 @@ export default {
|
||||
assignMismatchConfirm: 'Assign Anyway',
|
||||
assignPartialMismatchMessage: 'The spool material "{{spoolMaterial}}" is similar to but not exactly matching "{{trayMaterial}}" in {{location}}. Do you want to proceed?',
|
||||
assignProfileMismatchMessage: 'The spool profile "{{spoolProfile}}" does not match the tray profile "{{trayProfile}}" in {{location}}. Do you want to proceed?',
|
||||
assignReconfigureNote: 'The AMS slot will be reconfigured to use the spool\'s profile.',
|
||||
// Spoolman filament catalog picker
|
||||
spoolmanFilamentCatalog: 'Spoolman Filament Catalog',
|
||||
pickFromSpoolmanCatalog: 'Pick from Spoolman catalog…',
|
||||
|
||||
@@ -3839,6 +3839,7 @@ export default {
|
||||
assignMismatchConfirm: 'Asignar de todos modos',
|
||||
assignPartialMismatchMessage: 'El material de la bobina "{{spoolMaterial}}" es similar pero no coincide exactamente con "{{trayMaterial}}" en {{location}}. ¿Desea continuar?',
|
||||
assignProfileMismatchMessage: 'El perfil de la bobina "{{spoolProfile}}" no coincide con el perfil de la bandeja "{{trayProfile}}" en {{location}}. ¿Desea continuar?',
|
||||
assignReconfigureNote: 'La ranura del AMS se reconfigurará para usar el perfil de la bobina.',
|
||||
// Spoolman filament catalog picker
|
||||
spoolmanFilamentCatalog: 'Catálogo de filamentos de Spoolman',
|
||||
pickFromSpoolmanCatalog: 'Elegir del catálogo de Spoolman…',
|
||||
|
||||
@@ -3820,6 +3820,7 @@ export default {
|
||||
assignMismatchConfirm: 'Assigner quand même',
|
||||
assignPartialMismatchMessage: 'Le matériau de la bobine "{{spoolMaterial}}" est similaire, mais ne correspond pas exactement à "{{trayMaterial}}" dans {{location}}. Voulez-vous continuer ?',
|
||||
assignProfileMismatchMessage: 'Le profil de la bobine "{{spoolProfile}}" ne correspond pas au profil du plateau "{{trayProfile}}" dans {{location}}. Voulez-vous continuer ?',
|
||||
assignReconfigureNote: 'Le slot AMS sera reconfiguré pour utiliser le profil de la bobine.',
|
||||
// Spoolman filament catalog picker
|
||||
spoolmanFilamentCatalog: 'Catalogue de filaments Spoolman',
|
||||
pickFromSpoolmanCatalog: 'Choisir dans le catalogue Spoolman…',
|
||||
|
||||
@@ -3819,6 +3819,7 @@ export default {
|
||||
assignMismatchConfirm: 'Assegna comunque',
|
||||
assignPartialMismatchMessage: 'Il materiale della bobina "{{spoolMaterial}}" è simile ma non corrisponde esattamente a "{{trayMaterial}}" in {{location}}. Vuoi procedere?',
|
||||
assignProfileMismatchMessage: 'Il profilo della bobina "{{spoolProfile}}" non corrisponde al profilo del vassoio "{{trayProfile}}" in {{location}}. Vuoi procedere?',
|
||||
assignReconfigureNote: 'Lo slot AMS verrà riconfigurato per utilizzare il profilo della bobina.',
|
||||
// Spoolman filament catalog picker
|
||||
spoolmanFilamentCatalog: 'Catalogo filamenti Spoolman',
|
||||
pickFromSpoolmanCatalog: 'Scegli dal catalogo Spoolman…',
|
||||
|
||||
@@ -3831,6 +3831,7 @@ export default {
|
||||
assignMismatchConfirm: '強制的に割り当て',
|
||||
assignPartialMismatchMessage: 'スプールの材料「{{spoolMaterial}}」は「{{trayMaterial}}」に似ていますが、{{location}} と完全には一致しません。続行しますか?',
|
||||
assignProfileMismatchMessage: 'スプールのプロファイル「{{spoolProfile}}」は {{location}} のトレイプロファイル「{{trayProfile}}」と一致しません。続行しますか?',
|
||||
assignReconfigureNote: 'AMSスロットはスプールのプロファイルで再構成されます。',
|
||||
// Spoolman filament catalog picker
|
||||
spoolmanFilamentCatalog: 'Spoolmanフィラメントカタログ',
|
||||
pickFromSpoolmanCatalog: 'Spoolmanカタログから選択…',
|
||||
|
||||
@@ -3819,6 +3819,7 @@ export default {
|
||||
assignMismatchConfirm: 'Atribuir mesmo assim',
|
||||
assignPartialMismatchMessage: 'O material do carretel "{{spoolMaterial}}" é semelhante, mas não corresponde exatamente a "{{trayMaterial}}" em {{location}}. Deseja prosseguir?',
|
||||
assignProfileMismatchMessage: 'O perfil do carretel "{{spoolProfile}}" não corresponde ao perfil da bandeja "{{trayProfile}}" em {{location}}. Deseja prosseguir?',
|
||||
assignReconfigureNote: 'O slot do AMS será reconfigurado para usar o perfil do carretel.',
|
||||
// Spoolman filament catalog picker
|
||||
spoolmanFilamentCatalog: 'Catálogo de filamentos Spoolman',
|
||||
pickFromSpoolmanCatalog: 'Escolher do catálogo Spoolman…',
|
||||
|
||||
@@ -3651,6 +3651,7 @@ export default {
|
||||
assignMismatchConfirm: '仍然分配',
|
||||
assignPartialMismatchMessage: '线轴材料 "{{spoolMaterial}}" 与 {{location}} 的 "{{trayMaterial}}" 相近但不完全一致。是否继续?',
|
||||
assignProfileMismatchMessage: '线轴配置 "{{spoolProfile}}" 与 {{location}} 的料槽配置 "{{trayProfile}}" 不一致。是否继续?',
|
||||
assignReconfigureNote: 'AMS 槽位将重新配置为使用线轴的配置。',
|
||||
selectSpool: '选择要分配到此槽位的耗材',
|
||||
assigned: '已分配',
|
||||
assigning: '分配中...',
|
||||
|
||||
@@ -3651,6 +3651,7 @@ export default {
|
||||
assignMismatchConfirm: '仍然分配',
|
||||
assignPartialMismatchMessage: '料盤材料 "{{spoolMaterial}}" 與 {{location}} 的 "{{trayMaterial}}" 相近但不完全一致。是否繼續?',
|
||||
assignProfileMismatchMessage: '料盤設定 "{{spoolProfile}}" 與 {{location}} 的料槽設定 "{{trayProfile}}" 不一致。是否繼續?',
|
||||
assignReconfigureNote: 'AMS 槽位將重新設定為使用料盤的設定。',
|
||||
selectSpool: '選擇要分配到此槽位的耗材',
|
||||
assigned: '已分配',
|
||||
assigning: '分配中...',
|
||||
|
||||
Reference in New Issue
Block a user