Spaces:
Running
Running
| <html lang="es"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Calculadora de Costos Textiles</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .input-error { | |
| border-color: #f87171; | |
| background-color: #fee2e2; | |
| } | |
| .result-card { | |
| transition: all 0.3s ease; | |
| } | |
| .result-card:hover { | |
| transform: translateY(-5px); | |
| box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.1); | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8 max-w-4xl"> | |
| <div class="text-center mb-10"> | |
| <h1 class="text-3xl md:text-4xl font-bold text-indigo-700 mb-2">Calculadora Textil</h1> | |
| <p class="text-gray-600">Calcula el costo total de tela y sublimación para tus proyectos</p> | |
| </div> | |
| <div class="bg-white rounded-xl shadow-md p-6 mb-8"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <div> | |
| <label for="fabricPrice" class="block text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-ruler-combined text-indigo-500 mr-2"></i>Precio por m² de tela ($) | |
| </label> | |
| <div class="relative"> | |
| <input type="number" id="fabricPrice" min="0" step="0.01" value="25" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <span class="absolute right-3 top-2.5 text-gray-500">$/m²</span> | |
| </div> | |
| <p id="fabricError" class="text-red-500 text-xs mt-1 hidden">Ingrese un valor válido</p> | |
| </div> | |
| <div> | |
| <label for="sublimationPrice" class="block text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-fire text-indigo-500 mr-2"></i>Precio por m² de sublimación ($) | |
| </label> | |
| <div class="relative"> | |
| <input type="number" id="sublimationPrice" min="0" step="0.01" value="25" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <span class="absolute right-3 top-2.5 text-gray-500">$/m²</span> | |
| </div> | |
| <p id="sublimationError" class="text-red-500 text-xs mt-1 hidden">Ingrese un valor válido</p> | |
| </div> | |
| <div> | |
| <label for="fabricArea" class="block text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-square text-indigo-500 mr-2"></i>Metros cuadrados de tela por unidad | |
| </label> | |
| <div class="relative"> | |
| <input type="number" id="fabricArea" min="0" step="0.01" value="0.80" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <span class="absolute right-3 top-2.5 text-gray-500">m²</span> | |
| </div> | |
| <p id="areaError" class="text-red-500 text-xs mt-1 hidden">Ingrese un valor válido</p> | |
| </div> | |
| <div> | |
| <label for="sublimationArea" class="block text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-square text-indigo-500 mr-2"></i>Metros cuadrados de sublimación por unidad | |
| </label> | |
| <div class="relative"> | |
| <input type="number" id="sublimationArea" min="0" step="0.01" value="0.85" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <span class="absolute right-3 top-2.5 text-gray-500">m²</span> | |
| </div> | |
| <p id="sublimationAreaError" class="text-red-500 text-xs mt-1 hidden">Ingrese un valor válido</p> | |
| </div> | |
| <div class="relative"> | |
| <label for="quantity" class="block text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-boxes text-indigo-500 mr-2"></i>Cantidad de unidades | |
| </label> | |
| <input type="number" id="quantity" min="1" value="10" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <span class="absolute right-3 top-8 text-gray-500">unidades</span> | |
| </div> | |
| <div> | |
| <label for="sewingPrice" class="block text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-needle text-indigo-500 mr-2"></i>Precio de costura por unidad ($) | |
| </label> | |
| <div class="relative"> | |
| <input type="number" id="sewingPrice" min="0" step="0.01" value="12" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <span class="absolute right-3 top-2.5 text-gray-500">$/unidad</span> | |
| </div> | |
| <p id="sewingError" class="text-red-500 text-xs mt-1 hidden">Ingrese un valor válido</p> | |
| </div> | |
| <div> | |
| <label for="orderName" class="block text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-tag text-indigo-500 mr-2"></i>Nombre del pedido | |
| </label> | |
| <input type="text" id="orderName" value="Pedido 1" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| </div> | |
| <div> | |
| <label for="extraCosts" class="block text-sm font-medium text-gray-700 mb-1"> | |
| <i class="fas fa-plus-circle text-indigo-500 mr-2"></i>Costos extras por unidad ($) | |
| </label> | |
| <div class="relative"> | |
| <input type="number" id="extraCosts" min="0" step="0.01" value="0" | |
| class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500"> | |
| <span class="absolute right-3 top-2.5 text-gray-500">$/unidad</span> | |
| </div> | |
| <p id="extraCostsError" class="text-red-500 text-xs mt-1 hidden">Ingrese un valor válido</p> | |
| </div> | |
| </div> | |
| <div class="mt-6 flex justify-center"> | |
| <button id="calculateBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white font-medium py-2 px-6 rounded-lg transition duration-300 flex items-center"> | |
| <i class="fas fa-calculator mr-2"></i> Calcular Costos | |
| </button> | |
| </div> | |
| </div> | |
| <div id="resultsContainer" class="hidden w-full"> | |
| <h2 class="text-xl font-semibold text-gray-800 mb-4 flex items-center"> | |
| <i class="fas fa-chart-line text-indigo-500 mr-2"></i> Resultados | |
| </h2> | |
| <div class="grid grid-cols-1"> | |
| <div class="result-card bg-white rounded-xl shadow-md p-6 border-l-4 border-indigo-500 w-full"> | |
| <h3 class="font-medium text-gray-700 mb-4 flex items-center"> | |
| <i class="fas fa-tshirt text-indigo-500 mr-2"></i> Costo por 1 unidad | |
| </h3> | |
| <div class="space-y-3"> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-600">Costo tela (1 unidad):</span> | |
| <span id="fabricCostSingle" class="font-medium">$0.00</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-600">Costo sublimación (1 unidad):</span> | |
| <span id="sublimationCostSingle" class="font-medium">$0.00</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-600">Costo costura (1 unidad):</span> | |
| <span id="sewingCostSingle" class="font-medium">$0.00</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-600">Costos extras (1 unidad):</span> | |
| <span id="extraCostsSingle" class="font-medium">$0.00</span> | |
| </div> | |
| <div class="border-t border-gray-200 my-2"></div> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-800 font-semibold">Total por 1 unidad:</span> | |
| <span id="totalCostSingle" class="text-indigo-600 font-bold">$0.00</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-600">Costo tela:</span> | |
| <span id="fabricCostQuantity" class="font-medium">$0.00</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-600">Costo sublimación:</span> | |
| <span id="sublimationCostQuantity" class="font-medium">$0.00</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-600">Costos extras:</span> | |
| <span id="extraCostsQuantity" class="font-medium">$0.00</span> | |
| </div> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-600">Costo costura:</span> | |
| <span id="sewingCostQuantity" class="font-medium">$0.00</span> | |
| </div> | |
| <div class="border-t border-gray-200 my-2"></div> | |
| <div class="flex justify-between"> | |
| <span class="text-gray-800 font-semibold">Total por <span id="quantityDisplay">1</span> unidad(es):</span> | |
| <span id="totalCostQuantity" class="text-indigo-600 font-bold text-lg">$0.00</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-6 bg-white rounded-xl shadow-md p-6 w-full"> | |
| <h3 class="font-medium text-gray-700 mb-4 flex items-center"> | |
| <i class="fas fa-history text-indigo-500 mr-2"></i> Historial (últimos 10) | |
| </h3> | |
| <div id="historyList" class="space-y-3"> | |
| <!-- History items will be added here --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| document.addEventListener('DOMContentLoaded', function() { | |
| // Load history from localStorage | |
| let calculationHistory = JSON.parse(localStorage.getItem('textileCalcHistory')) || []; | |
| renderHistory(); | |
| const calculateBtn = document.getElementById('calculateBtn'); | |
| const resultsContainer = document.getElementById('resultsContainer'); | |
| // Input fields | |
| const fabricPriceInput = document.getElementById('fabricPrice'); | |
| const sublimationPriceInput = document.getElementById('sublimationPrice'); | |
| const fabricAreaInput = document.getElementById('fabricArea'); | |
| const sublimationAreaInput = document.getElementById('sublimationArea'); | |
| const sewingPriceInput = document.getElementById('sewingPrice'); | |
| // Error messages | |
| const fabricError = document.getElementById('fabricError'); | |
| const sublimationError = document.getElementById('sublimationError'); | |
| const areaError = document.getElementById('areaError'); | |
| const sublimationAreaError = document.getElementById('sublimationAreaError'); | |
| const sewingError = document.getElementById('sewingError'); | |
| // Result fields | |
| const fabricCostSingle = document.getElementById('fabricCostSingle'); | |
| const sublimationCostSingle = document.getElementById('sublimationCostSingle'); | |
| const totalCostSingle = document.getElementById('totalCostSingle'); | |
| const fabricCostBulk = document.getElementById('fabricCostBulk'); | |
| const sublimationCostBulk = document.getElementById('sublimationCostBulk'); | |
| const totalCostBulk = document.getElementById('totalCostBulk'); | |
| calculateBtn.addEventListener('click', calculateCosts); | |
| // Also calculate when pressing Enter in any input | |
| [fabricPriceInput, sublimationPriceInput, fabricAreaInput, sublimationAreaInput].forEach(input => { | |
| input.addEventListener('keypress', function(e) { | |
| if (e.key === 'Enter') { | |
| calculateCosts(); | |
| } | |
| }); | |
| }); | |
| function calculateCosts() { | |
| // Reset error states | |
| resetErrors(); | |
| // Get values | |
| const fabricPrice = parseFloat(fabricPriceInput.value); | |
| const sublimationPrice = parseFloat(sublimationPriceInput.value); | |
| const fabricArea = parseFloat(fabricAreaInput.value); | |
| const sublimationArea = parseFloat(sublimationAreaInput.value); | |
| const sewingPrice = parseFloat(sewingPriceInput.value); | |
| const extraCosts = parseFloat(document.getElementById('extraCosts').value) || 0; | |
| // Validate inputs | |
| let isValid = true; | |
| if (isNaN(fabricPrice) || fabricPrice < 0) { | |
| fabricError.classList.remove('hidden'); | |
| fabricPriceInput.classList.add('input-error'); | |
| isValid = false; | |
| } | |
| if (isNaN(sublimationPrice) || sublimationPrice < 0) { | |
| sublimationError.classList.remove('hidden'); | |
| sublimationPriceInput.classList.add('input-error'); | |
| isValid = false; | |
| } | |
| if (isNaN(fabricArea) || fabricArea <= 0) { | |
| areaError.classList.remove('hidden'); | |
| fabricAreaInput.classList.add('input-error'); | |
| isValid = false; | |
| } | |
| if (isNaN(sublimationArea) || sublimationArea < 0) { | |
| sublimationAreaError.classList.remove('hidden'); | |
| sublimationAreaInput.classList.add('input-error'); | |
| isValid = false; | |
| } | |
| if (isNaN(sewingPrice) || sewingPrice < 0) { | |
| sewingError.classList.remove('hidden'); | |
| sewingPriceInput.classList.add('input-error'); | |
| isValid = false; | |
| } | |
| if (!isValid) return; | |
| // Calculate costs | |
| const fabricCostPerUnit = fabricPrice * fabricArea; | |
| const sublimationCostPerUnit = sublimationPrice * sublimationArea; | |
| const totalCostPerUnit = fabricCostPerUnit + sublimationCostPerUnit + sewingPrice + extraCosts; | |
| const quantity = parseInt(document.getElementById('quantity').value) || 1; | |
| document.getElementById('quantityDisplay').textContent = quantity; | |
| const fabricCostForQuantity = fabricCostPerUnit * quantity; | |
| const sublimationCostForQuantity = sublimationCostPerUnit * quantity; | |
| const sewingCostForQuantity = sewingPrice * quantity; | |
| const extraCostsForQuantity = extraCosts * quantity; | |
| const totalCostForQuantity = totalCostPerUnit * quantity; | |
| // Update UI with results | |
| fabricCostSingle.textContent = `${fabricCostPerUnit.toFixed(2)}`; | |
| sublimationCostSingle.textContent = `${sublimationCostPerUnit.toFixed(2)}`; | |
| totalCostSingle.textContent = `${totalCostPerUnit.toFixed(2)}`; | |
| document.getElementById('sewingCostSingle').textContent = `${sewingPrice.toFixed(2)}`; | |
| document.getElementById('extraCostsSingle').textContent = `${extraCosts.toFixed(2)}`; | |
| document.getElementById('fabricCostQuantity').textContent = `${fabricCostForQuantity.toFixed(2)}`; | |
| document.getElementById('sublimationCostQuantity').textContent = `${sublimationCostForQuantity.toFixed(2)}`; | |
| document.getElementById('sewingCostQuantity').textContent = `${sewingCostForQuantity.toFixed(2)}`; | |
| document.getElementById('extraCostsQuantity').textContent = `${extraCostsForQuantity.toFixed(2)}`; | |
| document.getElementById('totalCostQuantity').textContent = `${totalCostForQuantity.toFixed(2)}`; | |
| // Add to history | |
| const orderName = document.getElementById('orderName').value || 'Pedido sin nombre'; | |
| const calculation = { | |
| date: new Date().toLocaleString(), | |
| fabricPrice, | |
| sublimationPrice, | |
| fabricArea, | |
| sublimationArea, | |
| sewingPrice, | |
| extraCosts, | |
| quantity, | |
| orderName, | |
| totalCostPerUnit, | |
| totalCostForQuantity | |
| }; | |
| calculationHistory.unshift(calculation); | |
| if (calculationHistory.length > 10) { | |
| calculationHistory = calculationHistory.slice(0, 10); | |
| } | |
| localStorage.setItem('textileCalcHistory', JSON.stringify(calculationHistory)); | |
| renderHistory(); | |
| // Show results | |
| resultsContainer.classList.remove('hidden'); | |
| // Smooth scroll to results | |
| resultsContainer.scrollIntoView({ behavior: 'smooth' }); | |
| } | |
| function renderHistory() { | |
| const historyList = document.getElementById('historyList'); | |
| historyList.innerHTML = ''; | |
| if (calculationHistory.length === 0) { | |
| historyList.innerHTML = '<p class="text-gray-500 text-center py-4">No hay cálculos recientes</p>'; | |
| return; | |
| } | |
| calculationHistory.forEach(item => { | |
| const historyItem = document.createElement('div'); | |
| historyItem.className = 'bg-gray-50 rounded-lg p-3 border border-gray-200'; | |
| historyItem.innerHTML = ` | |
| <div class="flex justify-between items-start"> | |
| <div> | |
| <span class="text-xs text-gray-500">${item.date}</span> | |
| <div class="text-sm mt-1"> | |
| <span class="font-medium">${item.orderName}</span> | |
| <span class="mx-2">•</span> | |
| <span>${item.quantity} unidad(es)</span> | |
| <span class="mx-2">•</span> | |
| <span>${item.fabricArea}m² tela</span> | |
| <span class="mx-2">•</span> | |
| <span>${item.sublimationArea}m² sublim</span> | |
| ${item.extraCosts > 0 ? `<span class="mx-2">•</span><span>Extras: ${item.extraCosts.toFixed(2)}</span>` : ''} | |
| </div> | |
| </div> | |
| <div class="text-right"> | |
| <div class="text-sm">Unidad: <span class="font-medium">${item.totalCostPerUnit.toFixed(2)}</span></div> | |
| <div class="text-sm">Total: <span class="font-medium text-indigo-600">${item.totalCostForQuantity.toFixed(2)}</span></div> | |
| </div> | |
| </div> | |
| `; | |
| historyList.appendChild(historyItem); | |
| }); | |
| } | |
| function resetErrors() { | |
| [fabricError, sublimationError, areaError, sublimationAreaError, sewingError].forEach(error => { | |
| error.classList.add('hidden'); | |
| }); | |
| [fabricPriceInput, sublimationPriceInput, fabricAreaInput, sublimationAreaInput, sewingPriceInput].forEach(input => { | |
| input.classList.remove('input-error'); | |
| }); | |
| } | |
| }); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo2" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=JymNils/calculadora-textil" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p><p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=JymNils/calculadora-textil" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p><p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=JymNils/calculadora-textil2" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |