a full workflow using htmx is finished.

Still needs lots of polish, but it works now.
This commit is contained in:
Chris Cochrun 2024-04-25 06:36:38 -05:00
parent ba1fdd967e
commit d653ccdf9f
4 changed files with 506 additions and 460 deletions

View file

@ -2048,6 +2048,10 @@ select {
height: 1000px; height: 1000px;
} }
.h-0 {
height: 0px;
}
.max-h-\[5rem\] { .max-h-\[5rem\] {
max-height: 5rem; max-height: 5rem;
} }
@ -2455,16 +2459,16 @@ select {
background-color: rgba(var(--color-neutral-500), var(--tw-bg-opacity)); background-color: rgba(var(--color-neutral-500), var(--tw-bg-opacity));
} }
.bg-primary-700 {
--tw-bg-opacity: 1;
background-color: rgba(var(--color-primary-700), var(--tw-bg-opacity));
}
.bg-\[\#ef4444\] { .bg-\[\#ef4444\] {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgb(239 68 68 / var(--tw-bg-opacity)); background-color: rgb(239 68 68 / var(--tw-bg-opacity));
} }
.bg-primary-700 {
--tw-bg-opacity: 1;
background-color: rgba(var(--color-primary-700), var(--tw-bg-opacity));
}
.bg-primary-600 { .bg-primary-600 {
--tw-bg-opacity: 1; --tw-bg-opacity: 1;
background-color: rgba(var(--color-primary-600), var(--tw-bg-opacity)); background-color: rgba(var(--color-primary-600), var(--tw-bg-opacity));
@ -2626,6 +2630,10 @@ select {
padding: 1.5rem; padding: 1.5rem;
} }
.p-8 {
padding: 2rem;
}
.px-0 { .px-0 {
padding-left: 0px; padding-left: 0px;
padding-right: 0px; padding-right: 0px;
@ -4731,6 +4739,11 @@ pre {
color: rgb(255 51 153 / var(--tw-text-opacity)); color: rgb(255 51 153 / var(--tw-text-opacity));
} }
.invalid\:text-\[\#f39\]:invalid {
--tw-text-opacity: 1;
color: rgb(255 51 153 / var(--tw-text-opacity));
}
.invalid\:ring-\[\#f39\]:invalid { .invalid\:ring-\[\#f39\]:invalid {
--tw-ring-opacity: 1; --tw-ring-opacity: 1;
--tw-ring-color: rgb(255 51 153 / var(--tw-ring-opacity)); --tw-ring-color: rgb(255 51 153 / var(--tw-ring-opacity));
@ -4955,6 +4968,90 @@ pre {
opacity: 1; opacity: 1;
} }
.peer:invalid ~ .peer-invalid\:visible {
visibility: visible;
}
.peer:invalid ~ .peer-invalid\:mt-10 {
margin-top: 2.5rem;
}
.peer:invalid ~ .peer-invalid\:mb-10 {
margin-bottom: 2.5rem;
}
.peer:invalid ~ .peer-invalid\:mb-16 {
margin-bottom: 4rem;
}
.peer:invalid ~ .peer-invalid\:mb-24 {
margin-bottom: 6rem;
}
.peer:invalid ~ .peer-invalid\:mb-32 {
margin-bottom: 8rem;
}
.peer:invalid ~ .peer-invalid\:h-8 {
height: 2rem;
}
.peer:invalid ~ .peer-invalid\:h-24 {
height: 6rem;
}
.peer:invalid ~ .peer-invalid\:h-32 {
height: 8rem;
}
.peer:invalid ~ .peer-invalid\:h-2 {
height: 0.5rem;
}
.peer:invalid ~ .peer-invalid\:basis-full {
flex-basis: 100%;
}
.peer:invalid ~ .peer-invalid\:p-8 {
padding: 2rem;
}
.peer:invalid ~ .peer-invalid\:p-2 {
padding: 0.5rem;
}
.peer:empty ~ .peer-empty\:visible {
visibility: visible;
}
.peer:empty ~ .peer-empty\:invisible {
visibility: hidden;
}
.peer:empty ~ .peer-empty\:mb-0 {
margin-bottom: 0px;
}
.peer:empty ~ .peer-empty\:h-0 {
height: 0px;
}
.peer:focus ~ .peer-focus\:visible {
visibility: visible;
}
.peer:invalid:focus ~ .peer-invalid\:peer-focus\:visible {
visibility: visible;
}
.peer:focus:invalid ~ .peer-focus\:peer-invalid\:visible {
visibility: visible;
}
.peer:invalid:focus ~ .peer-invalid\:peer-focus\:p-2 {
padding: 0.5rem;
}
[dir="ltr"] .ltr\:right-0 { [dir="ltr"] .ltr\:right-0 {
right: 0px; right: 0px;
} }

View file

@ -2,129 +2,6 @@
{{ $requiredField := "<span class='inline-block text-[#f39] text-sm align-super'>* required</span>" }} {{ $requiredField := "<span class='inline-block text-[#f39] text-sm align-super'>* required</span>" }}
<script>
function submitForm(e) {
e.preventDefault();
const form = document.getElementById('form');
const data = new FormData(form);
console.log(data.get("birthdate"));
const birthdate = new Date(data.get("birthdate"));
const age = calculate_age(birthdate);
data.append("age", age);
if (data.get("parentemail") == "") {
document.getElementById('warning-email').style.visibility = 'visible';
document.getElementById('warning-email').style.height = '';
document.getElementById('warning-email').style.margin = '';
return false;
} else {
document.getElementById('warning-email').style.visibility = 'hidden';
document.getElementById('warning-email').style.height = '0';
document.getElementById('warning-email').style.margin = '0';
}
if (data.get("firstname") == "") {
document.getElementById('warning-name').style.visibility = 'visible';
document.getElementById('warning-name').style.height = '';
document.getElementById('warning-name').style.margin = '';
return false;
} else {
document.getElementById('warning-name').style.visibility = 'hidden';
document.getElementById('warning-name').style.height = '0';
document.getElementById('warning-name').style.margin = '0';
}
if (data.get("lastname") == "") {
document.getElementById('warning-name').style.visibility = 'visible';
document.getElementById('warning-name').style.height = '';
document.getElementById('warning-name').style.margin = '';
return false;
} else {
document.getElementById('warning-name').style.visibility = 'hidden';
document.getElementById('warning-name').style.height = '0';
document.getElementById('warning-name').style.margin = '0';
}
if (data.get("parentfirstname") == "") {
document.getElementById('warning-name').style.visibility = 'visible';
document.getElementById('warning-name').style.height = '';
document.getElementById('warning-name').style.margin = '';
return false;
} else {
document.getElementById('warning-name').style.visibility = 'hidden';
document.getElementById('warning-name').style.height = '0';
document.getElementById('warning-name').style.margin = '0';
}
if (data.get("parentlastname") == "") {
document.getElementById('warning-name').style.visibility = 'visible';
document.getElementById('warning-name').style.height = '';
document.getElementById('warning-name').style.margin = '';
return false;
} else {
document.getElementById('warning-name').style.visibility = 'hidden';
document.getElementById('warning-name').style.height = '0';
document.getElementById('warning-name').style.margin = '0';
}
if (data.get("final-agreement") == "") {
document.getElementById('warning-agreement').style.visibility = 'visible';
document.getElementById('warning-agreement').style.height = '';
document.getElementById('warning-agreement').style.margin = '';
return false;
} else {
document.getElementById('warning-agreement').style.visibility = 'hidden';
document.getElementById('warning-agreement').style.height = '0';
document.getElementById('warning-agreement').style.margin = '0';
}
let base = "http://localhost:4242/camp-form";
/* let base = "https://api.tfcconnection.org/camp-form"; */
fetch(base, {
method: "POST",
body: data
}).then((res) => {
console.log(res);
if (res.ok) {
let payment = data.get('registration');
let health = data.get('health');
if (health === 'later') {
if (payment === 'now')
window.location.href = 'https://secure.myvanco.com/L-Z772/campaign/C-13JPJ';
else if (payment === 'full')
window.location.href = 'https://secure.myvanco.com/L-Z772/campaign/C-13JQE';
else
window.location.href = '/thankyou/';
}
else {
window.location.href = '/camp-health-form?registration=' + payment;
}
}
});
}
function calculate_age(dob) {
var diff_ms = Date.now() - dob.getTime();
var age_dt = new Date(diff_ms);
return Math.abs(age_dt.getUTCFullYear() - 1970);
}
function process() {
/* document.getElementById('mt-form').hidden = false */
document.getElementById('warning-email').style.visibility = 'hidden';
document.getElementById('warning-email').style.height = '0';
document.getElementById('warning-email').style.margin = '0';
document.getElementById('warning-name').style.visibility = 'hidden';
document.getElementById('warning-name').style.height = '0';
document.getElementById('warning-name').style.margin = '0';
document.getElementById('warning-agreement').style.visibility = 'hidden';
document.getElementById('warning-agreement').style.height = '0';
document.getElementById('warning-agreement').style.margin = '0';
document.getElementById('warning-other').style.visibility = 'hidden';
document.getElementById('warning-other').style.height = '0';
document.getElementById('warning-other').style.margin = '0';
}
document.addEventListener('DOMContentLoaded', process);
</script>
<div id="mt-form" class="form text-lg w-full"> <div id="mt-form" class="form text-lg w-full">
<form id='form' hx-post="http://localhost:4242/camp-api" autocomplete="on" method="post" target="_parent" class="w-full items-center flex flex-wrap"> <form id='form' hx-post="http://localhost:4242/camp-api" autocomplete="on" method="post" target="_parent" class="w-full items-center flex flex-wrap">
<h3 class="basis-full">Camp Form</h3> <h3 class="basis-full">Camp Form</h3>
@ -132,19 +9,18 @@
<label for="firstname" class="basis-full">What is your first and last name? <span class='inline-block text-[#f39] text-sm align-sub'>* required</span></label> <label for="firstname" class="basis-full">What is your first and last name? <span class='inline-block text-[#f39] text-sm align-sub'>* required</span></label>
<br/> <br/>
<input type="text" id="firstname" name="firstname" <input type="text" id="firstname" name="firstname"
placeholder="First Name" class="flex-1 form-input {{ $formClasses }}" placeholder="First Name" class="peer flex-1 form-input {{ $formClasses }}"
required> required>
<input type="text" id="lastname" name="lastname" <input type="text" id="lastname" name="lastname"
placeholder="Last Name" class="flex-1 form-input {{ $formClasses }}" placeholder="Last Name" class="peer flex-1 form-input {{ $formClasses }}"
required> required>
</div>
<div class="basis-full flex flex-wrap my-4"> <label for="parentfirstname" class="basis-full mt-2">What is your guardian's first and last name? <span class='inline-block text-[#f39] text-sm align-sub'>* required</span></label>
<label for="parentfirstname" class="basis-full">What is your guardian's first and last name? <span class='inline-block text-[#f39] text-sm align-sub'>* required</span></label>
<input type="text" id="parentfirstname" name="parentfirstname" <input type="text" id="parentfirstname" name="parentfirstname"
class="flex-1 form-input {{ $formClasses }}" class="peer flex-1 form-input {{ $formClasses }}"
placeholder="First Name" required> placeholder="First Name" required>
<input type="text" id="parentlastname" name="parentlastname" <input type="text" id="parentlastname" name="parentlastname"
class="flex-1 form-input {{ $formClasses }}" class="peer flex-1 form-input {{ $formClasses }}"
placeholder="Last Name" required> placeholder="Last Name" required>
</div> </div>
@ -211,17 +87,54 @@
<label for="parentphone" class="basis-full">Guardian's phone <span class='inline-block text-[#f39] text-sm align-sub'>* required</span></label> <label for="parentphone" class="basis-full">Guardian's phone <span class='inline-block text-[#f39] text-sm align-sub'>* required</span></label>
<input type="tel" id="parentphone" name="parentphone" <input type="tel" id="parentphone" name="parentphone"
class="basis-full form-input {{ $formClasses }}" class="basis-full peer form-input {{ $formClasses }}
required> invalid:text-[#f39] invalid:ring-[#f39]
focus:invalid:ring-[#f39]
focus:invalid:border-[#f39]"
required pattern="\(?\d{3}\)? ?-?\d{3} ?-?\d{4}"/>
<!-- <div id="warning-phone"
class="invisible flex
rounded-lg h-0
peer-invalid:p-2
peer-invalid:visible
peer-empty:invisible
peer-empty:h-0
peer-empty:mb-0
peer-invalid:basis-full
peer-invalid:mb-32">
<span class="text-[#f39] ltr:pr-3 rtl:pl-3 content-right float-right">
{{ partial "icon.html" (.Get 0 | default "triangle-exclamation") }} Make sure you have included a valid phone number for your guardian so we can contact them. Numbers are in the format "999-999-9999 or 0123456789". Make sure to include an area code.
</span>
</div>
-->
<div class="basis-full flex flex-wrap">
<label for="parentemail" class="basis-full">Guardian's Email <label for="parentemail" class="basis-full">Guardian's Email
<span class='inline-block text-[#f39] text-sm align-sub'>* required</span> <span class='inline-block text-[#f39] text-sm align-sub'>* required</span>
</label>
<input type="parentemail" id="parentemail" name="parentemail" <input type="parentemail" id="parentemail" name="parentemail"
pattern="^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$" pattern="^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*@[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*$"
class="basis-full peer form-input {{ $formClasses }} invalid:text-[#F39] invalid:ring-[#f39] focus:invalid:ring-[#f39] focus:invalid:border-[#f39]"> class="basis-full peer form-input
<span class='invisible text-[#f39] text-sm peer-invalid:visibile'>Please use a valid email</span> {{ $formClasses }}
invalid:text-[#F39]
invalid:ring-[#f39]
focus:invalid:ring-[#f39]
focus:invalid:border-[#f39]">
</label> <div id="warning-email"
class="invisible flex
rounded-lg h-0
peer-invalid:p-2 peer-invalid:visible
peer-invalid:basis-full
peer-invalid:mb-24">
<span class="text-[#f39] ltr:pr-3 rtl:pl-3 content-right float-right">
{{ partial "icon.html" (.Get 0 | default "triangle-exclamation") }} Make sure you have included an email for your guardian so we can contact them. Emails are in the format "name@website.end".
</span>
</div>
</div>
<label for="allergies" class="basis-full">Do you have any food allergies?</label> <label for="allergies" class="basis-full">Do you have any food allergies?</label>
<input type="text" id="allergies" name="allergies" <input type="text" id="allergies" name="allergies"
class="basis-full form-input {{ $formClasses }}"> class="basis-full form-input {{ $formClasses }}">
@ -298,34 +211,5 @@
<div class="basis-full mt-8"> <div class="basis-full mt-8">
<button type="submit" class="content-right rounded-lg bg-primary-700 h-12 w-24 focus:bg-primary-900 focus:ring focus:ring-primary-700 hover:bg-primary-900 float-right">Submit</button> <button type="submit" class="content-right rounded-lg bg-primary-700 h-12 w-24 focus:bg-primary-900 focus:ring focus:ring-primary-700 hover:bg-primary-900 float-right">Submit</button>
</div> </div>
<div id="warning-email" class="basis-full mt-10 flex px-4 py-3 rounded-lg bg-[#ef4444] dark:bg-[#ef4444]">
<span class="text-[#fca5a5] ltr:pr-3 rtl:pl-3 content-right float-right">
{{ partial "icon.html" (.Get 0 | default "triangle-exclamation") }}
Make sure you have included an email for your guardian so we can contact them.
</span>
</div>
<div id="warning-name" class="basis-full mt-10 flex px-4 py-3 rounded-lg bg-[#ef4444] dark:bg-[#ef4444]">
<span class="text-[#fca5a5] ltr:pr-3 rtl:pl-3 content-right float-right">
{{ partial "icon.html" (.Get 0 | default "triangle-exclamation") }}
Make sure that all names are filled out
</span>
</div>
<div id="warning-agreement" class="basis-full mt-10 flex px-4 py-3 rounded-lg bg-[#ef4444] dark:bg-[#ef4444]">
<span class="text-[#fca5a5] ltr:pr-3 rtl:pl-3 content-right float-right">
{{ partial "icon.html" (.Get 0 | default "triangle-exclamation") }}
You have to agree to the registration information.
</span>
</div>
<div id="warning-other" class="basis-full mt-10 flex px-4 py-3 rounded-lg bg-[#ef4444] dark:bg-[#ef4444]">
<span class="text-[#fca5a5] ltr:pr-3 rtl:pl-3 content-right float-right">
{{ partial "icon.html" (.Get 0 | default "triangle-exclamation") }}
Have you checked to make sure all required fields are filled out?
</span>
</div>
</form> </form>
</div> </div>

View file

@ -77,8 +77,8 @@
// For use in dev // For use in dev
// Can now start using this in production IF, // Can now start using this in production IF,
// I get the server running on the server // I get the server running on the server
let base = "https://api.tfcconnection.org/health-form"; /* let base = "https://api.tfcconnection.org/health-form"; */
/* let base = "http://localhost:4242/health-form"; */ let base = "http://localhost:4242/health-form";
fetch(base, { fetch(base, {
method: "POST", method: "POST",
body: data body: data
@ -208,14 +208,24 @@
const parentPhone = params.get('parentPhone'); const parentPhone = params.get('parentPhone');
console.log(mtRegistration); console.log(mtRegistration);
document.body.addEventListener('htmx:configRequest', function(evt) {
console.log(registration);
evt.detail.parameters['registration'] = registration;
console.log(evt);
});
</script> </script>
<div id="health-form" class="form text-lg w-full"> <div id="health-form" class="form text-lg w-full">
<form id='form' onsubmit="submitForm(event);" autocomplete="on" method="post" target="_parent" class="w-full items-center flex flex-wrap"> <form id='form'
hx-post="http://localhost:4242/health-form"
hx-encoding="multipart/form-data"
autocomplete="on" method="post"
target="_parent"
class="w-full items-center flex flex-wrap">
<h3 class="basis-full">2024-2025 Health Form</h3> <h3 class="basis-full">2024-2025 Health Form</h3>
<div class="basis-full flex flex-wrap my-4"> <div class="basis-full flex flex-wrap my-4">
<label for="firstname" class="basis-full">What is your first and last name?</label> <label for="firstname" class="basis-full">What is your first and last name? <span class='inline-block text-[#f39] text-sm'>* required</span></label>
<br/> <br/>
<input type="text" id="firstname" name="firstname" required <input type="text" id="firstname" name="firstname" required
placeholder="First Name" class="flex-1 form-input {{ $formClasses }}"> placeholder="First Name" class="flex-1 form-input {{ $formClasses }}">
@ -224,13 +234,13 @@
</div> </div>
<div class="basis-full my-8"> <div class="basis-full my-8">
<div class=""> <div class="">
<label for="birthdate" class="">When were you born?</label> <label for="birthdate" class="">When were you born? <span class='inline-block text-[#f39] text-sm'>* required</span></label>
<input type="date" id="birthdate" name="birthdate" <input type="date" id="birthdate" name="birthdate"
class="form-date {{ $formClasses }}" required> class="form-date {{ $formClasses }}" required>
</div> </div>
</div> </div>
<div class="basis-full flex flex-wrap my-4"> <div class="basis-full flex flex-wrap my-4">
<label for="parentfirstname" class="basis-full">What is your parent's first and last name?</label> <label for="parentfirstname" class="basis-full">What is your parent's first and last name? <span class='inline-block text-[#f39] text-sm'>* required</span></label>
<input type="text" id="parentfirstname" name="parentfirstname" <input type="text" id="parentfirstname" name="parentfirstname"
class="flex-1 form-input {{ $formClasses }}" class="flex-1 form-input {{ $formClasses }}"
placeholder="First Name" required> placeholder="First Name" required>
@ -239,7 +249,7 @@
placeholder="Last Name" required> placeholder="Last Name" required>
</div> </div>
<div class="basis-full flex flex-wrap my-4"> <div class="basis-full flex flex-wrap my-4">
<label for="street" class="basis-full">What is your address?</label> <label for="street" class="basis-full">What is your address? <span class='inline-block text-[#f39] text-sm'>* required</span></label>
<input type="text" id="street" name="street" <input type="text" id="street" name="street"
class="basis-full form-input {{ $formClasses }}" class="basis-full form-input {{ $formClasses }}"
placeholder="Street Address"> placeholder="Street Address">
@ -289,7 +299,7 @@
placeholder="Phone Number"> placeholder="Phone Number">
</div> </div>
<div class="basis-full my-4 flex flex-wrap items-center"> <div class="basis-full my-4 flex flex-wrap items-center">
<label for="medical-coverage" class="basis-full mt-8">Is this participant covered by medical insurance?</label> <label for="medical-coverage" class="basis-full mt-8">Is this participant covered by medical insurance? <span class='inline-block text-[#f39] text-sm'>* required</span></label>
<div class="basis-full flex flex-wrap items-center"> <div class="basis-full flex flex-wrap items-center">
<input type="radio" id="medical-coverage" name="medical-coverage" <input type="radio" id="medical-coverage" name="medical-coverage"
onclick="process()" value="yes" onclick="process()" value="yes"
@ -333,7 +343,7 @@
</div> </div>
<div class="basis-full my-4 flex flex-wrap items-center"> <div class="basis-full my-4 flex flex-wrap items-center">
<label for="agreement" class="basis-full mt-8">Do you agree with the above?</label> <label for="agreement" class="basis-full mt-8">Do you agree with the above? <span class='inline-block text-[#f39] text-sm'>* required</span></label>
<div class="basis-full flex flex-wrap items-center"> <div class="basis-full flex flex-wrap items-center">
<input type="radio" id="agreement" name="agreement" <input type="radio" id="agreement" name="agreement"
onclick="process()" value="yes" onclick="process()" value="yes"
@ -502,4 +512,4 @@
</div> </div>
</form> </form>
</div> </div>

View file

@ -381,6 +381,58 @@ with the image attached"
(:th (car row)) (:th (car row))
(:td (cdr row)))))))))))) (:td (cdr row))))))))))))
(tbnl:define-easy-handler (respond :uri "/health-form") ()
(setf (tbnl:header-out :access-control-expose-headers) "*")
(let* ((data (tbnl:post-parameters* tbnl:*request*))
(registration (cdr (assoc "registration" data :test 'string=)))
(image (cdr (assoc "image" data :test 'string=)))
(first-name (cdr (assoc "firstname" data :test 'string=)))
(last-name (cdr (assoc "lastname" data :test 'string=)))
(image (cdr (assoc "image" data :test 'string=)))
(attachment nil))
(loop :for d :in data
:do (progn
(uiop:println d)
(if (string= "firstname" (car d))
(progn
(uiop:println (cdr d))
(setf first-name (cdr d))))
(if (string= "lastname" (car d))
(progn
(uiop:println (cdr d))
(setf last-name (cdr d))))
(if (string= "image" (car d))
(let ((path (path-join
hunchentoot:*tmp-directory*
(format nil "~a_~a.~a" first-name last-name
(cadr (uiop:split-string
(car (last d 2)) :separator "."))))))
(uiop:copy-file
(cadr d)
(path-join
hunchentoot:*tmp-directory*
(format nil "~a_~a.~a" first-name last-name
(cadr (uiop:split-string
(car (last d 2)) :separator ".")))))
(setf attachment path)
(uiop:println attachment)))))
(uiop:println data)
(mail-health-form data attachment)
(cond ((string= registration "now")
(setf (hunchentoot:header-out :HX-Redirect) "https://secure.myvanco.com/L-Z772/campaign/C-13JPJ"))
((string= registration "full")
(setf (tbnl:header-out :HX-Redirect) "https://secure.myvanco.com/L-Z772/campaign/C-13JQE"))
((string= registration "later")
(with-html-string
(:div
:class "mt-8"
(:h2 (format nil
"Thank You ~A!"
(concat
first-name " " last-name)))
(:p :class "text-md"
"If you'd like to pay for your registration go to the donate tab in the top right when you are ready and find the camp registration option.")))))))
(hunchentoot:define-easy-handler (respond :uri "/camp-api") () (hunchentoot:define-easy-handler (respond :uri "/camp-api") ()
(let* ((request-type (hunchentoot:request-method hunchentoot:*request*)) (let* ((request-type (hunchentoot:request-method hunchentoot:*request*))
(data (hunchentoot:post-parameters* hunchentoot:*request*)) (data (hunchentoot:post-parameters* hunchentoot:*request*))
@ -403,14 +455,17 @@ with the image attached"
((string= registration "later") ((string= registration "later")
(let ((first-name (cdr (assoc "firstname" data :test 'string=))) (let ((first-name (cdr (assoc "firstname" data :test 'string=)))
(last-name (cdr (assoc "lastname" data :test 'string=)))) (last-name (cdr (assoc "lastname" data :test 'string=))))
(mail-camp-form data nil)
(with-html-string (with-html-string
(:div (:div
:class "mt-8" :class "mt-8"
(:p :class "text-lg" (:h2 (format nil
(format nil "Thank You ~A!"
"Thank You ~A, can't wait to see you at camp!!"
(concat (concat
first-name " " last-name))))))))) first-name " " last-name)))
(:p "Can't wait to see you at camp!!")
(:p :class "text-md"
"If you'd like to pay for your registration go to the donate tab in the top right when you are ready and find the camp registration option.")))))))
(progn (progn
(uiop:println "Health now") (uiop:println "Health now")
(setf (tbnl:header-out :HX-Redirect) (format nil "/camp-health-form?registration=~A" registration)) (setf (tbnl:header-out :HX-Redirect) (format nil "/camp-health-form?registration=~A" registration))