¡Esta es una revisión vieja del documento!
Tabla de Contenidos
Formulario Google Apps Script - Monocurso
Inicios
Mediante este formulario, buscamos una adaptacion al googleForm pero más personal… Se debe crear un nuevo “sheet”, y desde allí acceder a la pestaña extensiones y seleccionar Apps Script.
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('index')
.evaluate()
.setTitle('Agrega aquí el titulo de tu app');
}
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>EJEMPLO DE TITULO</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css">
<?!= include('CSS'); ?>
</head>
<body>
<main>
<!--<div class="form-container">
<h3>Gracias por Comunicarse</h3>
<p>Se ha superado el número de inscriptos en relación a las vacantes.</p>
<p>Le invitamos a inscribirse para el <strong>Segundo Sementre 2025.</strong></p> -->
<form id="registroForm">
<div class="form-group">
<h2>Curso para Docentes: </h2>
<h2>PRE-INSCRIPCIÓN AL CURSO</h2>
<h2>h2</h2>
<h3>h3</h3>
<img src="imagen que quieras" alt="Logo">
<p><strong>Importante:</strong> "Texto que quieras".</p>
</div>
<div class="form-group">
<label for="nombre">Nombres</label>
<input type="text" id="nombre" name="nombre" placeholder="Ingrese su nombre" required>
</div>
<div class="form-group">
<label for="apellido">Apellidos</label>
<input type="text" id="apellido" name="apellido" placeholder="Ingrese su apellido" required>
</div>
<div class="form-group">
<label for="dni">Documento</label>
<input type="text" id="dni" name="dni" placeholder="Ingrese su DNI" maxlength="8" required>
</div>
<div class="form-group">
<label for="telefono">Teléfono</label>
<input type="text" id="telefono" name="telefono" placeholder="Ingrese su teléfono" required>
</div>
<div class="form-group">
<label for="email">Email</label>
<input type="email" id="email" name="email" placeholder="Ingrese su email" required>
</div>
<div class="form-group">
<label for="localidad">Localidad</label>
<input type="text" id="localidad" name="localidad" placeholder="Ingrese su localidad" required>
</div>
<div class="form-group">
<label for="condicion">Condición</label> //Un Select
<select id="condicion" name="condicion" required>
<option value="">Seleccione...</option>
<option value="Estudiante">Estudiante</option>
<option value="Docente">Docente</option>
</select>
</div>
<div id="docenteExtras" style="display: none;">
<div class="form-group">
<label for="institucion">Institución</label>
<input type="text" id="institucion" name="institucion" placeholder="Ingrese la institución">
</div>
<div class="form-group">
<label for="nivelEducativo">Nivel Educativo</label>
<select id="nivelEducativo" name="nivelEducativo">
<option value="">Seleccione...</option>
<option value="Inicial">Inicial</option>
<option value="Primario">Primario</option>
<option value="Secundario">Secundario</option>
<option value="Superior">Superior</option>
<option value="Otro">Otro</option>
</select>
</div>
</div>
<div class="form-group">
<button type="submit">Enviar</button>
</div>
</form>
</div>
</main>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.js"></script>
<?!= include('JS'); ?>
<script>
document.getElementById('condicion').addEventListener('change', function() {
var extras = document.getElementById('docenteExtras');
extras.style.display = this.value === 'Docente' ? 'block' : 'none';
});
</script>
</body>
</html>
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, usando modularización de HTML, CSS y JS dentro de Apps Script.
CSS.html
<style>
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
align-items: center;
min-height: 100vh;
background-color: #2a395e;
color: #b6bed4;
}
.form-container {
width: 90%;
max-width: 600px;
background: #FEFADC;
padding: 20px;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
align-items: center;
box-shadow: -3px 3px 2px rgba(71, 69, 69, 0.269);
display: flex;
flex-direction: column;
justify-content: center;
align-content: center;
margin: 15px 0;
}
img {
width: 250px;
}
h2,
h3,
p {
text-align: center;
}
h2 {
font-size: 22px;
}
h3 {
font-size: 20px;
}
.form-group {
margin-bottom: 15px;
align-items: center;
display: flex;
flex-direction: column;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
font-size: 18px;
}
input {
width: 90%;
padding: 10px;
border: 1px solid #a9a4a4;
border-radius: 4px;
font-size: 16px;
}
select {
width: 93%;
padding: 10px;
border: 1px solid #a9a4a4;
border-radius: 4px;
font-size: 16px;
}
button {
margin-top: 25px;
width: 90%;
height: 60px;
align-items: center;
padding: 10px;
background-color: #59bd68;
color: rgb(255, 255, 255);
border: none;
border-radius: 4px;
font-size: 30px;
cursor: pointer;
box-shadow: -3px 3px 2px rgba(71, 69, 69, 0.269);
}
button:hover {
background-color: #06d425;
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: 10px;
font-size: 32px;
}
}
</style>
Este modulo o archivo hace el estilo de la web app
JS.html
<script>
document.getElementById("registroForm").addEventListener("submit", function(event) {
event.preventDefault();
// Mostrar loading
const swalLoading = Swal.fire({
title: 'Enviando datos...',
text: 'Estamos procesando su solicitud. Por favor espere.',
icon: 'info',
showConfirmButton: false,
allowOutsideClick: false,
willOpen: () => {
Swal.showLoading();
}
});
// Obtener valores
const nombre = document.getElementById("nombre").value.trim();
const apellido = document.getElementById("apellido").value.trim();
const telefono = document.getElementById("telefono").value.trim();
const dni = document.getElementById("dni").value.trim();
const email = document.getElementById("email").value.trim();
const localidad = document.getElementById("localidad").value.trim();
const condicion = document.getElementById("condicion").value;
const institucion = document.getElementById("institucion").value.trim();
const nivelEducativo = document.getElementById("nivelEducativo").value;
// Validación básica
if (!nombre || !apellido || !telefono || !dni || !localidad || !condicion || !email) {
swalLoading.close();
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Por favor complete todos los campos.',
confirmButtonText: 'Aceptar'
});
return;
}
// Validar DNI (8 números)
if (!/^\d{8}$/.test(dni)) {
swalLoading.close();
Swal.fire({
icon: 'error',
title: 'Error',
text: 'El DNI debe tener 8 dígitos.',
confirmButtonText: 'Aceptar'
});
return;
}
// Validar Teléfono (7 a 15 números)
if (!/^\d{7,15}$/.test(telefono)) {
swalLoading.close();
Swal.fire({
icon: 'error',
title: 'Error',
text: 'El teléfono debe tener entre 7 y 15 dígitos.',
confirmButtonText: 'Aceptar'
});
return;
}
// Validar Email
const emailPattern = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
if (!emailPattern.test(email)) {
swalLoading.close();
Swal.fire({
icon: 'error',
title: 'Error',
text: 'El formato del correo electrónico es inválido.',
confirmButtonText: 'Aceptar'
});
return;
}
// Enviar a Apps Script
google.script.run
.withSuccessHandler(function(response) {
swalLoading.close();
// 🚫 Cupos completos
if (response === "cuposCompletos") {
Swal.fire({
icon: 'warning',
title: 'Cupos Completos',
text: 'Se alcanzaron las 400 plazas disponibles. Espere al próximo Semestre.',
confirmButtonText: 'Aceptar',
confirmButtonColor: '#d33'
});
// Desactivar botón
document.getElementById("registroForm").reset();
document.querySelector("button[type='submit']").disabled = true;
return;
}
// ✅ Registro exitoso
if (response === "registrado") {
Swal.fire({
icon: 'success',
title: 'Registro Exitoso',
text: 'Usted ha sido registrado correctamente.',
confirmButtonText: 'Aceptar'
});
document.getElementById("registroForm").reset();
document.getElementById("docenteExtras").style.display = "none";
return;
}
// ⚠ DNI existente
if (response === "existente") {
Swal.fire({
icon: 'warning',
title: 'DNI ya registrado',
text: 'Este DNI ya se encuentra inscripto.',
confirmButtonText: 'Aceptar'
});
return;
}
// ⚠ Email existente
if (response === "emailExistente") {
Swal.fire({
icon: 'warning',
title: 'Email ya registrado',
text: 'Este correo electrónico ya se encuentra inscripto.',
confirmButtonText: 'Aceptar'
});
return;
}
// ❓ Respuesta inesperada
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Respuesta inesperada del servidor.',
confirmButtonText: 'Aceptar'
});
})
.withFailureHandler(function(error) {
swalLoading.close();
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Hubo un error al procesar su solicitud.',
confirmButtonText: 'Aceptar'
});
})
.registrarConductor(
nombre,
apellido,
telefono,
dni,
email,
localidad,
condicion,
institucion,
nivelEducativo
);
});
</script>
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, apellido, telefono, dni, email, localidad, condicion, institucion, nivelEducativo) {
var ss = SpreadsheetApp.openById('Acá pones el ID de tu Sheet'); // ID de la hoja
var sheet = ss.getSheetByName('registro'); //Acá el nombre de tu hoja, o tabla
var registros = sheet.getDataRange().getValues();
var MAX_CUPOS = 400; // Puse un cupo, para tener un freno.
if ((registros.length - 1) >= MAX_CUPOS) {
return "cuposCompletos";
}
// Verificar si el DNI ya existe (columna D - índice 3)
for (var i = 1; i < registros.length; i++) { // Empezamos en la fila 1 para omitir encabezados
if (registros[i][3] == dni) { // Columna D (DNI)
return "existente"; // DNI ya registrado
}
}
// Verificar si el Email ya existe (columna F - índice 5)
for (var i = 1; i < registros.length; i++) { // Empezamos en la fila 1 para omitir encabezados
if (registros[i][5] == email) { // Columna F (Email)
return "emailExistente"; // Email ya registrado
}
}
// 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, // B: NOMBRES
apellido, // C: APELLIDOS
dni, // D: DNI
telefono, // E: TELÉFONO
email, // F: EMAIL
localidad, // G: LOCALIDAD
condicion, // H: CONDICIÓN
institucion, // I: INSTITUCIÓN
nivelEducativo // J: NIVEL EDUCATIVO
]);
return "registrado"; // Confirmación de registro exitoso
}
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.
