import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Check, AlertTriangle, RefreshCw } from 'lucide-react'; import type { MatchedSpool } from '../../hooks/useSpoolBuddyState'; import { spoolbuddyApi } from '../../api/client'; import { SpoolIcon } from './SpoolIcon'; // Storage key for default core weight const DEFAULT_CORE_WEIGHT_KEY = 'spoolbuddy-default-core-weight'; function getDefaultCoreWeight(): number { try { const stored = localStorage.getItem(DEFAULT_CORE_WEIGHT_KEY); if (stored) { const weight = parseInt(stored, 10); if (weight >= 0 && weight <= 500) return weight; } } catch { // Ignore errors } return 250; // Default 250g (typical Bambu spool core) } interface SpoolInfoCardProps { spool: MatchedSpool; scaleWeight: number | null; onClose?: () => void; onSyncWeight?: () => void; onAssignToAms?: () => void; } export function SpoolInfoCard({ spool, scaleWeight, onClose, onSyncWeight, onAssignToAms }: SpoolInfoCardProps) { const { t } = useTranslation(); const [syncing, setSyncing] = useState(false); const [synced, setSynced] = useState(false); const colorHex = spool.rgba ? `#${spool.rgba.slice(0, 6)}` : '#808080'; // Use spool's core_weight if set, otherwise fall back to default const coreWeight = (spool.core_weight && spool.core_weight > 0) ? spool.core_weight : getDefaultCoreWeight(); // Gross weight from scale (live) or fallback const grossWeight = scaleWeight !== null ? Math.round(Math.max(0, scaleWeight)) : null; // Remaining filament = gross - core const remaining = grossWeight !== null ? Math.round(Math.max(0, grossWeight - coreWeight)) : null; const labelWeight = Math.round(spool.label_weight || 1000); const fillPercent = remaining !== null ? Math.min(100, Math.round((remaining / labelWeight) * 100)) : null; const fillColor = fillPercent !== null ? fillPercent > 50 ? '#22c55e' : fillPercent > 20 ? '#eab308' : '#ef4444' : '#808080'; // Weight comparison (scale vs calculated expected) const netWeight = Math.max(0, (spool.label_weight || 0) - (spool.weight_used || 0) ); const calculatedWeight = netWeight + coreWeight; const difference = grossWeight !== null ? grossWeight - calculatedWeight : null; const isMatch = difference !== null ? Math.abs(difference) <= 50 : null; const handleSyncWeight = async () => { if (scaleWeight === null) return; setSyncing(true); try { await spoolbuddyApi.updateSpoolWeight(spool.id, Math.round(scaleWeight)); setSynced(true); onSyncWeight?.(); setTimeout(() => setSynced(false), 3000); } catch (e) { console.error('Failed to sync weight:', e); } finally { setSyncing(false); } }; return (
{spool.brand} • {spool.material} {spool.subtype && ` ${spool.subtype}`}
{/* Filament remaining - big number */} {remaining !== null && ({t('spoolbuddy.spool.remaining', 'Remaining')}
{/* Fill bar */}{tagUid}