feat(quotesdb): show locked banner on /submit when submissions are closed

Check GET /api/status on mount; if submissions_locked is true, hide the
form and show a .submissions-closed-banner instead. Fail-open: on error,
treat as unlocked and display the form normally.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
main
Elijah Voigt 3 months ago
parent 0be1193759
commit 7d0df10d75

@ -16,6 +16,10 @@ use yew_router::prelude::*;
/// Provides a form for creating a new quote with fields for text, author, /// Provides a form for creating a new quote with fields for text, author,
/// source, date, tags, and an optional custom auth code. On success, navigates /// source, date, tags, and an optional custom auth code. On success, navigates
/// directly to the quote detail page and stores the auth code in session storage. /// directly to the quote detail page and stores the auth code in session storage.
///
/// On mount, fetches `GET /api/status` to check whether submissions are locked.
/// If locked, the form is hidden and a closed-submissions banner is shown instead.
/// If the status check fails, the form is shown (fail-open behaviour).
#[function_component(SubmitPage)] #[function_component(SubmitPage)]
pub fn submit_page() -> Html { pub fn submit_page() -> Html {
let text = use_state(String::new); let text = use_state(String::new);
@ -27,8 +31,26 @@ pub fn submit_page() -> Html {
let submitting = use_state(|| false); let submitting = use_state(|| false);
let error: UseStateHandle<Option<String>> = use_state(|| None); let error: UseStateHandle<Option<String>> = use_state(|| None);
let turnstile_token: UseStateHandle<Option<String>> = use_state(|| None); let turnstile_token: UseStateHandle<Option<String>> = use_state(|| None);
// None = loading, Some(true) = locked, Some(false) = open
let submissions_locked: UseStateHandle<Option<bool>> = use_state(|| None);
let navigator = use_navigator().unwrap(); let navigator = use_navigator().unwrap();
// Check submission lock state on mount. Fail-open: show the form on error.
{
let submissions_locked = submissions_locked.clone();
use_effect_with((), move |_| {
spawn_local(async move {
match api::get_status().await {
Ok(status) => submissions_locked.set(Some(status.submissions_locked)),
Err(_) => {
// Fail-open: show the form if status check fails.
submissions_locked.set(Some(false));
}
}
});
});
}
// Register the Turnstile callback in the global window object. // Register the Turnstile callback in the global window object.
{ {
let turnstile_token = turnstile_token.clone(); let turnstile_token = turnstile_token.clone();
@ -139,11 +161,16 @@ pub fn submit_page() -> Html {
<div class="page-submit"> <div class="page-submit">
<h1 class="page-submit__title">{ "Submit a Quote" }</h1> <h1 class="page-submit__title">{ "Submit a Quote" }</h1>
if let Some(err) = (*error).clone() { if *submissions_locked == Some(true) {
<ErrorDisplay message={err} /> <div class="submissions-closed-banner">
} <p>{ "Submissions are currently closed." }</p>
</div>
} else if submissions_locked.is_some() {
if let Some(err) = (*error).clone() {
<ErrorDisplay message={err} />
}
<form class="submit-form" onsubmit={onsubmit}> <form class="submit-form" onsubmit={onsubmit}>
<div class="submit-form__field"> <div class="submit-form__field">
<label class="submit-form__label" for="text">{ "Quote text *" }</label> <label class="submit-form__label" for="text">{ "Quote text *" }</label>
<textarea <textarea
@ -274,7 +301,8 @@ pub fn submit_page() -> Html {
} }
</button> </button>
</div> </div>
</form> </form>
}
</div> </div>
} }
} }

@ -563,6 +563,17 @@ code {
color: var(--color-text-muted); color: var(--color-text-muted);
} }
/* ── Submissions Closed Banner ─────────────────────────────── */
.submissions-closed-banner {
background: #fefce8;
border: 1px solid #fde047;
border-radius: var(--radius);
padding: 1rem 1.25rem;
margin-bottom: 1.5rem;
color: #854d0e;
font-size: 0.95rem;
}
/* ── Responsive ────────────────────────────────────────────── */ /* ── Responsive ────────────────────────────────────────────── */
@media (max-width: 640px) { @media (max-width: 640px) {
h1 { h1 {

Loading…
Cancel
Save