diff options
| author | Elizabeth Hunt <me@liz.coffee> | 2026-01-01 18:32:49 -0800 |
|---|---|---|
| committer | Elizabeth Hunt <me@liz.coffee> | 2026-01-01 18:32:49 -0800 |
| commit | 9af1854a7e35785a8e86426c4fb1edd465f164a3 (patch) | |
| tree | 8a070c6a9498d952c9ef4ba045f2ebfb25f7b335 /src/routes/contact | |
| parent | 0248a3899ed910f005dccaeefc1d9dcb893e8154 (diff) | |
| download | mistymountainstherapy-9af1854a7e35785a8e86426c4fb1edd465f164a3.tar.gz mistymountainstherapy-9af1854a7e35785a8e86426c4fb1edd465f164a3.zip | |
Massive refactor courtesy of 5 dollars of AI tokens
Diffstat (limited to 'src/routes/contact')
| -rw-r--r-- | src/routes/contact/+page.svelte | 325 | ||||
| -rw-r--r-- | src/routes/contact/index.svelte | 158 | ||||
| -rw-r--r-- | src/routes/contact/submit/+server.js (renamed from src/routes/contact/submit.js) | 31 |
3 files changed, 338 insertions, 176 deletions
diff --git a/src/routes/contact/+page.svelte b/src/routes/contact/+page.svelte new file mode 100644 index 0000000..c66d9df --- /dev/null +++ b/src/routes/contact/+page.svelte @@ -0,0 +1,325 @@ +<script> + import { browser } from '$app/environment'; + import HCaptcha from 'svelte-hcaptcha'; + + const mapsUrl = 'https://maps.app.goo.gl/s1AFqfKvUKgXDCrq5'; + + let submission = { + name: '', + email: '', + message: '', + phone: '', + captchaToken: '' + }; + + let captcha; + let noticeType = 'info'; + let noticeMessage = ''; + + function setNotice(type, message) { + noticeType = type; + noticeMessage = message; + } + + function handleSubmit(e) { + e.preventDefault(); + + if (!browser) return; + if (!submission.captchaToken) { + setNotice('error', 'Please complete the captcha first.'); + return; + } + + setNotice('info', 'Sending...'); + fetch('/contact/submit', { + method: 'POST', + body: JSON.stringify(submission) + }) + .then((x) => x.json()) + .then((x) => { + if (x.success) { + setNotice('success', 'Success! Reloading...'); + setTimeout(() => window.location.reload(), 1000); + } else { + setNotice('error', x.error ?? 'Something went wrong. Please try again.'); + } + }) + .catch((err) => { + console.log(err); + setNotice('error', 'Network error. Please try again.'); + }); + } + + function onCaptchaError() { + setNotice('error', 'Failed to verify captcha, try again.'); + captcha?.reset(); + submission.captchaToken = ''; + } + + function onCaptchaSuccess({ detail: { token } }) { + submission.captchaToken = token; + } +</script> + +<div class="contact"> + <header class="contact__header"> + <h1>Contact</h1> + </header> + + {#if noticeMessage} + <div + class={'notice notice--' + noticeType} + role={noticeType === 'error' ? 'alert' : 'status'} + aria-live={noticeType === 'error' ? 'assertive' : 'polite'} + > + {noticeMessage} + </div> + {/if} + + <div class="contact__grid"> + <div class="contact__sidebar"> + <section class="card" aria-label="Contact details"> + <h2>Details</h2> + + <div class="contact__methods"> + <a class="contact__method" href={mapsUrl} target="_blank" rel="noopener noreferrer"> + <span class="contact__icon" aria-hidden="true"><i class="bi bi-pin-map"></i></span> + <span class="contact__methodBody"> + <span class="contact__methodTitle">Office</span> + <span class="contact__methodText"> + 534 Trejo Street, Suite 200F<br />Rexburg, ID 83440 + </span> + </span> + </a> + + <a class="contact__method" href="tel:12084994517"> + <span class="contact__icon" aria-hidden="true"><i class="bi bi-telephone"></i></span> + <span class="contact__methodBody"> + <span class="contact__methodTitle">Phone</span> + <span class="contact__methodText">(208) 499 - 4517</span> + </span> + </a> + + <a class="contact__method" href="mailto:contact@mistymountainstherapy.com"> + <span class="contact__icon" aria-hidden="true"><i class="bi bi-envelope"></i></span> + <span class="contact__methodBody"> + <span class="contact__methodTitle">Email</span> + <span class="contact__methodText">contact@mistymountainstherapy.com</span> + </span> + </a> + </div> + </section> + + <section class="card card--warn" aria-label="Crisis resources"> + <h2>In a crisis?</h2> + <p style="margin: 0"> + We do not have a crisis line. If you or someone you know is in danger please call 911, visit your + nearest emergency room, call the National Suicide Prevention Lifeline at + <a href="tel:18002738255">(800)273-TALK</a> (8255), or text HELLO to 741-741. + </p> + </section> + </div> + + <section class="card" aria-label="Send a message"> + <h2>Send a message</h2> + <form class="contact__form" on:submit|preventDefault={handleSubmit}> + <div class="contact__fields"> + <label class="field"> + <span class="field__label">Email *</span> + <input + type="email" + class="form-control" + bind:value={submission.email} + placeholder="johnnyappleseed@example.com" + required + /> + </label> + + <label class="field"> + <span class="field__label">Name *</span> + <input + type="text" + class="form-control" + bind:value={submission.name} + placeholder="Johnny Appleseed" + required + /> + </label> + + <label class="field"> + <span class="field__label">Phone</span> + <input + type="text" + class="form-control" + bind:value={submission.phone} + placeholder="(208) 123-4567" + /> + </label> + + <label class="field"> + <span class="field__label">Message *</span> + <textarea + class="form-control" + bind:value={submission.message} + rows="6" + placeholder="Hello! I would like to schedule a free 15-minute consultation..." + required + ></textarea> + </label> + </div> + + <div class="contact__captcha"> + <HCaptcha + sitekey={import.meta.env.VITE_HCAPTCHA_KEY} + bind:this={captcha} + on:success={onCaptchaSuccess} + on:error={onCaptchaError} + /> + </div> + + <div class="contact__actions"> + <button type="submit" class="button button--primary" disabled={!submission.captchaToken}> + Submit + </button> + <p class="text-muted" style="margin: 0"> + Please watch spam for replies. + </p> + </div> + </form> + </section> + </div> +</div> + +<style> + .contact { + display: grid; + gap: var(--space-5); + } + + .contact__header { + display: grid; + gap: var(--space-2); + } + + .contact__lede { + margin: 0; + color: var(--color-muted); + } + + .contact__grid { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-5); + align-items: start; + } + + @media (min-width: 900px) { + .contact__grid { + grid-template-columns: 1fr minmax(0, 320px); + } + + .contact__sidebar { + order: 2; + } + } + + .contact__sidebar { + display: grid; + gap: var(--space-5); + min-width: 0; + } + + .contact__methods { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-3); + } + + .contact__method { + display: grid; + grid-template-columns: 2.25rem 1fr; + gap: var(--space-3); + align-items: start; + padding: var(--space-3); + border-radius: var(--radius-sm); + border: 1px solid var(--color-border); + text-decoration: none; + background: rgba(255, 255, 255, 0.55); + } + + .contact__method:hover { + border-color: var(--color-accent); + } + + .contact__icon { + display: inline-flex; + align-items: center; + justify-content: center; + width: 2.25rem; + height: 2.25rem; + border-radius: 999px; + background: color-mix(in srgb, var(--color-accent) 10%, transparent); + color: var(--color-accent-strong); + } + + .contact__icon i { + font-size: 1.1rem; + } + + .contact__methodBody { + display: grid; + gap: 0.15rem; + } + + .contact__methodTitle { + font-weight: 700; + } + + .contact__methodText { + color: var(--color-muted); + line-height: 1.35; + word-break: break-word; + } + + .contact__form { + display: grid; + gap: var(--space-4); + } + + .contact__fields { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-3); + } + + @media (min-width: 700px) { + .contact__fields { + grid-template-columns: 1fr; + } + + .field--full { + grid-column: 1 / -1; + } + } + + .field { + display: grid; + gap: var(--space-2); + } + + .field__label { + font-weight: 650; + } + + .contact__captcha { + display: grid; + justify-content: start; + } + + .contact__actions { + display: flex; + flex-wrap: wrap; + gap: var(--space-3); + align-items: center; + } +</style> diff --git a/src/routes/contact/index.svelte b/src/routes/contact/index.svelte deleted file mode 100644 index 5bd68f1..0000000 --- a/src/routes/contact/index.svelte +++ /dev/null @@ -1,158 +0,0 @@ -<script context="module"> - import { browser } from '$app/env'; - import { toast } from '@zerodevx/svelte-toast' - import HCaptcha from 'svelte-hcaptcha'; - - let submission = { - name: '', - email: '', - message: '', - phone: '', - captchaToken: '', - }; - - let captcha; - - function handleSubmit (e) { - e.preventDefault(); - - if (browser) { - const sendToast = toast.push('Sending...', { - duration: 300, - initial: 0, - next: 0.2, - dismissable: false - }); - fetch('/contact/submit', { - method: "POST", - body: JSON.stringify(submission) - }) - .then((x) => x.json()) - .then((x) => { - toast.set(sendToast, { next: 1 }); - - if (x.success) { - toast.push('Success! Reloading...', { - theme: { - '--toastBackground': '#48BB78', - '--toastBarBackground': '#2F855A' - }, - duration: 1000, - onpop: () => { window.location.reload(); }, - }); - } else if (x.error) { - toast.push(x.error, { - theme: { - '--toastBackground': '#F56565', - '--toastBarBackground': '#C53030' - } - }); - } - }) - .catch((err) => console.log(err)); - } - } - - function onCaptchaError () { - toast.push('Failed to verify captcha, try again.', { - theme: { - '--toastBackground': '#F56565', - '--toastBarBackground': '#C53030' - } - }); - captcha.reset(); - } - - function onCaptchaSuccess ({ detail: { token } }) { - submission.captchaToken = token; - } -</script> - -<main> - <h1 class="text-center">Get in touch.</h1> - <div class="d-flex justify-content-center flex-row row"> - <div class="border shadow bg-light py-2 col-lg-3 d-flex align-items-center flex-column m-2"> - <h1><i class="bi bi-map-fill"></i></h1> - <p style="hyphens: auto;"> - <a href="https://maps.app.goo.gl/s1AFqfKvUKgXDCrq5">534 Trejo Street - <br> - Suite 200F - <br> - Rexburg, ID - <br> - 83440 - </a> - </p> - </div> - <div class="border shadow bg-light py-2 col-lg-3 d-flex align-items-center flex-column m-2"> - <div><h1><i class="bi bi-phone-fill"></i></h1></div> - <p style="hyphens: auto;"> - Scheduling and Other - <br> - <a href="tel:12084994517">(208) 499 - 4517</a> - </p> - </div> - <div class="border shadow bg-light py-2 col-lg-3 d-flex align-items-center flex-column m-2" style="word-break: break-all;"> - <h1><i class="bi bi-envelope-fill"></i></h1> - <div> - <p style="hyphens: auto;"> - Billing - <br> - <a href="mailto:billing@mistymountainstherapy.com">billing@mistymountainstherapy.com</a> - </p> - <p style="hyphens: auto;"> - Inquiries - <br> - <a href="mailto:contact@mistymountainsthreapy.com">contact@mistymountainstherapy.com</a> - </p> - <div><em>note: please watch spam for replies</em></div> - </div> - </div> - </div> - <br> - <hr> - <form class="bg-light border shadow p-4" on:submit|preventDefault={handleSubmit}> - <h2>Or send us a message.</h2> - <div class="row mb-2"> - <div class="form-group col-md-6"> - <label for="email">Email *</label> - <input id="email" type="email" class="form-control" bind:value={submission.email} placeholder="johnnyappleseed@example.com" required> - </div> - <div class="form-group col-md-6"> - <label for="name">Name *</label> - <input id="name" type="text" class="form-control" bind:value={submission.name} placeholder="Johnny Appleseed" required> - </div> - </div> - <div class="form-group"> - <label for="phone">Phone</label> - <input id="phone" type="text" class="form-control" bind:value={submission.phone} placeholder="(208) 123-4567"> - </div> - <div class="form-group"> - <label for="message">Message *</label> - <textarea id="message" class="form-control" bind:value={submission.message} rows="3" placeholder="Hello! I would like to schedule a free 15-minute consultation..." required></textarea> - </div> - <div class="pt-2"> - <HCaptcha - sitekey={import.meta.env.VITE_HCAPTCHA_KEY} - bind:this={captcha} - on:success={onCaptchaSuccess} - on:error={onCaptchaError} - /> - </div> - <button type="submit" class="btn btn-primary">Submit</button> - <div class="my-2"><em>note: please watch spam for any replies</em></div> - </form> - <br> - - <div class="bg-light border shadow p-4"> - <div class="d-flex justify-content-center"> - <div class="col-md-8 mt-2"> - <div> - We do not have a crisis line. If you or someone you know is in danger please call 911, visit - your nearest emergency room, call the National Suicide Prevention Lifeline for free crisis - counseling at <a href="tel:18002738255">(800)273-TALK</a> (8255), or text HELLO to 741-741. - </div> - </div> - </div> - </div> -</main> diff --git a/src/routes/contact/submit.js b/src/routes/contact/submit/+server.js index 0befd0e..eed95fe 100644 --- a/src/routes/contact/submit.js +++ b/src/routes/contact/submit/+server.js @@ -39,7 +39,7 @@ class MistyMountainsMailer { } } -export async function post({ request }) { +export async function POST({ request }) { const body = await request.json(); const { HCAPTCHA_SECRET, FORM_TO_EMAIL } = process.env; const mailer = new MistyMountainsMailer( @@ -61,12 +61,10 @@ export async function post({ request }) { .catch(() => false); if (!captchaVerified) { - return { - statusCode: 400, - body: { - error: 'Captcha verification failed' - } - }; + return new Response(JSON.stringify({ error: 'Captcha verification failed' }), { + status: 400, + headers: { 'Content-Type': 'application/json' } + }); } const text = `<p>New MMT Message</h1> @@ -86,17 +84,14 @@ export async function post({ request }) { }); if (!messageSent) { - return { - statusCode: 500, - body: { - error: 'Message could not be sent' - } - }; + return new Response(JSON.stringify({ error: 'Message could not be sent' }), { + status: 500, + headers: { 'Content-Type': 'application/json' } + }); } - return { - body: { - success: true - } - }; + return new Response(JSON.stringify({ success: true }), { + status: 200, + headers: { 'Content-Type': 'application/json' } + }); } |
