From 4a8325aa35e3d74f5091eca2d3755af152f5247a Mon Sep 17 00:00:00 2001
From: Chris Cochrun <chris@cochrun.xyz>
Date: Mon, 1 Jul 2024 15:59:44 -0500
Subject: [PATCH] a much better camp_form.rs setup

---
 Cargo.lock           | 367 ++++++++++++++++++++++++++++++++++++++++++-
 Cargo.toml           |   2 +
 src/api/camp_form.rs | 232 ++++++++++++++++++---------
 src/api/errors.rs    |   5 -
 src/main.rs          |  30 +++-
 5 files changed, 551 insertions(+), 85 deletions(-)

diff --git a/Cargo.lock b/Cargo.lock
index 54b4660..4c7bef1 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -282,6 +282,33 @@ version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
 
+[[package]]
+name = "android-tzdata"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0"
+
+[[package]]
+name = "android_system_properties"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "anyhow"
+version = "1.0.86"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da"
+
+[[package]]
+name = "arc-swap"
+version = "1.7.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457"
+
 [[package]]
 name = "async-channel"
 version = "1.8.0"
@@ -488,6 +515,18 @@ version = "1.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
+[[package]]
+name = "chrono"
+version = "0.4.38"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401"
+dependencies = [
+ "android-tzdata",
+ "iana-time-zone",
+ "num-traits",
+ "windows-targets 0.52.5",
+]
+
 [[package]]
 name = "chumsky"
 version = "0.9.3"
@@ -612,6 +651,17 @@ dependencies = [
  "syn 2.0.43",
 ]
 
+[[package]]
+name = "derivative"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 1.0.109",
+]
+
 [[package]]
 name = "derive_more"
 version = "0.99.17"
@@ -625,6 +675,12 @@ dependencies = [
  "syn 1.0.109",
 ]
 
+[[package]]
+name = "destructure_traitobject"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3c877555693c14d2f84191cfd3ad8582790fc52b5e2274b40b59cf5f5cea25c7"
+
 [[package]]
 name = "digest"
 version = "0.10.7"
@@ -673,6 +729,12 @@ dependencies = [
  "termcolor",
 ]
 
+[[package]]
+name = "equivalent"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
+
 [[package]]
 name = "errno"
 version = "0.3.1"
@@ -904,7 +966,7 @@ dependencies = [
  "futures-sink",
  "futures-util",
  "http",
- "indexmap",
+ "indexmap 1.9.3",
  "slab",
  "tokio",
  "tokio-util",
@@ -1030,6 +1092,29 @@ dependencies = [
  "tokio-native-tls",
 ]
 
+[[package]]
+name = "iana-time-zone"
+version = "0.1.60"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e7ffbb5a1b541ea2561f8c41c087286cc091e21e556a4f09a8f6cbf17b69b141"
+dependencies = [
+ "android_system_properties",
+ "core-foundation-sys",
+ "iana-time-zone-haiku",
+ "js-sys",
+ "wasm-bindgen",
+ "windows-core",
+]
+
+[[package]]
+name = "iana-time-zone-haiku"
+version = "0.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f"
+dependencies = [
+ "cc",
+]
+
 [[package]]
 name = "ident_case"
 version = "1.0.1"
@@ -1056,6 +1141,16 @@ dependencies = [
  "hashbrown 0.12.3",
 ]
 
+[[package]]
+name = "indexmap"
+version = "2.2.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
+dependencies = [
+ "equivalent",
+ "hashbrown 0.14.3",
+]
+
 [[package]]
 name = "instant"
 version = "0.1.12"
@@ -1209,9 +1304,44 @@ version = "0.4.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
 dependencies = [
+ "serde",
  "value-bag",
 ]
 
+[[package]]
+name = "log-mdc"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a94d21414c1f4a51209ad204c1776a3d0765002c76c6abcb602a6f09f1e881c7"
+
+[[package]]
+name = "log4rs"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0816135ae15bd0391cf284eab37e6e3ee0a6ee63d2ceeb659862bd8d0a984ca6"
+dependencies = [
+ "anyhow",
+ "arc-swap",
+ "chrono",
+ "derivative",
+ "fnv",
+ "humantime",
+ "libc",
+ "log",
+ "log-mdc",
+ "once_cell",
+ "parking_lot",
+ "rand",
+ "serde",
+ "serde-value",
+ "serde_json",
+ "serde_yaml",
+ "thiserror",
+ "thread-id",
+ "typemap-ors",
+ "winapi",
+]
+
 [[package]]
 name = "markup"
 version = "0.15.0"
@@ -1238,6 +1368,30 @@ version = "0.1.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ffbee8634e0d45d258acb448e7eaab3fce7a0a467395d4d9f228e3c1f01fb2e4"
 
+[[package]]
+name = "maud"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "df518b75016b4289cdddffa1b01f2122f4a49802c93191f3133f6dc2472ebcaa"
+dependencies = [
+ "actix-web",
+ "futures-util",
+ "itoa",
+ "maud_macros",
+]
+
+[[package]]
+name = "maud_macros"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa453238ec218da0af6b11fc5978d3b5c3a45ed97b722391a2a11f3306274e18"
+dependencies = [
+ "proc-macro-error",
+ "proc-macro2",
+ "quote",
+ "syn 2.0.43",
+]
+
 [[package]]
 name = "memchr"
 version = "2.7.1"
@@ -1315,6 +1469,15 @@ dependencies = [
  "minimal-lexical",
 ]
 
+[[package]]
+name = "num-traits"
+version = "0.2.19"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+dependencies = [
+ "autocfg",
+]
+
 [[package]]
 name = "num_cpus"
 version = "1.15.0"
@@ -1375,6 +1538,15 @@ dependencies = [
  "vcpkg",
 ]
 
+[[package]]
+name = "ordered-float"
+version = "2.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c"
+dependencies = [
+ "num-traits",
+]
+
 [[package]]
 name = "parking"
 version = "2.1.0"
@@ -1462,6 +1634,29 @@ version = "0.2.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
 
+[[package]]
+name = "proc-macro-error"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
+dependencies = [
+ "proc-macro-error-attr",
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
+[[package]]
+name = "proc-macro-error-attr"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "version_check",
+]
+
 [[package]]
 name = "proc-macro2"
 version = "1.0.76"
@@ -1695,9 +1890,33 @@ checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed"
 
 [[package]]
 name = "serde"
-version = "1.0.163"
+version = "1.0.193"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2113ab51b87a539ae008b5c6c02dc020ffa39afd2d83cffcb3f4eb2722cebec2"
+checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde-value"
+version = "0.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c"
+dependencies = [
+ "ordered-float",
+ "serde",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.193"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.43",
+]
 
 [[package]]
 name = "serde_json"
@@ -1731,6 +1950,19 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "serde_yaml"
+version = "0.9.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a15e0ef66bf939a7c890a0bf6d5a733c70202225f9888a89ed5c62298b019129"
+dependencies = [
+ "indexmap 2.2.6",
+ "itoa",
+ "ryu",
+ "serde",
+ "unsafe-libyaml",
+]
+
 [[package]]
 name = "sha1"
 version = "0.10.5"
@@ -1862,7 +2094,9 @@ dependencies = [
  "futures-util",
  "lettre",
  "log",
+ "log4rs",
  "markup",
+ "maud",
  "reqwest",
  "sanitize-filename",
  "serde",
@@ -1870,6 +2104,36 @@ dependencies = [
  "uuid",
 ]
 
+[[package]]
+name = "thiserror"
+version = "1.0.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e3de26b0965292219b4287ff031fcba86837900fe9cd2b34ea8ad893c0953d2"
+dependencies = [
+ "thiserror-impl",
+]
+
+[[package]]
+name = "thiserror-impl"
+version = "1.0.55"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "268026685b2be38d7103e9e507c938a1fcb3d7e6eb15e87870b617bf37b6d581"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn 2.0.43",
+]
+
+[[package]]
+name = "thread-id"
+version = "4.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0ec81c46e9eb50deaa257be2f148adf052d1fb7701cfd55ccfab2525280b70b"
+dependencies = [
+ "libc",
+ "winapi",
+]
+
 [[package]]
 name = "time"
 version = "0.3.21"
@@ -1986,6 +2250,15 @@ version = "0.2.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed"
 
+[[package]]
+name = "typemap-ors"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a68c24b707f02dd18f1e4ccceb9d49f2058c2fb86384ef9972592904d7a28867"
+dependencies = [
+ "unsafe-any-ors",
+]
+
 [[package]]
 name = "typenum"
 version = "1.16.0"
@@ -2022,6 +2295,21 @@ dependencies = [
  "tinyvec",
 ]
 
+[[package]]
+name = "unsafe-any-ors"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0a303d30665362d9680d7d91d78b23f5f899504d4f08b3c4cf08d055d87c0ad"
+dependencies = [
+ "destructure_traitobject",
+]
+
+[[package]]
+name = "unsafe-libyaml"
+version = "0.2.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
+
 [[package]]
 name = "url"
 version = "2.5.0"
@@ -2186,6 +2474,15 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
 
+[[package]]
+name = "windows-core"
+version = "0.52.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
+dependencies = [
+ "windows-targets 0.52.5",
+]
+
 [[package]]
 name = "windows-sys"
 version = "0.42.0"
@@ -2249,6 +2546,22 @@ dependencies = [
  "windows_x86_64_msvc 0.48.0",
 ]
 
+[[package]]
+name = "windows-targets"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb"
+dependencies = [
+ "windows_aarch64_gnullvm 0.52.5",
+ "windows_aarch64_msvc 0.52.5",
+ "windows_i686_gnu 0.52.5",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc 0.52.5",
+ "windows_x86_64_gnu 0.52.5",
+ "windows_x86_64_gnullvm 0.52.5",
+ "windows_x86_64_msvc 0.52.5",
+]
+
 [[package]]
 name = "windows_aarch64_gnullvm"
 version = "0.42.2"
@@ -2261,6 +2574,12 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc"
 
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263"
+
 [[package]]
 name = "windows_aarch64_msvc"
 version = "0.42.2"
@@ -2273,6 +2592,12 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3"
 
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6"
+
 [[package]]
 name = "windows_i686_gnu"
 version = "0.42.2"
@@ -2285,6 +2610,18 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241"
 
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9"
+
 [[package]]
 name = "windows_i686_msvc"
 version = "0.42.2"
@@ -2297,6 +2634,12 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00"
 
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf"
+
 [[package]]
 name = "windows_x86_64_gnu"
 version = "0.42.2"
@@ -2309,6 +2652,12 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1"
 
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9"
+
 [[package]]
 name = "windows_x86_64_gnullvm"
 version = "0.42.2"
@@ -2321,6 +2670,12 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953"
 
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596"
+
 [[package]]
 name = "windows_x86_64_msvc"
 version = "0.42.2"
@@ -2333,6 +2688,12 @@ version = "0.48.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a"
 
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0"
+
 [[package]]
 name = "winreg"
 version = "0.10.1"
diff --git a/Cargo.toml b/Cargo.toml
index 6c47d9f..ed3bd99 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -21,3 +21,5 @@ uuid = "1.6.1"
 sanitize-filename = "0.5.0"
 lettre = { version = "0.11.3", features = ["smtp-transport"] }
 markup = "0.15.0"
+maud = { version = "0.26.0", features = ["actix-web"] }
+log4rs = "1.3.0"
diff --git a/src/api/camp_form.rs b/src/api/camp_form.rs
index 575b575..61faaa9 100644
--- a/src/api/camp_form.rs
+++ b/src/api/camp_form.rs
@@ -1,13 +1,15 @@
-use actix_multipart::form::{json, text::Text, MultipartForm};
-use actix_web::{post, HttpResponse};
+use std::collections::HashMap;
+
+use actix_multipart::form::{text::Text, MultipartForm};
+use actix_web::{http::StatusCode, post, HttpResponse, HttpResponseBuilder};
 use lettre::{
     message::MultiPart,
     transport::smtp::authentication::{Credentials, Mechanism},
     Message, SmtpTransport, Transport,
 };
-use reqwest::Request;
-
-use super::errors::ApiError;
+use maud::html;
+use maud::DOCTYPE;
+use reqwest::{Client, Error};
 
 #[derive(Debug, MultipartForm, Default)]
 struct CampForm {
@@ -19,6 +21,7 @@ struct CampForm {
     parent_first_name: Option<Text<String>>,
     #[multipart(rename = "parent-last-name")]
     parent_last_name: Option<Text<String>>,
+    #[multipart(rename = "birth-date")]
     birthdate: Option<Text<String>>,
     gender: Option<Text<String>>,
     street: Option<Text<String>>,
@@ -34,6 +37,8 @@ struct CampForm {
     allergies: Option<Text<String>>,
     week: Option<Text<String>>,
     registration: Option<Text<String>>,
+    #[multipart(rename = "health-form")]
+    health_form: Option<Text<String>>,
 }
 
 #[post("/camp-form")]
@@ -139,13 +144,23 @@ pub async fn camp_form(MultipartForm(form): MultipartForm<CampForm>) -> HttpResp
         .unwrap_or(&Text(String::from("")))
         .0
         .clone();
+    let health = form
+        .health_form
+        .as_ref()
+        .unwrap_or(&Text(String::from("")))
+        .0
+        .clone();
     let reg = registration.clone();
+
+    log::info!("Sending post to database");
+
     log::info!("{first} {last} signed up for camp!");
-    let email = markup::new! {
-        @markup::doctype()
+    let email = html! {
+        (DOCTYPE)
+            meta charset="utf-8";
             html {
                 head {
-                    title { @format!("{} {} signed up for camp!", first, last) }
+                    title { (first) " " (last) " signed up for camp!" }
                     style {
                         "table { border-collapse: collapse; width: 100% }"
                             "td, th { padding: 8px }"
@@ -156,68 +171,72 @@ pub async fn camp_form(MultipartForm(form): MultipartForm<CampForm>) -> HttpResp
                     }
                 }
                 body {
-                    h1 { @format!("Camp form for {} {}!", first, last) }
+                    h1 { "Camp form for " (first) " " (last) }
                     hr;
                     table {
                         tr {
                             th { "Name" }
-                            td { @format!("{} {}", first, last) }
+                            td { (first) " " (last) }
                         }
                         tr {
                             th { "Parent" }
-                            td { @parent }
+                            td { (parent) }
                         }
                         tr {
                             th { "Birthdate" }
-                            td { @birthdate }
+                            td { (birthdate) }
                         }
                         tr {
                             th { "Gender" }
-                            td { @gender }
+                            td { (gender) }
                         }
                         tr {
                             th { "Street" }
-                            td { @street }
+                            td { (street) }
                         }
                         tr {
                             th { "City" }
-                            td { @city }
+                            td { (city) }
                         }
                         tr {
                             th { "State" }
-                            td { @state }
+                            td { (state) }
                         }
                         tr {
                             th { "Zip" }
-                            td { @zip }
+                            td { (zip) }
                         }
                         tr {
                             th { "Parent Phone" }
-                            td { @parent_phone }
+                            td { (parent_phone) }
                         }
                         tr {
                             th { "Parent Email" }
-                            td { @parent_email }
+                            td { (parent_email) }
                         }
                         tr {
                             th { "Grade" }
-                            td { @grade }
+                            td { (grade) }
                         }
                         tr {
                             th { "Camper Allergies" }
-                            td { @allergies }
+                            td { (allergies) }
                         }
                         tr {
                             th { "T-Shirt Size" }
-                            td { @shirt }
+                            td { (shirt) }
                         }
                         tr {
                             th { "Week Choice" }
-                            td { @week }
+                            td { (week) }
+                        }
+                        tr {
+                            th { "Health Form" }
+                            td { (health) }
                         }
                         tr {
                             th { "Registration" }
-                            td { @registration }
+                            td { (registration) }
                         }
                     }
                 }
@@ -225,7 +244,7 @@ pub async fn camp_form(MultipartForm(form): MultipartForm<CampForm>) -> HttpResp
     };
     let multi = MultiPart::alternative_plain_html(
         String::from("A camp form was filled out!"),
-        email.to_string(),
+        email.into_string(),
     );
 
     if let Ok(m) = Message::builder()
@@ -249,78 +268,139 @@ pub async fn camp_form(MultipartForm(form): MultipartForm<CampForm>) -> HttpResp
             .authentication(vec![Mechanism::Plain])
             .build();
         match sender.send(&m) {
-            Ok(res) => log::info!("{:?}", res),
+            Ok(res) => log::info!(
+                "Successfully sent email to server with this response: {:?}",
+                res
+            ),
             Err(e) => log::error!("{e}"),
         }
     } else {
         log::info!("Email incorrect");
     }
 
-    match reg.as_str() {
+    match store_camp_form(form).await {
+        Ok(_) => log::info!("Successfully posted to nextcloud tables"),
+        Err(e) => log::error!("Error in posting camp data: {:?}", e),
+    }
+
+    match health.as_str() {
         "now" => {
-            log::info!("Sending them to pay for registration now");
+            log::info!("Sending them to fill out the health form");
             HttpResponse::Ok()
                 .insert_header(("Access-Control-Expose-Headers", "*"))
                 .insert_header((
                     "HX-Redirect",
-                    "https://secure.myvanco.com/L-Z772/campaign/C-13JPJ",
+                    format!(
+                        "https://tfcconnection.org/camp-health-form/?registration={}",
+                        reg.as_str()
+                    ),
                 ))
                 .finish()
         }
-        "full" => {
-            log::info!("Sending them to pay for the full registration now");
-            HttpResponse::Ok()
-                .insert_header(("Access-Control-Expose-Headers", "*"))
-                .insert_header((
-                    "HX-Redirect",
-                    "https://secure.myvanco.com/L-Z772/campaign/C-13JQE",
-                ))
-                .finish()
-        }
-        "later" => {
-            log::info!("{} would like to pay later", full_name);
-            let html = markup::new! {
-                div {
-                    class { "mt-8" }
-                    h2 {
-                        @format!("Thank you, {}!", full_name)
+        "later" => match reg.as_str() {
+            "now" => {
+                log::info!("Sending them to pay for registration now");
+                HttpResponse::Ok()
+                    .insert_header(("Access-Control-Expose-Headers", "*"))
+                    .insert_header((
+                        "HX-Redirect",
+                        "https://secure.myvanco.com/L-Z772/campaign/C-13JPJ",
+                    ))
+                    .finish()
+            }
+            "full" => {
+                log::info!("Sending them to pay for the full registration now");
+                HttpResponse::Ok()
+                    .insert_header(("Access-Control-Expose-Headers", "*"))
+                    .insert_header((
+                        "HX-Redirect",
+                        "https://secure.myvanco.com/L-Z772/campaign/C-13JQE",
+                    ))
+                    .finish()
+            }
+            "later" => {
+                log::info!("{} would like to pay later", full_name);
+                let html = html! {
+                    div {
+                        class { "mt-8" }
+                        h2 {
+                            "Thank you, " (full_name) "!"
+                        }
+                        p { "Can't wait to see you at camp!" }
+                        p {
+                            class { "" }
+                            "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."
+                        }
                     }
-                    p { "Can't wait to see you at camp!" }
-                    p {
-                        class { "" }
-                        "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."
+                };
+                HttpResponse::Ok().body(html.into_string())
+            }
+            _ => {
+                log::error!("Got registration error.....");
+                let html = html! {
+                    div {
+                        class { "mt-8" }
+                        h2 {
+                            "Thank you, " (full_name) "!"
+                        }
+                        p { "Can't wait to see you at camp!" }
+                        p {
+                            class { "" }
+                            "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."
+                        }
                     }
-                }
-            };
-            HttpResponse::Ok().body(html.to_string())
-        }
+                };
+                HttpResponse::Ok().body(html.into_string())
+            }
+        },
+
         _ => {
-            log::error!("Got registration error.....");
-            let html = markup::new! {
-                div {
-                    class { "mt-8" }
-                    h2 {
-                        @format!("Thank you, {}!", full_name)
-                    }
-                    p { "Can't wait to see you at camp!" }
-                    p {
-                        class { "" }
-                        "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."
-                    }
-                }
-            };
-            HttpResponse::Ok().body(html.to_string())
+            log::error!("Unknown selection for health. We don't know where to send the user.");
+            HttpResponseBuilder::new(StatusCode::IM_A_TEAPOT)
+                .body("Unknown selection for health. We don't know where to send the user.")
         }
     }
 }
 
-async fn store_camp_form(form: CampForm) -> Result<(), ApiError> {
-    let request = reqwest::Client::new();
-    let json = json::Json::from(form);
-    let res = request
-        .post("https://tbl.tfcconnection.org/")
-        .header("xc-token", "Ohah24HNGXVvvixv8tvVJW5uNNdWjDJHG1d4t3o9")
-        .body(json)
+async fn store_camp_form(form: CampForm) -> Result<(), Error> {
+    let request = Client::new();
+    let mut map = HashMap::new();
+    map.insert(
+        63,
+        format!(
+            "{} {}",
+            &form.first_name.unwrap_or(Text(String::new())).0,
+            &form.last_name.unwrap_or(Text(String::new())).0
+        ),
+    );
+    map.insert(
+        64,
+        format!(
+            "{} {}",
+            form.parent_first_name.unwrap_or(Text(String::new())).0,
+            form.parent_last_name.unwrap_or(Text(String::new())).0
+        ),
+    );
+    map.insert(65, form.parent_phone.unwrap_or(Text(String::new())).0);
+    map.insert(66, form.parent_email.unwrap_or(Text(String::new())).0);
+    map.insert(67, form.birthdate.unwrap_or(Text(String::new())).0);
+    map.insert(69, form.gender.unwrap_or(Text(String::new())).0);
+    map.insert(70, form.street.unwrap_or(Text(String::new())).0);
+    map.insert(71, form.city.unwrap_or(Text(String::new())).0);
+    map.insert(72, form.state.unwrap_or(Text(String::new())).0);
+    map.insert(73, form.zip.unwrap_or(Text(0)).0.to_string());
+    map.insert(74, form.grade.unwrap_or(Text(String::new())).0);
+    map.insert(75, form.week.unwrap_or(Text(String::new())).0);
+    map.insert(76, form.shirt.unwrap_or(Text(String::new())).0);
+    map.insert(77, form.registration.unwrap_or(Text(String::new())).0);
+    map.insert(115, form.health_form.unwrap_or(Text(String::new())).0);
+    let mut json = HashMap::new();
+    json.insert("data", map);
+    request
+        .post("https://staff.tfcconnection.org/apps/tables/api/1/tables/5/rows")
+        // .header("xc-token", "Ohah24HNGXVvvixv8tvVJW5uNNdWjDJHG1d4t3o9")
+        .basic_auth("chris", Some("2VHeGxeC^Zf9KqFK^G@Pt!zu2q^6@b"))
+        .json(&json)
         .send()
         .await?;
     Ok(())
diff --git a/src/api/errors.rs b/src/api/errors.rs
index 106d97a..e0f759a 100644
--- a/src/api/errors.rs
+++ b/src/api/errors.rs
@@ -18,8 +18,3 @@ pub struct ApiError {
     pub cause: Option<String>,
     pub error_type: ApiErrorType,
 }
-
-#[derive(Serialize)]
-pub struct ApiErrorResponse {
-    pub error: String,
-}
diff --git a/src/main.rs b/src/main.rs
index d98546b..075f3d7 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,10 +9,38 @@ use api::local_trip_form::local_form;
 use api::mt_form::mt_form;
 use api::parent_form::parent_form;
 use api::teacher_form::teacher_form;
+use log::LevelFilter;
+use log4rs::append::console::ConsoleAppender;
+use log4rs::append::file::FileAppender;
+use log4rs::config::{Appender, Root};
+use log4rs::encode::pattern::PatternEncoder;
+use log4rs::Config;
 
 #[actix_web::main]
 async fn main() -> std::io::Result<()> {
-    env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
+    // env_logger::init_from_env(env_logger::Env::new().default_filter_or("info"));
+    let stdout = ConsoleAppender::builder()
+        .encoder(Box::new(PatternEncoder::new(
+            "{d(%Y-%m-%d %H:%M:%S)} {h({l})} - {m}\n",
+        )))
+        .build();
+    let logfile = FileAppender::builder()
+        .encoder(Box::new(PatternEncoder::new(
+            "{d(%Y-%m-%d %H:%M:%S)} {h({l})} - {m}\n",
+        )))
+        .build("./tmp/api.log")?;
+
+    let config = Config::builder()
+        .appender(Appender::builder().build("stdout", Box::new(stdout)))
+        .appender(Appender::builder().build("logfile", Box::new(logfile)))
+        .build(
+            Root::builder()
+                .appenders(vec!["logfile", "stdout"])
+                .build(LevelFilter::Info),
+        )
+        .unwrap();
+
+    let _handle = log4rs::init_config(config).expect("error setting up logger");
 
     log::info!("creating temporary upload directory");
     std::fs::create_dir_all("./tmp")?;