- Fase A: license templates, search history, cost estimator - Fase B: import URL, bulk ZIP, batch download - Fase C: comparison mode, mesh validation, measurement tool - Fase D: cross-section clipping, overhang heatmap, layer animation - Refactor Pydantic/SQLAlchemy warnings - 24 tests pytest - README actualizado - WebP thumbnails, lazy loading, cache headers
270 lines
19 KiB
HTML
270 lines
19 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="es">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Subir Modelo - STL Repository</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<link rel="stylesheet" href="/static/css/style.css">
|
|
<script>
|
|
tailwind.config = {
|
|
theme: {
|
|
extend: {
|
|
colors: {
|
|
slate: { 850: '#172033', 950: '#020617' }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
</head>
|
|
<body class="bg-slate-950 text-slate-100 min-h-screen">
|
|
<!-- Navbar -->
|
|
<nav class="glass sticky top-0 z-50 border-b border-white/5">
|
|
<div class="max-w-7xl mx-auto px-6 py-4 flex items-center justify-between">
|
|
<a href="/" class="flex items-center gap-3 group">
|
|
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-cyan-500 to-blue-600 flex items-center justify-center text-xl shadow-lg group-hover:scale-110 transition-transform">
|
|
🖨️
|
|
</div>
|
|
<div>
|
|
<h1 class="text-xl font-bold bg-gradient-to-r from-cyan-400 to-blue-400 bg-clip-text text-transparent">STL Repository</h1>
|
|
<p class="text-xs text-slate-400 -mt-0.5">Modelos 3D para imprimir</p>
|
|
</div>
|
|
</a>
|
|
<button id="theme-toggle" class="p-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 border border-white/10 text-slate-400 hover:text-yellow-400 transition-colors mr-2" title="Cambiar tema">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z"></path></svg>
|
|
</button>
|
|
<a href="/" class="px-5 py-2.5 rounded-xl bg-slate-800 hover:bg-slate-700 border border-white/10 text-sm font-medium transition-colors">
|
|
Volver a Galeria
|
|
</a>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Main -->
|
|
<main class="max-w-3xl mx-auto px-6 py-10">
|
|
<div class="mb-8">
|
|
<h2 class="text-3xl font-bold mb-2">Subir nuevo modelo</h2>
|
|
<p class="text-slate-400">Selecciona uno o mas archivos STL/3MF, importa desde URL o sube un ZIP.</p>
|
|
</div>
|
|
|
|
<!-- Tabs -->
|
|
<div class="flex gap-2 mb-6">
|
|
<button id="tab-upload" class="flex-1 px-4 py-3 rounded-xl bg-cyan-500/20 text-cyan-400 border border-cyan-500/30 font-medium text-sm transition-all">
|
|
📁 Archivos
|
|
</button>
|
|
<button id="tab-url" class="flex-1 px-4 py-3 rounded-xl bg-slate-800 border border-white/10 text-slate-400 font-medium text-sm transition-all hover:bg-slate-700">
|
|
🌐 URL
|
|
</button>
|
|
<button id="tab-zip" class="flex-1 px-4 py-3 rounded-xl bg-slate-800 border border-white/10 text-slate-400 font-medium text-sm transition-all hover:bg-slate-700">
|
|
🗜️ ZIP
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Panel: Upload -->
|
|
<div id="panel-upload">
|
|
<!-- 3D Files Drop Zone -->
|
|
<div id="drop-zone" class="border-2 border-dashed border-slate-700 rounded-2xl p-10 text-center cursor-pointer transition-all hover:border-cyan-500/50 hover:bg-slate-900/40 mb-4 group">
|
|
<div class="w-16 h-16 rounded-2xl bg-slate-900 flex items-center justify-center mx-auto mb-3 group-hover:scale-110 transition-transform group-hover:bg-cyan-500/10">
|
|
<svg class="w-8 h-8 text-slate-400 group-hover:text-cyan-400 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M20 7l-8-4-8 4m16 0l-8 4m8-4v10l-8 4m0-10L4 7m8 4v10M4 7v10l8 4"></path></svg>
|
|
</div>
|
|
<h3 class="text-base font-semibold mb-1">Archivos 3D (STL / 3MF)</h3>
|
|
<p class="text-sm text-slate-500 mb-2">Arrastra uno o mas archivos aqui</p>
|
|
<p id="file-name" class="text-cyan-400 font-medium text-sm min-h-[20px]"></p>
|
|
<input type="file" id="file-input" accept=".stl,.3mf" multiple class="hidden">
|
|
</div>
|
|
|
|
<!-- Parts list -->
|
|
<div id="parts-list" class="space-y-2 mb-6"></div>
|
|
|
|
<!-- Images Drop Zone -->
|
|
<div id="images-drop-zone" class="border-2 border-dashed border-slate-700 rounded-2xl p-8 text-center cursor-pointer transition-all hover:border-fuchsia-500/50 hover:bg-slate-900/40 mb-8 group">
|
|
<div class="w-14 h-14 rounded-2xl bg-slate-900 flex items-center justify-center mx-auto mb-3 group-hover:scale-110 transition-transform group-hover:bg-fuchsia-500/10">
|
|
<svg class="w-7 h-7 text-slate-400 group-hover:text-fuchsia-400 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"></path></svg>
|
|
</div>
|
|
<h3 class="text-sm font-semibold mb-1">Imagenes de referencia</h3>
|
|
<p class="text-xs text-slate-500 mb-2">Opcional - JPG o PNG</p>
|
|
<p id="images-name" class="text-fuchsia-400 font-medium text-sm min-h-[20px]"></p>
|
|
<input type="file" id="images-input" accept=".jpg,.jpeg,.png" multiple class="hidden">
|
|
</div>
|
|
|
|
<!-- Form -->
|
|
<form id="upload-form" class="glass rounded-2xl p-8 space-y-5">
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Titulo <span class="text-cyan-500">*</span></label>
|
|
<input type="text" id="title" required placeholder="Ej: Soporte para telefono" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600">
|
|
</div>
|
|
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Descripcion</label>
|
|
<textarea id="description" rows="3" placeholder="Describe el modelo, materiales recomendados, etc." class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600 resize-none"></textarea>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Autor</label>
|
|
<input type="text" id="author" placeholder="Tu nombre o alias" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Licencia</label>
|
|
<select id="license-select" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all cursor-pointer text-slate-300 mb-2">
|
|
<option value="">Seleccionar licencia...</option>
|
|
<option value="CC0 1.0 Universal">CC0 - Dominio Publico</option>
|
|
<option value="CC-BY 4.0">CC-BY 4.0 - Atribucion</option>
|
|
<option value="CC-BY-SA 4.0">CC-BY-SA 4.0 - Compartir Igual</option>
|
|
<option value="CC-BY-NC 4.0">CC-BY-NC 4.0 - No Comercial</option>
|
|
<option value="CC-BY-NC-SA 4.0">CC-BY-NC-SA 4.0 - NC + SA</option>
|
|
<option value="CC-BY-ND 4.0">CC-BY-ND 4.0 - Sin Derivadas</option>
|
|
<option value="GPL-3.0">GPL-3.0</option>
|
|
<option value="MIT">MIT</option>
|
|
<option value="custom">Otra (personalizada)</option>
|
|
</select>
|
|
<input type="text" id="license" placeholder="Ej: Mi licencia personal" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600 hidden">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Categoria</label>
|
|
<select id="category" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all cursor-pointer text-slate-300">
|
|
<option value="">Sin categoria</option>
|
|
<option value="Arte">Arte</option>
|
|
<option value="Herramientas">Herramientas</option>
|
|
<option value="Juguetes">Juguetes</option>
|
|
<option value="Piezas">Piezas</option>
|
|
<option value="Decoracion">Decoracion</option>
|
|
<option value="Otros">Otros</option>
|
|
</select>
|
|
</div>
|
|
<div class="relative">
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Tags</label>
|
|
<input type="text" id="tags" placeholder="robot, articulado, util" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600">
|
|
<div id="tags-suggestions" class="absolute left-0 right-0 top-full mt-1 glass rounded-xl border border-white/10 overflow-hidden z-20 hidden"></div>
|
|
<p class="text-xs text-slate-500 mt-1">Separados por comas. Escribe para ver sugerencias.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pt-4">
|
|
<button type="submit" id="submit-btn" class="w-full md:w-auto px-8 py-3.5 rounded-xl bg-gradient-to-r from-cyan-500 to-blue-600 hover:from-cyan-400 hover:to-blue-500 text-white font-bold shadow-lg shadow-cyan-500/20 transition-all hover:scale-[1.02] disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2">
|
|
<svg id="btn-icon" class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path></svg>
|
|
<span id="btn-text">Subir Modelo</span>
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Panel: URL -->
|
|
<div id="panel-url" class="hidden">
|
|
<form id="url-form" class="glass rounded-2xl p-8 space-y-5">
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">URL del archivo <span class="text-cyan-500">*</span></label>
|
|
<input type="url" id="url-input" required placeholder="https://ejemplo.com/modelo.stl" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600">
|
|
<p class="text-xs text-slate-500 mt-1">Soporta archivos .stl y .3mf directos. Maximo 50MB.</p>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Titulo <span class="text-cyan-500">*</span></label>
|
|
<input type="text" id="url-title" required placeholder="Ej: Soporte para telefono" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Descripcion</label>
|
|
<textarea id="url-description" rows="3" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600 resize-none"></textarea>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Autor</label>
|
|
<input type="text" id="url-author" placeholder="Tu nombre o alias" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Licencia</label>
|
|
<input type="text" id="url-license" placeholder="Ej: CC-BY-SA 4.0" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600">
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Categoria</label>
|
|
<select id="url-category" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all cursor-pointer text-slate-300">
|
|
<option value="">Sin categoria</option>
|
|
<option value="Arte">Arte</option>
|
|
<option value="Herramientas">Herramientas</option>
|
|
<option value="Juguetes">Juguetes</option>
|
|
<option value="Piezas">Piezas</option>
|
|
<option value="Decoracion">Decoracion</option>
|
|
<option value="Otros">Otros</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Tags</label>
|
|
<input type="text" id="url-tags" placeholder="robot, articulado, util" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600">
|
|
</div>
|
|
</div>
|
|
<div class="pt-4">
|
|
<button type="submit" class="w-full md:w-auto px-8 py-3.5 rounded-xl bg-gradient-to-r from-cyan-500 to-blue-600 hover:from-cyan-400 hover:to-blue-500 text-white font-bold shadow-lg shadow-cyan-500/20 transition-all hover:scale-[1.02] flex items-center justify-center gap-2">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path></svg>
|
|
Importar desde URL
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Panel: ZIP -->
|
|
<div id="panel-zip" class="hidden">
|
|
<div id="zip-drop-zone" class="border-2 border-dashed border-slate-700 rounded-2xl p-10 text-center cursor-pointer transition-all hover:border-cyan-500/50 hover:bg-slate-900/40 mb-6 group">
|
|
<div class="w-16 h-16 rounded-2xl bg-slate-900 flex items-center justify-center mx-auto mb-3 group-hover:scale-110 transition-transform group-hover:bg-cyan-500/10">
|
|
<svg class="w-8 h-8 text-slate-400 group-hover:text-cyan-400 transition-colors" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"></path></svg>
|
|
</div>
|
|
<h3 class="text-base font-semibold mb-1">Archivo ZIP</h3>
|
|
<p class="text-sm text-slate-500 mb-2">Arrastra un ZIP con archivos STL/3MF</p>
|
|
<p id="zip-name" class="text-cyan-400 font-medium text-sm min-h-[20px]"></p>
|
|
<input type="file" id="zip-input" accept=".zip" class="hidden">
|
|
</div>
|
|
|
|
<form id="zip-form" class="glass rounded-2xl p-8 space-y-5">
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Descripcion (aplicada a todos)</label>
|
|
<textarea id="zip-description" rows="2" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600 resize-none"></textarea>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Autor</label>
|
|
<input type="text" id="zip-author" placeholder="Tu nombre o alias" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600">
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Licencia</label>
|
|
<input type="text" id="zip-license" placeholder="Ej: CC-BY-SA 4.0" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600">
|
|
</div>
|
|
</div>
|
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-5">
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Categoria</label>
|
|
<select id="zip-category" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all cursor-pointer text-slate-300">
|
|
<option value="">Sin categoria</option>
|
|
<option value="Arte">Arte</option>
|
|
<option value="Herramientas">Herramientas</option>
|
|
<option value="Juguetes">Juguetes</option>
|
|
<option value="Piezas">Piezas</option>
|
|
<option value="Decoracion">Decoracion</option>
|
|
<option value="Otros">Otros</option>
|
|
</select>
|
|
</div>
|
|
<div>
|
|
<label class="block text-sm font-medium text-slate-300 mb-1.5">Tags (aplicados a todos)</label>
|
|
<input type="text" id="zip-tags" placeholder="robot, articulado, util" class="w-full px-4 py-3 rounded-xl bg-slate-900/60 border border-white/10 focus:border-cyan-500 focus:outline-none focus:ring-2 focus:ring-cyan-500/20 transition-all placeholder:text-slate-600">
|
|
</div>
|
|
</div>
|
|
<div class="pt-4">
|
|
<button type="submit" class="w-full md:w-auto px-8 py-3.5 rounded-xl bg-gradient-to-r from-cyan-500 to-blue-600 hover:from-cyan-400 hover:to-blue-500 text-white font-bold shadow-lg shadow-cyan-500/20 transition-all hover:scale-[1.02] flex items-center justify-center gap-2">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"></path></svg>
|
|
Procesar ZIP
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</main>
|
|
|
|
<div id="toast-container"></div>
|
|
|
|
<script src="/static/js/theme.js"></script>
|
|
<script src="/static/js/api.js"></script>
|
|
<script src="/static/js/upload.js"></script>
|
|
</body>
|
|
</html>
|