mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2026-01-15 08:22:59 -05:00
finally got myself a real contact form 📬
This commit is contained in:
@@ -3,6 +3,7 @@ import "./src/emoji.js";
|
||||
import "./src/counter.js";
|
||||
import "./src/clipboard.js";
|
||||
import "./src/anchor.js";
|
||||
import "./src/contact.js";
|
||||
import "./src/projects.js";
|
||||
|
||||
export default () => {};
|
||||
|
||||
79
assets/js/src/contact.js
Normal file
79
assets/js/src/contact.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import "vanilla-hcaptcha";
|
||||
import fetch from "cross-fetch";
|
||||
|
||||
// don't continue if there isn't a contact form on this page
|
||||
// TODO: be better and only do any of this on /contact/
|
||||
const contactForm = document.getElementById("contact-form");
|
||||
|
||||
if (contactForm) {
|
||||
contactForm.addEventListener("submit", (event) => {
|
||||
// immediately prevent <form> from actually submitting to a new page
|
||||
event.preventDefault();
|
||||
|
||||
const submitButton = document.getElementById("contact-form-btn-submit");
|
||||
|
||||
// disable the whole form if the button has been disabled below (on success)
|
||||
if (submitButton.disabled === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
// change button appearance between click and server response
|
||||
submitButton.innerText = "Sending...";
|
||||
submitButton.disabled = true; // prevent accidental multiple submissions
|
||||
submitButton.style.cursor = "default";
|
||||
|
||||
// feedback <span>s for later
|
||||
const successSpan = document.getElementById("contact-form-result-success");
|
||||
const errorSpan = document.getElementById("contact-form-result-error");
|
||||
|
||||
// https://simonplend.com/how-to-use-fetch-to-post-form-data-as-json-to-your-api/
|
||||
const formData = Object.fromEntries(new FormData(event.currentTarget).entries());
|
||||
|
||||
fetch(contactForm.action, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
},
|
||||
body: JSON.stringify(formData),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.success === true) {
|
||||
// handle successful submission
|
||||
// we can disable submissions & hide the send button now
|
||||
submitButton.disabled = true;
|
||||
submitButton.style.display = "none";
|
||||
|
||||
// just in case there *was* a PEBCAK error and it was corrected
|
||||
errorSpan.style.display = "none";
|
||||
|
||||
// let user know we were successful
|
||||
successSpan.innerText = "Success! You should hear from me soon. :)";
|
||||
} else {
|
||||
// pass on an error sent by the server
|
||||
throw new Error(data.message);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
const message = error instanceof Error ? error.message : "Unknown";
|
||||
|
||||
// give user feedback based on the error message returned
|
||||
if (message === "invalidCaptcha") {
|
||||
errorSpan.innerText = "Error: Did you remember to click the CAPTCHA?";
|
||||
} else if (message === "invalidEmail") {
|
||||
errorSpan.innerText = "Error: Please double check your email address.";
|
||||
} else if (message === "missingData") {
|
||||
errorSpan.innerText = "Error: Please make sure that all fields are filled in.";
|
||||
} else {
|
||||
// something else went wrong, and it's probably my fault...
|
||||
errorSpan.innerText = "Internal server error. Try again later?";
|
||||
}
|
||||
|
||||
// reset submit button to let user try again
|
||||
submitButton.innerText = "Try Again";
|
||||
submitButton.disabled = false;
|
||||
submitButton.style.cursor = "pointer";
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -11,6 +11,7 @@
|
||||
@use "../pages/videos";
|
||||
@use "../pages/etc";
|
||||
@use "../pages/projects";
|
||||
@use "../pages/contact";
|
||||
@use "../pages/fourOhFour";
|
||||
|
||||
// Responsive Awesomeness
|
||||
@@ -26,5 +27,6 @@
|
||||
@include videos.responsive();
|
||||
@include etc.responsive();
|
||||
@include projects.responsive();
|
||||
@include contact.responsive();
|
||||
@include fourOhFour.responsive();
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ $themes: (
|
||||
super-light: #f4f4f4,
|
||||
super-duper-light: #fbfbfb,
|
||||
links: #0e6dc2,
|
||||
success: #0b890f,
|
||||
error: #ff1b1b,
|
||||
),
|
||||
dark: (
|
||||
background-inner: #1e1e1e,
|
||||
@@ -74,6 +76,8 @@ $themes: (
|
||||
super-light: #272727,
|
||||
super-duper-light: #1f1f1f,
|
||||
links: #88c7ff,
|
||||
success: #78df55,
|
||||
error: #f54545,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
@use "pages/videos";
|
||||
@use "pages/etc";
|
||||
@use "pages/projects";
|
||||
@use "pages/contact";
|
||||
@use "pages/fourOhFour";
|
||||
|
||||
// Miscellaneous
|
||||
|
||||
111
assets/sass/pages/_contact.scss
Normal file
111
assets/sass/pages/_contact.scss
Normal file
@@ -0,0 +1,111 @@
|
||||
@use "../abstracts/themes";
|
||||
|
||||
// Contact Styles
|
||||
div.layout-contact {
|
||||
max-width: 600px;
|
||||
padding: 1.5em 0;
|
||||
|
||||
h1 {
|
||||
margin-bottom: 0.4em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.9em;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
|
||||
input[type="text"],
|
||||
input[type="email"],
|
||||
select,
|
||||
textarea {
|
||||
width: 100%;
|
||||
padding: 0.8em;
|
||||
margin: 0.6em 0;
|
||||
border: 1px solid;
|
||||
border-radius: 0.3em;
|
||||
font-size: 0.9em;
|
||||
|
||||
@include themes.themed(color, "text");
|
||||
@include themes.themed(background-color, "super-duper-light");
|
||||
@include themes.themed(border-color, "light");
|
||||
}
|
||||
|
||||
textarea {
|
||||
height: 10em;
|
||||
margin-bottom: 0;
|
||||
|
||||
// allow vertical resizing & disable horizontal
|
||||
resize: vertical; // stylelint-disable-line plugin/no-unsupported-browser-features
|
||||
}
|
||||
|
||||
div#contact-form-action-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
button {
|
||||
padding: 0.8em 1.2em;
|
||||
margin-right: 1.5em;
|
||||
border: 0;
|
||||
border-radius: 0.3em;
|
||||
cursor: pointer;
|
||||
line-height: 1.5;
|
||||
user-select: none;
|
||||
|
||||
@include themes.themed(color, "text");
|
||||
@include themes.themed(background-color, "kinda-light");
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
@include themes.themed(color, "super-duper-light");
|
||||
@include themes.themed(background-color, "links");
|
||||
}
|
||||
|
||||
img.emoji {
|
||||
margin-right: 0.4em;
|
||||
cursor: inherit;
|
||||
}
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 0.9em;
|
||||
font-weight: 600;
|
||||
|
||||
&#contact-form-result-success {
|
||||
@include themes.themed(color, "success");
|
||||
}
|
||||
|
||||
&#contact-form-result-error {
|
||||
@include themes.themed(color, "error");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// hcaptcha widget
|
||||
#contact-form-captcha {
|
||||
display: block;
|
||||
margin: 1.2em 0;
|
||||
}
|
||||
|
||||
span#contact-form-md-info {
|
||||
display: block;
|
||||
font-size: 0.75em;
|
||||
margin-top: 0.25em;
|
||||
margin-left: 0.75em;
|
||||
|
||||
a {
|
||||
// disable fancy underline without `.no-underline`
|
||||
background: none !important;
|
||||
padding: 0;
|
||||
|
||||
&:first-of-type {
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Responsive
|
||||
// stylelint-disable-next-line block-no-empty
|
||||
@mixin responsive() {
|
||||
}
|
||||
Reference in New Issue
Block a user