Fix get_resource/transition_answer: scope feature lookup to its own alternative
Deploy / deploy (push) Successful in 28s

Both resolved a feature by name flattened across every alternative on
the question, not scoped to the one the caller actually meant - fine
when every question had exactly one alternative with a resource
feature, silently wrong now that /review has three (Applicants,
Inquiries, Subscribers all use the same empty feature name). Every
lookup always resolved to the first alternative's feature - Subscribers
was reading the (empty) Applicants bucket instead of its own, and
Invite/Decline/Mark-handled buttons would have had the same problem
had two of those transitions ever been clicked side by side.

Threaded a new `alternative` parameter through get_resource,
transition_answer, and their client-side callers
(ResourceFeature/ResourceValue/AnswerRow) - the fix lives entirely in
the already-generic resource-fetching machinery, no per-alternative
special-casing.
This commit is contained in:
Bendik Aagaard Lynghaug
2026-08-05 15:04:02 +02:00
parent c73981da2d
commit 6df895b1d3
3 changed files with 41 additions and 7 deletions
+16 -2
View File
@@ -420,11 +420,13 @@ fn AlternativeCard(
let file_refs = file_refs.clone();
let field_prefix = field_prefix.clone();
let question_id = question_id.clone();
let alt_name = alternative.name.clone();
move |feature| {
let field_map = field_map.clone();
let file_refs = file_refs.clone();
let field_prefix = field_prefix.clone();
let question_id = question_id.clone();
let alt_name = alt_name.clone();
let resource = feature.resource.clone();
let feature_name = feature.name.clone();
view! {
@@ -445,6 +447,7 @@ fn AlternativeCard(
view! {
<ResourceFeature
question_id=question_id.clone()
alternative=alt_name.clone()
feature_name=feature_name.clone()
transitions=spec.transitions.clone()
/>
@@ -603,16 +606,18 @@ fn AlternativeCard(
#[component]
fn ResourceFeature(
question_id: String,
alternative: String,
feature_name: String,
transitions: Vec<Transition>,
) -> impl IntoView {
let data = Resource::new(
{
let question_id = question_id.clone();
let alternative = alternative.clone();
let feature_name = feature_name.clone();
move || (question_id.clone(), feature_name.clone())
move || (question_id.clone(), alternative.clone(), feature_name.clone())
},
|(q, f)| get_resource(q, f),
|(q, a, f)| get_resource(q, a, f),
);
view! {
@@ -620,6 +625,7 @@ fn ResourceFeature(
<Suspense fallback=|| view! { <p class="resource-loading">"loading…"</p> }>
{move || {
let question_id = question_id.clone();
let alternative = alternative.clone();
let feature_name = feature_name.clone();
let transitions = transitions.clone();
data.get()
@@ -628,6 +634,7 @@ fn ResourceFeature(
view! {
<ResourceValue
question_id=question_id
alternative=alternative
feature_name=feature_name
transitions=transitions
value=value
@@ -653,6 +660,7 @@ fn ResourceFeature(
#[component]
fn ResourceValue(
question_id: String,
alternative: String,
feature_name: String,
transitions: Vec<Transition>,
value: serde_json::Value,
@@ -671,12 +679,14 @@ fn ResourceValue(
key=|a| a.id.clone()
children={
let question_id = question_id.clone();
let alternative = alternative.clone();
let feature_name = feature_name.clone();
let transitions = transitions.clone();
move |answer: Answer| {
view! {
<AnswerRow
question_id=question_id.clone()
alternative=alternative.clone()
feature_name=feature_name.clone()
transitions=transitions.clone()
answer=answer
@@ -701,6 +711,7 @@ fn ResourceValue(
#[component]
fn AnswerRow(
question_id: String,
alternative: String,
feature_name: String,
transitions: Vec<Transition>,
answer: Answer,
@@ -755,10 +766,12 @@ fn AnswerRow(
key=|t| t.to.clone()
children={
let question_id = question_id.clone();
let alternative = alternative.clone();
let feature_name = feature_name.clone();
let item_id = item_id.clone();
move |t: Transition| {
let question_id = question_id.clone();
let alternative = alternative.clone();
let feature_name = feature_name.clone();
let item_id = item_id.clone();
let to = t.to.clone();
@@ -768,6 +781,7 @@ fn AnswerRow(
on:click=move |_| {
transition.dispatch(TransitionAnswer {
question_id: question_id.clone(),
alternative: alternative.clone(),
feature_name: feature_name.clone(),
item_id: item_id.clone(),
to: to.clone(),