diff --git a/kitchen/src/web/mod.rs b/kitchen/src/web/mod.rs index c7fc01d..b4ad46a 100644 --- a/kitchen/src/web/mod.rs +++ b/kitchen/src/web/mod.rs @@ -258,6 +258,23 @@ async fn api_all_plans( } } +async fn api_save_plan_for_date( + Extension(app_store): Extension>, + session: storage::UserIdFromSession, + Path(date): Path, + Json(meal_plan): Json>, +) -> api::EmptyResponse { + use storage::{UserId, UserIdFromSession::FoundUserId}; + if let FoundUserId(UserId(id)) = session { + app_store + .save_meal_plan(id.as_str(), &meal_plan, date) + .await + .into() + } else { + api::EmptyResponse::Unauthorized + } +} + async fn api_save_plan( Extension(app_store): Extension>, session: storage::UserIdFromSession, @@ -484,7 +501,10 @@ fn mk_v2_routes() -> Router { // mealplan api path routes .route("/plan", get(api_plan).post(api_save_plan)) .route("/plan/since/:date", get(api_plan_since)) - .route("/plan/at/:date", get(api_plan_for_date)) + .route( + "/plan/at/:date", + get(api_plan_for_date).post(api_save_plan_for_date), + ) .route("/plan/all", get(api_all_plans)) .route( "/inventory", diff --git a/web/src/app_state.rs b/web/src/app_state.rs index 17daf80..4aac4a3 100644 --- a/web/src/app_state.rs +++ b/web/src/app_state.rs @@ -75,7 +75,7 @@ pub enum Message { SaveState(Option>), LoadState(Option>), UpdateStaples(String, Option>), - SelectPlanDate(NaiveDate), + SelectPlanDate(NaiveDate, Option>), } impl Debug for Message { @@ -116,7 +116,7 @@ impl Debug for Message { Self::SaveState(_) => write!(f, "SaveState"), Self::LoadState(_) => write!(f, "LoadState"), Self::UpdateStaples(arg, _) => f.debug_tuple("UpdateStaples").field(arg).finish(), - Self::SelectPlanDate(arg) => f.debug_tuple("SelectPlanDate").field(arg).finish(), + Self::SelectPlanDate(arg, _) => f.debug_tuple("SelectPlanDate").field(arg).finish(), } } } @@ -452,7 +452,7 @@ impl MessageMapper for StateMachine { }); return; } - Message::SelectPlanDate(date) => { + Message::SelectPlanDate(date, callback) => { let store = self.store.clone(); let local_store = self.local_store.clone(); spawn_local_scoped(cx, async move { @@ -476,6 +476,8 @@ impl MessageMapper for StateMachine { local_store.set_plan_date(&date); original.set(original_copy); + + callback.map(|f| f()); }); // NOTE(jwall): Because we do our signal set above in the async block // we have to return here to avoid lifetime issues and double setting diff --git a/web/src/components/header.rs b/web/src/components/header.rs index 15e93af..bda1fc4 100644 --- a/web/src/components/header.rs +++ b/web/src/components/header.rs @@ -26,7 +26,7 @@ pub fn Header<'ctx, G: Html>(cx: Scope<'ctx>, h: StateHandler<'ctx>) -> View nav(class="no-print") { h1(class="title") { "Kitchen" } ul { - li { a(href="/ui/planning/plan") { "MealPlan" } } + li { a(href="/ui/planning/select") { "MealPlan" } } li { a(href="/ui/manage/ingredients") { "Manage" } } li { a(href="/ui/login") { (login.get()) } } } diff --git a/web/src/components/plan_list.rs b/web/src/components/plan_list.rs index 56f1e63..277f1db 100644 --- a/web/src/components/plan_list.rs +++ b/web/src/components/plan_list.rs @@ -29,16 +29,24 @@ pub struct PlanListProps<'ctx> { pub fn PlanList<'ctx, G: Html>(cx: Scope<'ctx>, props: PlanListProps<'ctx>) -> View { let PlanListProps { sh, list } = props; view! {cx, - Indexed( - iterable=list, - view=move |cx, date| { - let date_display = format!("{}", date); - view!{cx, - button(on:click=move |_| { - sh.dispatch(cx, Message::SelectPlanDate(date)) - }) { (date_display) } - } - }, - ) + div() { + table() { + Indexed( + iterable=list, + view=move |cx, date| { + let date_display = format!("{}", date); + view!{cx, + tr() { + td() { + span(role="button", class="outline", on:click=move |_| { + sh.dispatch(cx, Message::SelectPlanDate(date, None)) + }) { (date_display) } + } + } + } + }, + ) + } + } } } diff --git a/web/src/pages/planning/select.rs b/web/src/pages/planning/select.rs index 565dad9..78894e1 100644 --- a/web/src/pages/planning/select.rs +++ b/web/src/pages/planning/select.rs @@ -12,7 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. use super::PlanningPage; -use crate::{app_state::StateHandler, components::PlanList}; +use crate::{ + app_state::{Message, StateHandler}, + components::PlanList, +}; use chrono::NaiveDate; use sycamore::prelude::*; @@ -30,6 +33,15 @@ pub fn SelectPage<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> Vie view! {cx, PlanningPage( selected=Some("Select".to_owned()), - ) { PlanList(sh=sh, list=plan_dates) } + ) { + PlanList(sh=sh, list=plan_dates) + span(role="button", on:click=move |_| { + sh.dispatch(cx, Message::SelectPlanDate(chrono::offset::Local::now().naive_local().date(), Some(Box::new(|| { + sycamore_router::navigate("/ui/planning/plan"); + })))) + }) { + "Start Plan for Today" + } + } } }