Browse Source

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.
maziggy 1 ngày trước cách đây
mục cha
commit
9347921378

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
CHANGELOG.md


+ 21 - 12
frontend/src/components/AssignSpoolModal.tsx

@@ -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')}

+ 14 - 10
frontend/src/components/spoolbuddy/AssignToAmsModal.tsx

@@ -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')}

+ 1 - 0
frontend/src/i18n/locales/de.ts

@@ -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…',

+ 1 - 0
frontend/src/i18n/locales/en.ts

@@ -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…',

+ 1 - 0
frontend/src/i18n/locales/es.ts

@@ -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…',

+ 1 - 0
frontend/src/i18n/locales/fr.ts

@@ -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…',

+ 1 - 0
frontend/src/i18n/locales/it.ts

@@ -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…',

+ 1 - 0
frontend/src/i18n/locales/ja.ts

@@ -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カタログから選択…',

+ 1 - 0
frontend/src/i18n/locales/pt-BR.ts

@@ -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…',

+ 1 - 0
frontend/src/i18n/locales/zh-CN.ts

@@ -3651,6 +3651,7 @@ export default {
     assignMismatchConfirm: '仍然分配',
     assignPartialMismatchMessage: '线轴材料 "{{spoolMaterial}}" 与 {{location}} 的 "{{trayMaterial}}" 相近但不完全一致。是否继续?',
     assignProfileMismatchMessage: '线轴配置 "{{spoolProfile}}" 与 {{location}} 的料槽配置 "{{trayProfile}}" 不一致。是否继续?',
+    assignReconfigureNote: 'AMS 槽位将重新配置为使用线轴的配置。',
     selectSpool: '选择要分配到此槽位的耗材',
     assigned: '已分配',
     assigning: '分配中...',

+ 1 - 0
frontend/src/i18n/locales/zh-TW.ts

@@ -3651,6 +3651,7 @@ export default {
     assignMismatchConfirm: '仍然分配',
     assignPartialMismatchMessage: '料盤材料 "{{spoolMaterial}}" 與 {{location}} 的 "{{trayMaterial}}" 相近但不完全一致。是否繼續?',
     assignProfileMismatchMessage: '料盤設定 "{{spoolProfile}}" 與 {{location}} 的料槽設定 "{{trayProfile}}" 不一致。是否繼續?',
+    assignReconfigureNote: 'AMS 槽位將重新設定為使用料盤的設定。',
     selectSpool: '選擇要分配到此槽位的耗材',
     assigned: '已分配',
     assigning: '分配中...',

Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
static/assets/index-Ci-vx1Gm.js


+ 1 - 1
static/index.html

@@ -26,7 +26,7 @@
 
     <!-- Splash screens for iOS -->
     <link rel="apple-touch-startup-image" href="/img/android-chrome-512x512.png" />
-    <script type="module" crossorigin src="/assets/index-KFJfWuRR.js"></script>
+    <script type="module" crossorigin src="/assets/index-Ci-vx1Gm.js"></script>
     <link rel="stylesheet" crossorigin href="/assets/index-y4woBlMv.css">
   </head>
   <body>

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác