97 lines
3.5 KiB
Plaintext
97 lines
3.5 KiB
Plaintext
#!
|
|
export TITLE="Contact"
|
|
export DESCRIPTION="Contact Joe Carstairs"
|
|
pp "${SHARE}"/components/meta.upphtml
|
|
#!
|
|
|
|
#!
|
|
pp "${SHARE}"/components/otp-dialog.upphtml
|
|
#!
|
|
|
|
<form class=contact-form>
|
|
<h1>Contact me</h1>
|
|
<p hidden class=error></p>
|
|
<label for=name>Name</label>
|
|
<input id=name name=name type=text required>
|
|
<label for=email>Email</label>
|
|
<input id=email name=email type=email required>
|
|
<label for=message>Message</label>
|
|
<textarea id=message name=message required></textarea>
|
|
<input type=submit value=Send>
|
|
</form>
|
|
|
|
<section class=success hidden=true>
|
|
<h1>You sent me a message!</h1>
|
|
<p>Thanks for that. I may be in touch.</p>
|
|
<p>In case you forgot, your message was this:</p>
|
|
<hr />
|
|
<dl>
|
|
<dt>Name</dt> <dd class=sentname>???</dd>
|
|
<dt>Email</dt> <dd class=sentemail>???</dd>
|
|
<dt>Message</dt> <dd class=sentmessage>???</dd>
|
|
</dl>
|
|
</section>
|
|
|
|
<script type=module>
|
|
import { resendOtp } from '/scripts/contact/resend-otp.js';
|
|
import { submitContactForm } from '/scripts/contact/submit-contact-form.js';
|
|
import { submitOtpForm } from '/scripts/contact/submit-otp-form.js';
|
|
|
|
function locateOrPanic(selector, desc, root) {
|
|
const elem = (root ?? document).querySelector(selector);
|
|
if (!elem) {
|
|
alert('Technical error: could not locate ' + desc + '. Please let Joe know if you have another means of contacting him.');
|
|
throw new Error('Could not locate ' + desc);
|
|
}
|
|
return elem;
|
|
}
|
|
|
|
const contactForm = locateOrPanic('form.contact-form', 'contact form');
|
|
const otpDialog = locateOrPanic('dialog.otp-dialog', 'OTP dialog');
|
|
const otpForm = locateOrPanic('form.otp-form', 'OTP form');
|
|
const successSection = document.querySelector('section.success');
|
|
let resendButtonInterval = undefined;
|
|
|
|
const selectors = {
|
|
contactForm: {
|
|
emailElem: () => locateOrPanic('input[name=email]', 'email input', contactForm),
|
|
errorElem: () => contactForm.querySelector('.error'),
|
|
nameElem: () => locateOrPanic('input[name=name]', 'name input', contactForm),
|
|
messageElem: () => locateOrPanic('textarea[name=message]', 'message textarea', contactForm),
|
|
self: () => contactForm,
|
|
submitButton: () => contactForm.querySelector('input[type=submit]'),
|
|
},
|
|
otpDialog: {
|
|
allOtpInputs: () => otpForm.querySelectorAll('input:not([type=submit])'),
|
|
errorElem: () => otpDialog.querySelector('.error'),
|
|
firstOtpInput: () => otpForm.querySelector('input:not([type=submit]):first-child'),
|
|
otpForm: () => otpForm,
|
|
otpRecipient: () => otpDialog.querySelector('.otp-recipient'),
|
|
otpValidUntil: () => otpDialog.querySelector('.otp-valid-until'),
|
|
resendButton: () => otpDialog.querySelector('button.resend-button'),
|
|
self: () => otpDialog,
|
|
submitButton: () => otpForm.querySelector('input[type=submit]'),
|
|
},
|
|
successSection: {
|
|
email: () => successSection?.querySelector('.sentemail') ?? null,
|
|
name: () => successSection?.querySelector('.sentname') ?? null,
|
|
message: () => successSection?.querySelector('.sentmessage') ?? null,
|
|
self: () => successSection,
|
|
}
|
|
};
|
|
|
|
contactForm.addEventListener('submit', async (event) => {
|
|
event.preventDefault();
|
|
({ resendButtonInterval } = await submitContactForm(selectors, resendButtonInterval));
|
|
});
|
|
|
|
selectors.otpDialog.resendButton()?.addEventListener('click', async () => {
|
|
({ resendButtonInterval } = await resendOtp(selectors, resendButtonInterval));
|
|
});
|
|
|
|
otpForm.addEventListener('submit', async (event) => {
|
|
event.preventDefault();
|
|
await submitOtpForm(selectors);
|
|
});
|
|
</script>
|