| | |
| | |
| |
|
| | |
| | var productosSheet = "Productos"; |
| | var pedidosSheet = "Pedidos"; |
| | var productosData = []; |
| | var stlFiles = {}; |
| | var currentColor = "#000000"; |
| | var currentModel = null; |
| |
|
| | |
| | function onOpen() { |
| | var ui = SpreadsheetApp.getUi(); |
| | ui.createMenu('3D Viewer') |
| | .addItem('Agregar al carrito', 'addToCart') |
| | .addSeparator() |
| | .addItem('Limpiar carrito', 'clearCart') |
| | .addToUi(); |
| | |
| | |
| | loadProducts(); |
| | |
| | |
| | init3DViewer(); |
| | } |
| |
|
| | |
| | function loadProducts() { |
| | var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(productosSheet); |
| | if (!sheet) return; |
| | |
| | var data = sheet.getDataRange().getValues(); |
| | productosData = []; |
| | |
| | for (var i = 1; i < data.length; i++) { |
| | if (data[i][0] && data[i][1]) { |
| | productosData.push({ |
| | nombre: data[i][0], |
| | precio: data[i][1], |
| | vida: data[i][2], |
| | inventario: data[i][3], |
| | id: data[i][4] |
| | }); |
| | } |
| | } |
| | |
| | Logger.log('Productos cargados: ' + productosData.length); |
| | } |
| |
|
| | |
| | function init3DViewer() { |
| | |
| | var html = HtmlService.createHtmlOutputFromFile('viewer') |
| | .setWidth(400) |
| | .setHeight(400); |
| | |
| | SpreadsheetApp.getUi().showModelessDialog(html, 'Visor 3D'); |
| | } |
| |
|
| | |
| | function addToCart(productName, quantity, client) { |
| | |
| | var product = productosData.find(p => |
| | p.nombre.toLowerCase() === productName.toLowerCase() |
| | ); |
| | |
| | if (!product) { |
| | SpreadsheetApp.getUi().alert('Producto no encontrado: ' + productName); |
| | return; |
| | } |
| | |
| | if (product.inventario <= 0) { |
| | SpreadsheetApp.getUi().alert('Producto sin inventario: ' + productName); |
| | return; |
| | } |
| | |
| | |
| | var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(pedidosSheet); |
| | if (!sheet) { |
| | SpreadsheetApp.getUi().alert('Hoja de pedidos no encontrada'); |
| | return; |
| | } |
| | |
| | sheet.appendRow([ |
| | product.nombre, |
| | quantity, |
| | client, |
| | product.inventario, |
| | product.id |
| | ]); |
| | |
| | |
| | updateInventory(product.nombre, -quantity); |
| | |
| | SpreadsheetApp.getUi().alert('Producto agregado al carrito: ' + product.nombre); |
| | } |
| |
|
| | |
| | function updateInventory(productName, quantity) { |
| | var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(productosSheet); |
| | if (!sheet) return; |
| | |
| | var data = sheet.getDataRange().getValues(); |
| | |
| | for (var i = 1; i < data.length; i++) { |
| | if (data[i][0] && data[i][0].toLowerCase() === productName.toLowerCase()) { |
| | var currentInv = parseInt(data[i][3]) || 0; |
| | var newInv = currentInv + quantity; |
| | sheet.getRange(i+1, 4).setValue(newInv); |
| | break; |
| | } |
| | } |
| | } |
| |
|
| | |
| | function clearCart() { |
| | var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(pedidosSheet); |
| | if (!sheet) return; |
| | |
| | var lastRow = sheet.getLastRow(); |
| | if (lastRow > 1) { |
| | sheet.deleteRows(2, lastRow - 1); |
| | SpreadsheetApp.getUi().alert('Carrito limpiado'); |
| | } |
| | } |
| |
|
| | |
| | function loadSTLFiles() { |
| | |
| | |
| | stlFiles = { |
| | 'greca': 'data/stl/greca.stl', |
| | 'minimalista': 'data/stl/minimalista.stl', |
| | 'cl谩sico': 'data/stl/cl谩sico.stl', |
| | 'moderno': 'data/stl/moderno.stl' |
| | }; |
| | |
| | Logger.log('STL files cargados: ' + Object.keys(stlFiles).length); |
| | } |
| |
|
| | |
| | function changeColor(color) { |
| | currentColor = color; |
| | |
| | var app = UiApp.getActiveApplication(); |
| | if (app) { |
| | app.getElementById('modelColor').setStyleAttribute('color', color); |
| | } |
| | |
| | SpreadsheetApp.getUi().showModelessDialog( |
| | HtmlService.createHtmlOutput( |
| | '<div id="modelColor" style="color:' + color + '">Color actualizado</div>' |
| | ).setWidth(200).setHeight(100), |
| | 'Color' |
| | ); |
| | } |
| |
|
| | |
| | function loadModel(modelName) { |
| | currentModel = modelName; |
| | |
| | Logger.log('Cargando modelo: ' + modelName); |
| | } |
| |
|
| | |
| | function getViewerHTML() { |
| | return ` |
| | <!DOCTYPE html> |
| | <html> |
| | <head> |
| | <title>Visor 3D Interactivo</title> |
| | <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> |
| | <style> |
| | body { |
| | margin: 0; |
| | padding: 10px; |
| | background: #f0f0f0; |
| | font-family: Arial, sans-serif; |
| | } |
| | #viewer { |
| | width: 100%; |
| | height: 300px; |
| | border: 1px solid #ccc; |
| | background: #ffffff; |
| | } |
| | .color-picker { |
| | display: flex; |
| | gap: 5px; |
| | margin-top: 10px; |
| | } |
| | .color-btn { |
| | width: 30px; |
| | height: 30px; |
| | border: none; |
| | border-radius: 50%; |
| | cursor: pointer; |
| | } |
| | </style> |
| | </head> |
| | <body> |
| | <h3>Visor 3D - Selecciona un modelo</h3> |
| | <div id="viewer"></div> |
| | <div class="color-picker"> |
| | <button class="color-btn" style="background: #000000" onclick="selectColor('#000000')"></button> |
| | <button class="color-btn" style="background: #ff0000" onclick="selectColor('#ff0000')"></button> |
| | <button class="color-btn" style="background: #00ff00" onclick="selectColor('#00ff00')"></button> |
| | <button class="color-btn" style="background: #0000ff" onclick="selectColor('#0000ff')"></button> |
| | <button class="color-btn" style="background: #ffff00" onclick="selectColor('#ffff00')"></button> |
| | <button class="color-btn" style="background: #ff00ff" onclick="selectColor('#ff00ff')"></button> |
| | <button class="color-btn" style="background: #00ffff" onclick="selectColor('#00ffff')"></button> |
| | </div> |
| | |
| | <script> |
| | let scene, camera, renderer, model; |
| | let currentColor = '#000000'; |
| | |
| | function init() { |
| | scene = new THREE.Scene(); |
| | camera = new THREE.PerspectiveCamera(75, 1, 0.1, 1000); |
| | camera.position.z = 5; |
| | |
| | renderer = new THREE.WebGLRenderer({ antialias: true }); |
| | renderer.setSize(380, 280); |
| | document.getElementById('viewer').appendChild(renderer.domElement); |
| | |
| | |
| | const gridHelper = new THREE.GridHelper(10, 10); |
| | scene.add(gridHelper); |
| | |
| | animate(); |
| | } |
| | |
| | function animate() { |
| | requestAnimationFrame(animate); |
| | if (model) { |
| | model.rotation.y += 0.01; |
| | } |
| | renderer.render(scene, camera); |
| | } |
| | |
| | function selectColor(color) { |
| | currentColor = color; |
| | if (model) { |
| | model.traverse((child) => { |
| | if (child.isMesh) { |
| | child.material.color.set(color); |
| | } |
| | }); |
| | } |
| | google.script.run.withSuccessHandler(function() {}).changeColor(color); |
| | } |
| | |
| | function loadModel(modelName) { |
| | |
| | if (model) { |
| | scene.remove(model); |
| | } |
| | |
| | |
| | const geometry = new THREE.BoxGeometry(2, 2, 2); |
| | const material = new THREE.MeshPhongMaterial({ |
| | color: currentColor, |
| | shininess: 100 |
| | }); |
| | model = new THREE.Mesh(geometry, material); |
| | scene.add(model); |
| | |
| | |
| | const ambientLight = new THREE.AmbientLight(0x404040); |
| | scene.add(ambientLight); |
| | |
| | const pointLight = new THREE.PointLight(0xffffff, 1, 100); |
| | pointLight.position.set(10, 10, 10); |
| | scene.add(pointLight); |
| | |
| | google.script.run.withSuccessHandler(function() {}).loadModel(modelName); |
| | } |
| | |
| | |
| | window.onload = function() { |
| | init(); |
| | |
| | loadModel('greca'); |
| | }; |
| | </script> |
| | </body> |
| | </html> |
| | `; |
| | } |
| |
|
| | |
| | function showViewer() { |
| | var html = HtmlService.createHtmlOutput(getViewerHTML()) |
| | .setWidth(420) |
| | .setHeight(450); |
| | |
| | SpreadsheetApp.getUi().showModelessDialog(html, 'Visor 3D'); |
| | } |