diff --git a/recipes/src/lib.rs b/recipes/src/lib.rs index 95960af..d4e4866 100644 --- a/recipes/src/lib.rs +++ b/recipes/src/lib.rs @@ -101,6 +101,7 @@ impl RecipeEntry { pub struct Recipe { pub title: String, pub desc: Option, + pub serving_count: Option, pub steps: Vec, } @@ -110,6 +111,7 @@ impl Recipe { title: title.into(), desc: desc.map(|s| s.into()), steps: Vec::new(), + serving_count: Default::default(), } } diff --git a/web/src/app_state.rs b/web/src/app_state.rs index d7e1571..95c9ee3 100644 --- a/web/src/app_state.rs +++ b/web/src/app_state.rs @@ -151,13 +151,14 @@ pub fn parse_recipes( Some(parsed) => { let mut parsed_map = BTreeMap::new(); for r in parsed { - let recipe = match parse::as_recipe(&r.recipe_text()) { + let mut recipe = match parse::as_recipe(&r.recipe_text()) { Ok(r) => r, Err(e) => { error!("Error parsing recipe {}", e); continue; } }; + recipe.serving_count = r.serving_count; parsed_map.insert(r.recipe_id().to_owned(), recipe); } Ok(Some(parsed_map)) diff --git a/web/src/components/recipe.rs b/web/src/components/recipe.rs index 9ccb4a8..c8bb6c1 100644 --- a/web/src/components/recipe.rs +++ b/web/src/components/recipe.rs @@ -49,7 +49,15 @@ pub fn Editor<'ctx, G: Html>(cx: Scope<'ctx>, props: RecipeComponentProps<'ctx>) let store = crate::api::HttpStore::get_from_context(cx); let recipe: &Signal = create_signal(cx, RecipeEntry::new(&recipe_id, String::new())); - let text = create_signal(cx, String::new()); + let text = create_signal(cx, String::from("0")); + let serving_count_str = create_signal(cx, String::new()); + let serving_count = create_memo(cx, || { + if let Ok(count) = serving_count_str.get().parse::() { + count + } else { + 0 + } + }); let error_text = create_signal(cx, String::from("Parse results...")); let aria_hint = create_signal(cx, "false"); let category = create_signal(cx, "Entree".to_owned()); @@ -83,6 +91,10 @@ pub fn Editor<'ctx, G: Html>(cx: Scope<'ctx>, props: RecipeComponentProps<'ctx>) label(for="recipe_category") { "Category" } input(name="recipe_category", bind:value=category, on:change=move |_| dirty.set(true)) } + div { + label(for="serving_count") { "Serving Count" } + input(name="serving_count", bind:value=serving_count_str, on:change=move |_| dirty.set(true)) + } div { div(class="row-flex") { label(for="recipe_text", class="block align-stretch expand-height") { "Recipe: " } @@ -119,7 +131,7 @@ pub fn Editor<'ctx, G: Html>(cx: Scope<'ctx>, props: RecipeComponentProps<'ctx>) id: id.get_untracked().as_ref().clone(), text: text.get_untracked().as_ref().clone(), category, - serving_count: None, + serving_count: Some(*serving_count.get()), }; sh.dispatch(cx, Message::SaveRecipe(recipe_entry, None)); dirty.set(false); @@ -171,18 +183,22 @@ pub fn Viewer<'ctx, G: Html>(cx: Scope<'ctx>, props: RecipeComponentProps<'ctx>) let recipe_signal = sh.get_selector(cx, move |state| { if let Some(recipe) = state.get().recipes.get(&recipe_id) { let title = recipe.title.clone(); + let serving_count = recipe.serving_count.clone(); let desc = recipe.desc.clone().unwrap_or_else(|| String::new()); let steps = recipe.steps.clone(); - Some((title, desc, steps)) + Some((title, serving_count, desc, steps)) } else { None } }); - if let Some((title, desc, steps)) = recipe_signal.get().as_ref().clone() { + if let Some((title, serving_count, desc, steps)) = recipe_signal.get().as_ref().clone() { debug!("Viewing recipe."); view.set(view! {cx, div(class="recipe") { h1(class="recipe_title") { (title) } + div(class="serving_count") { + "Serving Count: " (serving_count.map(|v| format!("{}", v)).unwrap_or_else(|| "Unconfigured".to_owned())) + } div(class="recipe_description") { (desc) } diff --git a/web/src/components/recipe_plan.rs b/web/src/components/recipe_plan.rs index 83b2081..f14ef38 100644 --- a/web/src/components/recipe_plan.rs +++ b/web/src/components/recipe_plan.rs @@ -60,8 +60,14 @@ pub fn CategoryGroup<'ctx, G: Html>( iterable=r, view=move |cx, sig| { let title = create_memo(cx, move || sig.get().1.title.clone()); + let serving_count = create_memo(cx, move || sig.get().1.serving_count.clone()); view! {cx, - div(class="cell column-flex justify-end align-stretch") { RecipeSelection(i=sig.get().0.to_owned(), title=title, sh=sh) } + div(class="cell column-flex justify-end align-stretch") { + RecipeSelection( + i=sig.get().0.to_owned(), + title=title, sh=sh, + serving_count=serving_count, + ) } } }, key=|sig| sig.get().0.to_owned(), diff --git a/web/src/components/recipe_selection.rs b/web/src/components/recipe_selection.rs index fba5176..112963b 100644 --- a/web/src/components/recipe_selection.rs +++ b/web/src/components/recipe_selection.rs @@ -23,6 +23,7 @@ use crate::components::NumberField; pub struct RecipeCheckBoxProps<'ctx> { pub i: String, pub title: &'ctx ReadSignal, + pub serving_count: &'ctx ReadSignal>, pub sh: StateHandler<'ctx>, } @@ -35,7 +36,7 @@ pub fn RecipeSelection<'ctx, G: Html>( cx: Scope<'ctx>, props: RecipeCheckBoxProps<'ctx>, ) -> View { - let RecipeCheckBoxProps { i, title, sh } = props; + let RecipeCheckBoxProps { i, title, sh, serving_count, } = props; let id = Rc::new(i); let id_for_count = id.clone(); // NOTE(jwall): The below get's a little tricky. We need a separate signal to bind for the @@ -66,6 +67,9 @@ pub fn RecipeSelection<'ctx, G: Html>( let for_id = name.clone(); view! {cx, label(for=for_id, class="flex-item-grow") { a(href=href) { (*title) } } + div { + "Serves: " (serving_count.get().map(|v| v.to_string()).unwrap_or("Unconfigured".to_owned())) + } NumberField(name=name, class="flex-item-shrink".to_string(), counter=count, min=0.0, on_change=Some(move |_| { debug!(idx=%id, count=%(*count.get_untracked()), "setting recipe count"); sh.dispatch(cx, Message::UpdateRecipeCount(id.as_ref().clone(), *count.get_untracked() as u32));