Browse Source

Merge branch '0.2.0b' into main

MartinNYHC 3 months ago
parent
commit
f054ad7836

+ 1 - 0
CHANGELOG.md

@@ -22,6 +22,7 @@ All notable changes to Bambuddy will be documented in this file.
 - **Print Queue: Clear Plate Confirmation** — When a print finishes or fails and more items are queued, the printer card now shows a "Clear Plate & Start Next" button. The scheduler no longer auto-starts the next print while the printer is in FINISH or FAILED state — the user must confirm the build plate has been cleared first. This prevents prints from starting on a dirty plate. The button respects the `printers:control` permission and is available in all supported languages (en/de/ja).
 - **Print Queue: Clear Plate Confirmation** — When a print finishes or fails and more items are queued, the printer card now shows a "Clear Plate & Start Next" button. The scheduler no longer auto-starts the next print while the printer is in FINISH or FAILED state — the user must confirm the build plate has been cleared first. This prevents prints from starting on a dirty plate. The button respects the `printers:control` permission and is available in all supported languages (en/de/ja).
 
 
 ### Improved
 ### Improved
+- **Skip Objects: Confirmation Dialog** ([#346](https://github.com/maziggy/bambuddy/issues/346)) — Added a warning confirmation modal before skipping an object during a print. Shows the object name and warns the action is irreversible. Prevents accidentally skipping the wrong object. Translated in all 4 locales (en, de, ja, it).
 - **Additional Currency Options** ([#329](https://github.com/maziggy/bambuddy/issues/329), [#333](https://github.com/maziggy/bambuddy/issues/333)) — Added 17 additional currencies to the cost tracking dropdown: HKD, INR, KRW, SEK, NOK, DKK, PLN, BRL, TWD, SGD, NZD, MXN, CZK, THB, ZAR, RUB.
 - **Additional Currency Options** ([#329](https://github.com/maziggy/bambuddy/issues/329), [#333](https://github.com/maziggy/bambuddy/issues/333)) — Added 17 additional currencies to the cost tracking dropdown: HKD, INR, KRW, SEK, NOK, DKK, PLN, BRL, TWD, SGD, NZD, MXN, CZK, THB, ZAR, RUB.
 - **Move Email Settings Under Authentication Tab** — Renamed the settings "Users" tab to "Authentication" and moved the standalone "Global Email" tab into it as an "Email Authentication" sub-tab. Groups email/SMTP configuration with user management where it logically belongs. Legacy `?tab=email` URLs are handled automatically.
 - **Move Email Settings Under Authentication Tab** — Renamed the settings "Users" tab to "Authentication" and moved the standalone "Global Email" tab into it as an "Email Authentication" sub-tab. Groups email/SMTP configuration with user management where it logically belongs. Legacy `?tab=email` URLs are handled automatically.
 
 

+ 18 - 1
frontend/src/components/SkipObjectsModal.tsx

@@ -1,9 +1,11 @@
+import { useState } from 'react';
 import { useQuery, useMutation } from '@tanstack/react-query';
 import { useQuery, useMutation } from '@tanstack/react-query';
 import { useTranslation } from 'react-i18next';
 import { useTranslation } from 'react-i18next';
 import { X, Loader2, Monitor, AlertCircle, Box } from 'lucide-react';
 import { X, Loader2, Monitor, AlertCircle, Box } from 'lucide-react';
 import { api } from '../api/client';
 import { api } from '../api/client';
 import { useToast } from '../contexts/ToastContext';
 import { useToast } from '../contexts/ToastContext';
 import { useAuth } from '../contexts/AuthContext';
 import { useAuth } from '../contexts/AuthContext';
+import { ConfirmModal } from './ConfirmModal';
 
 
 // Custom Skip Objects icon - arrow jumping over boxes
 // Custom Skip Objects icon - arrow jumping over boxes
 export const SkipObjectsIcon = ({ className }: { className?: string }) => (
 export const SkipObjectsIcon = ({ className }: { className?: string }) => (
@@ -28,6 +30,7 @@ export function SkipObjectsModal({ printerId, isOpen, onClose }: SkipObjectsModa
   const { t } = useTranslation();
   const { t } = useTranslation();
   const { showToast } = useToast();
   const { showToast } = useToast();
   const { hasPermission } = useAuth();
   const { hasPermission } = useAuth();
+  const [pendingSkip, setPendingSkip] = useState<{ id: number; name: string } | null>(null);
 
 
   const { data: status } = useQuery({
   const { data: status } = useQuery({
     queryKey: ['printerStatus', printerId],
     queryKey: ['printerStatus', printerId],
@@ -47,6 +50,7 @@ export function SkipObjectsModal({ printerId, isOpen, onClose }: SkipObjectsModa
     mutationFn: (objectIds: number[]) => api.skipObjects(printerId, objectIds),
     mutationFn: (objectIds: number[]) => api.skipObjects(printerId, objectIds),
     onSuccess: (data) => {
     onSuccess: (data) => {
       showToast(data.message || t('printers.skipObjects.objectsSkipped'));
       showToast(data.message || t('printers.skipObjects.objectsSkipped'));
+      setPendingSkip(null);
       refetchObjects();
       refetchObjects();
     },
     },
     onError: (error: Error) => showToast(error.message || t('printers.toast.failedToSkipObjects'), 'error'),
     onError: (error: Error) => showToast(error.message || t('printers.toast.failedToSkipObjects'), 'error'),
@@ -55,6 +59,7 @@ export function SkipObjectsModal({ printerId, isOpen, onClose }: SkipObjectsModa
   if (!isOpen) return null;
   if (!isOpen) return null;
 
 
   return (
   return (
+    <>
     <div
     <div
       className="fixed inset-0 z-50 flex items-center justify-center"
       className="fixed inset-0 z-50 flex items-center justify-center"
       onClick={onClose}
       onClick={onClose}
@@ -243,7 +248,7 @@ export function SkipObjectsModal({ printerId, isOpen, onClose }: SkipObjectsModa
                     {/* Skip button */}
                     {/* Skip button */}
                     {!obj.skipped ? (
                     {!obj.skipped ? (
                       <button
                       <button
-                        onClick={() => skipObjectsMutation.mutate([obj.id])}
+                        onClick={() => setPendingSkip({ id: obj.id, name: obj.name })}
                         disabled={skipObjectsMutation.isPending || (status?.layer_num ?? 0) <= 1 || !hasPermission('printers:control')}
                         disabled={skipObjectsMutation.isPending || (status?.layer_num ?? 0) <= 1 || !hasPermission('printers:control')}
                         className={`px-4 py-2 text-xs font-medium rounded-lg transition-colors ${
                         className={`px-4 py-2 text-xs font-medium rounded-lg transition-colors ${
                           (status?.layer_num ?? 0) <= 1 || !hasPermission('printers:control')
                           (status?.layer_num ?? 0) <= 1 || !hasPermission('printers:control')
@@ -267,5 +272,17 @@ export function SkipObjectsModal({ printerId, isOpen, onClose }: SkipObjectsModa
         )}
         )}
       </div>
       </div>
     </div>
     </div>
+    {pendingSkip && (
+      <ConfirmModal
+        variant="warning"
+        title={t('printers.skipObjects.confirmTitle')}
+        message={t('printers.skipObjects.confirmMessage', { name: pendingSkip.name })}
+        confirmText={t('printers.skipObjects.skip')}
+        isLoading={skipObjectsMutation.isPending}
+        onConfirm={() => skipObjectsMutation.mutate([pendingSkip.id])}
+        onCancel={() => setPendingSkip(null)}
+      />
+    )}
+  </>
   );
   );
 }
 }

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

@@ -331,6 +331,8 @@ export default {
       activeCount: '{{count}} aktiv',
       activeCount: '{{count}} aktiv',
       waitForLayer: 'Warten Sie auf Schicht 2+ zum Überspringen von Objekten (aktuell Schicht {{layer}})',
       waitForLayer: 'Warten Sie auf Schicht 2+ zum Überspringen von Objekten (aktuell Schicht {{layer}})',
       skip: 'Überspringen',
       skip: 'Überspringen',
+      confirmTitle: 'Objekt überspringen?',
+      confirmMessage: 'Möchten Sie "{{name}}" wirklich überspringen? Dies kann nicht rückgängig gemacht werden.',
     },
     },
     // Confirm modals
     // Confirm modals
     confirm: {
     confirm: {

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

@@ -331,6 +331,8 @@ export default {
       activeCount: '{{count}} active',
       activeCount: '{{count}} active',
       waitForLayer: 'Wait for layer 2+ to skip objects (currently layer {{layer}})',
       waitForLayer: 'Wait for layer 2+ to skip objects (currently layer {{layer}})',
       skip: 'Skip',
       skip: 'Skip',
+      confirmTitle: 'Skip Object?',
+      confirmMessage: 'Are you sure you want to skip "{{name}}"? This cannot be undone.',
     },
     },
     // Confirm modals
     // Confirm modals
     confirm: {
     confirm: {

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

@@ -322,6 +322,8 @@ export default {
       activeCount: '{{count}} attivi',
       activeCount: '{{count}} attivi',
       waitForLayer: 'Attendi il layer 2+ per saltare oggetti (attualmente layer {{layer}})',
       waitForLayer: 'Attendi il layer 2+ per saltare oggetti (attualmente layer {{layer}})',
       skip: 'Salta',
       skip: 'Salta',
+      confirmTitle: 'Saltare oggetto?',
+      confirmMessage: 'Sei sicuro di voler saltare "{{name}}"? Questa azione non può essere annullata.',
     },
     },
     // Confirm modals
     // Confirm modals
     confirm: {
     confirm: {

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

@@ -293,6 +293,8 @@ export default {
       skipping: 'スキップ中...',
       skipping: 'スキップ中...',
       noObjectsSelected: 'オブジェクトが選択されていません',
       noObjectsSelected: 'オブジェクトが選択されていません',
       selectObjectsToSkip: '現在の印刷からスキップするオブジェクトを選択してください',
       selectObjectsToSkip: '現在の印刷からスキップするオブジェクトを選択してください',
+      confirmTitle: 'オブジェクトをスキップしますか?',
+      confirmMessage: '「{{name}}」をスキップしますか?この操作は元に戻せません。',
     },
     },
     confirm: {
     confirm: {
       deleteTitle: 'プリンターを削除',
       deleteTitle: 'プリンターを削除',

File diff suppressed because it is too large
+ 0 - 0
static/assets/index-CYXpThrD.js


+ 1 - 1
static/index.html

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

Some files were not shown because too many files changed in this diff