Parcourir la source

[Feature] Improve AssignSpoolModal filtering logic (#889)

[Feature] Improve AssignSpoolModal filtering logic (#889)
Keybored il y a 5 mois
Parent
commit
d4913ef0df

+ 70 - 34
frontend/src/components/AssignSpoolModal.tsx

@@ -1,4 +1,4 @@
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
 import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { X, Loader2, Package, Search } from 'lucide-react';
 import { X, Loader2, Package, Search } from 'lucide-react';
@@ -27,7 +27,11 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
   const { t } = useTranslation();
   const { t } = useTranslation();
   const queryClient = useQueryClient();
   const queryClient = useQueryClient();
   const { showToast } = useToast();
   const { showToast } = useToast();
+  const [disableFiltering, setDisableFiltering] = useState(false);
   const [selectedSpoolId, setSelectedSpoolId] = useState<number | null>(null);
   const [selectedSpoolId, setSelectedSpoolId] = useState<number | null>(null);
+  useEffect(() => {
+    setSelectedSpoolId(null);
+  }, [disableFiltering]);
   const [searchFilter, setSearchFilter] = useState('');
   const [searchFilter, setSearchFilter] = useState('');
   const [pendingAssignId, setPendingAssignId] = useState<number | null>(null);
   const [pendingAssignId, setPendingAssignId] = useState<number | null>(null);
   const [showMismatchConfirm, setShowMismatchConfirm] = useState(false);
   const [showMismatchConfirm, setShowMismatchConfirm] = useState(false);
@@ -39,6 +43,12 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
     trayProfile?: string;
     trayProfile?: string;
   } | null>(null);
   } | null>(null);
 
 
+  useEffect(() => {
+    if (isOpen) {
+      setDisableFiltering(false);
+    }
+  }, [isOpen]);
+
   const { data: spools, isLoading } = useQuery({
   const { data: spools, isLoading } = useQuery({
     queryKey: ['inventory-spools'],
     queryKey: ['inventory-spools'],
     queryFn: () => api.getSpools(),
     queryFn: () => api.getSpools(),
@@ -128,16 +138,28 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
     !assignedSpoolIds.has(spool.id) && (isExternalSlot || (!spool.tag_uid && !spool.tray_uuid))
     !assignedSpoolIds.has(spool.id) && (isExternalSlot || (!spool.tag_uid && !spool.tray_uuid))
   );
   );
 
 
-  const filteredSpools = manualSpools?.filter((spool: InventorySpool) => {
-    if (!searchFilter) return true;
+  // Filtering logic with toggle: search filter always applies, AMS tray profile filter is optional
+  let filteredSpools = manualSpools;
+  if (!disableFiltering) {
+    if (trayInfo?.profile || trayInfo?.type) {
+      const trayProfile = normalizeValue(trayInfo.profile || trayInfo.type);
+      filteredSpools = filteredSpools?.filter((spool: InventorySpool) => {
+        const spoolProfile = normalizeValue(spool.slicer_filament_name || spool.slicer_filament);
+        return trayProfile && spoolProfile && spoolProfile === trayProfile;
+      });
+    }
+  }
+  if (searchFilter && filteredSpools) {
     const q = searchFilter.toLowerCase();
     const q = searchFilter.toLowerCase();
-    return (
-      spool.material.toLowerCase().includes(q) ||
-      (spool.brand?.toLowerCase().includes(q) ?? false) ||
-      (spool.color_name?.toLowerCase().includes(q) ?? false) ||
-      (spool.subtype?.toLowerCase().includes(q) ?? false)
-    );
-  });
+    filteredSpools = filteredSpools.filter((spool: InventorySpool) => {
+      return (
+        spool.material.toLowerCase().includes(q) ||
+        (spool.brand?.toLowerCase().includes(q) ?? false) ||
+        (spool.color_name?.toLowerCase().includes(q) ?? false) ||
+        (spool.subtype?.toLowerCase().includes(q) ?? false)
+      );
+    });
+  }
 
 
   const handleAssign = () => {
   const handleAssign = () => {
     if (!selectedSpoolId) return;
     if (!selectedSpoolId) return;
@@ -192,13 +214,13 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
 
 
   return (
   return (
     <>
     <>
-      <div className="fixed inset-0 z-50 flex items-center justify-center">
+      <div className="fixed inset-0 z-50 flex items-start sm:items-center justify-center p-4 overflow-y-auto">
         <div
         <div
           className="absolute inset-0 bg-black/60 backdrop-blur-sm"
           className="absolute inset-0 bg-black/60 backdrop-blur-sm"
           onClick={onClose}
           onClick={onClose}
         />
         />
 
 
-      <div className="relative w-full max-w-2xl mx-4 bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-xl shadow-2xl">
+      <div className="relative w-full max-w-2xl bg-bambu-dark-secondary border border-bambu-dark-tertiary rounded-xl shadow-2xl max-h-[90vh] overflow-hidden flex flex-col my-auto">
         {/* Header */}
         {/* Header */}
         <div className="flex items-center justify-between p-4 border-b border-bambu-dark-tertiary">
         <div className="flex items-center justify-between p-4 border-b border-bambu-dark-tertiary">
           <div className="flex items-center gap-2">
           <div className="flex items-center gap-2">
@@ -214,7 +236,7 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
         </div>
         </div>
 
 
         {/* Content */}
         {/* Content */}
-        <div className="p-4 space-y-4">
+        <div className="p-4 space-y-4 overflow-y-auto">
           {/* Tray info */}
           {/* Tray info */}
           {trayInfo && (
           {trayInfo && (
             <div className="p-3 bg-bambu-dark rounded-lg border border-bambu-dark-tertiary">
             <div className="p-3 bg-bambu-dark rounded-lg border border-bambu-dark-tertiary">
@@ -295,27 +317,41 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
           </div>
           </div>
         </div>
         </div>
 
 
-        {/* Footer */}
-        <div className="flex justify-end gap-2 p-4 border-t border-bambu-dark-tertiary">
-          <Button variant="secondary" onClick={onClose}>
-            {t('common.cancel')}
-          </Button>
-          <Button
-            onClick={handleAssign}
-            disabled={!selectedSpoolId || assignMutation.isPending}
-          >
-            {assignMutation.isPending ? (
-              <>
-                <Loader2 className="w-4 h-4 animate-spin" />
-                {t('inventory.assigning')}
-              </>
-            ) : (
-              <>
-                <Package className="w-4 h-4" />
-                {t('inventory.assignSpool')}
-              </>
-            )}
-          </Button>
+        {/* Footer with filtering toggle */}
+        <div className="flex justify-between items-center p-4 border-t border-bambu-dark-tertiary">
+          <div className="flex items-center gap-2">
+            <input
+              id="disable-filtering-toggle"
+              type="checkbox"
+              checked={disableFiltering}
+              onChange={() => setDisableFiltering(v => !v)}
+              className="accent-bambu-green w-4 h-4 rounded focus:ring-0 border-bambu-dark-tertiary"
+            />
+            <label htmlFor="disable-filtering-toggle" className="text-xs text-bambu-gray select-none cursor-pointer">
+              {t('inventory.showAllSpools')}
+            </label>
+          </div>
+          <div className="flex gap-2">
+            <Button variant="secondary" onClick={onClose}>
+              {t('common.cancel')}
+            </Button>
+            <Button
+              onClick={handleAssign}
+              disabled={!selectedSpoolId || assignMutation.isPending}
+            >
+              {assignMutation.isPending ? (
+                <>
+                  <Loader2 className="w-4 h-4 animate-spin" />
+                  {t('inventory.assigning')}
+                </>
+              ) : (
+                <>
+                  <Package className="w-4 h-4" />
+                  {t('inventory.assignSpool')}
+                </>
+              )}
+            </Button>
+          </div>
         </div>
         </div>
 
 
 
 

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

@@ -3028,6 +3028,7 @@ export default {
     assigned: 'Zugewiesen',
     assigned: 'Zugewiesen',
     assigning: 'Wird zugewiesen...',
     assigning: 'Wird zugewiesen...',
     searchSpools: 'Spulen suchen...',
     searchSpools: 'Spulen suchen...',
+    showAllSpools: 'Alle Spulen anzeigen',
     allMaterials: 'Alle Materialien',
     allMaterials: 'Alle Materialien',
     filterByBrand: 'Nach Marke filtern...',
     filterByBrand: 'Nach Marke filtern...',
     showArchived: 'Archivierte anzeigen',
     showArchived: 'Archivierte anzeigen',

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

@@ -3029,6 +3029,7 @@ export default {
     assigned: 'Assigned',
     assigned: 'Assigned',
     assigning: 'Assigning...',
     assigning: 'Assigning...',
     searchSpools: 'Search spools...',
     searchSpools: 'Search spools...',
+    showAllSpools: 'Show all spools',
     allMaterials: 'All Materials',
     allMaterials: 'All Materials',
     filterByBrand: 'Filter by brand...',
     filterByBrand: 'Filter by brand...',
     showArchived: 'Show archived',
     showArchived: 'Show archived',

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

@@ -3015,6 +3015,7 @@ export default {
     assigned: 'Assigné',
     assigned: 'Assigné',
     assigning: 'Assignation...',
     assigning: 'Assignation...',
     searchSpools: 'Chercher bobines...',
     searchSpools: 'Chercher bobines...',
+    showAllSpools: 'Afficher toutes les bobines',
     allMaterials: 'Tous Matériaux',
     allMaterials: 'Tous Matériaux',
     filterByBrand: 'Filtrer par marque...',
     filterByBrand: 'Filtrer par marque...',
     showArchived: 'Afficher archivées',
     showArchived: 'Afficher archivées',

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

@@ -3014,6 +3014,7 @@ export default {
     assigned: 'Assegnato',
     assigned: 'Assegnato',
     assigning: 'Assegnazione...',
     assigning: 'Assegnazione...',
     searchSpools: 'Cerca bobine...',
     searchSpools: 'Cerca bobine...',
+    showAllSpools: 'Mostra tutte le bobine',
     allMaterials: 'Tutti i Materiali',
     allMaterials: 'Tutti i Materiali',
     filterByBrand: 'Filtra per marchio...',
     filterByBrand: 'Filtra per marchio...',
     showArchived: 'Mostra archiviate',
     showArchived: 'Mostra archiviate',

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

@@ -3027,6 +3027,7 @@ export default {
     assigned: '割り当て済み',
     assigned: '割り当て済み',
     assigning: '割り当て中...',
     assigning: '割り当て中...',
     searchSpools: 'スプールを検索...',
     searchSpools: 'スプールを検索...',
+    showAllSpools: 'すべてのスプールを表示',
     allMaterials: 'すべての素材',
     allMaterials: 'すべての素材',
     filterByBrand: 'ブランドで絞り込み...',
     filterByBrand: 'ブランドで絞り込み...',
     showArchived: 'アーカイブ済みを表示',
     showArchived: 'アーカイブ済みを表示',

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

@@ -3014,6 +3014,7 @@ export default {
     assigned: 'Atribuído',
     assigned: 'Atribuído',
     assigning: 'Atribuindo...',
     assigning: 'Atribuindo...',
     searchSpools: 'Pesquisar carretéis...',
     searchSpools: 'Pesquisar carretéis...',
+    showAllSpools: 'Mostrar todos os carretéis',
     allMaterials: 'Todos os Materiais',
     allMaterials: 'Todos os Materiais',
     filterByBrand: 'Filtrar por marca...',
     filterByBrand: 'Filtrar por marca...',
     showArchived: 'Mostrar arquivados',
     showArchived: 'Mostrar arquivados',

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

@@ -3019,6 +3019,7 @@ export default {
     assigned: '已分配',
     assigned: '已分配',
     assigning: '分配中...',
     assigning: '分配中...',
     searchSpools: '搜索耗材...',
     searchSpools: '搜索耗材...',
+    showAllSpools: '显示所有耗材',
     allMaterials: '所有材料',
     allMaterials: '所有材料',
     filterByBrand: '按品牌筛选...',
     filterByBrand: '按品牌筛选...',
     showArchived: '显示已归档',
     showArchived: '显示已归档',