Fix bucket-404 on empty resources, redesign review actions as select-then-confirm, CSS polish
Deploy / deploy (push) Successful in 35s

get_resource's Kv path treated a not-yet-created bucket (nothing
submitted there yet) as a hard error instead of an empty list -
projects bucket never got created since the backfill found nothing to
migrate, so /review's Projects alternative 404'd outright. Now matches
store_answer's own "doesn't exist yet is normal" posture; a specific
key request still errors, only listing degrades gracefully.

AnswerRow's Invite/Decline buttons fired transition_answer immediately
on click, with no staging step and no visible confirmation once it
landed (the resource list never refetched, so a click barely looked
like it did anything). Redesigned as toggle-select (reusing the
.select-option pattern from the resource-backed select requirement)
plus one explicit Confirm button; a successful transition now refetches
the parent Resource so the row actually reflects the change.

CSS: soften the hard cut where the hero canvas meets the page
background below it (gradient fade over the last few ems, matching
--paper); center the "Asked by X" responsible note with the em-dash
starting its own line; give it and the report button real styling
(previously unstyled default text).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Bendik Aagaard Lynghaug
2026-08-06 12:42:54 +02:00
co-authored by Claude Sonnet 5
parent 11c6f3e8f7
commit a200161334
3 changed files with 134 additions and 45 deletions
+14 -8
View File
@@ -133,13 +133,19 @@ async fn fetch_resource_value(
let value = match &resource.source {
ResourceSource::Kv { bucket } => {
let store = state
.jetstream
.get_key_value(bucket)
.await
.map_err(|e| ServerFnError::new(format!("resource bucket unavailable: {e}")))?;
match &resource.key {
Some(key) => {
// A bucket only ever gets created on first write
// (answers::store_answer's own create-on-first-use, or the
// event-sourcing projection upsert) - a bucket declared in
// content but never yet written to is a completely normal
// "nothing submitted here yet" state, not an error. Listing
// it reads the same as an empty bucket would; asking for
// one specific key that can't possibly exist yet still
// surfaces as an error, same as key-not-found.
let store = state.jetstream.get_key_value(bucket).await.ok();
match (&resource.key, store) {
(Some(_), None) => return Err(ServerFnError::new("resource key not found")),
(None, None) => serde_json::Value::Array(Vec::new()),
(Some(key), Some(store)) => {
let bytes = store
.get(key)
.await
@@ -147,7 +153,7 @@ async fn fetch_resource_value(
.ok_or_else(|| ServerFnError::new("resource key not found"))?;
serde_json::from_slice(&bytes).map_err(|e| ServerFnError::new(e.to_string()))?
}
None => {
(None, Some(store)) => {
use futures::TryStreamExt;
let keys: Vec<String> = store
.keys()