<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Calculadora de precios · Manualidades</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600;9..144,700&family=Space+Mono:wght@400;700&family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2" defer></script>
<style>
:root{
--paper: #F3ECDA; --paper-dark: #E7D9BB; --card: #FBF7EC;
--ink: #2B2420; --ink-soft: #6b6152; --thread: #B23A3A;
--teal: #3F6E68; --mustard: #C98A2C; --line: #CBBB98; --danger: #a13d3d;
}
*{box-sizing:border-box;}
html,body{margin:0;padding:0;}
body{
background: var(--paper);
background-image: radial-gradient(var(--paper-dark) 0.7px, transparent 0.7px);
background-size: 14px 14px;
color: var(--ink); font-family:'Inter', sans-serif; -webkit-font-smoothing:antialiased;
min-height:100vh;
}
.wrap{ max-width: 1080px; margin: 0 auto; padding: 32px 20px 60px; }
header{ margin-bottom: 28px; display:flex; justify-content:space-between; align-items:flex-start; gap:16px; flex-wrap:wrap;}
.eyebrow{ font-family:'Space Mono', monospace; font-size:12px; letter-spacing:.14em; text-transform:uppercase; color: var(--thread); display:flex; align-items:center; gap:8px;}
.eyebrow::before{ content:""; width:8px; height:8px; border-radius:50%; background: var(--thread); display:inline-block;}
h1{ font-family:'Fraunces', serif; font-weight:600; font-size: clamp(26px, 4vw, 38px); line-height:1.05; margin:6px 0 8px;}
header p{ color: var(--ink-soft); font-size:14px; max-width:520px; line-height:1.5; margin:0;}
.user-chip{ font-family:'Space Mono', monospace; font-size:12px; color: var(--ink-soft); text-align:right;}
.user-chip button{ display:block; margin-top:6px; background:none; border:1px solid var(--line); color:var(--ink); padding:5px 10px; border-radius:6px; cursor:pointer; font-family:'Inter',sans-serif; font-size:12px;}
.user-chip button:hover{ background: var(--paper-dark); }
/* ---- AUTH ---- */
.auth-wrap{ max-width: 420px; margin: 60px auto; }
.auth-card{
background: var(--card); border:1px solid var(--line); border-radius:10px;
padding: 30px 28px; position:relative; box-shadow: 0 8px 24px rgba(43,36,32,0.10);
}
.auth-card .tag-hole{ position:absolute; top:16px; right:20px; width:14px; height:14px; border-radius:50%; border:3px solid var(--ink); background: var(--paper);}
.auth-title{ font-family:'Fraunces', serif; font-weight:700; font-size:24px; margin: 0 0 4px;}
.auth-sub{ font-size:13px; color: var(--ink-soft); margin: 0 0 20px;}
.field{ margin-bottom:14px; }
label{ font-size:13px; color: var(--ink-soft); display:block; margin-bottom:4px; }
input[type="text"], input[type="number"], input[type="email"], input[type="password"]{
width:100%; font-family:'Space Mono', monospace; font-size:14px; padding:10px 11px;
border:1px solid var(--line); border-radius:6px; background:#fff; color: var(--ink);
}
input:focus-visible{ outline:2px solid var(--teal); outline-offset:1px; }
.btn{ font-family:'Inter', sans-serif; font-weight:600; font-size:14px; padding:11px 16px; border-radius:6px; border:none; cursor:pointer; width:100%;}
.btn-primary{ background: var(--ink); color: var(--paper); }
.btn-primary:hover{ background:#171310; }
.btn-primary:disabled{ opacity:.6; cursor:default; }
.auth-toggle{ text-align:center; margin-top:16px; font-size:13px; color: var(--ink-soft); }
.auth-toggle a{ color: var(--teal); cursor:pointer; font-weight:600; text-decoration:none; }
.msg{ font-family:'Space Mono', monospace; font-size:12.5px; padding:9px 10px; border-radius:6px; margin-bottom:14px; }
.msg.error{ background:#f6e6e6; border:1px solid #e3bcbc; color: var(--danger); }
.msg.ok{ background: #e7efe9; border:1px solid #bcd6c4; color: var(--teal); }
/* ---- INACTIVE ---- */
.inactive-wrap{ max-width: 460px; margin: 100px auto; text-align:center; }
.inactive-wrap h2{ font-family:'Fraunces', serif; font-size:22px; margin-bottom:8px;}
.inactive-wrap p{ color: var(--ink-soft); font-size:14px; line-height:1.5; }
/* ---- APP LAYOUT (calculadora) ---- */
.grid{ display:grid; grid-template-columns: 1.15fr 0.85fr; gap:28px; align-items:start; }
@media (max-width: 860px){ .grid{ grid-template-columns: 1fr; } }
.card{ background: var(--card); border:1px solid var(--line); border-radius:10px; padding:20px 22px; margin-bottom:20px; box-shadow: 0 1px 0 rgba(43,36,32,0.03); }
.card h2{ font-family:'Fraunces', serif; font-size:17px; font-weight:600; margin:0 0 14px; display:flex; align-items:center; gap:8px; }
.card h2 .num{ font-family:'Space Mono', monospace; font-size:12px; color: var(--card); background: var(--ink); width:20px; height:20px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center; }
.row2{ display:grid; grid-template-columns:1fr 1fr; gap:12px; }
.material-row{ display:grid; grid-template-columns:1fr 130px 34px; gap:8px; margin-bottom:8px; align-items:center; }
.material-row .del{ width:34px; height:34px; border-radius:6px; border:1px solid var(--line); background:#fff; color: var(--danger); cursor:pointer; font-size:16px; line-height:1; }
.material-row .del:hover{ background:#f5e6e6; }
.add-btn{ font-family:'Space Mono', monospace; font-size:13px; background:none; border:1px dashed var(--ink-soft); color: var(--ink); padding:8px 12px; border-radius:6px; cursor:pointer; margin-top:4px; width:auto;}
.add-btn:hover{ background: var(--paper-dark); }
.subtotal-line{ display:flex; justify-content:space-between; font-family:'Space Mono', monospace; font-size:13px; color: var(--ink-soft); border-top:1px dashed var(--line); padding-top:10px; margin-top:6px; }
.margen-wrap{ display:flex; align-items:center; gap:14px; }
input[type="range"]{ flex:1; accent-color: var(--thread); }
.margen-val{ font-family:'Space Mono', monospace; font-weight:700; font-size:18px; color: var(--thread); min-width:52px; text-align:right; }
.ticket-outer{ position: sticky; top:20px; }
.ticket{ background: var(--card); border:1px solid var(--line); border-radius:2px; position:relative; padding:26px 22px 22px; box-shadow:0 8px 24px rgba(43,36,32,0.10); }
.ticket::before, .ticket::after{ content:""; position:absolute; left:0; right:0; height:11px; background: linear-gradient(-45deg, var(--paper) 6px, transparent 0) 0 6px/12px 12px repeat-x, linear-gradient(45deg, var(--paper) 6px, transparent 0) 0 6px/12px 12px repeat-x; }
.ticket::before{ top:-10px; }
.ticket::after{ bottom:-10px; transform: rotate(180deg); }
.tag-hole{ position:absolute; top:16px; right:20px; width:16px; height:16px; border-radius:50%; border:3px solid var(--ink); background: var(--paper); }
.ticket-eyebrow{ font-family:'Space Mono', monospace; font-size:11px; letter-spacing:.12em; text-transform:uppercase; color: var(--ink-soft); }
.ticket-title{ font-family:'Fraunces', serif; font-size:22px; font-weight:700; margin:2px 0 14px; padding-right:30px; line-height:1.15; }
.tline{ display:flex; justify-content:space-between; font-family:'Space Mono', monospace; font-size:12.5px; padding:3px 0; color: var(--ink); }
.tline .l{ color: var(--ink-soft); padding-right:8px; }
.divider{ border-top:1px dashed var(--line); margin:10px 0; }
.divider.thick{ border-top:2px solid var(--ink); }
.price-block{ text-align:center; margin:16px 0 6px; padding:14px 10px; background: var(--paper-dark); border-radius:6px; }
.price-block .lbl{ font-family:'Space Mono', monospace; font-size:11px; letter-spacing:.1em; text-transform:uppercase; color: var(--ink-soft); }
.price-block .price{ font-family:'Fraunces', serif; font-weight:700; font-size:34px; color: var(--thread); margin-top:2px; }
.ganancia-row{ display:flex; justify-content:space-between; font-family:'Space Mono', monospace; font-size:13px; margin-top:10px; color: var(--teal); font-weight:700; }
.warn{ font-family:'Space Mono', monospace; font-size:11.5px; color: var(--danger); background:#f6e6e6; border:1px solid #e3bcbc; border-radius:6px; padding:8px 10px; margin-top:12px; }
.save-bar{ display:flex; gap:8px; margin-top:16px; }
.save-bar .btn{ width:auto; }
.btn-ghost{ background:transparent; border:1px solid var(--line); color: var(--ink); }
.btn-ghost:hover{ background: var(--paper-dark); }
.saved-list{ margin-top:14px; }
.saved-item{ display:flex; justify-content:space-between; align-items:center; font-family:'Space Mono', monospace; font-size:12px; padding:7px 0; border-bottom:1px dashed var(--line); }
.saved-item button{ background:none; border:none; cursor:pointer; font-family:'Inter',sans-serif; font-size:12px; color: var(--teal); font-weight:600; margin-left:8px; }
.saved-item .del-saved{ color: var(--danger); }
.empty-note{ font-size:12px; color: var(--ink-soft); font-family:'Space Mono',monospace; }
.note{ font-size:11.5px; color: var(--ink-soft); margin-top:10px; line-height:1.4; }
.hidden{ display:none !important; }
</style>
</head>
<body>
<!-- ============ PANTALLA: CONFIG FALTANTE ============ -->
<div id="configScreen" class="wrap hidden">
<div class="auth-wrap">
<div class="auth-card">
<h2 class="auth-title">Falta configurar Supabase</h2>
<p class="auth-sub">Abre este archivo y reemplaza <code>SUPABASE_URL</code> y <code>SUPABASE_ANON_KEY</code> al inicio del script por los valores de tu proyecto (Supabase > Project Settings > API). Instrucciones completas en README.md.</p>
</div>
</div>
</div>
<!-- ============ PANTALLA: LOGIN / REGISTRO ============ -->
<div id="authScreen" class="wrap hidden">
<div class="auth-wrap">
<div class="auth-card">
<div class="tag-hole"></div>
<h1 class="auth-title" id="authTitle">Ingresa a tu cuenta</h1>
<p class="auth-sub" id="authSub">Calculadora de precios para manualidades</p>
<div id="authMsg"></div>
<div class="field" id="nombreNegocioField" style="display:none;">
<label for="nombreNegocio">Nombre de tu emprendimiento</label>
<input type="text" id="nombreNegocio" placeholder="Ej: Detalles con Amor">
</div>
<div class="field">
<label for="email">Correo</label>
<input type="email" id="email" placeholder="tucorreo@ejemplo.com">
</div>
<div class="field">
<label for="password">Contraseña</label>
<input type="password" id="password" placeholder="Mínimo 6 caracteres">
</div>
<button class="btn btn-primary" id="authBtn" onclick="submitAuth()">Ingresar</button>
<div class="auth-toggle">
<span id="toggleText">¿No tienes cuenta?</span> <a onclick="toggleAuthMode()" id="toggleLink">Regístrate</a>
</div>
</div>
</div>
</div>
<!-- ============ PANTALLA: CUENTA INACTIVA ============ -->
<div id="inactiveScreen" class="wrap hidden">
<div class="inactive-wrap">
<h2>Tu acceso está inactivo</h2>
<p>Contacta a quien te vendió esta herramienta para reactivar tu cuenta.</p>
<button class="btn btn-ghost" style="width:auto; margin-top:16px;" onclick="cerrarSesion()">Cerrar sesión</button>
</div>
</div>
<!-- ============ PANTALLA: APP (CALCULADORA) ============ -->
<div id="appScreen" class="wrap hidden">
<header>
<div>
<div class="eyebrow">Taller de precios</div>
<h1>¿En cuánto sale y en cuánto se vende?</h1>
<p>Registra lo que gastas y lo que inviertes en tu manualidad y obtén un precio de venta que sí te deje ganancia.</p>
</div>
<div class="user-chip">
<span id="userEmailChip"></span>
<button onclick="cerrarSesion()">Cerrar sesión</button>
</div>
</header>
<div class="grid">
<div>
<div class="card">
<h2><span class="num">1</span>Tu producto</h2>
<div class="row2">
<div class="field"><label for="prodName">Nombre del producto</label><input type="text" id="prodName" value="Desayuno sorpresa" oninput="calcular()"></div>
<div class="field"><label for="cantidad">Unidades en este lote</label><input type="number" id="cantidad" min="1" value="1" oninput="calcular()"></div>
</div>
</div>
<div class="card">
<h2><span class="num">2</span>Materiales e insumos</h2>
<div id="materialesList"></div>
<button class="add-btn" onclick="agregarMaterial('', 0)">+ Agregar material</button>
<div class="subtotal-line"><span>Subtotal materiales</span><span id="subtotalMateriales">$0</span></div>
</div>
<div class="card">
<h2><span class="num">3</span>Tu mano de obra</h2>
<div class="row2">
<div class="field"><label for="horas">Horas invertidas</label><input type="number" id="horas" min="0" step="0.25" value="1.5" oninput="calcular()"></div>
<div class="field"><label for="valorHora">Valor de tu hora (COP)</label><input type="number" id="valorHora" min="0" step="1000" value="10000" oninput="calcular()"></div>
</div>
<div class="subtotal-line"><span>Subtotal mano de obra</span><span id="subtotalManoObra">$0</span></div>
</div>
<div class="card">
<h2><span class="num">4</span>Costos adicionales</h2>
<div class="row2">
<div class="field"><label for="empaque">Empaque y decoración (COP)</label><input type="number" id="empaque" min="0" step="500" value="2000" oninput="calcular()"></div>
<div class="field"><label for="transporte">Transporte / domicilio (COP)</label><input type="number" id="transporte" min="0" step="500" value="0" oninput="calcular()"></div>
</div>
<div class="field"><label for="indirectos">Costos indirectos por pedido (COP)</label><input type="number" id="indirectos" min="0" step="500" value="2000" oninput="calcular()"></div>
</div>
<div class="card">
<h2><span class="num">5</span>Margen de ganancia deseado</h2>
<div class="margen-wrap">
<input type="range" id="margen" min="0" max="80" value="35" oninput="calcular()">
<div class="margen-val" id="margenVal">35%</div>
</div>
<p class="note">El margen es el % del precio de venta que queda como ganancia. Entre 30% y 45% es un rango típico para manualidades y detalles por encargo.</p>
</div>
</div>
<div class="ticket-outer">
<div class="ticket">
<div class="tag-hole"></div>
<div class="ticket-eyebrow">Ticket de venta</div>
<div class="ticket-title" id="ticketTitle">Desayuno sorpresa</div>
<div id="ticketMateriales"></div>
<div class="tline"><span class="l">Mano de obra</span><span id="tManoObra">$0</span></div>
<div class="tline"><span class="l">Empaque / decoración</span><span id="tEmpaque">$0</span></div>
<div class="tline"><span class="l">Transporte</span><span id="tTransporte">$0</span></div>
<div class="tline"><span class="l">Costos indirectos</span><span id="tIndirectos">$0</span></div>
<div class="divider thick"></div>
<div class="tline" style="font-weight:700;"><span class="l">COSTO TOTAL POR UNIDAD</span><span id="tCostoTotal">$0</span></div>
<div class="tline"><span class="l">Margen aplicado</span><span id="tMargen">35%</span></div>
<div class="price-block"><div class="lbl">Precio de venta sugerido</div><div class="price" id="tPrecioVenta">$0</div></div>
<div class="ganancia-row"><span>Ganancia por unidad</span><span id="tGananciaUnidad">$0</span></div>
<div id="loteBlock" class="hidden">
<div class="divider"></div>
<div class="tline"><span class="l">Ingreso total del lote</span><span id="tIngresoLote">$0</span></div>
<div class="ganancia-row"><span>Ganancia total del lote</span><span id="tGananciaLote">$0</span></div>
</div>
<div id="warnBox"></div>
<div class="save-bar">
<button class="btn btn-primary" onclick="guardarProducto()">Guardar este cálculo</button>
</div>
<div class="saved-list" id="savedList"></div>
</div>
</div>
</div>
</div>
<script>
// ======================================================
// 1) CONFIGURA AQUÍ TU PROYECTO DE SUPABASE
// (Supabase > Project Settings > API)
// ======================================================
const SUPABASE_URL = "sb_publishable_atW95iG3Iour2zwNL8Z0Dw_7tR7v_Uz";
const SUPABASE_ANON_KEY = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImlwZ2pxcXBid2Vkemhrbmp5Z3lyIiwicm9sZSI6ImFub24iLCJpYXQiOjE3ODU0MzMxMzgsImV4cCI6MjEwMTAwOTEzOH0.RM4bIYyMh8-t78bjFcMLZgzAnPuPWtMW8WIgeF34Zfc";
// ======================================================
let supabase;
let currentUser = null;
let authMode = 'login'; // o 'signup'
function showScreen(id){
['configScreen','authScreen','inactiveScreen','appScreen'].forEach(s=>{
document.getElementById(s).classList.toggle('hidden', s !== id);
});
}
function init(){
if(!SUPABASE_URL || SUPABASE_URL.includes('TU_SUPABASE') || !SUPABASE_ANON_KEY || SUPABASE_ANON_KEY.includes('TU_SUPABASE')){
showScreen('configScreen');
return;
}
if(!window.supabase){
document.getElementById('configScreen').innerHTML = '<div class="auth-wrap"><div class="auth-card"><h2 class="auth-title">No se pudo cargar Supabase</h2><p class="auth-sub">Revisa tu conexión a internet y recarga la página. Esta app necesita conexión para el login.</p></div></div>';
showScreen('configScreen');
return;
}
supabase = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
showScreen('authScreen');
checkSession();
supabase.auth.onAuthStateChange((_event, session) => {
if(session && session.user){ handleLoggedIn(session.user); }
});
}
async function checkSession(){
const { data } = await supabase.auth.getSession();
if(data && data.session && data.session.user){
handleLoggedIn(data.session.user);
}
}
async function handleLoggedIn(user){
currentUser = user;
const { data: perfil, error } = await supabase.from('perfiles').select('activo, nombre_negocio').eq('id', user.id).single();
if(error || !perfil){
showScreen('authScreen');
return;
}
if(perfil.activo === false){
showScreen('inactiveScreen');
return;
}
document.getElementById('userEmailChip').textContent = perfil.nombre_negocio ? perfil.nombre_negocio : user.email;
showScreen('appScreen');
await renderGuardados();
}
function toggleAuthMode(){
authMode = authMode === 'login' ? 'signup' : 'login';
document.getElementById('authMsg').innerHTML = '';
if(authMode === 'signup'){
document.getElementById('authTitle').textContent = 'Crea tu cuenta';
document.getElementById('authBtn').textContent = 'Registrarme';
document.getElementById('toggleText').textContent = '¿Ya tienes cuenta?';
document.getElementById('toggleLink').textContent = 'Inicia sesión';
document.getElementById('nombreNegocioField').style.display = 'block';
} else {
document.getElementById('authTitle').textContent = 'Ingresa a tu cuenta';
document.getElementById('authBtn').textContent = 'Ingresar';
document.getElementById('toggleText').textContent = '¿No tienes cuenta?';
document.getElementById('toggleLink').textContent = 'Regístrate';
document.getElementById('nombreNegocioField').style.display = 'none';
}
}
async function submitAuth(){
const email = document.getElementById('email').value.trim();
const password = document.getElementById('password').value;
const msgBox = document.getElementById('authMsg');
const btn = document.getElementById('authBtn');
msgBox.innerHTML = '';
if(!email || !password){
msgBox.innerHTML = '<div class="msg error">Completa correo y contraseña.</div>';
return;
}
btn.disabled = true;
try{
if(authMode === 'signup'){
const nombreNegocio = document.getElementById('nombreNegocio').value.trim();
const { error } = await supabase.auth.signUp({
email, password,
options: { data: { nombre_negocio: nombreNegocio } }
});
if(error){ msgBox.innerHTML = '<div class="msg error">'+error.message+'</div>'; }
else{ msgBox.innerHTML = '<div class="msg ok">Cuenta creada. Si tu proyecto pide confirmación por correo, revisa tu bandeja antes de ingresar.</div>'; }
} else {
const { error } = await supabase.auth.signInWithPassword({ email, password });
if(error){ msgBox.innerHTML = '<div class="msg error">'+error.message+'</div>'; }
}
} catch(e){
msgBox.innerHTML = '<div class="msg error">Ocurrió un error. Intenta de nuevo.</div>';
}
btn.disabled = false;
}
async function cerrarSesion(){
await supabase.auth.signOut();
currentUser = null;
showScreen('authScreen');
}
// ================= CALCULADORA =================
let materiales = [];
let matIdCounter = 0;
function agregarMaterial(nombre, costo){
const id = 'm' + (matIdCounter++);
materiales.push({id, nombre: nombre || '', costo: costo || 0});
renderMateriales();
calcular();
}
function eliminarMaterial(id){
materiales = materiales.filter(m => m.id !== id);
renderMateriales();
calcular();
}
function renderMateriales(){
const list = document.getElementById('materialesList');
list.innerHTML = '';
materiales.forEach(m => {
const row = document.createElement('div');
row.className = 'material-row';
row.innerHTML = `
<input type="text" placeholder="Ej: cinta, flores, chocolates" value="${escapeAttr(m.nombre)}" oninput="actualizarMaterial('${m.id}','nombre',this.value)">
<input type="number" min="0" step="500" value="${m.costo}" oninput="actualizarMaterial('${m.id}','costo',this.value)">
<button class="del" onclick="eliminarMaterial('${m.id}')" aria-label="Eliminar material">×</button>
`;
list.appendChild(row);
});
}
function actualizarMaterial(id, campo, valor){
const m = materiales.find(x => x.id === id);
if(!m) return;
m[campo] = campo === 'costo' ? (parseFloat(valor) || 0) : valor;
calcular();
}
function escapeAttr(s){ return String(s).replace(/&/g,'&').replace(/"/g,'"').replace(/</g,'<'); }
function fmt(n){ return '$' + Math.round(n).toLocaleString('es-CO'); }
function calcular(){
const nombre = document.getElementById('prodName').value || 'Producto';
const cantidad = Math.max(1, parseFloat(document.getElementById('cantidad').value) || 1);
const horas = parseFloat(document.getElementById('horas').value) || 0;
const valorHora = parseFloat(document.getElementById('valorHora').value) || 0;
const empaque = parseFloat(document.getElementById('empaque').value) || 0;
const transporte = parseFloat(document.getElementById('transporte').value) || 0;
const indirectos = parseFloat(document.getElementById('indirectos').value) || 0;
const margen = parseFloat(document.getElementById('margen').value) || 0;
document.getElementById('margenVal').textContent = margen + '%';
document.getElementById('ticketTitle').textContent = nombre;
const subtotalMateriales = materiales.reduce((s,m) => s + (m.costo || 0), 0);
const subtotalManoObra = horas * valorHora;
document.getElementById('subtotalMateriales').textContent = fmt(subtotalMateriales);
document.getElementById('subtotalManoObra').textContent = fmt(subtotalManoObra);
const costoTotal = subtotalMateriales + subtotalManoObra + empaque + transporte + indirectos;
let precioVenta;
const warnBox = document.getElementById('warnBox');
if(margen >= 100){
precioVenta = costoTotal;
warnBox.innerHTML = '<div class="warn">El margen no puede ser 100% o más. Ajusta el porcentaje.</div>';
} else {
precioVenta = costoTotal / (1 - margen/100);
warnBox.innerHTML = '';
}
const gananciaUnidad = precioVenta - costoTotal;
const ingresoLote = precioVenta * cantidad;
const gananciaLote = gananciaUnidad * cantidad;
const ticketMat = document.getElementById('ticketMateriales');
ticketMat.innerHTML = '';
materiales.forEach(m => {
const line = document.createElement('div');
line.className = 'tline';
line.innerHTML = `<span class="l">${escapeAttr(m.nombre || 'Material sin nombre')}</span><span>${fmt(m.costo)}</span>`;
ticketMat.appendChild(line);
});
document.getElementById('tManoObra').textContent = fmt(subtotalManoObra);
document.getElementById('tEmpaque').textContent = fmt(empaque);
document.getElementById('tTransporte').textContent = fmt(transporte);
document.getElementById('tIndirectos').textContent = fmt(indirectos);
document.getElementById('tCostoTotal').textContent = fmt(costoTotal);
document.getElementById('tMargen').textContent = margen + '%';
document.getElementById('tPrecioVenta').textContent = fmt(precioVenta);
document.getElementById('tGananciaUnidad').textContent = fmt(gananciaUnidad);
const loteBlock = document.getElementById('loteBlock');
if(cantidad > 1){
loteBlock.classList.remove('hidden');
document.getElementById('tIngresoLote').textContent = fmt(ingresoLote);
document.getElementById('tGananciaLote').textContent = fmt(gananciaLote);
} else {
loteBlock.classList.add('hidden');
}
}
// ---- Guardado en Supabase (por usuario autenticado) ----
async function guardarProducto(){
if(!currentUser) return;
const nombre = document.getElementById('prodName').value || 'Producto sin nombre';
const precioTxt = document.getElementById('tPrecioVenta').textContent;
try{
const { error } = await supabase.from('calculos').insert({
user_id: currentUser.id,
nombre,
precio: precioTxt,
datos: { materiales, cantidad: document.getElementById('cantidad').value }
});
if(error){ console.error(error); return; }
await renderGuardados();
} catch(e){ console.error(e); }
}
async function eliminarGuardado(id){
try{
await supabase.from('calculos').delete().eq('id', id).eq('user_id', currentUser.id);
await renderGuardados();
} catch(e){ console.error(e); }
}
async function renderGuardados(){
const el = document.getElementById('savedList');
if(!currentUser){ el.innerHTML = ''; return; }
try{
const { data, error } = await supabase.from('calculos').select('*').eq('user_id', currentUser.id).order('creado_en', { ascending:false }).limit(30);
if(error || !data || data.length === 0){
el.innerHTML = '<div class="empty-note">Aún no has guardado ningún cálculo.</div>';
return;
}
el.innerHTML = data.map(p => `
<div class="saved-item">
<span>${escapeAttr(p.nombre)} · ${p.precio}</span>
<span><button class="del-saved" onclick="eliminarGuardado('${p.id}')">eliminar</button></span>
</div>
`).join('');
} catch(e){
el.innerHTML = '<div class="empty-note">Aún no has guardado ningún cálculo.</div>';
}
}
// Init con ejemplo de "desayuno sorpresa"
agregarMaterial('Base o canasta', 8000);
agregarMaterial('Frutas y snacks', 15000);
agregarMaterial('Flores', 6000);
agregarMaterial('Chocolates o dulces', 5000);
agregarMaterial('Cinta y decoración', 3000);
calcular();
window.addEventListener('load', init);
</script>
</body>
</html>