/** * New Features Implementation * - Filament/Resin Cost Calculator * - Full-text Search Enhancement * - License Management * - Printer Integration (Bambu) * - Dark/Light Theme Toggle */ /** * Show Printer Settings Modal */ function showPrinterSettings() { loadPrinters(); openModal('printerSettingsModal'); } /** * Load user's connected printers */ function loadPrinters() { fetch(`${API_BASE}/printers/printers`, { headers: { 'Authorization': `Bearer ${authToken}` } }) .then(res => res.json()) .then(data => { displayPrinters(data.printers || []); }) .catch(err => console.error('Error loading printers:', err)); } /** * Display connected printers */ function displayPrinters(printers) { const printerList = document.getElementById('printerList'); if (printers.length === 0) { printerList.innerHTML = '

No printers connected yet.

'; return; } printerList.innerHTML = printers.map(printer => `

${printer.printer_name || 'Unnamed Printer'}

Type: ${printer.printer_type}

Serial: ${printer.serial_number}

Model: ${printer.model_name || 'N/A'}

`).join(''); } /** * Add new Bambu printer */ function handleAddPrinter(event) { event.preventDefault(); const printerName = document.getElementById('printerName').value; const printerSerial = document.getElementById('printerSerial').value; const printerModel = document.getElementById('printerModel').value; const printerToken = document.getElementById('printerToken').value; fetch(`${API_BASE}/printers/bambu/connect`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authToken}` }, body: JSON.stringify({ printerName, serialNumber: printerSerial, modelName: printerModel, accessToken: printerToken }) }) .then(res => res.json()) .then(data => { if (data.printerConnected) { showNotification('Printer connected successfully!', 'success'); document.querySelector('#printerSettingsModal form').reset(); loadPrinters(); } else { showNotification(data.error || 'Failed to connect printer', 'error'); } }) .catch(err => { showNotification('Error connecting printer: ' + err.message, 'error'); }); } /** * Remove connected printer */ function removePrinter(printerId) { if (!confirm('Are you sure you want to disconnect this printer?')) { return; } fetch(`${API_BASE}/printers/printers/${printerId}`, { method: 'DELETE', headers: { 'Authorization': `Bearer ${authToken}` } }) .then(res => res.json()) .then(data => { showNotification('Printer disconnected', 'success'); loadPrinters(); }) .catch(err => showNotification('Error removing printer: ' + err.message, 'error')); } /** * Refresh printer status */ function refreshPrinterStatus(printerId) { fetch(`${API_BASE}/printers/bambu/${printerId}/status`, { headers: { 'Authorization': `Bearer ${authToken}` } }) .then(res => res.json()) .then(data => { showNotification('Printer status: ' + JSON.stringify(data.data || data.error), 'info'); }) .catch(err => showNotification('Error fetching printer status', 'error')); } /** * Show cost calculator modal */ function showCostCalculator() { openModal('costCalculatorModal'); updateCostEstimate(); } /** * Update cost estimate for selected models */ function updateCostEstimate() { const selectedModels = Array.from(document.querySelectorAll('.model-card.selected')).map(card => ({ id: parseInt(card.dataset.modelId), name: card.querySelector('.model-name').textContent, file_size: parseInt(card.dataset.fileSize) })); const materialType = document.getElementById('materialType')?.value || 'pla'; const costResults = document.getElementById('costResults'); if (selectedModels.length === 0) { costResults.innerHTML = '

Select models from the grid to see cost estimates

'; return; } const modelIds = selectedModels.map(m => m.id); fetch(`${API_BASE}/models/batch/cost`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${authToken}` }, body: JSON.stringify({ modelIds, materialType }) }) .then(res => res.json()) .then(data => { displayCostResults(data); }) .catch(err => { costResults.innerHTML = `

Error calculating costs: ${err.message}

`; }); } /** * Display cost calculation results */ function displayCostResults(data) { const costResults = document.getElementById('costResults'); const cardsHtml = data.models.map(model => `

${model.name}

Weight: ${model.weight.toFixed(1)}g
Units (${model.material}): ${model.units.toFixed(3)}
Cost per Unit: $${model.costPerUnit.toFixed(2)}
Estimated Cost: $${model.estimatedCost.toFixed(2)}
Confidence: ${model.confidence}
`).join(''); costResults.innerHTML = cardsHtml + `

$${data.totalCost.toFixed(2)}

Total Estimated Cost for ${data.models.length} model(s)

Average: $${data.averageCost.toFixed(2)} per model

`; } /** * Calculate cost for single model */ function calculateModelCost(modelId, fileSize, materialType = 'pla') { fetch(`${API_BASE}/models/${modelId}/cost?materialType=${materialType}`, { headers: { 'Authorization': `Bearer ${authToken}` } }) .then(res => res.json()) .then(data => { return data; }) .catch(err => console.error('Error calculating cost:', err)); } /** * Add cost badge to model card */ function addCostBadge(modelCard, cost) { const existing = modelCard.querySelector('.model-cost-badge'); if (existing) existing.remove(); const badge = document.createElement('div'); badge.className = 'model-cost-badge'; badge.textContent = `$${cost.toFixed(2)}`; modelCard.querySelector('.model-info')?.appendChild(badge); } /** * Enhanced search with full-text support */ function handleAdvancedSearch(query) { const params = new URLSearchParams({ search: query, license: document.getElementById('filterLicense')?.value || '', fileType: document.getElementById('filterFileType')?.value || '', hasSupports: document.getElementById('filterSupports')?.checked || false, sortBy: document.getElementById('sortBy')?.value || 'created_at', sortOrder: document.getElementById('sortOrder')?.value || 'DESC' }); fetch(`${API_BASE}/models?${params}`, { headers: { 'Authorization': `Bearer ${authToken}` } }) .then(res => res.json()) .then(data => { displayModels(data.models); }) .catch(err => console.error('Error searching:', err)); } /** * Apply all filters including license */ function applyFilters() { const searchTerm = document.getElementById('searchInput')?.value || ''; handleAdvancedSearch(searchTerm); } /** * Display license in model card */ function displayModelLicense(modelCard, license) { const licenseEl = document.createElement('p'); licenseEl.style.fontSize = '0.75rem'; licenseEl.style.color = 'var(--text-secondary)'; licenseEl.textContent = `📜 ${license}`; modelCard.querySelector('.model-info')?.appendChild(licenseEl); } /** * Theme toggle */ function toggleTheme() { if (typeof setTheme === 'function') { const current = localStorage.getItem('theme') || 'light'; const newTheme = current === 'light' ? 'dark' : 'light'; setTheme(newTheme); } } /** * Update model card with additional info (license, cost) */ function enhanceModelCard(modelCard, model) { // Add license info if (model.license && model.license !== 'Unknown') { displayModelLicense(modelCard, model.license); } // Add cost estimate for selected material const defaultMaterial = localStorage.getItem('defaultMaterial') || 'pla'; // Note: Cost will be added when user interacts or opens calculator } // Auto-load materials on page init document.addEventListener('DOMContentLoaded', () => { // Load materials for the cost calculator fetch(`${API_BASE}/models/config/materials`) .then(res => res.json()) .then(data => { console.log('Available materials:', data.materials); }) .catch(err => console.error('Error loading materials:', err)); }); export { showPrinterSettings, loadPrinters, showCostCalculator, updateCostEstimate, handleAdvancedSearch, applyFilters, toggleTheme };