Proyecto JavaScript simulador de envío de emails

// Variables

const email = document.querySelector('#email');
const asunto = document.querySelector('#asunto');
const mensaje = document.querySelector('#mensaje');
const btnEnviar = document.querySelector('#enviar');
const btnReset = document.querySelector('#resetBtn');
const formulario = document.querySelector('#enviar-mail');
const er = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;

// Listeners

eventListeners();
function eventListeners() {
    document.addEventListener('DOMContentLoaded', iniciarApp); //addEventListener espera la carga completa del HTML para iniciar el formulario.           
    email.addEventListener('blur', validarFormulario); // No hace falta el paréntesis de la función
    asunto.addEventListener('blur', validarFormulario); // Listeners para arrancar cualquiera la función validarFormulario
    mensaje.addEventListener('blur', validarFormulario);       
    btnReset.addEventListener('click', resetearFormulario);
    formulario.addEventListener('submit', enviarEmail);
}

// Funciones

function iniciarApp() {
     btnEnviar.disabled = true; // Con esto todo sigue visualmente igual pero el botón no hace nada
     btnEnviar.classList.add('cursor-not-allowed', 'opacity-50'); // Para que el cursor tome forme de no permitido y enga color pálido 

     email.classList.remove('border-red-500', 'border-green-500'); //Esto aquí lo tuve que agregar yo mismo porque cuando se reseteaba el formulario resulta que la función 
    //  email.classList.remove('border-green-500');  //iniciarApp no incluía eliminar los cambios que se van originando en la función "validarFormulario".
     asunto.classList.remove('border-red-500', 'border-green-500');
    //  asunto.classList.remove('border-green-500'); 
     mensaje.classList.remove('border-red-500', 'border-green-500');
    //  mensaje.classList.remove('border-green-500'); 

    const error = document.querySelector('p.error'); //Esto también lo agregué yo mismo, que flojera el instructor de no dejar bien los efectos del botón de
        if(error){                                   //reseteo del formulario
            error.remove();
        }
 }

function validarFormulario(e) {
    if(e.target.value.length > 0) { //Toma la longitud del valor el evento, si es mayor que cero y se produce el evento blur es que algo swe escribió
        const error = document.querySelector('p.error'); //Si no hay error busca algún párrafo con la clase error y ,
        if(error){ //si y solo si el la variable "error" fue creada porque se encontró un párrafo con la clase "error",
            error.remove(); // se borra ese párrafo. Se incluye el if para que en este paso no de un error al querer borrar un elemento que no existe
        }
        e.target.classList.remove('border', 'border-red-500'); //remueve las clases que dan formato de error al campo imput correspondiente
        e.target.classList.add('border', 'border-green-500'); //coloca las clases que dan formato de llenado correcto al campo imput correspondiente
    }else{
        e.target.classList.remove('border', 'border-green-500'); // Si hay error remueve estas clases del campo imput correspondiente
        e.target.classList.add('border', 'border-red-500');  // y coloca estas otras al campo imput correspondiente
        mostrarError('Todos los campos son obligatorios'); // llamar la función que muestra el mensaje de error y se le pasa el argumento que es un string
    }
    if(e.target.type === 'email') { //En caso que se trate del campo email se hace una corroboración extra que sobre escribe la enteior
        if(er.test (e.target.value)) { //se prueba la expresión regular con el valor del campo, si es true
            const error = document.querySelector('p.error'); //lo mismo, se detecta si existe el párrafo de error creado con la función "mostrarError"
            if(error){ //si y solo si el la variable "error" fue creada porque se encontró un párrafo con la clase "error",
                error.remove(); // se borra ese párrafo. Se incluye el if para que en este paso no de un error al querer borrar un elemento que no existe
            }
            e.target.classList.remove('border', 'border-red-500'); //Se remueven los estilos correspondientes al campo imput correspondiente y 
            e.target.classList.add('border', 'border-green-500'); // y se colocan los estilos de llenado correcto
        }else{
            e.target.classList.remove('border', 'border-green-500'); //Si es false, se remueven los estilos correspondientes al campo imput correspondiente
            e.target.classList.add('border', 'border-red-500');  // y se añaden los estilos de error y
            mostrarError('El email no es válido'); //Se manda a llamar la función que muestra el error
        }
    }  

    if(er.test (email.value) && asunto.value !== '' && mensaje.value !== '') { //email.value, no e.target.value, ojo
        btnEnviar.disabled = false; // Habilitamos el botón
        btnEnviar.classList.remove('cursor-not-allowed', 'opacity-50'); //Removemos las clases que hacen ver al botón opaco y el modo cursol no permitido
    }
 }//No he podido aclarar la duda de por qué no aparece el mensaje "email no válido" si entro al campo email y genero el evento blur sin escribir nada

 function mostrarError(mensaje) {
    const errores = document.querySelectorAll('.error'); // querySelectorAll arma un array o colección de elementos con los elementos que contangan esa clase
    if(errores.length === 0) { // Si y solo si el array es igual a cero ejecuta lo siguiente, si no, lo deja así para no repetir mensaje de error
                                //Esto a diferencia de querySelector(), que devuelve un booleano
        const mensajeError = document.createElement('p'); //Creo un párrafo para  luego
        mensajeError.textContent = mensaje;                 // meterle un texto con el valor de "mensaje"
        mensajeError.classList.add('border', 'border-red-500', 'background-red-100', 'text-red-500', 'p-3', 'mt-5', 'text-center', 'error'); //estilos
        formulario.appendChild(mensajeError); //Se inserta el párrafo al final del formulario
    }   
}

function enviarEmail(e) {
    e.preventDefault(); //El instructor no fue claro, solo dice que siempre que sea tipo submit hay que colocarlo
    console.log(e);
    const spinner = document.querySelector('#spinner'); //No es necesario que esta variable sea globel. 
    spinner.style.display = 'flex'; //Me da curiosidad que estoy asignando estilos a un div, imagino que el querySelector toma el elemento y el Id es si

    setTimeout( () => {
        spinner.style.display = 'none';

        const parrafo = document.createElement('p');
        parrafo.textContent = 'El mensaje se envió correctamente';
        parrafo.classList.add('text-center', 'my-10', 'p-2', 'bg-green-500', 'text-white', 'font-bold', 'uppercase');
        formulario.insertBefore(parrafo, spinner);

        setTimeout( () => {
            parrafo.remove();
            resetearFormulario();
        }, 1500);
    }, 3000);
}

function resetearFormulario(e) {
    e.preventDefault(); //Por algún motivo que desconozco, el evento "click" en mi botón resetear estaba activando la función enviar email. Tuve que mandarle
                        //el evento a la función y aplicar un preventDefault y se solucionó. Tal cual como se hizo para el botón "enviar email" que al igual
                        //que este, es de tipo submit.
    console.log(e);
    formulario.reset(); //Esto es un método para los formularios, automáticamente los resetea.

    iniciarApp();
}


"http://127.0.0.1:5500/Material/Curso%20JS%20Moderno/16-PROYECTO-EnviarEmail/index.html"
"http://127.0.0.1:5500/Material/Curso%20JS%20Moderno/16-PROYECTO-EnviarEmail/index.html"