codes:registroone
Diferencias
Muestra las diferencias entre dos versiones de la página.
| Ambos lados, revisión anteriorRevisión previaPróxima revisión | Revisión previa | ||
| codes:registroone [2026/02/19 13:23] – ggaita | codes:registroone [2026/02/19 14:28] (actual) – ggaita | ||
|---|---|---|---|
| Línea 1: | Línea 1: | ||
| - | ======Formulario Google | + | ======Formulario Google |
| ------ | ------ | ||
| + | |||
| + | ====Inicios==== | ||
| + | |||
| + | Mediante este formulario, buscamos una adaptacion al googleForm pero más personal... | ||
| + | Se debe crear un nuevo " | ||
| + | |||
| + | |||
| + | ====Modulos==== | ||
| + | |||
| + | La app se resuelve en 5 modulos o archivos, cada uno con la extensión .gs o .html, propias de apps script. | ||
| + | |||
| + | ==server.gs== | ||
| + | |||
| + | < | ||
| + | |||
| + | function doGet(e) { | ||
| + | return HtmlService.createTemplateFromFile(' | ||
| + | .evaluate() | ||
| + | .setTitle(' | ||
| + | } | ||
| + | |||
| + | function include(filename) { | ||
| + | return HtmlService.createHtmlOutputFromFile(filename).getContent(); | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | Este módulo: | ||
| + | |||
| + | * Recibe una petición web. | ||
| + | * Carga index.html. | ||
| + | * Procesa el template. | ||
| + | * Define el título. | ||
| + | * Permite incluir archivos HTML adicionales. | ||
| + | |||
| + | ==index.html== | ||
| + | |||
| + | < | ||
| + | < | ||
| + | <html lang=" | ||
| + | < | ||
| + | <meta charset=" | ||
| + | <meta name=" | ||
| + | < | ||
| + | <link rel=" | ||
| + | <?!= include(' | ||
| + | </ | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | <p>Se ha superado el número de inscriptos en relación a las vacantes.</ | ||
| + | <p>Le invitamos a inscribirse para el < | ||
| + | <form id=" | ||
| + | <div class=" | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | < | ||
| + | <img src=" | ||
| + | < | ||
| + | </ | ||
| + | <div class=" | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | <div class=" | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | <div class=" | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | <div class=" | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | <div class=" | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | <div class=" | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | <div class=" | ||
| + | <label for=" | ||
| + | <select id=" | ||
| + | <option value=""> | ||
| + | <option value=" | ||
| + | <option value=" | ||
| + | </ | ||
| + | </ | ||
| + | | ||
| + | <div id=" | ||
| + | <div class=" | ||
| + | <label for=" | ||
| + | <input type=" | ||
| + | </ | ||
| + | <div class=" | ||
| + | <label for=" | ||
| + | <select id=" | ||
| + | <option value=""> | ||
| + | <option value=" | ||
| + | <option value=" | ||
| + | <option value=" | ||
| + | <option value=" | ||
| + | <option value=" | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | <div class=" | ||
| + | <button type=" | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | <script src=" | ||
| + | <?!= include(' | ||
| + | < | ||
| + | document.getElementById(' | ||
| + | var extras = document.getElementById(' | ||
| + | extras.style.display = this.value === ' | ||
| + | }); | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | Este archivo es la interfaz de usuario de tu Web App, que muestra un formulario dinámico con validación básica y campos condicionales, | ||
| + | |||
| + | ==CSS.html== | ||
| + | |||
| + | < | ||
| + | < | ||
| + | |||
| + | body { | ||
| + | font-family: | ||
| + | margin: 0; | ||
| + | padding: 0; | ||
| + | display: flex; | ||
| + | justify-content: | ||
| + | align-items: | ||
| + | align-items: | ||
| + | min-height: 100vh; | ||
| + | background-color: | ||
| + | color: #b6bed4; | ||
| + | } | ||
| + | |||
| + | .form-container { | ||
| + | width: 90%; | ||
| + | max-width: 600px; | ||
| + | background: #FEFADC; | ||
| + | padding: 20px; | ||
| + | border-radius: | ||
| + | box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | ||
| + | align-items: | ||
| + | box-shadow: -3px 3px 2px rgba(71, 69, 69, 0.269); | ||
| + | display: flex; | ||
| + | flex-direction: | ||
| + | justify-content: | ||
| + | align-content: | ||
| + | margin: 15px 0; | ||
| + | } | ||
| + | |||
| + | img { | ||
| + | width: 250px; | ||
| + | } | ||
| + | |||
| + | h2, | ||
| + | h3, | ||
| + | p { | ||
| + | text-align: center; | ||
| + | } | ||
| + | |||
| + | h2 { | ||
| + | font-size: 22px; | ||
| + | } | ||
| + | |||
| + | h3 { | ||
| + | font-size: 20px; | ||
| + | } | ||
| + | |||
| + | .form-group { | ||
| + | margin-bottom: | ||
| + | align-items: | ||
| + | display: flex; | ||
| + | flex-direction: | ||
| + | } | ||
| + | |||
| + | label { | ||
| + | display: block; | ||
| + | margin-bottom: | ||
| + | font-weight: | ||
| + | font-size: 18px; | ||
| + | } | ||
| + | |||
| + | input { | ||
| + | width: 90%; | ||
| + | padding: 10px; | ||
| + | border: 1px solid #a9a4a4; | ||
| + | border-radius: | ||
| + | font-size: 16px; | ||
| + | } | ||
| + | |||
| + | select { | ||
| + | width: 93%; | ||
| + | padding: 10px; | ||
| + | border: 1px solid #a9a4a4; | ||
| + | border-radius: | ||
| + | font-size: 16px; | ||
| + | } | ||
| + | |||
| + | button { | ||
| + | margin-top: 25px; | ||
| + | width: 90%; | ||
| + | height: 60px; | ||
| + | align-items: | ||
| + | padding: 10px; | ||
| + | background-color: | ||
| + | color: rgb(255, 255, 255); | ||
| + | border: none; | ||
| + | border-radius: | ||
| + | font-size: 30px; | ||
| + | cursor: pointer; | ||
| + | box-shadow: -3px 3px 2px rgba(71, 69, 69, 0.269); | ||
| + | } | ||
| + | |||
| + | button: | ||
| + | background-color: | ||
| + | color: black; | ||
| + | } | ||
| + | |||
| + | @media (max-width: 1024px) { | ||
| + | |||
| + | .form-container { | ||
| + | padding: 15px; | ||
| + | } | ||
| + | |||
| + | input, select { | ||
| + | font-size: 28px; | ||
| + | } | ||
| + | |||
| + | button { | ||
| + | font-size: 30px; | ||
| + | } | ||
| + | h2 { | ||
| + | font-size: 50px; | ||
| + | } | ||
| + | |||
| + | h3 { | ||
| + | font-size: 32px; | ||
| + | } | ||
| + | |||
| + | p { | ||
| + | font-size: 28px; | ||
| + | } | ||
| + | |||
| + | label { | ||
| + | margin-bottom: | ||
| + | font-size: 32px; | ||
| + | } | ||
| + | } | ||
| + | |||
| + | </ | ||
| + | </ | ||
| + | |||
| + | Este modulo o archivo hace el estilo de la web app | ||
| + | |||
| + | ==JS.html== | ||
| + | |||
| + | < | ||
| + | < | ||
| + | document.getElementById(" | ||
| + | event.preventDefault(); | ||
| + | |||
| + | // Mostrar loading | ||
| + | const swalLoading = Swal.fire({ | ||
| + | title: ' | ||
| + | text: ' | ||
| + | icon: ' | ||
| + | showConfirmButton: | ||
| + | allowOutsideClick: | ||
| + | willOpen: () => { | ||
| + | Swal.showLoading(); | ||
| + | } | ||
| + | }); | ||
| + | |||
| + | // Obtener valores | ||
| + | const nombre = document.getElementById(" | ||
| + | const apellido = document.getElementById(" | ||
| + | const telefono = document.getElementById(" | ||
| + | const dni = document.getElementById(" | ||
| + | const email = document.getElementById(" | ||
| + | const localidad = document.getElementById(" | ||
| + | const condicion = document.getElementById(" | ||
| + | const institucion = document.getElementById(" | ||
| + | const nivelEducativo = document.getElementById(" | ||
| + | |||
| + | // Validación básica | ||
| + | if (!nombre || !apellido || !telefono || !dni || !localidad || !condicion || !email) { | ||
| + | swalLoading.close(); | ||
| + | Swal.fire({ | ||
| + | icon: ' | ||
| + | title: ' | ||
| + | text: 'Por favor complete todos los campos.', | ||
| + | confirmButtonText: | ||
| + | }); | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | // Validar DNI (8 números) | ||
| + | if (!/ | ||
| + | swalLoading.close(); | ||
| + | Swal.fire({ | ||
| + | icon: ' | ||
| + | title: ' | ||
| + | text: 'El DNI debe tener 8 dígitos.', | ||
| + | confirmButtonText: | ||
| + | }); | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | // Validar Teléfono (7 a 15 números) | ||
| + | if (!/ | ||
| + | swalLoading.close(); | ||
| + | Swal.fire({ | ||
| + | icon: ' | ||
| + | title: ' | ||
| + | text: 'El teléfono debe tener entre 7 y 15 dígitos.', | ||
| + | confirmButtonText: | ||
| + | }); | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | // Validar Email | ||
| + | const emailPattern = / | ||
| + | if (!emailPattern.test(email)) { | ||
| + | swalLoading.close(); | ||
| + | Swal.fire({ | ||
| + | icon: ' | ||
| + | title: ' | ||
| + | text: 'El formato del correo electrónico es inválido.', | ||
| + | confirmButtonText: | ||
| + | }); | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | // Enviar a Apps Script | ||
| + | google.script.run | ||
| + | .withSuccessHandler(function(response) { | ||
| + | swalLoading.close(); | ||
| + | |||
| + | // 🚫 Cupos completos | ||
| + | if (response === " | ||
| + | Swal.fire({ | ||
| + | icon: ' | ||
| + | title: 'Cupos Completos', | ||
| + | text: 'Se alcanzaron las 400 plazas disponibles. Espere al próximo Semestre.', | ||
| + | confirmButtonText: | ||
| + | confirmButtonColor: | ||
| + | }); | ||
| + | |||
| + | // Desactivar botón | ||
| + | document.getElementById(" | ||
| + | document.querySelector(" | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | // ✅ Registro exitoso | ||
| + | if (response === " | ||
| + | Swal.fire({ | ||
| + | icon: ' | ||
| + | title: ' | ||
| + | text: 'Usted ha sido registrado correctamente.', | ||
| + | confirmButtonText: | ||
| + | }); | ||
| + | |||
| + | document.getElementById(" | ||
| + | document.getElementById(" | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | // ⚠ DNI existente | ||
| + | if (response === " | ||
| + | Swal.fire({ | ||
| + | icon: ' | ||
| + | title: 'DNI ya registrado', | ||
| + | text: 'Este DNI ya se encuentra inscripto.', | ||
| + | confirmButtonText: | ||
| + | }); | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | // ⚠ Email existente | ||
| + | if (response === " | ||
| + | Swal.fire({ | ||
| + | icon: ' | ||
| + | title: 'Email ya registrado', | ||
| + | text: 'Este correo electrónico ya se encuentra inscripto.', | ||
| + | confirmButtonText: | ||
| + | }); | ||
| + | return; | ||
| + | } | ||
| + | |||
| + | // ❓ Respuesta inesperada | ||
| + | Swal.fire({ | ||
| + | icon: ' | ||
| + | title: ' | ||
| + | text: ' | ||
| + | confirmButtonText: | ||
| + | }); | ||
| + | |||
| + | }) | ||
| + | .withFailureHandler(function(error) { | ||
| + | swalLoading.close(); | ||
| + | Swal.fire({ | ||
| + | icon: ' | ||
| + | title: ' | ||
| + | text: 'Hubo un error al procesar su solicitud.', | ||
| + | confirmButtonText: | ||
| + | }); | ||
| + | }) | ||
| + | .registrarConductor( | ||
| + | nombre, | ||
| + | apellido, | ||
| + | telefono, | ||
| + | dni, | ||
| + | email, | ||
| + | localidad, | ||
| + | condicion, | ||
| + | institucion, | ||
| + | nivelEducativo | ||
| + | ); | ||
| + | |||
| + | }); | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | Este modulo: | ||
| + | |||
| + | * Intercepta el envío del formulario. | ||
| + | * Valida los datos en el navegador. | ||
| + | * Muestra un loading profesional. | ||
| + | * Envía los datos al servidor (Apps Script). | ||
| + | * Maneja todas las posibles respuestas del backend. | ||
| + | * Muestra mensajes según el resultado. | ||
| + | |||
| + | Es el controlador frontend que valida datos, se comunica con Apps Script y maneja la lógica de inscripción según la respuesta del servidor. | ||
| + | |||
| + | ==validator.gs== | ||
| + | |||
| + | < | ||
| + | function registrarConductor(nombre, | ||
| + | var ss = SpreadsheetApp.openById(' | ||
| + | var sheet = ss.getSheetByName(' | ||
| + | | ||
| + | var registros = sheet.getDataRange().getValues(); | ||
| + | |||
| + | var MAX_CUPOS = 400; // Puse un cupo, para tener un freno. | ||
| + | |||
| + | if ((registros.length - 1) >= MAX_CUPOS) { | ||
| + | return " | ||
| + | } | ||
| + | |||
| + | // Verificar si el DNI ya existe (columna D - índice 3) | ||
| + | for (var i = 1; i < registros.length; | ||
| + | if (registros[i][3] == dni) { // Columna D (DNI) | ||
| + | return " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Verificar si el Email ya existe (columna F - índice 5) | ||
| + | for (var i = 1; i < registros.length; | ||
| + | if (registros[i][5] == email) { // Columna F (Email) | ||
| + | return " | ||
| + | } | ||
| + | } | ||
| + | |||
| + | // Calcular el siguiente ID (columna A) | ||
| + | var id = registros.length; | ||
| + | |||
| + | // Agregar un nuevo registro (orden correcto según las columnas) | ||
| + | sheet.appendRow([ | ||
| + | id, // A: ID | ||
| + | nombre, | ||
| + | apellido, | ||
| + | dni, // D: DNI | ||
| + | telefono, | ||
| + | email, | ||
| + | localidad, | ||
| + | condicion, | ||
| + | institucion, | ||
| + | nivelEducativo | ||
| + | ]); | ||
| + | |||
| + | return " | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | Esta función es el controlador de negocio que valida cupos, previene duplicados y guarda la inscripción en Google Sheets, devolviendo estados específicos al frontend. | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | [[CoDES: | ||
codes/registroone.1771507425.txt.gz · Última modificación: por ggaita
