mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-21 10:15:04 +02:00
Add spool inventory: AMS slot assignment, usage tracking, and remaining weight editing
Built-in spool inventory with AMS slot assignment for non-BL spools, automatic filament usage tracking via 3MF estimates, and remaining weight editing in the spool form. Inventory features: - AMS slot assignment: assign/unassign manual spools to AMS tray slots - Filter out Bambu Lab spools (RFID-managed) from assignment modal - Filter out already-assigned spools (one spool per slot) - Auto-unlink manual assignments when a BL spool is inserted - Hide assign/unassign UI on BL-occupied slots - Case-insensitive fingerprint matching for auto-unlink logic Usage tracking: - 3MF-based filament consumption tracking for non-BL spools - Extract per-filament used_g from archived 3MF slice_info - Map 3MF slot_id to AMS (ams_id, tray_id) positions - Scale estimates by print progress for failed/aborted prints - Skip BL spools (tracked via AMS remain% delta, Path 1) - Prevent double-counting with handled_trays set Other: - Add remaining weight field to spool edit form - Fix hardcoded "Empty" string in AssignSpoolModal (now i18n) - Add backend unit tests for usage_tracker (11 tests) - Add frontend tests for AssignSpoolModal filters (5 tests) - Update CHANGELOG, README, wiki docs, and website
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { SlicerSetting } from '../../api/client';
|
||||
import type { SlicerSetting, LocalPreset } from '../../api/client';
|
||||
import type { ColorPreset, FilamentOption } from './types';
|
||||
import { KNOWN_VARIANTS, DEFAULT_BRANDS, RECENT_COLORS_KEY, MAX_RECENT_COLORS } from './constants';
|
||||
|
||||
@@ -91,8 +91,8 @@ export function parsePresetName(name: string): { brand: string; material: string
|
||||
return { brand, material, variant };
|
||||
}
|
||||
|
||||
// Extract unique brands from cloud presets
|
||||
export function extractBrandsFromPresets(presets: SlicerSetting[]): string[] {
|
||||
// Extract unique brands from cloud presets and local presets
|
||||
export function extractBrandsFromPresets(presets: SlicerSetting[], localPresets?: LocalPreset[]): string[] {
|
||||
const brandSet = new Set<string>(DEFAULT_BRANDS);
|
||||
|
||||
for (const preset of presets) {
|
||||
@@ -102,14 +102,56 @@ export function extractBrandsFromPresets(presets: SlicerSetting[]): string[] {
|
||||
}
|
||||
}
|
||||
|
||||
// Also extract brands from local presets
|
||||
if (localPresets) {
|
||||
for (const preset of localPresets) {
|
||||
if (preset.filament_vendor && preset.filament_vendor.length > 1) {
|
||||
brandSet.add(preset.filament_vendor);
|
||||
} else {
|
||||
const { brand } = parsePresetName(preset.name);
|
||||
if (brand && brand.length > 1) {
|
||||
brandSet.add(brand);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(brandSet).sort((a, b) => a.localeCompare(b));
|
||||
}
|
||||
|
||||
// Build filament options from cloud presets
|
||||
// Build filament options from local presets (OrcaSlicer imports)
|
||||
function buildLocalFilamentOptions(localPresets: LocalPreset[]): FilamentOption[] {
|
||||
const filamentPresets = localPresets.filter(p => p.preset_type === 'filament');
|
||||
if (filamentPresets.length === 0) return [];
|
||||
|
||||
const presetsMap = new Map<string, FilamentOption>();
|
||||
for (const preset of filamentPresets) {
|
||||
const baseName = preset.name.replace(/@.*$/, '').trim();
|
||||
const existing = presetsMap.get(baseName);
|
||||
if (existing) {
|
||||
existing.allCodes.push(String(preset.id));
|
||||
} else {
|
||||
// Use filament_type as the code if available (e.g. "GFL00"), otherwise use the id
|
||||
const code = preset.filament_type || String(preset.id);
|
||||
presetsMap.set(baseName, {
|
||||
code,
|
||||
name: baseName,
|
||||
displayName: baseName,
|
||||
isCustom: false,
|
||||
allCodes: [code],
|
||||
});
|
||||
}
|
||||
}
|
||||
return Array.from(presetsMap.values()).sort((a, b) => a.displayName.localeCompare(b.displayName));
|
||||
}
|
||||
|
||||
// Build filament options: cloud presets → local presets → hardcoded fallback
|
||||
export function buildFilamentOptions(
|
||||
cloudPresets: SlicerSetting[],
|
||||
configuredPrinterModels: Set<string>,
|
||||
localPresets?: LocalPreset[],
|
||||
): FilamentOption[] {
|
||||
// 1. Cloud presets (highest priority)
|
||||
if (cloudPresets.length > 0) {
|
||||
const customPresets: FilamentOption[] = [];
|
||||
const defaultPresetsMap = new Map<string, FilamentOption>();
|
||||
@@ -155,7 +197,13 @@ export function buildFilamentOptions(
|
||||
].sort((a, b) => a.displayName.localeCompare(b.displayName));
|
||||
}
|
||||
|
||||
// Fallback to hardcoded defaults
|
||||
// 2. Local presets (OrcaSlicer imports)
|
||||
if (localPresets && localPresets.length > 0) {
|
||||
const localOptions = buildLocalFilamentOptions(localPresets);
|
||||
if (localOptions.length > 0) return localOptions;
|
||||
}
|
||||
|
||||
// 3. Hardcoded fallback
|
||||
return FALLBACK_PRESETS;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user