Skip to main content

Overview

The ITV application has two main form sections:
  1. Initial Client Form - Captures client information (index.html lines 31-56)
  2. Dynamic Vehicle Forms - Generated per vehicle (script.js lines 58-102)

Initial Client Form Structure

The initial form collects basic client information:
<div class="bloque1">
    <div class="input-group">
        <label for="nombre">Nombre</label>
        <input type="text" id="nombre" placeholder="Ingrese su nombre" required>
    </div>
    <div class="input-group">
        <label for="apellidos">Apellidos</label>
        <input type="text" id="apellidos" placeholder="Ingrese sus apellidos" required>
    </div>
    <div class="input-group">
        <label for="razonSocial">Razón Social</label>
        <input type="text" id="razonSocial" placeholder="Empresa o particular" required>
    </div>
    <div class="input-group">
        <label for="nAutos">Nº de Vehículos</label>
        <input type="text" id="nAutos" placeholder="Cantidad de vehículos" required>
    </div>
</div>

Current Fields

Field IDLabelTypeRequired
nombreNombreTextYes
apellidosApellidosTextYes
razonSocialRazón SocialTextYes
nAutosNº de VehículosTextYes
The form uses a responsive grid layout (.bloque1) that automatically adjusts to 1fr columns on mobile devices.

Adding New Client Fields

Example: Add Email Field

1

Add HTML Input

Edit source/index.html and add a new input group:
<div class="input-group">
    <label for="email">Email</label>
    <input type="email" id="email" placeholder="[email protected]" required>
</div>
2

Update Data Collection

Modify obtenerDatosCliente() in script.js (lines 154-161):
function obtenerDatosCliente() {
    return {
        nombre: document.getElementById("nombre").value.trim(),
        apellidos: document.getElementById("apellidos").value.trim(),
        razonSocial: document.getElementById("razonSocial").value.trim(),
        email: document.getElementById("email").value.trim(),  // New
        nAutos: numeroAutos
    };
}
3

Update Display Logic

Modify mostrarInformacionCliente() in script.js (lines 210-218):
function mostrarInformacionCliente(datos) {
    document.getElementById("informacion").innerHTML = `
        <h3>👤 Información del Cliente</h3>
        <div>Nombre: <span>${datos.nombre}</span></div>
        <div>Apellidos: <span>${datos.apellidos}</span></div>
        <div>Email: <span>${datos.email}</span></div>
        <div>Razón Social: <span>${datos.razonSocial}</span></div>
        <div>Nº de Vehículos: <span>${datos.nAutos}</span></div>
    `;
}

Example: Add Phone Number Field

<div class="input-group">
    <label for="telefono">Teléfono</label>
    <input type="tel" id="telefono" placeholder="+34 XXX XXX XXX" 
           pattern="[+0-9\s-]+" required>
</div>
When adding required fields, ensure you update the validation logic in the enviar() function (lines 8-38) to check for the new field values.

Dynamic Vehicle Form Structure

Vehicle forms are generated dynamically by the crearFormularioVehiculo() function:
function crearFormularioVehiculo(numeroVehiculo) {
    return `
        <div class="bloque" data-vehiculo="${numeroVehiculo}">
            <h3>🚙 Vehículo Nº ${numeroVehiculo}</h3>

            <p>🔧 Motorización:</p>
            <label>
                <input type="radio" id="radio1${numeroVehiculo}" 
                       name="motorizacion${numeroVehiculo}" 
                       value="Diesel" required>
                🛢️ Diesel
            </label>
            <label>
                <input type="radio" id="radio2${numeroVehiculo}" 
                       name="motorizacion${numeroVehiculo}" 
                       value="Gasolina">
                ⛽ Gasolina
            </label>
            <label>
                <input type="radio" id="radio3${numeroVehiculo}" 
                       name="motorizacion${numeroVehiculo}" 
                       value="Hibrido">
                🔋 Híbrido
            </label>
            <label>
                <input type="radio" id="radio4${numeroVehiculo}" 
                       name="motorizacion${numeroVehiculo}" 
                       value="Electrico">
                ⚡ Eléctrico
            </label>
            
            <div class="aprobados">
                <p>📋 Estado de la ITV:</p>
                <label>
                    <input type="radio" id="radio5${numeroVehiculo}" 
                           value="Aprobo" name="aprobados${numeroVehiculo}">
                    ✅ Aprobado
                </label>
                <label>
                    <input type="radio" id="radio6${numeroVehiculo}" 
                           value="NoAprobo" name="aprobados${numeroVehiculo}">
                    ❌ No Aprobado
                </label>
            </div>
            
            <label for="observaciones${numeroVehiculo}">📝 Observaciones del vehículo:</label>
            <textarea
                id="observaciones${numeroVehiculo}"
                name="observaciones${numeroVehiculo}"
                class="caja-texto"
                placeholder="Ingrese observaciones sobre el estado del vehículo..."
                maxlength="250"
                rows="4">
            </textarea>
        </div>
    `;
}

Vehicle Form Fields

Field TypePurposeValues
Radio buttonsMotorization typeDiesel, Gasolina, Híbrido, Eléctrico
Radio buttonsITV StatusAprobo, NoAprobo
TextareaObservationsFree text (max 250 chars)

Adding Fields to Vehicle Forms

Example: Add License Plate Field

function crearFormularioVehiculo(numeroVehiculo) {
    return `
        <div class="bloque" data-vehiculo="${numeroVehiculo}">
            <h3>🚙 Vehículo Nº ${numeroVehiculo}</h3>
            
            <!-- New field -->
            <div class="input-group" style="margin-bottom: 1rem;">
                <label for="matricula${numeroVehiculo}">🔖 Matrícula</label>
                <input type="text" 
                       id="matricula${numeroVehiculo}" 
                       name="matricula${numeroVehiculo}"
                       placeholder="1234ABC"
                       pattern="[0-9]{4}[A-Z]{3}"
                       required
                       style="padding: 0.875rem 1rem; border: 2px solid #e5e7eb; 
                              border-radius: 0.5rem; font-size: 1rem; width: 100%;">
            </div>

            <p>🔧 Motorización:</p>
            <!-- ... rest of form ... -->
        </div>
    `;
}

Update Data Processing

After adding new fields, update the procesarDatosVehiculos() function:
function procesarDatosVehiculos() {
    vehiculosData = [];

    for (let i = 1; i <= numeroAutos; i++) {
        const motorizacion = document.querySelector(`input[name="motorizacion${i}"]:checked`).value;
        const estadoITV = document.querySelector(`input[name="aprobados${i}"]:checked`).value;
        const observaciones = document.getElementById(`observaciones${i}`).value.trim();
        
        // New field
        const matricula = document.getElementById(`matricula${i}`).value.trim();

        vehiculosData.push({
            numero: i,
            matricula: matricula,  // Add to data object
            motorizacion: motorizacion,
            estadoITV: estadoITV,
            observaciones: observaciones
        });
    }
}

Field Validation

The application includes validation logic in the validarFormulariosVehiculos() function:
function validarFormulariosVehiculos() {
    for (let i = 1; i <= numeroAutos; i++) {
        // Verificar motorización
        const motorizacionSeleccionada = document.querySelector(
            `input[name="motorizacion${i}"]:checked`
        );
        if (!motorizacionSeleccionada) {
            mostrarError(`Por favor, seleccione la motorización para el vehículo ${i}.`);
            return false;
        }

        // Verificar estado ITV
        const itvSeleccionado = document.querySelector(
            `input[name="aprobados${i}"]:checked`
        );
        if (!itvSeleccionado) {
            mostrarError(`Por favor, seleccione el estado ITV para el vehículo ${i}.`);
            return false;
        }
    }
    return true;
}

Add Custom Validation

function validarFormulariosVehiculos() {
    for (let i = 1; i <= numeroAutos; i++) {
        // ... existing validations ...
        
        // Validate license plate
        const matricula = document.getElementById(`matricula${i}`);
        if (matricula && !matricula.value.trim()) {
            mostrarError(`Por favor, ingrese la matrícula del vehículo ${i}.`);
            return false;
        }
        
        // Validate format (Spanish license plate: 4 digits + 3 letters)
        const matriculaPattern = /^[0-9]{4}[A-Z]{3}$/;
        if (matricula && !matriculaPattern.test(matricula.value.trim())) {
            mostrarError(`Formato incorrecto para matrícula del vehículo ${i}. Use: 1234ABC`);
            return false;
        }
    }
    return true;
}

Input Auto-Formatting

The application includes automatic text capitalization:
const camposTexto = ['nombre', 'apellidos', 'razonSocial'];
camposTexto.forEach(campo => {
    const input = document.getElementById(campo);
    input.addEventListener("input", function() {
        this.value = this.value.charAt(0).toUpperCase() + this.value.slice(1);
    });
});

Add Custom Auto-Formatting

// Add this to inicializarEventos() function
function formatearMatriculas() {
    // This would run after vehicle forms are created
    for (let i = 1; i <= numeroAutos; i++) {
        const matriculaInput = document.getElementById(`matricula${i}`);
        if (matriculaInput) {
            matriculaInput.addEventListener("input", function() {
                this.value = this.value.toUpperCase();
            });
        }
    }
}

// Call after generating forms in enviar()
function enviar() {
    // ... existing code ...
    generarFormulariosVehiculos();
    
    // Add formatting
    setTimeout(() => {
        formatearMatriculas();
    }, 100);
}

Textarea Configuration

The observations field uses the .caja-texto class:
.caja-texto {
    background: #ffffff;
    border: 2px solid #e5e7eb;
    border-radius: 0.5rem;
    padding: 1rem;
    width: 100% !important;
    font-size: 0.95rem;
    font-family: inherit;
    transition: all 0.2s ease;
    resize: vertical;
    min-height: 100px;
    margin: 0.5rem 0 !important;
}

.caja-texto:focus {
    outline: none;
    border-color: #2563eb;
    box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
}

Customize Textarea Size

<textarea
    id="observaciones${numeroVehiculo}"
    class="caja-texto"
    placeholder="Ingrese observaciones..."
    maxlength="500"
    rows="8"
    style="min-height: 150px;">
</textarea>

Number Input Validation

The vehicle count field has special validation:
const nAutosInput = document.getElementById("nAutos");
nAutosInput.addEventListener("input", function() {
    const valor = parseInt(this.value);
    if (valor > 50) {
        this.value = 50;
        mostrarError("El número máximo de vehículos es 50.");
    }
    if (valor < 0) {
        this.value = "";
    }
});

// Permitir solo números
nAutosInput.addEventListener("keypress", function(e) {
    if (!/[0-9]/.test(e.key) && !['Backspace', 'Delete', 'Tab', 'Enter'].includes(e.key)) {
        e.preventDefault();
    }
});

Field Styling Reference

.input-group {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

Best Practices

Use Semantic HTML

Use appropriate input types (email, tel, number) for better mobile experience

Add Validation

Always validate both client-side and ensure proper error messages

Maintain Consistency

Use existing CSS classes (.input-group, .caja-texto) for styling

Test Responsively

Test new fields on mobile devices - forms use responsive grids
When adding fields to dynamic vehicle forms, remember to:
  1. Update crearFormularioVehiculo()
  2. Update procesarDatosVehiculos()
  3. Update validarFormulariosVehiculos() if required
  4. Update display functions if showing the data

Quick Reference

FileFunctionPurpose
index.htmlN/AStatic client form HTML
script.js:58-102crearFormularioVehiculo()Generate vehicle form HTML
script.js:189-204procesarDatosVehiculos()Collect form data
script.js:167-184validarFormulariosVehiculos()Validate inputs
script.js:154-161obtenerDatosCliente()Get client data
styles.css:109-143.input-group, input[type="text"]Field styling

Build docs developers (and LLMs) love