mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-08 15:11:21 +02:00
Merge pull request #361 from Keybored02/0.2.0b
Add material mismatch check
This commit is contained in:
@@ -87,6 +87,7 @@ async def get_settings(
|
||||
"spoolman_enabled",
|
||||
"spoolman_disable_weight_sync",
|
||||
"spoolman_report_partial_usage",
|
||||
"disable_filament_warnings",
|
||||
"check_updates",
|
||||
"check_printer_firmware",
|
||||
"virtual_printer_enabled",
|
||||
@@ -237,6 +238,7 @@ async def get_spoolman_settings(
|
||||
spoolman_sync_mode = await get_setting(db, "spoolman_sync_mode") or "auto"
|
||||
spoolman_disable_weight_sync = await get_setting(db, "spoolman_disable_weight_sync") or "false"
|
||||
spoolman_report_partial_usage = await get_setting(db, "spoolman_report_partial_usage") or "true"
|
||||
disable_filament_warnings = await get_setting(db, "disable_filament_warnings") or "false"
|
||||
|
||||
return {
|
||||
"spoolman_enabled": spoolman_enabled,
|
||||
@@ -244,6 +246,7 @@ async def get_spoolman_settings(
|
||||
"spoolman_sync_mode": spoolman_sync_mode,
|
||||
"spoolman_disable_weight_sync": spoolman_disable_weight_sync,
|
||||
"spoolman_report_partial_usage": spoolman_report_partial_usage,
|
||||
"disable_filament_warnings": disable_filament_warnings,
|
||||
}
|
||||
|
||||
|
||||
@@ -273,6 +276,8 @@ async def update_spoolman_settings(
|
||||
await set_setting(db, "spoolman_disable_weight_sync", settings["spoolman_disable_weight_sync"])
|
||||
if "spoolman_report_partial_usage" in settings:
|
||||
await set_setting(db, "spoolman_report_partial_usage", settings["spoolman_report_partial_usage"])
|
||||
if "disable_filament_warnings" in settings:
|
||||
await set_setting(db, "disable_filament_warnings", settings["disable_filament_warnings"])
|
||||
|
||||
await db.commit()
|
||||
db.expire_all()
|
||||
|
||||
@@ -31,6 +31,10 @@ class AppSettings(BaseModel):
|
||||
default=True,
|
||||
description="Report Partial Usage for Failed Prints. When a print fails or is cancelled, report the estimated filament used up to that point based on layer progress.",
|
||||
)
|
||||
disable_filament_warnings: bool = Field(
|
||||
default=False,
|
||||
description="Disable insufficient filament warnings when printing or queueing prints",
|
||||
)
|
||||
|
||||
# Updates
|
||||
check_updates: bool = Field(default=True, description="Automatically check for updates on startup")
|
||||
@@ -158,6 +162,7 @@ class AppSettingsUpdate(BaseModel):
|
||||
spoolman_sync_mode: str | None = None
|
||||
spoolman_disable_weight_sync: bool | None = None
|
||||
spoolman_report_partial_usage: bool | None = None
|
||||
disable_filament_warnings: bool | None = None
|
||||
check_updates: bool | None = None
|
||||
check_printer_firmware: bool | None = None
|
||||
notification_language: str | None = None
|
||||
|
||||
@@ -748,6 +748,8 @@ export interface AppSettings {
|
||||
// Date/time format settings
|
||||
date_format: 'system' | 'us' | 'eu' | 'iso';
|
||||
time_format: 'system' | '12h' | '24h';
|
||||
// Filament tracking
|
||||
disable_filament_warnings: boolean; // Disable insufficient filament warnings when printing/queueing
|
||||
// Default printer
|
||||
default_printer_id: number | null;
|
||||
// Dark mode theme settings
|
||||
@@ -3362,9 +3364,9 @@ export const api = {
|
||||
body: JSON.stringify({ tray_uuid: trayUuid }),
|
||||
}),
|
||||
getSpoolmanSettings: () =>
|
||||
request<{ spoolman_enabled: string; spoolman_url: string; spoolman_sync_mode: string; spoolman_disable_weight_sync: string; spoolman_report_partial_usage: string; }>('/settings/spoolman'),
|
||||
updateSpoolmanSettings: (data: { spoolman_enabled?: string; spoolman_url?: string; spoolman_sync_mode?: string; spoolman_disable_weight_sync?: string; spoolman_report_partial_usage?: string; }) =>
|
||||
request<{ spoolman_enabled: string; spoolman_url: string; spoolman_sync_mode: string; spoolman_disable_weight_sync: string; spoolman_report_partial_usage: string; }>('/settings/spoolman', {
|
||||
request<{ spoolman_enabled: string; spoolman_url: string; spoolman_sync_mode: string; spoolman_disable_weight_sync: string; spoolman_report_partial_usage: string; disable_filament_warnings: string; }>('/settings/spoolman'),
|
||||
updateSpoolmanSettings: (data: { spoolman_enabled?: string; spoolman_url?: string; spoolman_sync_mode?: string; spoolman_disable_weight_sync?: string; spoolman_report_partial_usage?: string; disable_filament_warnings?: string; }) =>
|
||||
request<{ spoolman_enabled: string; spoolman_url: string; spoolman_sync_mode: string; spoolman_disable_weight_sync: string; spoolman_report_partial_usage: string; disable_filament_warnings: string; }>('/settings/spoolman', {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data),
|
||||
}),
|
||||
|
||||
@@ -5,6 +5,7 @@ import { X, Loader2, Package, Check, Search } from 'lucide-react';
|
||||
import { api } from '../api/client';
|
||||
import type { InventorySpool, SpoolAssignment } from '../api/client';
|
||||
import { Button } from './Button';
|
||||
import { ConfirmModal } from './ConfirmModal';
|
||||
import { useToast } from '../contexts/ToastContext';
|
||||
|
||||
interface AssignSpoolModalProps {
|
||||
@@ -26,6 +27,8 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
|
||||
const { showToast } = useToast();
|
||||
const [selectedSpoolId, setSelectedSpoolId] = useState<number | null>(null);
|
||||
const [searchFilter, setSearchFilter] = useState('');
|
||||
const [pendingAssignId, setPendingAssignId] = useState<number | null>(null);
|
||||
const [showMismatchConfirm, setShowMismatchConfirm] = useState(false);
|
||||
|
||||
const { data: spools, isLoading } = useQuery({
|
||||
queryKey: ['inventory-spools'],
|
||||
@@ -53,6 +56,8 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
|
||||
});
|
||||
queryClient.invalidateQueries({ queryKey: ['spool-assignments'] });
|
||||
showToast(t('inventory.assignSuccess'), 'success');
|
||||
setShowMismatchConfirm(false);
|
||||
setPendingAssignId(null);
|
||||
onClose();
|
||||
},
|
||||
onError: (error: Error) => {
|
||||
@@ -86,10 +91,35 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
|
||||
);
|
||||
});
|
||||
|
||||
const normalizeMaterial = (value: string | undefined | null) =>
|
||||
(value ?? '').trim().toUpperCase();
|
||||
|
||||
const materialsMatch = (spoolMaterial: string, trayMaterial: string) => {
|
||||
const normalizedSpool = normalizeMaterial(spoolMaterial);
|
||||
const normalizedTray = normalizeMaterial(trayMaterial);
|
||||
if (!normalizedSpool || !normalizedTray) return true;
|
||||
if (normalizedTray.includes(normalizedSpool)) return true;
|
||||
if (normalizedSpool.includes(normalizedTray)) return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
const handleAssign = () => {
|
||||
if (selectedSpoolId) {
|
||||
assignMutation.mutate(selectedSpoolId);
|
||||
if (!selectedSpoolId) return;
|
||||
const selectedSpool = spools?.find((spool: InventorySpool) => spool.id === selectedSpoolId);
|
||||
if (selectedSpool && trayInfo?.type) {
|
||||
const mismatch = !materialsMatch(selectedSpool.material, trayInfo.type);
|
||||
if (mismatch) {
|
||||
setPendingAssignId(selectedSpoolId);
|
||||
setShowMismatchConfirm(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
assignMutation.mutate(selectedSpoolId);
|
||||
};
|
||||
|
||||
const handleConfirmMismatch = () => {
|
||||
if (!pendingAssignId) return;
|
||||
assignMutation.mutate(pendingAssignId);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -228,6 +258,27 @@ export function AssignSpoolModal({ isOpen, onClose, printerId, amsId, trayId, tr
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{showMismatchConfirm && trayInfo && selectedSpoolId && (
|
||||
<ConfirmModal
|
||||
title={t('inventory.assignMismatchTitle')}
|
||||
message={t('inventory.assignMismatchMessage', {
|
||||
spoolMaterial: spools?.find((spool: InventorySpool) => spool.id === selectedSpoolId)?.material ?? '',
|
||||
trayMaterial: trayInfo.type,
|
||||
location: trayInfo.location,
|
||||
})}
|
||||
confirmText={t('inventory.assignMismatchConfirm')}
|
||||
variant="warning"
|
||||
isLoading={assignMutation.isPending}
|
||||
onConfirm={handleConfirmMismatch}
|
||||
onCancel={() => {
|
||||
if (!assignMutation.isPending) {
|
||||
setShowMismatchConfirm(false);
|
||||
setPendingAssignId(null);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ export function ConfirmModal({
|
||||
},
|
||||
warning: {
|
||||
icon: 'text-yellow-400',
|
||||
button: 'bg-yellow-500 hover:bg-yellow-600',
|
||||
button: 'bg-yellow-500 hover:bg-yellow-600 text-black',
|
||||
},
|
||||
default: {
|
||||
icon: 'text-bambu-green',
|
||||
@@ -77,7 +77,7 @@ export function ConfirmModal({
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<h3 className="text-lg font-semibold text-white mb-2">{title}</h3>
|
||||
<p className="text-bambu-gray text-sm">{message}</p>
|
||||
<p className="text-bambu-gray text-sm whitespace-pre-line">{message}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-3 mt-6">
|
||||
|
||||
@@ -3,13 +3,14 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { X, Printer, Loader2, Calendar, Pencil, AlertCircle, AlertTriangle } from 'lucide-react';
|
||||
import { api } from '../../api/client';
|
||||
import type { PrintQueueItemCreate, PrintQueueItemUpdate } from '../../api/client';
|
||||
import type { PrintQueueItemCreate, PrintQueueItemUpdate, SpoolAssignment } from '../../api/client';
|
||||
import { Card, CardContent } from '../Card';
|
||||
import { Button } from '../Button';
|
||||
import { ConfirmModal } from '../ConfirmModal';
|
||||
import { useToast } from '../../contexts/ToastContext';
|
||||
import { useFilamentMapping } from '../../hooks/useFilamentMapping';
|
||||
import { buildLoadedFilaments, useFilamentMapping } from '../../hooks/useFilamentMapping';
|
||||
import { useMultiPrinterFilamentMapping, type PerPrinterConfig } from '../../hooks/useMultiPrinterFilamentMapping';
|
||||
import { isPlaceholderDate } from '../../utils/amsHelpers';
|
||||
import { getGlobalTrayId, isPlaceholderDate } from '../../utils/amsHelpers';
|
||||
import { toDateTimeLocalValue } from '../../utils/date';
|
||||
import { PrinterSelector } from './PrinterSelector';
|
||||
import { PlateSelector } from './PlateSelector';
|
||||
@@ -50,6 +51,13 @@ export function PrintModal({
|
||||
// Determine if we're printing a library file
|
||||
const isLibraryFile = !!libraryFileId && !archiveId;
|
||||
|
||||
type FilamentWarningItem = {
|
||||
printerName: string;
|
||||
slotLabel: string;
|
||||
requiredGrams: number;
|
||||
remainingGrams: number;
|
||||
};
|
||||
|
||||
// Multiple printer selection (used for all modes now)
|
||||
const [selectedPrinters, setSelectedPrinters] = useState<number[]>(() => {
|
||||
// Initialize with the queue item's printer if editing
|
||||
@@ -155,6 +163,8 @@ export function PrintModal({
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [submitProgress, setSubmitProgress] = useState({ current: 0, total: 0 });
|
||||
|
||||
const [filamentWarningItems, setFilamentWarningItems] = useState<FilamentWarningItem[] | null>(null);
|
||||
|
||||
// Track which printers have had the "Expand custom mapping by default" setting applied
|
||||
// This ensures the setting only affects initial state, not preventing unchecking
|
||||
const [initialExpandApplied, setInitialExpandApplied] = useState<Set<number>>(new Set());
|
||||
@@ -175,6 +185,13 @@ export function PrintModal({
|
||||
queryFn: api.getPrinters,
|
||||
});
|
||||
|
||||
const { data: spoolAssignments } = useQuery({
|
||||
queryKey: ['spool-assignments'],
|
||||
queryFn: () => api.getAssignments(),
|
||||
staleTime: 30 * 1000,
|
||||
enabled: (mode === 'reprint' || mode === 'add-to-queue') && assignmentMode === 'printer',
|
||||
});
|
||||
|
||||
// Fetch archive details to get sliced_for_model
|
||||
const { data: archiveDetails } = useQuery({
|
||||
queryKey: ['archive', archiveId],
|
||||
@@ -329,6 +346,35 @@ export function PrintModal({
|
||||
const isMultiPlate = platesData?.is_multi_plate ?? false;
|
||||
const plates = platesData?.plates ?? [];
|
||||
|
||||
const spoolAssignmentsByPrinter = useMemo(() => {
|
||||
const map = new Map<number, Map<number, SpoolAssignment>>();
|
||||
if (!spoolAssignments) return map;
|
||||
spoolAssignments.forEach((assignment) => {
|
||||
const globalTrayId = getGlobalTrayId(
|
||||
assignment.ams_id,
|
||||
assignment.tray_id,
|
||||
assignment.ams_id < 0
|
||||
);
|
||||
const printerMap = map.get(assignment.printer_id) ?? new Map();
|
||||
printerMap.set(globalTrayId, assignment);
|
||||
map.set(assignment.printer_id, printerMap);
|
||||
});
|
||||
return map;
|
||||
}, [spoolAssignments]);
|
||||
|
||||
const filamentWarningMessage = useMemo(() => {
|
||||
if (!filamentWarningItems || filamentWarningItems.length === 0) return '';
|
||||
const lines = filamentWarningItems.map((item) =>
|
||||
t('printModal.insufficientFilamentLine', {
|
||||
printer: item.printerName,
|
||||
slot: item.slotLabel,
|
||||
required: Math.round(item.requiredGrams),
|
||||
remaining: Math.round(item.remainingGrams),
|
||||
})
|
||||
);
|
||||
return [t('printModal.insufficientFilamentMessage'), ...lines].join('\n');
|
||||
}, [filamentWarningItems, t]);
|
||||
|
||||
// Add to queue mutation (single printer)
|
||||
const addToQueueMutation = useMutation({
|
||||
mutationFn: (data: PrintQueueItemCreate) => api.addToQueue(data),
|
||||
@@ -348,9 +394,71 @@ export function PrintModal({
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = async (e?: React.FormEvent) => {
|
||||
const handleSubmit = async (e?: React.FormEvent, options?: { skipFilamentCheck?: boolean }) => {
|
||||
e?.preventDefault();
|
||||
|
||||
if (
|
||||
!options?.skipFilamentCheck &&
|
||||
!settings?.disable_filament_warnings &&
|
||||
(mode === 'reprint' || mode === 'add-to-queue') &&
|
||||
assignmentMode === 'printer'
|
||||
) {
|
||||
const warningItems: FilamentWarningItem[] = [];
|
||||
const filamentReqs = effectiveFilamentReqs?.filaments ?? [];
|
||||
|
||||
if (filamentReqs.length > 0 && spoolAssignmentsByPrinter.size > 0) {
|
||||
const getRemainingWeight = (labelWeight: number, weightUsed: number) => {
|
||||
if (!Number.isFinite(labelWeight) || labelWeight <= 0) return null;
|
||||
if (!Number.isFinite(weightUsed) || weightUsed < 0) return null;
|
||||
return Math.max(0, labelWeight - weightUsed);
|
||||
};
|
||||
|
||||
for (const printerId of selectedPrinters) {
|
||||
const printerMapping = selectedPrinters.length > 1
|
||||
? multiPrinterMapping.getFinalMapping(printerId)
|
||||
: amsMapping;
|
||||
if (!printerMapping) continue;
|
||||
|
||||
const printerStatusForWarning = selectedPrinters.length > 1
|
||||
? multiPrinterMapping.printerResults.find((result) => result.printerId === printerId)?.status
|
||||
: printerStatus;
|
||||
|
||||
const loadedFilaments = buildLoadedFilaments(printerStatusForWarning);
|
||||
const slotLabelByTray = new Map(loadedFilaments.map((f) => [f.globalTrayId, f.label]));
|
||||
const assignments = spoolAssignmentsByPrinter.get(printerId);
|
||||
const printerName = printers?.find((p) => p.id === printerId)?.name ?? `Printer ${printerId}`;
|
||||
|
||||
if (!assignments) continue;
|
||||
|
||||
filamentReqs.forEach((req) => {
|
||||
if (!req.slot_id || req.slot_id <= 0) return;
|
||||
const globalTrayId = printerMapping[req.slot_id - 1];
|
||||
if (!Number.isFinite(globalTrayId) || globalTrayId < 0) return;
|
||||
|
||||
const assignment = assignments.get(globalTrayId);
|
||||
const spool = assignment?.spool;
|
||||
if (!spool) return;
|
||||
|
||||
const remainingGrams = getRemainingWeight(spool.label_weight, spool.weight_used);
|
||||
if (remainingGrams === null) return;
|
||||
if (remainingGrams >= req.used_grams) return;
|
||||
|
||||
warningItems.push({
|
||||
printerName,
|
||||
slotLabel: slotLabelByTray.get(globalTrayId) ?? `Slot ${req.slot_id}`,
|
||||
requiredGrams: req.used_grams,
|
||||
remainingGrams,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (warningItems.length > 0) {
|
||||
setFilamentWarningItems(warningItems);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate printer/model selection
|
||||
if (assignmentMode === 'printer' && selectedPrinters.length === 0) {
|
||||
showToast('Please select at least one printer', 'error');
|
||||
@@ -738,6 +846,21 @@ export function PrintModal({
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{filamentWarningItems && filamentWarningItems.length > 0 && (
|
||||
<ConfirmModal
|
||||
title={t('printModal.insufficientFilamentTitle')}
|
||||
message={filamentWarningMessage}
|
||||
confirmText={t('printModal.printAnyway')}
|
||||
cancelText={t('common.cancel')}
|
||||
variant="warning"
|
||||
onConfirm={() => {
|
||||
setFilamentWarningItems(null);
|
||||
void handleSubmit(undefined, { skipFilamentCheck: true });
|
||||
}}
|
||||
onCancel={() => setFilamentWarningItems(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ export function SpoolmanSettings() {
|
||||
const [localSyncMode, setLocalSyncMode] = useState('auto');
|
||||
const [localDisableWeightSync, setLocalDisableWeightSync] = useState(false);
|
||||
const [localReportPartialUsage, setLocalReportPartialUsage] = useState(true);
|
||||
const [localDisableFilamentWarnings, setLocalDisableFilamentWarnings] = useState(false);
|
||||
const [selectedPrinterId, setSelectedPrinterId] = useState<number | 'all'>('all');
|
||||
const [isInitialized, setIsInitialized] = useState(false);
|
||||
const [showAllSkipped, setShowAllSkipped] = useState(false);
|
||||
@@ -48,6 +49,7 @@ export function SpoolmanSettings() {
|
||||
setLocalSyncMode(settings.spoolman_sync_mode || 'auto');
|
||||
setLocalDisableWeightSync(settings.spoolman_disable_weight_sync === 'true');
|
||||
setLocalReportPartialUsage(settings.spoolman_report_partial_usage !== 'false');
|
||||
setLocalDisableFilamentWarnings(settings.disable_filament_warnings === 'true');
|
||||
setIsInitialized(true);
|
||||
}
|
||||
}, [settings]);
|
||||
@@ -62,7 +64,8 @@ export function SpoolmanSettings() {
|
||||
(settings.spoolman_url || '') !== localUrl ||
|
||||
(settings.spoolman_sync_mode || 'auto') !== localSyncMode ||
|
||||
(settings.spoolman_disable_weight_sync === 'true') !== localDisableWeightSync ||
|
||||
(settings.spoolman_report_partial_usage !== 'false') !== localReportPartialUsage;
|
||||
(settings.spoolman_report_partial_usage !== 'false') !== localReportPartialUsage ||
|
||||
(settings.disable_filament_warnings === 'true') !== localDisableFilamentWarnings;
|
||||
|
||||
if (hasChanges) {
|
||||
const timeoutId = setTimeout(() => {
|
||||
@@ -71,7 +74,7 @@ export function SpoolmanSettings() {
|
||||
return () => clearTimeout(timeoutId);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [localEnabled, localUrl, localSyncMode, localDisableWeightSync, localReportPartialUsage, isInitialized]);
|
||||
}, [localEnabled, localUrl, localSyncMode, localDisableWeightSync, localReportPartialUsage, localDisableFilamentWarnings, isInitialized]);
|
||||
|
||||
// Save mutation
|
||||
const saveMutation = useMutation({
|
||||
@@ -82,10 +85,12 @@ export function SpoolmanSettings() {
|
||||
spoolman_sync_mode: localSyncMode,
|
||||
spoolman_disable_weight_sync: localDisableWeightSync ? 'true' : 'false',
|
||||
spoolman_report_partial_usage: localReportPartialUsage ? 'true' : 'false',
|
||||
disable_filament_warnings: localDisableFilamentWarnings ? 'true' : 'false',
|
||||
}),
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries({ queryKey: ['spoolman-settings'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['spoolman-status'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['settings'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['spool-assignments'] });
|
||||
showToast(t('settings.toast.settingsSaved'));
|
||||
},
|
||||
@@ -234,6 +239,25 @@ export function SpoolmanSettings() {
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Disable Filament Warnings toggle - applies to both modes */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<p className="text-white">{t('settings.disableFilamentWarnings')}</p>
|
||||
<p className="text-sm text-bambu-gray">
|
||||
{t('settings.disableFilamentWarningsDesc')}
|
||||
</p>
|
||||
</div>
|
||||
<label className="relative inline-flex items-center cursor-pointer">
|
||||
<input
|
||||
type="checkbox"
|
||||
checked={localDisableFilamentWarnings}
|
||||
onChange={(e) => setLocalDisableFilamentWarnings(e.target.checked)}
|
||||
className="sr-only peer"
|
||||
/>
|
||||
<div className="w-11 h-6 bg-bambu-dark-tertiary peer-focus:outline-none rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-bambu-green"></div>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{/* Built-in Inventory details */}
|
||||
{!localEnabled && (
|
||||
<div className="p-3 bg-bambu-green/5 border border-bambu-green/20 rounded-lg">
|
||||
|
||||
@@ -1164,6 +1164,8 @@ export default {
|
||||
// Filament Tracking Mode
|
||||
filamentTracking: 'Filament-Verfolgung',
|
||||
filamentTrackingDesc: 'Wählen Sie, wie Sie Ihre Filamentspulen verfolgen möchten. Sie können das integrierte Inventar oder einen externen Spoolman-Server verwenden.',
|
||||
disableFilamentWarnings: 'Filament-Warnungen deaktivieren',
|
||||
disableFilamentWarningsDesc: 'Keine Warnungen über unzureichendes Filament beim Drucken oder Einreihen anzeigen',
|
||||
trackingModeBuiltIn: 'Integriertes Inventar',
|
||||
trackingModeBuiltInDesc: 'RFID-Erkennung und Verbrauchserfassung inklusive',
|
||||
trackingModeSpoolmanDesc: 'Externer Filament-Management-Server',
|
||||
@@ -2486,6 +2488,9 @@ export default {
|
||||
unassignSpool: 'Zuweisung aufheben',
|
||||
assignSuccess: 'Spule zugewiesen und AMS-Slot konfiguriert',
|
||||
assignFailed: 'Spulenzuweisung fehlgeschlagen',
|
||||
assignMismatchTitle: 'Material stimmt nicht überein',
|
||||
assignMismatchMessage: 'Das Material der ausgewählten Spule ({{spoolMaterial}}) stimmt nicht mit dem AMS-Profil ({{trayMaterial}}) für {{location}} überein. Trotzdem zuweisen?',
|
||||
assignMismatchConfirm: 'Trotzdem zuweisen',
|
||||
selectSpool: 'Wählen Sie eine Spule für diesen Slot',
|
||||
assigned: 'Zugewiesen',
|
||||
assigning: 'Wird zugewiesen...',
|
||||
@@ -2650,6 +2655,10 @@ export default {
|
||||
rightNozzle: 'R',
|
||||
leftNozzleTooltip: 'Linke Düse',
|
||||
rightNozzleTooltip: 'Rechte Düse',
|
||||
insufficientFilamentTitle: 'Nicht genug Filament',
|
||||
insufficientFilamentMessage: 'Einige zugewiesene Spulen haben weniger Filament als dieser Druck benötigt:',
|
||||
insufficientFilamentLine: '{{printer}} - {{slot}}: benötigt {{required}}g, verbleibend {{remaining}}g',
|
||||
printAnyway: 'Trotzdem drucken',
|
||||
},
|
||||
|
||||
// Backup
|
||||
|
||||
@@ -1164,6 +1164,8 @@ export default {
|
||||
// Filament Tracking Mode
|
||||
filamentTracking: 'Filament Tracking',
|
||||
filamentTrackingDesc: 'Choose how to track your filament spools. You can use the built-in inventory or connect an external Spoolman server.',
|
||||
disableFilamentWarnings: 'Disable filament warnings',
|
||||
disableFilamentWarningsDesc: 'Don\'t show warnings about insufficient filament when printing or queueing',
|
||||
trackingModeBuiltIn: 'Built-in Inventory',
|
||||
trackingModeBuiltInDesc: 'RFID auto-matching and usage tracking included',
|
||||
trackingModeSpoolmanDesc: 'External filament management server',
|
||||
@@ -2486,6 +2488,9 @@ export default {
|
||||
unassignSpool: 'Unassign',
|
||||
assignSuccess: 'Spool assigned and AMS slot configured',
|
||||
assignFailed: 'Failed to assign spool',
|
||||
assignMismatchTitle: 'Material mismatch',
|
||||
assignMismatchMessage: 'The selected spool material ({{spoolMaterial}}) does not match the AMS profile ({{trayMaterial}}) for {{location}}. Assign anyway?',
|
||||
assignMismatchConfirm: 'Assign Anyway',
|
||||
selectSpool: 'Select a spool to assign to this slot',
|
||||
assigned: 'Assigned',
|
||||
assigning: 'Assigning...',
|
||||
@@ -2654,6 +2659,10 @@ export default {
|
||||
rightNozzle: 'R',
|
||||
leftNozzleTooltip: 'Left nozzle',
|
||||
rightNozzleTooltip: 'Right nozzle',
|
||||
insufficientFilamentTitle: 'Not enough filament',
|
||||
insufficientFilamentMessage: 'Some assigned spools have less filament remaining than this print needs:',
|
||||
insufficientFilamentLine: '{{printer}} - {{slot}}: needs {{required}}g, remaining {{remaining}}g',
|
||||
printAnyway: 'Print anyway',
|
||||
},
|
||||
|
||||
// Backup
|
||||
|
||||
@@ -1160,6 +1160,8 @@ export default {
|
||||
// Filament Tracking Mode
|
||||
filamentTracking: 'Suivi de Filament',
|
||||
filamentTrackingDesc: 'Choisissez comment suivre vos bobines. Utilisez l\'inventaire intégré ou connectez un serveur Spoolman.',
|
||||
disableFilamentWarnings: 'Désactiver les avertissements de filament',
|
||||
disableFilamentWarningsDesc: 'Ne pas afficher les avertissements de filament insuffisant lors de l\'impression ou de la mise en file d\'attente',
|
||||
trackingModeBuiltIn: 'Inventaire Intégré',
|
||||
trackingModeBuiltInDesc: 'Correspondance RFID et suivi de consommation inclus',
|
||||
trackingModeSpoolmanDesc: 'Serveur de gestion externe',
|
||||
@@ -2482,6 +2484,9 @@ export default {
|
||||
unassignSpool: 'Désassigner',
|
||||
assignSuccess: 'Bobine assignée et slot AMS configuré',
|
||||
assignFailed: 'Échec assignation',
|
||||
assignMismatchTitle: 'Matériau non correspondant',
|
||||
assignMismatchMessage: 'Le matériau de la bobine sélectionnée ({{spoolMaterial}}) ne correspond pas au profil AMS ({{trayMaterial}}) pour {{location}}. Assigner quand même ?',
|
||||
assignMismatchConfirm: 'Assigner quand même',
|
||||
selectSpool: 'Choisir une bobine pour ce slot',
|
||||
assigned: 'Assigné',
|
||||
assigning: 'Assignation...',
|
||||
@@ -2650,6 +2655,10 @@ export default {
|
||||
rightNozzle: 'D',
|
||||
leftNozzleTooltip: 'Buse gauche',
|
||||
rightNozzleTooltip: 'Buse droite',
|
||||
insufficientFilamentTitle: 'Filament insuffisant',
|
||||
insufficientFilamentMessage: 'Certaines bobines assignées ont moins de filament restant que nécessaire pour cette impression :',
|
||||
insufficientFilamentLine: '{{printer}} - {{slot}} : nécessite {{required}}g, restant {{remaining}}g',
|
||||
printAnyway: 'Imprimer quand même',
|
||||
},
|
||||
|
||||
// Backup
|
||||
|
||||
@@ -1094,6 +1094,17 @@ export default {
|
||||
turnOn: 'Accendi',
|
||||
turnOff: 'Spegni',
|
||||
},
|
||||
// Filament Tracking Mode
|
||||
filamentTracking: 'Tracciamento filamento',
|
||||
filamentTrackingDesc: 'Scegli come tracciare le tue bobine di filamento. Puoi utilizzare l\'inventario integrato o collegare un server Spoolman esterno.',
|
||||
disableFilamentWarnings: 'Disabilita avvisi filamento',
|
||||
disableFilamentWarningsDesc: 'Non mostrare avvisi per filamento insufficiente durante la stampa o l\'accodamento',
|
||||
trackingModeBuiltIn: 'Inventario integrato',
|
||||
trackingModeBuiltInDesc: 'Corrispondenza RFID automatica e tracciamento utilizzo inclusi',
|
||||
trackingModeSpoolmanDesc: 'Server di gestione filamenti esterno',
|
||||
builtInFeatureRfid: 'Rileva automaticamente le bobine RFID Bambu Lab nell\'AMS',
|
||||
builtInFeatureUsage: 'Traccia il consumo di filamento per stampa',
|
||||
builtInFeatureCatalog: 'Gestisci bobine, colori e profili K-factor',
|
||||
// Spoolman
|
||||
spoolmanEnabled: 'Abilita integrazione Spoolman',
|
||||
spoolmanUrl: 'URL Spoolman',
|
||||
@@ -2305,6 +2316,9 @@ export default {
|
||||
unassignSpool: 'Deassegna',
|
||||
assignSuccess: 'Bobina assegnata e slot AMS configurato',
|
||||
assignFailed: 'Assegnazione bobina fallita',
|
||||
assignMismatchTitle: 'Materiale non corrispondente',
|
||||
assignMismatchMessage: 'Il materiale della bobina selezionata ({{spoolMaterial}}) non corrisponde al profilo AMS ({{trayMaterial}}) per {{location}}. Assegnare comunque?',
|
||||
assignMismatchConfirm: 'Assegna comunque',
|
||||
selectSpool: 'Seleziona una bobina da assegnare a questo slot',
|
||||
assigned: 'Assegnato',
|
||||
assigning: 'Assegnazione...',
|
||||
@@ -2378,6 +2392,10 @@ export default {
|
||||
rightNozzle: 'R',
|
||||
leftNozzleTooltip: 'Ugello sinistro',
|
||||
rightNozzleTooltip: 'Ugello destro',
|
||||
insufficientFilamentTitle: 'Filamento insufficiente',
|
||||
insufficientFilamentMessage: 'Alcune bobine assegnate hanno meno filamento rimanente di quanto necessario per questa stampa:',
|
||||
insufficientFilamentLine: '{{printer}} - {{slot}}: necessita di {{required}}g, rimanenti {{remaining}}g',
|
||||
printAnyway: 'Stampa comunque',
|
||||
},
|
||||
|
||||
// Backup
|
||||
|
||||
@@ -1404,6 +1404,8 @@ export default {
|
||||
// フィラメント追跡モード
|
||||
filamentTracking: 'フィラメント追跡',
|
||||
filamentTrackingDesc: 'フィラメントスプールの追跡方法を選択してください。内蔵インベントリまたは外部Spoolmanサーバーを使用できます。',
|
||||
disableFilamentWarnings: 'フィラメント警告を無効化',
|
||||
disableFilamentWarningsDesc: '印刷またはキュー追加時にフィラメント不足の警告を表示しない',
|
||||
trackingModeBuiltIn: '内蔵インベントリ',
|
||||
trackingModeBuiltInDesc: 'RFID自動検出と使用量追跡を含む',
|
||||
trackingModeSpoolmanDesc: '外部フィラメント管理サーバー',
|
||||
@@ -2417,6 +2419,9 @@ export default {
|
||||
unassignSpool: '割り当て解除',
|
||||
assignSuccess: 'スプールを割り当て、AMSスロットを設定しました',
|
||||
assignFailed: 'スプールの割り当てに失敗しました',
|
||||
assignMismatchTitle: '素材が一致しません',
|
||||
assignMismatchMessage: '選択したスプールの素材({{spoolMaterial}})が、{{location}} のAMSプロファイル({{trayMaterial}})と一致しません。割り当てを続行しますか?',
|
||||
assignMismatchConfirm: '続行する',
|
||||
selectSpool: 'このスロットに割り当てるスプールを選択',
|
||||
assigned: '割り当て済み',
|
||||
assigning: '割り当て中...',
|
||||
@@ -2570,6 +2575,10 @@ export default {
|
||||
rightNozzle: 'R',
|
||||
leftNozzleTooltip: '左ノズル',
|
||||
rightNozzleTooltip: '右ノズル',
|
||||
insufficientFilamentTitle: 'フィラメントが不足しています',
|
||||
insufficientFilamentMessage: '割り当てられたスプールの一部は、この印刷に必要な量より残量が少ないです:',
|
||||
insufficientFilamentLine: '{{printer}} - {{slot}}: 必要 {{required}}g、残り {{remaining}}g',
|
||||
printAnyway: 'それでも印刷',
|
||||
},
|
||||
backup: {
|
||||
restoreBackup: 'バックアップの復元',
|
||||
|
||||
Reference in New Issue
Block a user