diff --git a/pages/resources/com/jessebrault/site/contact/ContactPageTemplate.wvc b/pages/resources/com/jessebrault/site/contact/ContactPageTemplate.wvc index a9f5c6a..d8aca90 100644 --- a/pages/resources/com/jessebrault/site/contact/ContactPageTemplate.wvc +++ b/pages/resources/com/jessebrault/site/contact/ContactPageTemplate.wvc @@ -8,33 +8,33 @@ import com.jessebrault.site.StandardPage

Contact

Please use the following form to contact Jesse Brault directly.

-
+
- - -

 

+ + +

 

- - -

 

+ + +

 

- - -

 

+ + +

 

- - -

 

+ + +

 

- +

 

diff --git a/static/contact.js b/static/contact.js index 7b9ed13..25e7100 100644 --- a/static/contact.js +++ b/static/contact.js @@ -1,37 +1,104 @@ -window.addEventListener('load', function () { - const contactForm = document.querySelector('form.contact') - contactForm.addEventListener('submit', function (submitEvent) { - submitEvent.preventDefault() - const formData = new FormData(contactForm) - const toSend = { - name: formData.get('name'), - email: formData.get('email'), - institution: formData.get('institution'), - message: formData.get('message') +window.addEventListener('load', () => { + const contactForm = document.getElementById('contact-form') + + const nameMessage = document.getElementById('name-message') + const emailMessage = document.getElementById('email-message') + const messageMessage = document.getElementById('message-message') + const submitMessage = document.getElementById('submit-message') + + const nameInput = document.getElementById('name-input') + const emailInput = document.getElementById('email-input') + const messageInput = document.getElementById('message-input') + const submitInput = this.document.getElementById('submit-input') + + const validateName = () => { + if (nameInput.validity.valid) { + nameMessage.innerHTML = ' ' + nameInput.classList.remove('invalid') + } else { + nameMessage.innerText = 'Name is required.' + nameInput.classList.add('invalid') } - fetch('https://api.jessebrault.com/contact/jessebrault', { - body: JSON.stringify(toSend), - headers: { - 'Content-type': 'application/json' - }, - method: 'POST' // TODO: signal - }) - .then(function (response) { - if (response.status === 400) { - response.json().then(function (body) { - body.errors.forEach(function ({ field, message }) { - const element = document.getElementById(`${field}-message`) - element.innerText = message - }) - }) - } else if (response.status === 500) { - const submitMessageElement = document.getElementById('submit-message') - submitMessageElement.innerText = 'There was internal server error. Please try again later.' - } else { - const submitMessageElement = document.getElementById('submit-message') - submitMessageElement.innerText = 'Your message was successfully sent!' - } + submitMessage.innerHTML = ' ' + } + + nameInput.addEventListener('focusout', validateName) + nameInput.addEventListener('input', validateName) + + const validateEmail = () => { + if (emailInput.validity.valid) { + emailMessage.innerHTML = ' ' + emailInput.classList.remove('invalid') + } else if (emailInput.validity.valueMissing) { + emailMessage.innerText = 'Email is required.' + emailInput.classList.add('invalid') + } else if (emailInput.validity.typeMismatch) { + emailMessage.innerText = 'Email must be a valid email.' + emailInput.classList.add('invalid') + } + submitMessage.innerHTML = ' ' + } + + emailInput.addEventListener('focusout', validateEmail) + emailInput.addEventListener('input', validateEmail) + + const validateMessage = () => { + if (messageInput.validity.valid) { + messageMessage.innerHTML = ' ' + messageInput.classList.remove('invalid') + } else { + messageMessage.innerText = 'Message is required.' + messageInput.classList.add('invalid') + } + submitMessage.innerHTML = ' ' + } + + messageInput.addEventListener('focusout', validateMessage) + messageInput.addEventListener('input', validateMessage) + + contactForm.addEventListener('submit', submitEvent => { + submitEvent.preventDefault() + validateName() + validateEmail() + validateMessage() + if (nameInput.validity.valid && emailInput.validity.valid && messageInput.validity.valid) { + const formData = new FormData(contactForm) + const toSend = { + name: formData.get('name'), + email: formData.get('email'), + institution: formData.get('institution'), + message: formData.get('message') + } + submitInput.disabled = true + submitMessage.innerText = 'Sending...' + fetch('https://api.jessebrault.com/contact/jessebrault', { + body: JSON.stringify(toSend), + headers: { + 'Content-type': 'application/json' + }, + method: 'POST' }) - .catch(function (error) {}) + .then(function (response) { + if (response.status === 400) { + response.json().then(body => { + body.errors.forEach(({ field, message }) => { + const element = document.getElementById(`${field}-message`) + element.innerText = message + }) + }) + } else if (response.status === 500) { + submitMessage.classList.add('error') + submitMessage.innerText = 'There was internal server error. Please try again later.' + } else { + submitMessage.classList.remove('error') + submitMessage.innerText = 'Your message was successfully sent!' + setTimeout(() => { + submitMessage.innerHTML = ' ' + }, 5000) + } + submitInput.disabled = false + }) + .catch(error => {}) + } }) }) diff --git a/static/main.css b/static/main.css index 4c117a9..7b1e3bb 100644 --- a/static/main.css +++ b/static/main.css @@ -416,10 +416,19 @@ form.contact { cursor: pointer; } +.contact .control input.invalid, +.contact .control textarea.invalid { + border: 1px solid red; +} + .contact .control .message { margin: 0; } +.contact .control .error { + color: red; +} + @media screen and (max-width: 767px) { nav ul { position: absolute;