Bläddra i källkod

fix(archive): Edit Archive sets finished outcomes only, cancelled and aborted apart

maziggy 6 dagar sedan
förälder
incheckning
a3ab88c9b2
100 ändrade filer med 258 tillägg och 33 borttagningar
  1. 1 0
      CHANGELOG.md
  2. 3 2
      backend/app/schemas/archive.py
  3. 47 0
      backend/tests/integration/test_archive_edit_status_words.py
  4. 83 0
      frontend/src/__tests__/components/EditArchiveModal.test.tsx
  5. 18 0
      frontend/src/__tests__/pages/ArchivesPage.test.tsx
  6. 15 9
      frontend/src/components/EditArchiveModal.tsx
  7. 5 1
      frontend/src/i18n/locales/de.ts
  8. 5 1
      frontend/src/i18n/locales/en.ts
  9. 5 1
      frontend/src/i18n/locales/es.ts
  10. 5 1
      frontend/src/i18n/locales/fr.ts
  11. 5 1
      frontend/src/i18n/locales/it.ts
  12. 5 1
      frontend/src/i18n/locales/ja.ts
  13. 5 1
      frontend/src/i18n/locales/ko.ts
  14. 5 1
      frontend/src/i18n/locales/nl.ts
  15. 5 1
      frontend/src/i18n/locales/pt-BR.ts
  16. 5 1
      frontend/src/i18n/locales/ru.ts
  17. 5 1
      frontend/src/i18n/locales/sv.ts
  18. 5 1
      frontend/src/i18n/locales/tr.ts
  19. 5 1
      frontend/src/i18n/locales/uk.ts
  20. 5 1
      frontend/src/i18n/locales/zh-CN.ts
  21. 5 1
      frontend/src/i18n/locales/zh-TW.ts
  22. 13 2
      frontend/src/pages/ArchivesPage.tsx
  23. 0 0
      static/assets/AdditionalSection-CZn3_pKy.js
  24. 0 0
      static/assets/AmsUnitCard-CJACgSos.js
  25. 0 0
      static/assets/ArchivesPage-Dy6tDDn0.js
  26. 0 0
      static/assets/AssignToAmsModal-CtCjQwbL.js
  27. 0 0
      static/assets/CamWallPage-BQJf3qJZ.js
  28. 0 0
      static/assets/CameraDiagnoseModal-C6cKYWvj.js
  29. 0 0
      static/assets/CameraPage-BkE7EuyK.js
  30. 0 0
      static/assets/CameraWall-qQ7lQYZA.js
  31. 0 0
      static/assets/ColumnConfigModal-BrEbfgWg.js
  32. 0 0
      static/assets/ConfigureAmsSlotModal-Dr_V3vF3.js
  33. 0 0
      static/assets/ConnectAuthorizePage-M08647AT.js
  34. 0 0
      static/assets/ContextMenu-CnYm28oc.js
  35. 0 0
      static/assets/ExternalLinkPage-bM_IorUg.js
  36. 0 1
      static/assets/FileManagerPage-oat2ylDG.js
  37. 0 0
      static/assets/FileUploadModal-CKfaMRtB.js
  38. 0 0
      static/assets/FinancePage-1b0s0qiU.js
  39. 0 0
      static/assets/GCodeViewerPage-B8w5TWpO.js
  40. 0 0
      static/assets/GroupEditPage-CnY4ilza.js
  41. 0 0
      static/assets/ImagePreviewModal-DL9nYPvj.js
  42. 0 0
      static/assets/InventoryPage-D8d-9g9k.js
  43. 0 0
      static/assets/LibraryTrashPage-kNUP18hT.js
  44. 0 0
      static/assets/LoginPage-D-99b2Fa.js
  45. 0 0
      static/assets/MaintenancePage-BttkgD_f.js
  46. 0 0
      static/assets/ModelSourcesPage-L94h0POt.js
  47. 0 0
      static/assets/NotificationsPage-CrdJeOZl.js
  48. 0 1
      static/assets/PdfPreviewModal-DAh8vr89.js
  49. 0 0
      static/assets/PhotoGalleryModal-CAJi69EL.js
  50. 0 0
      static/assets/PrinterLocationsPage-BGAFA85u.js
  51. 0 0
      static/assets/PrintersPage-CHiaLexC.js
  52. 0 0
      static/assets/ProfilesPage-fLhMiQgw.js
  53. 0 0
      static/assets/ProjectDetailPage-CtjBribi.js
  54. 0 0
      static/assets/ProjectsPage-EG6nc2MT.js
  55. 0 0
      static/assets/QueuePage-BwltX2if.js
  56. 0 0
      static/assets/RichTextEditor-CbMkddSn.js
  57. 0 0
      static/assets/SettingsPage-BBcx5B20.js
  58. 0 0
      static/assets/SetupPage-C-TuTIoO.js
  59. 0 0
      static/assets/SliceModal-DXY3IDdK.js
  60. 0 0
      static/assets/SpoolBuddyAmsPage-BUMeqb6P.js
  61. 0 0
      static/assets/SpoolBuddyCalibrationPage-B9wONcuB.js
  62. 0 0
      static/assets/SpoolBuddyDashboard-DEabzGGL.js
  63. 0 0
      static/assets/SpoolBuddyInventoryPage-D5lAT0pR.js
  64. 1 1
      static/assets/SpoolBuddySettingsPage-19e459Na.js
  65. 0 0
      static/assets/SpoolBuddyWriteTagPage-IECptjD2.js
  66. 0 0
      static/assets/SpreadsheetPreviewModal-BHltkJT-.js
  67. 0 0
      static/assets/StatsPage-BcLPJsH9.js
  68. 0 0
      static/assets/StreamOverlayPage-Cd-HZL8C.js
  69. 0 0
      static/assets/SystemInfoPage-Cvc3clMd.js
  70. 0 0
      static/assets/de-B5PD1nV9.js
  71. 0 0
      static/assets/de-oqV-S-IY.js
  72. 0 0
      static/assets/es-C2EAQisu.js
  73. 0 0
      static/assets/es-CbXlOTcr.js
  74. 0 0
      static/assets/fr-Dfzey0Ru.js
  75. 0 0
      static/assets/fr-sh2oOClc.js
  76. 0 0
      static/assets/index-BvLJnwiO.js
  77. 1 0
      static/assets/index-CUHRZFW3.css
  78. 0 1
      static/assets/index-LBjsy7mY.css
  79. 1 1
      static/assets/inventorySearch-CB-zSbfy.js
  80. 0 0
      static/assets/it-D4-UWamR.js
  81. 0 0
      static/assets/it-DgTXaOlw.js
  82. 0 0
      static/assets/ja-BsJsm65Z.js
  83. 0 0
      static/assets/ja-D0KrNphO.js
  84. 0 0
      static/assets/ko-BzNdQiW6.js
  85. 0 0
      static/assets/ko-CRRBf-Pd.js
  86. 0 0
      static/assets/locationSensorDefaults-BgQfFSrv.js
  87. 0 0
      static/assets/nl-Dg6mqVom.js
  88. 0 0
      static/assets/nl-Dxjwas54.js
  89. 0 0
      static/assets/pdf-C9Q45Ubw.js
  90. 0 0
      static/assets/pt-BR-BlXI84o5.js
  91. 0 0
      static/assets/pt-BR-TiQIWPhR.js
  92. 0 0
      static/assets/ru-Bnyoq0kN.js
  93. 0 0
      static/assets/ru-DVjTv0eJ.js
  94. 0 0
      static/assets/sv-D-8Gw-l7.js
  95. 0 0
      static/assets/sv-DcSlS_oI.js
  96. 0 0
      static/assets/tr-BvfmwCOv.js
  97. 0 0
      static/assets/tr-DKGsTdh_.js
  98. 0 0
      static/assets/uk-DdthraQj.js
  99. 0 0
      static/assets/uk-De9fAlq_.js
  100. 0 0
      static/assets/utils-BJJ4uumy.js

+ 1 - 0
CHANGELOG.md

@@ -164,6 +164,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **The frontend build no longer warns about `path` and `crypto` being externalized for the STEP previewer (#2976)** — `occt-import-js`, the Emscripten build behind STEP previews, requires both modules, but only inside its `ENVIRONMENT_IS_NODE` branches; in the browser it loads its `.wasm` from the URL the preview worker passes and draws randomness from `crypto.getRandomValues`. Vite still externalized both and printed two warnings on every build. `vite.config.ts` now drops exactly those two warnings for that one package through `build.rolldownOptions.onLog`, so an externalization anywhere else, or of any other module, still shows.
 
 ### Fixed
+- **Archive status in Edit Archive** — the list offers Completed, Failed, Aborted and Cancelled. Choosing "Cancelled" used to save "aborted", which the statistics count as a failure, and a cancelled archive opened showing "Completed". An archive can no longer be set to "Printing" by hand, which had it closed again at the next reconnect as a missed print, with notifications and an extra Print Log entry. The archive card labels an aborted print "aborted" and now also marks a cancelled one, which looked like a normal completed print. Moving an aborted print to Cancelled keeps its recorded reason; marking a cancelled print Completed clears it.
 - **Archive costs** —
   - **Recalculate Costs** and **Rescan** price one print of the archive. An archive printed three times was given the cost of all three. Usage recorded without a price is priced at the default filament cost instead of counting as free. Print History and the statistics still count every print with its own cost.
   - Editing an archive's filament weight now reprices it, on the card and on the print it was copied to.

+ 3 - 2
backend/app/schemas/archive.py

@@ -32,8 +32,9 @@ class ArchiveUpdate(IgnoresNullForRequiredColumns, ArchiveBase):
     __orm_table__ = "print_archives"
     printer_id: int | None = None
     project_id: int | None = None
-    # Allow changing status (e.g., clearing failed flag)
-    status: str | None = None
+    # A finished outcome only. 'printing' would be closed again at the next
+    # reconnect as a missed completion, with notifications and a run of its own.
+    status: str | None = Field(default=None, pattern="^(completed|failed|aborted|cancelled)$")
     # Post-print quality verdict (#1898): 'good' / 'reject'; null clears it.
     user_verdict: str | None = Field(default=None, pattern="^(good|reject)$")
     # Which surface the verdict came from, for the "recorded when the plate was

+ 47 - 0
backend/tests/integration/test_archive_edit_status_words.py

@@ -0,0 +1,47 @@
+"""The statuses an archive can be given by hand.
+
+The finished outcomes only: completed, failed, aborted and cancelled. An
+archive set to 'printing' by hand is closed again at the next reconnect as a
+missed completion, with notifications and a Print History entry of its own.
+"""
+
+import pytest
+from httpx import AsyncClient
+from sqlalchemy import select
+
+from backend.app.models.print_log import PrintLogEntry
+
+
+@pytest.mark.asyncio
+@pytest.mark.integration
+@pytest.mark.parametrize("status", ["completed", "failed", "aborted", "cancelled"])
+async def test_a_finished_outcome_is_set_and_mirrored(
+    async_client: AsyncClient, archive_factory, printer_factory, db_session, status
+):
+    printer = await printer_factory()
+    archive = await archive_factory(printer.id, status="completed" if status != "completed" else "failed")
+
+    response = await async_client.patch(f"/api/v1/archives/{archive.id}", json={"status": status})
+
+    assert response.status_code == 200
+    assert response.json()["status"] == status
+    run = await db_session.scalar(select(PrintLogEntry).where(PrintLogEntry.archive_id == archive.id))
+    await db_session.refresh(run)
+    assert run.status == status
+
+
+@pytest.mark.asyncio
+@pytest.mark.integration
+@pytest.mark.parametrize("status", ["printing", "archived", "stopped", "bogus"])
+async def test_any_other_status_is_refused(
+    async_client: AsyncClient, archive_factory, printer_factory, db_session, status
+):
+    printer = await printer_factory()
+    archive = await archive_factory(printer.id, status="completed")
+
+    response = await async_client.patch(f"/api/v1/archives/{archive.id}", json={"status": status})
+
+    assert response.status_code == 422
+    run = await db_session.scalar(select(PrintLogEntry).where(PrintLogEntry.archive_id == archive.id))
+    await db_session.refresh(run)
+    assert run.status == "completed"

+ 83 - 0
frontend/src/__tests__/components/EditArchiveModal.test.tsx

@@ -311,6 +311,89 @@ describe('EditArchiveModal', () => {
     });
   });
 
+  describe('status', () => {
+    function patchSpy() {
+      const seen: { body?: Record<string, unknown> } = {};
+      server.use(
+        http.patch('/api/v1/archives/:id', async ({ request }) => {
+          seen.body = (await request.json()) as Record<string, unknown>;
+          return HttpResponse.json({ ...mockArchive, ...seen.body });
+        }),
+      );
+      return seen;
+    }
+    const statusSelect = () =>
+      (screen.getAllByRole('combobox') as HTMLSelectElement[]).find((select) =>
+        Array.from(select.options).some((option) => option.value === 'cancelled'),
+      )!;
+
+    it('offers the finished outcomes, cancelled and aborted apart', () => {
+      render(<EditArchiveModal archive={{ ...mockArchive, status: 'completed' }} onClose={mockOnClose} onSave={mockOnSave} />);
+
+      const options = Array.from(statusSelect().options).map((o) => [o.value, o.textContent]);
+      expect(options).toEqual([
+        ['completed', 'Completed'],
+        ['failed', 'Failed'],
+        ['aborted', 'Aborted'],
+        ['cancelled', 'Cancelled'],
+      ]);
+    });
+
+    it('opens a cancelled archive as cancelled', () => {
+      render(<EditArchiveModal archive={{ ...mockArchive, status: 'cancelled' }} onClose={mockOnClose} onSave={mockOnSave} />);
+
+      expect(statusSelect().value).toBe('cancelled');
+    });
+
+    it('shows a printing archive as it is and leaves it out of an untouched save', async () => {
+      const user = userEvent.setup();
+      const seen = patchSpy();
+      render(<EditArchiveModal archive={{ ...mockArchive, status: 'printing' }} onClose={mockOnClose} onSave={mockOnSave} />);
+
+      const current = Array.from(statusSelect().options).find((o) => o.value === 'printing');
+      expect(current?.disabled).toBe(true);
+      await user.click(screen.getByRole('button', { name: /save/i }));
+
+      await waitFor(() => expect(seen.body).toBeDefined());
+      expect(seen.body).not.toHaveProperty('status');
+    });
+
+    it('keeps the recorded reason when an aborted print is moved to cancelled', async () => {
+      const user = userEvent.setup();
+      const seen = patchSpy();
+      render(<EditArchiveModal archive={{ ...mockArchive, status: 'aborted', failure_reason: 'userCancelled' }} onClose={mockOnClose} onSave={mockOnSave} />);
+
+      await user.selectOptions(statusSelect(), 'cancelled');
+      await user.click(screen.getByRole('button', { name: /save/i }));
+
+      await waitFor(() => expect(seen.body?.status).toBe('cancelled'));
+      expect(seen.body).not.toHaveProperty('failure_reason');
+    });
+
+    it('clears the reason of a cancelled print marked completed', async () => {
+      const user = userEvent.setup();
+      const seen = patchSpy();
+      render(<EditArchiveModal archive={{ ...mockArchive, status: 'cancelled', failure_reason: 'noStatusUpdate' }} onClose={mockOnClose} onSave={mockOnSave} />);
+
+      await user.selectOptions(statusSelect(), 'completed');
+      await user.click(screen.getByRole('button', { name: /save/i }));
+
+      await waitFor(() => expect(seen.body?.status).toBe('completed'));
+      expect(seen.body?.failure_reason).toBeNull();
+    });
+
+    it('sends cancelled when cancelled is chosen', async () => {
+      const user = userEvent.setup();
+      const seen = patchSpy();
+      render(<EditArchiveModal archive={{ ...mockArchive, status: 'failed' }} onClose={mockOnClose} onSave={mockOnSave} />);
+
+      await user.selectOptions(statusSelect(), 'cancelled');
+      await user.click(screen.getByRole('button', { name: /save/i }));
+
+      await waitFor(() => expect(seen.body?.status).toBe('cancelled'));
+    });
+  });
+
   describe('filament grams (#1820)', () => {
     // A print archived without its 3MF carries no weight at all, and no rescan
     // can supply one — there is no file to read. Typing it here is the only

+ 18 - 0
frontend/src/__tests__/pages/ArchivesPage.test.tsx

@@ -566,6 +566,24 @@ describe('ArchivesPage', () => {
     });
   });
 
+  describe('outcome badges', () => {
+    it('labels an aborted print as aborted and a cancelled one as cancelled', async () => {
+      server.use(
+        http.get('/api/v1/archives/', () =>
+          HttpResponse.json([
+            { ...mockArchives[0], id: 201, print_name: 'StoppedOnPrinter', status: 'aborted' },
+            { ...mockArchives[0], id: 202, print_name: 'StoppedInBambuddy', status: 'cancelled' },
+          ]),
+        ),
+      );
+      render(<ArchivesPage />);
+
+      await screen.findByText('StoppedOnPrinter');
+      expect(screen.getByText('aborted')).toBeInTheDocument();
+      expect(screen.getByText('cancelled')).toBeInTheDocument();
+    });
+  });
+
   describe('Not Printed / Printed collections', () => {
     const mixedStatusArchives = [
       { ...mockArchives[0], id: 100, print_name: 'NeverPrinted', status: 'archived', started_at: null, completed_at: null },

+ 15 - 9
frontend/src/components/EditArchiveModal.tsx

@@ -31,8 +31,9 @@ export const FAILURE_REASON_KEYS = [
   'other',
 ] as const;
 
-// Keys for archive statuses - translated at render time
-const ARCHIVE_STATUS_KEYS = ['completed', 'failed', 'aborted', 'printing'] as const;
+// The finished outcomes an archive can be given by hand (the API refuses any
+// other). Translated at render time.
+const ARCHIVE_STATUS_KEYS: readonly string[] = ['completed', 'failed', 'aborted', 'cancelled'];
 
 // Mirrors the API's own bound on filament_used_grams. Clamped here as well so
 // the field cannot produce a request the backend would reject with a 422 —
@@ -286,13 +287,10 @@ export function EditArchiveModal({ archive, onClose, existingTags = [] }: EditAr
       updateData.failure_reason = failureReason || undefined;
     } else if (status === 'completed' && userVerdict === 'reject') {
       updateData.failure_reason = failureReason || undefined;
-    } else if (archive.status === 'failed' || archive.status === 'aborted' || archive.user_verdict === 'reject') {
-      // Clear failure_reason when neither a failure nor a reject carries one.
-      // Gated on the archive having held a reject verdict rather than on the
-      // field merely being populated: a 'cancelled' or 'stopped' archive is
-      // neither failed nor aborted, its reason is written by the machine
-      // ("Stopped by user (printer was offline)"), and the status dropdown has
-      // no option that would let the user put it back.
+    } else if (status === 'completed' && archive.failure_reason) {
+      // A completed print without a reject carries no reason. A cancelled one
+      // keeps its own: the machine wrote it ("User cancelled", "No status
+      // update received") and the dialog has no field to put it back.
       updateData.failure_reason = null;
     }
 
@@ -548,12 +546,20 @@ export function EditArchiveModal({ archive, onClose, existingTags = [] }: EditAr
               }}
               className="w-full px-3 py-2 bg-bambu-dark border border-bambu-dark-tertiary rounded-lg text-white focus:border-bambu-green focus:outline-none"
             >
+              {/* Any other status (still printing, never printed) is shown as it
+                  is, and is only sent if the user picks an outcome. */}
+              {archive.status && !ARCHIVE_STATUS_KEYS.includes(archive.status) && (
+                <option value={archive.status} disabled>
+                  {t(`editArchive.statuses.${archive.status}`, { defaultValue: archive.status })}
+                </option>
+              )}
               {ARCHIVE_STATUS_KEYS.map((statusKey) => (
                 <option key={statusKey} value={statusKey}>
                   {t(`editArchive.statuses.${statusKey}`)}
                 </option>
               ))}
             </select>
+            <p className="text-xs text-bambu-gray mt-1">{t('editArchive.statusHelp')}</p>
           </div>
 
           {/* Outcome verdict (#1898) - only meaningful on completed prints */}

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

@@ -1144,7 +1144,8 @@ export default {
       moreOptions: 'Rechtsklick für mehr Optionen',
       addToFavorites: 'Zu Favoriten hinzufügen',
       removeFromFavorites: 'Aus Favoriten entfernen',
-      cancelled: 'abgebrochen',
+      aborted: 'abgebrochen',
+      cancelled: 'gestoppt',
       failed: 'fehlgeschlagen',
       confirmPending: 'Ergebnis?',
       confirmPendingTitle: 'Dieser Druck wartet auf eine Ergebnis-Bewertung — zum Antworten klicken',
@@ -5929,6 +5930,7 @@ export default {
     filamentUsedPlaceholder: 'z. B. 46.16',
     filamentUsedHelp: 'Von Hand eintragen, wenn ein Druck ohne 3MF archiviert wurde, damit er in die Filament-Summen eingeht. Beim erneuten Einlesen eines Archivs mit 3MF wird der Wert wieder aus der Datei übernommen.',
     filamentUsedLocked: 'Nur Benutzer, die alle Archive bearbeiten dürfen, können diesen Wert ändern: Spätere Drucke dieses Archivs werden danach berechnet.',
+    statusHelp: 'Fehlgeschlagen und Abgebrochen zählen in der Statistik als Fehler. Gestoppt zählt als angehalten und senkt die Erfolgsquote nicht.',
     notes: 'Notizen',
     notesPlaceholder: 'Notizen zu diesem Druck hinzufügen...',
     externalLink: 'Externer Link',
@@ -5973,6 +5975,8 @@ export default {
       completed: 'Abgeschlossen',
       failed: 'Fehlgeschlagen',
       aborted: 'Abgebrochen',
+      cancelled: 'Gestoppt',
+      archived: 'Archiviert',
       printing: 'Druckt',
     },
   },

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

@@ -1151,6 +1151,7 @@ export default {
       moreOptions: 'Right-click for more options',
       addToFavorites: 'Add to favorites',
       removeFromFavorites: 'Remove from favorites',
+      aborted: 'aborted',
       cancelled: 'cancelled',
       failed: 'failed',
       confirmPending: 'outcome?',
@@ -5982,6 +5983,7 @@ export default {
     filamentUsedPlaceholder: 'e.g. 46.16',
     filamentUsedHelp: 'Set this by hand when a print archived without its 3MF, so it still counts towards the filament totals. Rescanning an archive that has its 3MF reads the figure back from the file.',
     filamentUsedLocked: 'Only users who can edit all archives can change this figure: later prints of this archive are charged from it.',
+    statusHelp: 'Failed and Aborted count as failures in the statistics. Cancelled counts as stopped and does not lower the success rate.',
     notes: 'Notes',
     notesPlaceholder: 'Add notes about this print...',
     externalLink: 'External Link',
@@ -6025,7 +6027,9 @@ export default {
     statuses: {
       completed: 'Completed',
       failed: 'Failed',
-      aborted: 'Cancelled',
+      aborted: 'Aborted',
+      cancelled: 'Cancelled',
+      archived: 'Archived',
       printing: 'Printing',
     },
   },

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

@@ -1144,6 +1144,7 @@ export default {
       moreOptions: 'Haga clic con el botón derecho para más opciones',
       addToFavorites: 'Añadir a favoritos',
       removeFromFavorites: 'Quitar de favoritos',
+      aborted: 'abortada',
       cancelled: 'cancelada',
       failed: 'fallida',
       confirmPending: '¿resultado?',
@@ -5937,6 +5938,7 @@ export default {
     filamentUsedPlaceholder: 'p. ej. 46.16',
     filamentUsedHelp: 'Introdúcelo a mano cuando una impresión se haya archivado sin su 3MF, para que siga contando en los totales de filamento. Al volver a escanear un archivo que sí tiene su 3MF, el valor se lee de nuevo del fichero.',
     filamentUsedLocked: 'Solo los usuarios que pueden editar todos los archivos pueden cambiar este valor: las impresiones posteriores de este archivo se cobran a partir de él.',
+    statusHelp: 'Fallida y Abortada cuentan como fallos en las estadísticas. Cancelada cuenta como detenida y no reduce la tasa de éxito.',
     notes: 'Notas',
     notesPlaceholder: 'Añada notas sobre esta impresión...',
     externalLink: 'Enlace externo',
@@ -5980,7 +5982,9 @@ export default {
     statuses: {
       completed: 'Completada',
       failed: 'Fallida',
-      aborted: 'Cancelada',
+      aborted: 'Abortada',
+      cancelled: 'Cancelada',
+      archived: 'Archivada',
       printing: 'Imprimiendo',
     },
   },

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

@@ -1144,6 +1144,7 @@ export default {
       moreOptions: 'Clic droit pour plus d\'options',
       addToFavorites: 'Ajouter aux favoris',
       removeFromFavorites: 'Retirer des favoris',
+      aborted: 'interrompu',
       cancelled: 'annulé',
       failed: 'échoué',
       confirmPending: 'résultat ?',
@@ -5917,6 +5918,7 @@ export default {
     filamentUsedPlaceholder: 'p. ex. 46.16',
     filamentUsedHelp: 'À saisir à la main lorsqu\'une impression a été archivée sans son 3MF, afin qu\'elle compte quand même dans les totaux de filament. Une nouvelle analyse d\'une archive qui possède son 3MF relit la valeur depuis le fichier.',
     filamentUsedLocked: 'Seuls les utilisateurs autorisés à modifier toutes les archives peuvent changer cette valeur : les impressions suivantes de cette archive sont facturées à partir d\'elle.',
+    statusHelp: 'Échouée et Interrompue comptent comme des échecs dans les statistiques. Annulée compte comme arrêtée et ne fait pas baisser le taux de réussite.',
     notes: 'Notes',
     notesPlaceholder: 'Notes sur l\'impression...',
     externalLink: 'Lien externe',
@@ -5960,7 +5962,9 @@ export default {
     statuses: {
       completed: 'Réussie',
       failed: 'Échouée',
-      aborted: 'Annulée',
+      aborted: 'Interrompue',
+      cancelled: 'Annulée',
+      archived: 'Archivée',
       printing: 'Impression',
     },
   },

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

@@ -1144,6 +1144,7 @@ export default {
       moreOptions: 'Clic destro per altre opzioni',
       addToFavorites: 'Aggiungi ai preferiti',
       removeFromFavorites: 'Rimuovi dai preferiti',
+      aborted: 'interrotto',
       cancelled: 'annullato',
       failed: 'fallito',
       confirmPending: 'esito?',
@@ -5916,6 +5917,7 @@ export default {
     filamentUsedPlaceholder: 'es. 46.16',
     filamentUsedHelp: 'Inseriscilo a mano quando una stampa è stata archiviata senza il suo 3MF, così da farla rientrare comunque nei totali del filamento. La riscansione di un archivio che ha il suo 3MF rilegge il valore dal file.',
     filamentUsedLocked: 'Solo gli utenti che possono modificare tutti gli archivi possono cambiare questo valore: le stampe successive di questo archivio vengono addebitate in base ad esso.',
+    statusHelp: 'Fallito e Interrotto contano come errori nelle statistiche. Annullato conta come fermato e non abbassa la percentuale di successo.',
     notes: 'Note',
     notesPlaceholder: 'Aggiungi note su questa stampa...',
     externalLink: 'Link esterno',
@@ -5959,7 +5961,9 @@ export default {
     statuses: {
       completed: 'Completato',
       failed: 'Fallito',
-      aborted: 'Annullato',
+      aborted: 'Interrotto',
+      cancelled: 'Annullato',
+      archived: 'Archiviato',
       printing: 'In stampa',
     },
   },

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

@@ -1143,6 +1143,7 @@ export default {
       moreOptions: 'その他のオプション',
       addToFavorites: 'お気に入りに追加',
       removeFromFavorites: 'お気に入りから削除',
+      aborted: '中断',
       cancelled: 'キャンセル',
       failed: '失敗',
       confirmPending: '結果は?',
@@ -5930,6 +5931,7 @@ export default {
     filamentUsedPlaceholder: '例: 46.16',
     filamentUsedHelp: '3MF なしでアーカイブされた印刷は、手入力するとフィラメント合計に反映されます。3MF があるアーカイブを再スキャンすると、この値はファイルから読み直されます。',
     filamentUsedLocked: 'この値を変更できるのは、すべてのアーカイブを編集できるユーザーだけです。このアーカイブの以降の印刷はこの値に基づいて課金されます。',
+    statusHelp: '統計では「失敗」と「中断」は失敗として数えられます。「キャンセル」は停止として数えられ、成功率を下げません。',
     notes: 'メモ',
     notesPlaceholder: 'この印刷についてメモを追加...',
     externalLink: '外部リンク',
@@ -5973,7 +5975,9 @@ export default {
     statuses: {
       completed: '完了',
       failed: '失敗',
-      aborted: 'キャンセル',
+      aborted: '中断',
+      cancelled: 'キャンセル',
+      archived: 'アーカイブ済み',
       printing: '印刷中',
     },
   },

+ 5 - 1
frontend/src/i18n/locales/ko.ts

@@ -1100,6 +1100,7 @@ export default {
       moreOptions: '더 많은 옵션은 우클릭',
       addToFavorites: '즐겨찾기에 추가',
       removeFromFavorites: '즐겨찾기에서 제거',
+      aborted: '중단됨',
       cancelled: '취소됨',
       failed: '실패',
       confirmPending: '결과는?',
@@ -5677,6 +5678,7 @@ export default {
     filamentUsedPlaceholder: '예: 46.16',
     filamentUsedHelp: '3MF 없이 보관된 인쇄는 직접 입력하면 필라멘트 합계에 반영됩니다. 3MF가 있는 보관 항목을 다시 스캔하면 이 값은 파일에서 다시 읽어옵니다.',
     filamentUsedLocked: '모든 아카이브를 편집할 수 있는 사용자만 이 값을 변경할 수 있습니다. 이 아카이브의 이후 출력은 이 값을 기준으로 청구됩니다.',
+    statusHelp: '통계에서 \'실패\'와 \'중단됨\'은 실패로 집계됩니다. \'취소됨\'은 중지로 집계되며 성공률을 낮추지 않습니다.',
     notes: '메모',
     notesPlaceholder: '이 인쇄에 대한 메모 추가...',
     externalLink: '외부 링크',
@@ -5718,7 +5720,9 @@ export default {
     statuses: {
       completed: '완료됨',
       failed: '실패',
-      aborted: '취소됨',
+      aborted: '중단됨',
+      cancelled: '취소됨',
+      archived: '보관됨',
       printing: '인쇄 중'
     }
   },

+ 5 - 1
frontend/src/i18n/locales/nl.ts

@@ -1149,6 +1149,7 @@ export default {
       moreOptions: 'Klik met rechts voor meer opties',
       addToFavorites: 'Toevoegen aan favorieten',
       removeFromFavorites: 'Uit favorieten verwijderen',
+      aborted: 'afgebroken',
       cancelled: 'geannuleerd',
       failed: 'mislukt',
       confirmPending: 'resultaat?',
@@ -5980,6 +5981,7 @@ export default {
     filamentUsedPlaceholder: 'bijv. 46.16',
     filamentUsedHelp: 'Stel dit handmatig in wanneer een afdruk zonder 3MF is gearchiveerd, zodat deze toch meetelt in de filamenttotalen. Bij het opnieuw scannen van een archief met een 3MF wordt de waarde weer uit het bestand gelezen.',
     filamentUsedLocked: 'Alleen gebruikers die alle archieven mogen bewerken, kunnen deze waarde wijzigen: latere afdrukken van dit archief worden ermee afgerekend.',
+    statusHelp: 'Mislukt en Afgebroken tellen in de statistieken als mislukt. Geannuleerd telt als gestopt en verlaagt het succespercentage niet.',
     notes: 'Notities',
     notesPlaceholder: 'Voeg notities over deze afdruk toe...',
     externalLink: 'Externe link',
@@ -6023,7 +6025,9 @@ export default {
     statuses: {
       completed: 'Voltooid',
       failed: 'Mislukt',
-      aborted: 'Geannuleerd',
+      aborted: 'Afgebroken',
+      cancelled: 'Geannuleerd',
+      archived: 'Gearchiveerd',
       printing: 'Bezig met afdrukken',
     },
   },

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

@@ -1144,6 +1144,7 @@ export default {
       moreOptions: 'Clique com o botão direito para mais opções',
       addToFavorites: 'Adicionar aos favoritos',
       removeFromFavorites: 'Remover dos favoritos',
+      aborted: 'abortado',
       cancelled: 'cancelado',
       failed: 'falha',
       confirmPending: 'resultado?',
@@ -5916,6 +5917,7 @@ export default {
     filamentUsedPlaceholder: 'ex.: 46.16',
     filamentUsedHelp: 'Informe manualmente quando uma impressão for arquivada sem o 3MF, para que ela ainda conte nos totais de filamento. Reescanear um arquivo que tem o 3MF lê o valor novamente do arquivo.',
     filamentUsedLocked: 'Somente usuários que podem editar todos os arquivos podem alterar este valor: as impressões seguintes deste arquivo são cobradas com base nele.',
+    statusHelp: 'Falhou e Abortado contam como falhas nas estatísticas. Cancelado conta como parado e não reduz a taxa de sucesso.',
     notes: 'Notas',
     notesPlaceholder: 'Adicione notas sobre esta impressão...',
     externalLink: 'Link Externo',
@@ -5959,7 +5961,9 @@ export default {
     statuses: {
       completed: 'Concluído',
       failed: 'Falhou',
-      aborted: 'Cancelado',
+      aborted: 'Abortado',
+      cancelled: 'Cancelado',
+      archived: 'Arquivado',
       printing: 'Imprimindo',
     },
   },

+ 5 - 1
frontend/src/i18n/locales/ru.ts

@@ -1099,6 +1099,7 @@ export default {
       moreOptions: "Щёлкните правой кнопкой для дополнительных действий",
       addToFavorites: "Добавить в избранное",
       removeFromFavorites: "Удалить из избранного",
+      aborted: "прервано",
       cancelled: "отменено",
       failed: "ошибка",
       confirmPending: "результат?",
@@ -5665,6 +5666,7 @@ export default {
     filamentUsedPlaceholder: "напр. 46.16",
     filamentUsedHelp: "Укажите вручную, если печать заархивирована без 3MF — тогда она попадёт в итоги по филаменту. При повторном сканировании архива с 3MF значение снова считывается из файла.",
     filamentUsedLocked: "Изменить это значение могут только пользователи, которым разрешено редактировать все архивы: по нему рассчитываются последующие печати этого архива.",
+    statusHelp: "Ошибка и Прервано считаются в статистике неудачами. Отменено считается остановкой и не снижает процент успешных печатей.",
     notes: "Заметки",
     notesPlaceholder: "Добавьте заметки об этой печати...",
     externalLink: "Внешняя ссылка",
@@ -5706,7 +5708,9 @@ export default {
     statuses: {
       completed: "Завершено",
       failed: "Ошибка",
-      aborted: "Отменено",
+      aborted: "Прервано",
+      cancelled: "Отменено",
+      archived: "В архиве",
       printing: "Печать",
     },
   },

+ 5 - 1
frontend/src/i18n/locales/sv.ts

@@ -1149,7 +1149,8 @@ export default {
       moreOptions: 'Högerklicka för fler alternativ',
       addToFavorites: 'Lägg till i favoriter',
       removeFromFavorites: 'Ta bort från favoriter',
-      cancelled: 'avbruten',
+      aborted: 'avbruten',
+      cancelled: 'stoppad',
       failed: 'misslyckad',
       confirmPending: 'resultat?',
       confirmPendingTitle: 'Denna utskrift bad om en resultatbedömning — klicka för att svara',
@@ -5978,6 +5979,7 @@ errors: {
     filamentUsedPlaceholder: 't.ex. 46,16',
     filamentUsedHelp: 'Ställ in detta manuellt när en utskrift arkiveras utan sin 3MF, så att den fortfarande räknas mot filamenttotalen. Om man skannar om ett arkiv som har sin 3MF läser man tillbaka siffran från filen.',
     filamentUsedLocked: 'Endast användare som får redigera alla arkiv kan ändra detta värde: senare utskrifter av arkivet debiteras utifrån det.',
+    statusHelp: 'Misslyckad och Avbruten räknas som fel i statistiken. Stoppad räknas som stoppad och sänker inte andelen lyckade utskrifter.',
     notes: 'Anteckningar',
     notesPlaceholder: 'Lägg till anteckningar om denna utskrift...',
     externalLink: 'Extern länk',
@@ -6022,6 +6024,8 @@ errors: {
       completed: 'Slutförd',
       failed: 'Misslyckad',
       aborted: 'Avbruten',
+      cancelled: 'Stoppad',
+      archived: 'Arkiverad',
       printing: 'Skriver ut',
     },
   },

+ 5 - 1
frontend/src/i18n/locales/tr.ts

@@ -1144,6 +1144,7 @@ export default {
       moreOptions: 'Daha fazla seçenek için sağ tıklayın',
       addToFavorites: 'Favorilere ekle',
       removeFromFavorites: 'Favorilerden kaldır',
+      aborted: 'yarıda kesildi',
       cancelled: 'iptal edildi',
       failed: 'başarısız',
       confirmPending: 'sonuç?',
@@ -5901,6 +5902,7 @@ export default {
     filamentUsedPlaceholder: 'ör. 46.16',
     filamentUsedHelp: 'Bir baskı 3MF dosyası olmadan arşivlendiyse elle girin; böylece filament toplamlarına yine de dahil olur. 3MF dosyası olan bir arşiv yeniden tarandığında bu değer dosyadan yeniden okunur.',
     filamentUsedLocked: 'Bu değeri yalnızca tüm arşivleri düzenleyebilen kullanıcılar değiştirebilir: bu arşivin sonraki baskıları buna göre ücretlendirilir.',
+    statusHelp: 'İstatistiklerde Başarısız ve Yarıda kesildi hata olarak sayılır. İptal edildi durdurulmuş sayılır ve başarı oranını düşürmez.',
     notes: 'Notlar',
     notesPlaceholder: 'Bu baskı hakkında notlar ekleyin...',
     externalLink: 'Harici Bağlantı',
@@ -5942,7 +5944,9 @@ export default {
     statuses: {
       completed: 'Tamamlandı',
       failed: 'Başarısız',
-      aborted: 'İptal edildi',
+      aborted: 'Yarıda kesildi',
+      cancelled: 'İptal edildi',
+      archived: 'Arşivlendi',
       printing: 'Yazdırılıyor',
     },
   },

+ 5 - 1
frontend/src/i18n/locales/uk.ts

@@ -1148,6 +1148,7 @@ export default {
       moreOptions: "Клацніть правою кнопкою миші, щоб отримати додаткові параметри",
       addToFavorites: "Додати в обране",
       removeFromFavorites: "Видалити з вибраного",
+      aborted: "перервано",
       cancelled: "скасовано",
       failed: "не вдалося",
       confirmPending: "результат?",
@@ -5970,6 +5971,7 @@ export default {
     filamentUsedPlaceholder: "напр. 46.16",
     filamentUsedHelp: "Вкажіть вручну, якщо друк заархівовано без 3MF — тоді він потрапить до підсумків з філаменту. Повторне сканування архіву, який має 3MF, зчитує значення з файлу.",
     filamentUsedLocked: "Змінити це значення можуть лише користувачі, яким дозволено редагувати всі архіви: за ним розраховуються наступні друки цього архіву.",
+    statusHelp: "Не вдалося та Перервано вважаються в статистиці невдачами. Скасовано вважається зупинкою і не знижує відсоток успішних друків.",
     notes: "Примітки",
     notesPlaceholder: "Додайте примітки щодо цього друку...",
     externalLink: "Зовнішнє посилання",
@@ -6013,7 +6015,9 @@ export default {
     statuses: {
       completed: "Виконано",
       failed: "Не вдалося",
-      aborted: "Скасовано",
+      aborted: "Перервано",
+      cancelled: "Скасовано",
+      archived: "В архіві",
       printing: "Друк",
     },
   },

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

@@ -1144,6 +1144,7 @@ export default {
       moreOptions: '右键查看更多选项',
       addToFavorites: '添加到收藏',
       removeFromFavorites: '从收藏中移除',
+      aborted: '已中止',
       cancelled: '已取消',
       failed: '失败',
       confirmPending: '结果?',
@@ -5916,6 +5917,7 @@ export default {
     filamentUsedPlaceholder: '例如 46.16',
     filamentUsedHelp: '当打印任务在没有 3MF 的情况下归档时,可手动填写,使其仍计入耗材总量。重新扫描带有 3MF 的归档时,该数值会从文件中重新读取。',
     filamentUsedLocked: '只有可以编辑所有归档的用户才能更改此数值:此归档之后的打印将据此计费。',
+    statusHelp: '在统计中,“失败”和“已中止”计为失败。“已取消”计为停止,不会降低成功率。',
     notes: '备注',
     notesPlaceholder: '添加关于此打印的备注...',
     externalLink: '外部链接',
@@ -5959,7 +5961,9 @@ export default {
     statuses: {
       completed: '已完成',
       failed: '失败',
-      aborted: '已取消',
+      aborted: '已中止',
+      cancelled: '已取消',
+      archived: '已归档',
       printing: '打印中',
     },
   },

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

@@ -1144,6 +1144,7 @@ export default {
       moreOptions: '右鍵檢視更多選項',
       addToFavorites: '新增到收藏',
       removeFromFavorites: '從收藏中移除',
+      aborted: '已中止',
       cancelled: '已取消',
       failed: '失敗',
       confirmPending: '成品結果?',
@@ -5916,6 +5917,7 @@ export default {
     filamentUsedPlaceholder: '例如 46.16',
     filamentUsedHelp: '當列印工作在沒有 3MF 的情況下封存時,可手動填寫,使其仍計入耗材總量。重新掃描具有 3MF 的封存項目時,此數值會從檔案重新讀取。',
     filamentUsedLocked: '只有可以編輯所有封存的使用者才能變更此數值:此封存之後的列印將據此計費。',
+    statusHelp: '在統計中,「失敗」和「已中止」計為失敗。「已取消」計為停止,不會降低成功率。',
     notes: '備註',
     notesPlaceholder: '新增關於此列印的備註...',
     externalLink: '外部連結',
@@ -5959,7 +5961,9 @@ export default {
     statuses: {
       completed: '已完成',
       failed: '失敗',
-      aborted: '已取消',
+      aborted: '已中止',
+      cancelled: '已取消',
+      archived: '已封存',
       printing: '列印中',
     },
   },

+ 13 - 2
frontend/src/pages/ArchivesPage.tsx

@@ -1019,7 +1019,13 @@ function ArchiveCard({
         </button>
         {(archive.status === 'failed' || archive.status === 'aborted') && (
           <div className="absolute top-2 left-12 px-2 py-1 rounded text-xs bg-status-error/80 text-white">
-            {archive.status === 'aborted' ? t('archives.card.cancelled') : t('archives.card.failed')}
+            {archive.status === 'aborted' ? t('archives.card.aborted') : t('archives.card.failed')}
+          </div>
+        )}
+        {/* Stopped rather than failed: counted apart from failures in the statistics. */}
+        {archive.status === 'cancelled' && (
+          <div className="absolute top-2 left-12 px-2 py-1 rounded text-xs bg-status-warning/80 text-white">
+            {t('archives.card.cancelled')}
           </div>
         )}
         {/* Outcome-confirmation badges (#1898): a completed print either
@@ -2427,7 +2433,12 @@ function ArchiveListRow({
             <p className="text-white text-sm truncate">{archive.print_name || archive.filename}</p>
             {(archive.status === 'failed' || archive.status === 'aborted') && (
               <span className="px-1.5 py-0.5 rounded text-[10px] leading-tight bg-status-error/80 text-white flex-shrink-0">
-                {archive.status === 'aborted' ? t('archives.card.cancelled') : t('archives.card.failed')}
+                {archive.status === 'aborted' ? t('archives.card.aborted') : t('archives.card.failed')}
+              </span>
+            )}
+            {archive.status === 'cancelled' && (
+              <span className="px-1.5 py-0.5 rounded text-[10px] leading-tight bg-status-warning/80 text-white flex-shrink-0">
+                {t('archives.card.cancelled')}
               </span>
             )}
             {/* Outcome-confirmation badges (#1898), compact list variants */}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/AdditionalSection-CZn3_pKy.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/AmsUnitCard-CJACgSos.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ArchivesPage-Dy6tDDn0.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/AssignToAmsModal-CtCjQwbL.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/CamWallPage-BQJf3qJZ.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/CameraDiagnoseModal-C6cKYWvj.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/CameraPage-BkE7EuyK.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/CameraWall-qQ7lQYZA.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ColumnConfigModal-BrEbfgWg.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ConfigureAmsSlotModal-Dr_V3vF3.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ConnectAuthorizePage-M08647AT.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ContextMenu-CnYm28oc.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ExternalLinkPage-bM_IorUg.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 1
static/assets/FileManagerPage-oat2ylDG.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/FileUploadModal-CKfaMRtB.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/FinancePage-1b0s0qiU.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/GCodeViewerPage-B8w5TWpO.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/GroupEditPage-CnY4ilza.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ImagePreviewModal-DL9nYPvj.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/InventoryPage-D8d-9g9k.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/LibraryTrashPage-kNUP18hT.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/LoginPage-D-99b2Fa.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/MaintenancePage-BttkgD_f.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ModelSourcesPage-L94h0POt.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/NotificationsPage-CrdJeOZl.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 1
static/assets/PdfPreviewModal-DAh8vr89.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/PhotoGalleryModal-CAJi69EL.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/PrinterLocationsPage-BGAFA85u.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/PrintersPage-CHiaLexC.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ProfilesPage-fLhMiQgw.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ProjectDetailPage-CtjBribi.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ProjectsPage-EG6nc2MT.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/QueuePage-BwltX2if.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/RichTextEditor-CbMkddSn.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/SettingsPage-BBcx5B20.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/SetupPage-C-TuTIoO.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/SliceModal-DXY3IDdK.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/SpoolBuddyAmsPage-BUMeqb6P.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/SpoolBuddyCalibrationPage-B9wONcuB.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/SpoolBuddyDashboard-DEabzGGL.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/SpoolBuddyInventoryPage-D5lAT0pR.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 1
static/assets/SpoolBuddySettingsPage-19e459Na.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/SpoolBuddyWriteTagPage-IECptjD2.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/SpreadsheetPreviewModal-BHltkJT-.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/StatsPage-BcLPJsH9.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/StreamOverlayPage-Cd-HZL8C.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/SystemInfoPage-Cvc3clMd.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/de-B5PD1nV9.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/de-oqV-S-IY.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/es-C2EAQisu.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/es-CbXlOTcr.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/fr-Dfzey0Ru.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/fr-sh2oOClc.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/index-BvLJnwiO.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
static/assets/index-CUHRZFW3.css


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 1
static/assets/index-LBjsy7mY.css


+ 1 - 1
static/assets/inventorySearch-CbvL5APk.js → static/assets/inventorySearch-CB-zSbfy.js

@@ -1 +1 @@
-import{mt as e}from"./index-ohIj6le-.js";function t(t,n){let r=n.trim().toLowerCase();if(!r)return!0;let i=r.match(/^#(\d+)$/);return i?t.id===Number(i[1]):String(t.id).includes(r)||t.material.toLowerCase().includes(r)||(t.brand?.toLowerCase().includes(r)??!1)||(t.color_name?.toLowerCase().includes(r)??!1)||(e(t.color_name,t.rgba,t.color_name_is_synthesized)?.toLowerCase().includes(r)??!1)||(t.subtype?.toLowerCase().includes(r)??!1)||(t.note?.toLowerCase().includes(r)??!1)||(t.slicer_filament_name?.toLowerCase().includes(r)??!1)||(t.storage_location?.toLowerCase().includes(r)??!1)||(t.material_number?.toLowerCase().includes(r)??!1)||(t.suppliers?.some(e=>e.supplier_name.toLowerCase().includes(r)||(e.supplier_article_number?.toLowerCase().includes(r)??!1))??!1)}function n(e,n){return n?e.filter(e=>t(e,n)):e}export{n as t};
+import{mt as e}from"./index-BvLJnwiO.js";function t(t,n){let r=n.trim().toLowerCase();if(!r)return!0;let i=r.match(/^#(\d+)$/);return i?t.id===Number(i[1]):String(t.id).includes(r)||t.material.toLowerCase().includes(r)||(t.brand?.toLowerCase().includes(r)??!1)||(t.color_name?.toLowerCase().includes(r)??!1)||(e(t.color_name,t.rgba,t.color_name_is_synthesized)?.toLowerCase().includes(r)??!1)||(t.subtype?.toLowerCase().includes(r)??!1)||(t.note?.toLowerCase().includes(r)??!1)||(t.slicer_filament_name?.toLowerCase().includes(r)??!1)||(t.storage_location?.toLowerCase().includes(r)??!1)||(t.material_number?.toLowerCase().includes(r)??!1)||(t.suppliers?.some(e=>e.supplier_name.toLowerCase().includes(r)||(e.supplier_article_number?.toLowerCase().includes(r)??!1))??!1)}function n(e,n){return n?e.filter(e=>t(e,n)):e}export{n as t};

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/it-D4-UWamR.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/it-DgTXaOlw.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ja-BsJsm65Z.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ja-D0KrNphO.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ko-BzNdQiW6.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ko-CRRBf-Pd.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/locationSensorDefaults-BgQfFSrv.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/nl-Dg6mqVom.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/nl-Dxjwas54.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/pdf-C9Q45Ubw.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/pt-BR-BlXI84o5.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/pt-BR-TiQIWPhR.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ru-Bnyoq0kN.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/ru-DVjTv0eJ.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/sv-D-8Gw-l7.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/sv-DcSlS_oI.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/tr-BvfmwCOv.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/tr-DKGsTdh_.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/uk-DdthraQj.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/uk-De9fAlq_.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
static/assets/utils-BJJ4uumy.js


Vissa filer visades inte eftersom för många filer har ändrats