Static HTML/CSS/JS portfolio for the design studio 2RE03: locally vendored Bootstrap 5.3.3, self-hosted Inter + Space Grotesk fonts, six pages, Collins-style scroll-snap carousels, a case-studies index with 10 detail pages, and a Web3Forms contact form.
95 lines
2.8 KiB
JavaScript
95 lines
2.8 KiB
JavaScript
/* ==========================================================================
|
|
2RE03 site scripts
|
|
- Footer year
|
|
- Contact form: client-side validation + AJAX submit to Web3Forms
|
|
========================================================================== */
|
|
(function () {
|
|
"use strict";
|
|
|
|
/* --- Footer year ------------------------------------------------------- */
|
|
document.querySelectorAll("[data-year]").forEach(function (el) {
|
|
el.textContent = String(new Date().getFullYear());
|
|
});
|
|
|
|
/* --- Contact form ------------------------------------------------------ */
|
|
var form = document.getElementById("contactForm");
|
|
if (!form) {
|
|
return;
|
|
}
|
|
|
|
var status = document.getElementById("formStatus");
|
|
var submitBtn = form.querySelector('button[type="submit"]');
|
|
var submitLabel = submitBtn ? submitBtn.innerHTML : "";
|
|
|
|
function showStatus(message, variant) {
|
|
if (!status) {
|
|
return;
|
|
}
|
|
status.textContent = message;
|
|
status.className = "alert mt-3 mb-0 alert-" + variant;
|
|
status.hidden = false;
|
|
status.focus();
|
|
}
|
|
|
|
function clearStatus() {
|
|
if (!status) {
|
|
return;
|
|
}
|
|
status.textContent = "";
|
|
status.hidden = true;
|
|
}
|
|
|
|
function setBusy(isBusy) {
|
|
if (!submitBtn) {
|
|
return;
|
|
}
|
|
submitBtn.disabled = isBusy;
|
|
submitBtn.innerHTML = isBusy ? "Sending…" : submitLabel;
|
|
}
|
|
|
|
form.addEventListener("submit", function (event) {
|
|
event.preventDefault();
|
|
clearStatus();
|
|
|
|
// Basic validation: required fields + email format (via input[type=email]).
|
|
if (!form.checkValidity()) {
|
|
form.classList.add("was-validated");
|
|
var firstInvalid = form.querySelector(":invalid");
|
|
if (firstInvalid) {
|
|
firstInvalid.focus();
|
|
}
|
|
showStatus("Please fix the highlighted fields and try again.", "danger");
|
|
return;
|
|
}
|
|
|
|
setBusy(true);
|
|
|
|
fetch(form.action, {
|
|
method: "POST",
|
|
body: new FormData(form),
|
|
headers: { Accept: "application/json" }
|
|
})
|
|
.then(function (response) {
|
|
return response
|
|
.json()
|
|
.catch(function () { return {}; })
|
|
.then(function (data) { return { ok: response.ok, data: data }; });
|
|
})
|
|
.then(function (result) {
|
|
var data = result.data || {};
|
|
if (result.ok && data.success) {
|
|
form.reset();
|
|
form.classList.remove("was-validated");
|
|
showStatus("Thanks, your message is on its way. We'll get back to you soon.", "success");
|
|
} else {
|
|
showStatus(data.message || "Something went wrong. Please try again, or email us directly.", "danger");
|
|
}
|
|
})
|
|
.catch(function () {
|
|
showStatus("Network error. Please check your connection and try again.", "danger");
|
|
})
|
|
.finally(function () {
|
|
setBusy(false);
|
|
});
|
|
});
|
|
})();
|