From acf23d5430b033302478d7032a0e64f8dfc483e7 Mon Sep 17 00:00:00 2001
From: Mitchell M
Date: Tue, 6 Oct 2026 14:33:29 -0500
Subject: [PATCH] implement code submission feature with routing and server
validation
---
src/app.rs | 10 ++++-
src/codes/code1.rs | 5 +++
src/codes/mod.rs | 105 +++++++++++++++++++++++++++++++++++++++++++++
src/lib.rs | 1 +
src/manage.rs | 9 +++-
style/main.scss | 19 ++++++++
6 files changed, 146 insertions(+), 3 deletions(-)
create mode 100644 src/codes/code1.rs
create mode 100644 src/codes/mod.rs
diff --git a/src/app.rs b/src/app.rs
index 993bb9e..d5efaf9 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -2,9 +2,10 @@ use leptos::prelude::*;
use leptos_meta::{provide_meta_context, MetaTags, Stylesheet, Title};
use leptos_router::{
components::{Route, Router, Routes},
- StaticSegment,
+ ParamSegment, StaticSegment,
};
+use crate::codes::{HomePage, SubmitPage};
use crate::manage::ManagePage;
pub fn shell(options: LeptosOptions) -> impl IntoView {
@@ -36,13 +37,18 @@ pub fn App() -> impl IntoView {
// sets the document title
-
+
// content for this welcome page
+
+
diff --git a/src/codes/code1.rs b/src/codes/code1.rs
new file mode 100644
index 0000000..7e167cf
--- /dev/null
+++ b/src/codes/code1.rs
@@ -0,0 +1,5 @@
+pub const CODE: &str = "Qj4eAn";
+pub const LIVE_MESSAGE: &str = "Welcome! Your code has been accepted.";
+pub const NOT_LIVE_MESSAGE: &str = "Thanks for submitting your code. Please try again later.";
+
+pub(super) fn on_submit(_live: bool) {}
diff --git a/src/codes/mod.rs b/src/codes/mod.rs
new file mode 100644
index 0000000..8db2b20
--- /dev/null
+++ b/src/codes/mod.rs
@@ -0,0 +1,105 @@
+use leptos::prelude::*;
+use leptos_router::{
+ components::A,
+ hooks::{use_navigate, use_params_map},
+ NavigateOptions,
+};
+
+#[cfg(feature = "ssr")]
+mod code1;
+
+#[server]
+async fn submit_code(code: String) -> Result {
+ let live = crate::manage::server_live_state();
+ let message = if code == crate::codes::code1::CODE {
+ crate::codes::code1::on_submit(live);
+
+ if live {
+ crate::codes::code1::LIVE_MESSAGE
+ } else {
+ crate::codes::code1::NOT_LIVE_MESSAGE
+ }
+ } else {
+ "This code is not recognized."
+ };
+
+ Ok(message.to_string())
+}
+
+fn encode_path_segment(value: &str) -> String {
+ value
+ .bytes()
+ .map(|byte| {
+ if byte.is_ascii_alphanumeric() || b"-_.~".contains(&byte) {
+ (byte as char).to_string()
+ } else {
+ format!("%{byte:02X}")
+ }
+ })
+ .collect()
+}
+
+#[component]
+pub fn HomePage() -> impl IntoView {
+ let (code, set_code) = signal(String::new());
+ let navigate = use_navigate();
+
+ let on_submit = move |event: leptos::ev::SubmitEvent| {
+ event.prevent_default();
+ let code = encode_path_segment(code.get_untracked().trim());
+ if !code.is_empty() {
+ navigate(&format!("/submit/{code}"), NavigateOptions::default());
+ }
+ };
+
+ view! {
+
+ "CODE SUBMISSION"
+ "Submit a code"
+ "Enter your code to continue."
+
+
+ }
+}
+
+#[component]
+pub fn SubmitPage() -> impl IntoView {
+ let params = use_params_map();
+ let submitted_code = move || params.with(|params| params.get("code").unwrap_or_default());
+ let submission = Resource::new(move || submitted_code(), |code| submit_code(code));
+
+ view! {
+
+ "CODE SUBMISSION"
+ "Checking code..."
}>
+ {move || Suspend::new(async move {
+ match submission.await {
+ Ok(message) => view! {
+ "Submission result"
+ {message}
+ }.into_any(),
+ Err(error) => view! {
+
+ "Unable to check code: " {error.to_string()}
+
+ }.into_any(),
+ }
+ })}
+
+ "Back to home"
+
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index c52c809..b9d274a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,4 +1,5 @@
pub mod app;
+pub mod codes;
pub mod manage;
#[cfg(feature = "hydrate")]
diff --git a/src/manage.rs b/src/manage.rs
index 642e8d6..55d91d3 100644
--- a/src/manage.rs
+++ b/src/manage.rs
@@ -1,4 +1,5 @@
use leptos::prelude::*;
+use leptos_router::components::A;
#[cfg(feature = "ssr")]
use std::sync::atomic::{AtomicBool, Ordering};
@@ -6,12 +7,17 @@ use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(feature = "ssr")]
static SERVER_LIVE: AtomicBool = AtomicBool::new(false);
+#[cfg(feature = "ssr")]
+pub(crate) fn server_live_state() -> bool {
+ SERVER_LIVE.load(Ordering::SeqCst)
+}
+
#[cfg(feature = "ssr")]
const STATUS_SECRET_KEY: &str = "password";
#[server]
async fn get_live_state() -> Result {
- Ok(SERVER_LIVE.load(Ordering::SeqCst))
+ Ok(server_live_state())
}
#[server]
@@ -46,6 +52,7 @@ pub fn ManagePage() -> impl IntoView {
}
})}
+ "Back to home"
}
}
diff --git a/style/main.scss b/style/main.scss
index d659011..9bc3e7b 100644
--- a/style/main.scss
+++ b/style/main.scss
@@ -50,6 +50,11 @@ h1 {
gap: 12px;
}
+.submit-form {
+ display: grid;
+ gap: 12px;
+}
+
.key-label {
color: #d3dceb;
font-size: 0.9rem;
@@ -155,6 +160,20 @@ h1 {
line-height: 1.5;
}
+.home-link {
+ display: inline-block;
+ margin-top: 24px;
+ color: #8bbaff;
+ font-size: 0.9rem;
+ font-weight: 600;
+ text-decoration: none;
+}
+
+.home-link:hover {
+ color: #b3d2ff;
+ text-decoration: underline;
+}
+
@media (prefers-reduced-motion: reduce) {
* {
transition-duration: 0.01ms !important;