mirror of
https://github.com/zaphar/kitchen.git
synced 2025-07-22 19:40:14 -04:00
Handle messages for selecting a plan_date
This commit is contained in:
parent
3f0836dad2
commit
f808ca8585
@ -116,6 +116,16 @@
|
||||
},
|
||||
"query": "insert into staples (user_id, content) values (?, ?)\n on conflict(user_id) do update set content = excluded.content"
|
||||
},
|
||||
"1b6fd91460bef61cf02f210404a4ca57b520c969d1f9613e7101ee6aa7a9962a": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Right": 6
|
||||
}
|
||||
},
|
||||
"query": "insert into modified_amts(user_id, name, form, measure_type, amt, plan_date)\n values (?, ?, ?, ?, ?, ?) on conflict (user_id, name, form, measure_type, plan_date) do update set amt=excluded.amt"
|
||||
},
|
||||
"2582522f8ca9f12eccc70a3b339d9030aee0f52e62d6674cfd3862de2a68a177": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@ -236,6 +246,40 @@
|
||||
},
|
||||
"query": "insert into recipes (user_id, recipe_id, recipe_text) values (?, ?, ?)\n on conflict(user_id, recipe_id) do update set recipe_text=excluded.recipe_text"
|
||||
},
|
||||
"4237ff804f254c122a36a14135b90434c6576f48d3a83245503d702552ea9f30": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "amt",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
}
|
||||
},
|
||||
"query": "select\n name,\n amt\nfrom extra_items\nwhere\n user_id = ?\n and plan_date = ?"
|
||||
},
|
||||
"5883c4a57def93cca45f8f9d81c8bba849547758217cd250e7ab28cc166ab42b": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Right": 5
|
||||
}
|
||||
},
|
||||
"query": "insert into filtered_ingredients(user_id, name, form, measure_type, plan_date)\n values (?, ?, ?, ?, ?) on conflict(user_id, name, form, measure_type, plan_date) DO NOTHING"
|
||||
},
|
||||
"5d743897fb0d8fd54c3708f1b1c6e416346201faa9e28823c1ba5a421472b1fa": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@ -264,6 +308,42 @@
|
||||
},
|
||||
"query": "select content from staples where user_id = ?"
|
||||
},
|
||||
"699ff0f0d4d4c6e26a21c1922a5b5249d89ed1677680a2276899a7f8b26344ee": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "form",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "measure_type",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "amt",
|
||||
"ordinal": 3,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
}
|
||||
},
|
||||
"query": "select\n modified_amts.name,\n modified_amts.form,\n modified_amts.measure_type,\n modified_amts.amt\nfrom modified_amts\nwhere\n user_id = ?\n and plan_date = ?"
|
||||
},
|
||||
"6c43908d90f229b32ed8b1b076be9b452a995e1b42ba2554e947c515b031831a": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
@ -294,6 +374,36 @@
|
||||
},
|
||||
"query": "delete from sessions where id = ?"
|
||||
},
|
||||
"7695a0602395006f9b76ecd4d0cb5ecd5dee419b71b3b0b9ea4f47a83f3df41a": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
{
|
||||
"name": "name",
|
||||
"ordinal": 0,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "form",
|
||||
"ordinal": 1,
|
||||
"type_info": "Text"
|
||||
},
|
||||
{
|
||||
"name": "measure_type",
|
||||
"ordinal": 2,
|
||||
"type_info": "Text"
|
||||
}
|
||||
],
|
||||
"nullable": [
|
||||
false,
|
||||
false,
|
||||
false
|
||||
],
|
||||
"parameters": {
|
||||
"Right": 2
|
||||
}
|
||||
},
|
||||
"query": "select\n filtered_ingredients.name,\n filtered_ingredients.form,\n filtered_ingredients.measure_type\nfrom filtered_ingredients\nwhere\n user_id = ?\n and plan_date = ?"
|
||||
},
|
||||
"7f4abc448b16e8b6b2bb74f8e810e245e81b38e1407085a20d28bfddfc06891f": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
@ -414,6 +524,16 @@
|
||||
},
|
||||
"query": "with max_date as (\n select user_id, max(date(plan_date)) as plan_date from plan_recipes group by user_id\n)\n\nselect plan_recipes.plan_date as \"plan_date: NaiveDate\", plan_recipes.recipe_id, plan_recipes.count\n from plan_recipes\n inner join max_date on plan_recipes.user_id = max_date.user_id\nwhere\n plan_recipes.user_id = ?\n and plan_recipes.plan_date = max_date.plan_date"
|
||||
},
|
||||
"ba07658eb11f9d6cfdb5dbee4496b2573f1e51f4b4d9ae760eca3b977649b5c7": {
|
||||
"describe": {
|
||||
"columns": [],
|
||||
"nullable": [],
|
||||
"parameters": {
|
||||
"Right": 4
|
||||
}
|
||||
},
|
||||
"query": "insert into extra_items (user_id, name, amt, plan_date)\nvalues (?, ?, ?, ?)\non conflict (user_id, name, plan_date) do update set amt=excluded.amt"
|
||||
},
|
||||
"c988364f9f83f4fa8bd0e594bab432ee7c9ec47ca40f4d16e5e2a8763653f377": {
|
||||
"describe": {
|
||||
"columns": [
|
||||
|
@ -293,6 +293,26 @@ async fn api_inventory_v2(
|
||||
}
|
||||
}
|
||||
|
||||
async fn api_inventory_for_date(
|
||||
Extension(app_store): Extension<Arc<storage::SqliteStore>>,
|
||||
session: storage::UserIdFromSession,
|
||||
Path(date): Path<chrono::NaiveDate>,
|
||||
) -> api::InventoryResponse {
|
||||
use storage::{UserId, UserIdFromSession::FoundUserId};
|
||||
if let FoundUserId(UserId(id)) = session {
|
||||
app_store
|
||||
.fetch_inventory_for_date(id, date)
|
||||
.await
|
||||
.map(|d| {
|
||||
let data: api::InventoryData = d.into();
|
||||
data
|
||||
})
|
||||
.into()
|
||||
} else {
|
||||
api::Response::Unauthorized
|
||||
}
|
||||
}
|
||||
|
||||
async fn api_inventory(
|
||||
Extension(app_store): Extension<Arc<storage::SqliteStore>>,
|
||||
session: storage::UserIdFromSession,
|
||||
@ -309,6 +329,35 @@ async fn api_inventory(
|
||||
}
|
||||
}
|
||||
|
||||
async fn api_save_inventory_for_date(
|
||||
Extension(app_store): Extension<Arc<storage::SqliteStore>>,
|
||||
session: storage::UserIdFromSession,
|
||||
Path(date): Path<NaiveDate>,
|
||||
Json((filtered_ingredients, modified_amts, extra_items)): Json<(
|
||||
Vec<IngredientKey>,
|
||||
Vec<(IngredientKey, String)>,
|
||||
Vec<(String, String)>,
|
||||
)>,
|
||||
) -> api::EmptyResponse {
|
||||
use storage::{UserId, UserIdFromSession::FoundUserId};
|
||||
if let FoundUserId(UserId(id)) = session {
|
||||
let filtered_ingredients = filtered_ingredients.into_iter().collect();
|
||||
let modified_amts = modified_amts.into_iter().collect();
|
||||
app_store
|
||||
.save_inventory_data_for_date(
|
||||
id,
|
||||
&date,
|
||||
filtered_ingredients,
|
||||
modified_amts,
|
||||
extra_items,
|
||||
)
|
||||
.await
|
||||
.into()
|
||||
} else {
|
||||
api::EmptyResponse::Unauthorized
|
||||
}
|
||||
}
|
||||
|
||||
async fn save_inventory_data(
|
||||
app_store: Arc<storage::SqliteStore>,
|
||||
id: String,
|
||||
@ -441,6 +490,10 @@ fn mk_v2_routes() -> Router {
|
||||
"/inventory",
|
||||
get(api_inventory_v2).post(api_save_inventory_v2),
|
||||
)
|
||||
.route(
|
||||
"/inventory/at/:date",
|
||||
get(api_inventory_for_date).post(api_save_inventory_for_date),
|
||||
)
|
||||
// TODO(jwall): This is now deprecated but will still work
|
||||
.route("/categories", get(api_categories).post(api_save_categories))
|
||||
.route(
|
||||
|
@ -0,0 +1,8 @@
|
||||
select
|
||||
filtered_ingredients.name,
|
||||
filtered_ingredients.form,
|
||||
filtered_ingredients.measure_type
|
||||
from filtered_ingredients
|
||||
where
|
||||
user_id = ?
|
||||
and plan_date = ?
|
9
kitchen/src/web/storage/fetch_modified_amts_for_date.sql
Normal file
9
kitchen/src/web/storage/fetch_modified_amts_for_date.sql
Normal file
@ -0,0 +1,9 @@
|
||||
select
|
||||
modified_amts.name,
|
||||
modified_amts.form,
|
||||
modified_amts.measure_type,
|
||||
modified_amts.amt
|
||||
from modified_amts
|
||||
where
|
||||
user_id = ?
|
||||
and plan_date = ?
|
@ -145,6 +145,16 @@ pub trait APIStore {
|
||||
date: NaiveDate,
|
||||
) -> Result<()>;
|
||||
|
||||
async fn fetch_inventory_for_date<S: AsRef<str> + Send>(
|
||||
&self,
|
||||
user_id: S,
|
||||
date: NaiveDate,
|
||||
) -> Result<(
|
||||
Vec<IngredientKey>,
|
||||
Vec<(IngredientKey, String)>,
|
||||
Vec<(String, String)>,
|
||||
)>;
|
||||
|
||||
async fn fetch_latest_inventory_data<S: AsRef<str> + Send>(
|
||||
&self,
|
||||
user_id: S,
|
||||
@ -154,6 +164,15 @@ pub trait APIStore {
|
||||
Vec<(String, String)>,
|
||||
)>;
|
||||
|
||||
async fn save_inventory_data_for_date<S: AsRef<str> + Send>(
|
||||
&self,
|
||||
user_id: S,
|
||||
date: &NaiveDate,
|
||||
filtered_ingredients: BTreeSet<IngredientKey>,
|
||||
modified_amts: BTreeMap<IngredientKey, String>,
|
||||
extra_items: Vec<(String, String)>,
|
||||
) -> Result<()>;
|
||||
|
||||
async fn save_inventory_data<S: AsRef<str> + Send>(
|
||||
&self,
|
||||
user_id: S,
|
||||
@ -652,7 +671,89 @@ impl APIStore for SqliteStore {
|
||||
Ok(Some(result))
|
||||
}
|
||||
|
||||
// TODO(jwall): Do we need fetch for date variants of this.
|
||||
async fn fetch_inventory_for_date<S: AsRef<str> + Send>(
|
||||
&self,
|
||||
user_id: S,
|
||||
date: NaiveDate,
|
||||
) -> Result<(
|
||||
Vec<IngredientKey>,
|
||||
Vec<(IngredientKey, String)>,
|
||||
Vec<(String, String)>,
|
||||
)> {
|
||||
let user_id = user_id.as_ref();
|
||||
struct FilteredIngredientRow {
|
||||
name: String,
|
||||
form: String,
|
||||
measure_type: String,
|
||||
}
|
||||
let filtered_ingredient_rows: Vec<FilteredIngredientRow> = sqlx::query_file_as!(
|
||||
FilteredIngredientRow,
|
||||
"src/web/storage/fetch_filtered_ingredients_for_date.sql",
|
||||
user_id,
|
||||
date,
|
||||
)
|
||||
.fetch_all(self.pool.as_ref())
|
||||
.await?;
|
||||
let mut filtered_ingredients = Vec::new();
|
||||
for row in filtered_ingredient_rows {
|
||||
filtered_ingredients.push(IngredientKey::new(
|
||||
row.name,
|
||||
if row.form.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(row.form)
|
||||
},
|
||||
row.measure_type,
|
||||
));
|
||||
}
|
||||
struct ModifiedAmtRow {
|
||||
name: String,
|
||||
form: String,
|
||||
measure_type: String,
|
||||
amt: String,
|
||||
}
|
||||
let modified_amt_rows = sqlx::query_file_as!(
|
||||
ModifiedAmtRow,
|
||||
"src/web/storage/fetch_modified_amts_for_date.sql",
|
||||
user_id,
|
||||
date,
|
||||
)
|
||||
.fetch_all(self.pool.as_ref())
|
||||
.await?;
|
||||
let mut modified_amts = Vec::new();
|
||||
for row in modified_amt_rows {
|
||||
modified_amts.push((
|
||||
IngredientKey::new(
|
||||
row.name,
|
||||
if row.form.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(row.form)
|
||||
},
|
||||
row.measure_type,
|
||||
),
|
||||
row.amt,
|
||||
));
|
||||
}
|
||||
pub struct ExtraItemRow {
|
||||
name: String,
|
||||
amt: String,
|
||||
}
|
||||
let extra_items_rows = sqlx::query_file_as!(
|
||||
ExtraItemRow,
|
||||
"src/web/storage/fetch_extra_items_for_date.sql",
|
||||
user_id,
|
||||
date,
|
||||
)
|
||||
.fetch_all(self.pool.as_ref())
|
||||
.await?;
|
||||
let mut extra_items = Vec::new();
|
||||
for row in extra_items_rows {
|
||||
extra_items.push((row.name, row.amt));
|
||||
}
|
||||
Ok((filtered_ingredients, modified_amts, extra_items))
|
||||
}
|
||||
|
||||
async fn fetch_latest_inventory_data<S: AsRef<str> + Send>(
|
||||
&self,
|
||||
user_id: S,
|
||||
@ -732,6 +833,66 @@ impl APIStore for SqliteStore {
|
||||
Ok((filtered_ingredients, modified_amts, extra_items))
|
||||
}
|
||||
|
||||
async fn save_inventory_data_for_date<S: AsRef<str> + Send>(
|
||||
&self,
|
||||
user_id: S,
|
||||
date: &NaiveDate,
|
||||
filtered_ingredients: BTreeSet<IngredientKey>,
|
||||
modified_amts: BTreeMap<IngredientKey, String>,
|
||||
extra_items: Vec<(String, String)>,
|
||||
) -> Result<()> {
|
||||
let user_id = user_id.as_ref();
|
||||
let mut transaction = self.pool.as_ref().begin().await?;
|
||||
// store the filtered_ingredients
|
||||
for key in filtered_ingredients {
|
||||
let name = key.name();
|
||||
let form = key.form();
|
||||
let measure_type = key.measure_type();
|
||||
sqlx::query_file!(
|
||||
"src/web/storage/save_filtered_ingredients_for_date.sql",
|
||||
user_id,
|
||||
name,
|
||||
form,
|
||||
measure_type,
|
||||
date,
|
||||
)
|
||||
.execute(&mut transaction)
|
||||
.await?;
|
||||
}
|
||||
// store the modified amts
|
||||
for (key, amt) in modified_amts {
|
||||
let name = key.name();
|
||||
let form = key.form();
|
||||
let measure_type = key.measure_type();
|
||||
let amt = &amt;
|
||||
sqlx::query_file!(
|
||||
"src/web/storage/save_modified_amts_for_date.sql",
|
||||
user_id,
|
||||
name,
|
||||
form,
|
||||
measure_type,
|
||||
amt,
|
||||
date,
|
||||
)
|
||||
.execute(&mut transaction)
|
||||
.await?;
|
||||
}
|
||||
// Store the extra items
|
||||
for (name, amt) in extra_items {
|
||||
sqlx::query_file!(
|
||||
"src/web/storage/store_extra_items_for_date.sql",
|
||||
user_id,
|
||||
name,
|
||||
amt,
|
||||
date
|
||||
)
|
||||
.execute(&mut transaction)
|
||||
.await?;
|
||||
}
|
||||
transaction.commit().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn save_inventory_data<S: AsRef<str> + Send>(
|
||||
&self,
|
||||
user_id: S,
|
||||
|
@ -0,0 +1,2 @@
|
||||
insert into filtered_ingredients(user_id, name, form, measure_type, plan_date)
|
||||
values (?, ?, ?, ?, ?) on conflict(user_id, name, form, measure_type, plan_date) DO NOTHING
|
2
kitchen/src/web/storage/save_modified_amts_for_date.sql
Normal file
2
kitchen/src/web/storage/save_modified_amts_for_date.sql
Normal file
@ -0,0 +1,2 @@
|
||||
insert into modified_amts(user_id, name, form, measure_type, amt, plan_date)
|
||||
values (?, ?, ?, ?, ?, ?) on conflict (user_id, name, form, measure_type, plan_date) do update set amt=excluded.amt
|
3
kitchen/src/web/storage/store_extra_items_for_date.sql
Normal file
3
kitchen/src/web/storage/store_extra_items_for_date.sql
Normal file
@ -0,0 +1,3 @@
|
||||
insert into extra_items (user_id, name, amt, plan_date)
|
||||
values (?, ?, ?, ?)
|
||||
on conflict (user_id, name, plan_date) do update set amt=excluded.amt
|
@ -257,6 +257,27 @@ impl LocalStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_plan_date(&self, date: &NaiveDate) {
|
||||
self.store
|
||||
.set(
|
||||
"plan:date",
|
||||
&to_string(&date).expect("Failed to serialize plan:date"),
|
||||
)
|
||||
.expect("Failed to store plan:date");
|
||||
}
|
||||
|
||||
pub fn get_plan_date(&self) -> Option<NaiveDate> {
|
||||
if let Some(date) = self
|
||||
.store
|
||||
.get("plan:date")
|
||||
.expect("Failed to get plan date")
|
||||
{
|
||||
Some(from_str(&date).expect("Failed to deserialize plan_date"))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_inventory_data(
|
||||
&self,
|
||||
) -> Option<(
|
||||
@ -606,7 +627,7 @@ impl HttpStore {
|
||||
|
||||
pub async fn fetch_plan_for_date(
|
||||
&self,
|
||||
date: NaiveDate,
|
||||
date: &NaiveDate,
|
||||
) -> Result<Option<Vec<(String, i32)>>, Error> {
|
||||
let mut path = self.v2_path();
|
||||
path.push_str("/plan");
|
||||
@ -643,6 +664,48 @@ impl HttpStore {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn fetch_inventory_for_date(
|
||||
&self,
|
||||
date: &NaiveDate,
|
||||
) -> Result<
|
||||
(
|
||||
BTreeSet<IngredientKey>,
|
||||
BTreeMap<IngredientKey, String>,
|
||||
Vec<(String, String)>,
|
||||
),
|
||||
Error,
|
||||
> {
|
||||
let mut path = self.v2_path();
|
||||
path.push_str("/inventory");
|
||||
path.push_str("/at");
|
||||
path.push_str(&format!("/{}", date));
|
||||
let resp = reqwasm::http::Request::get(&path).send().await?;
|
||||
if resp.status() != 200 {
|
||||
let err = Err(format!("Status: {}", resp.status()).into());
|
||||
Ok(match self.local_store.get_inventory_data() {
|
||||
Some(val) => val,
|
||||
None => return err,
|
||||
})
|
||||
} else {
|
||||
debug!("We got a valid response back");
|
||||
let InventoryData {
|
||||
filtered_ingredients,
|
||||
modified_amts,
|
||||
extra_items,
|
||||
} = resp
|
||||
.json::<InventoryResponse>()
|
||||
.await
|
||||
.map_err(|e| format!("{}", e))?
|
||||
.as_success()
|
||||
.unwrap();
|
||||
Ok((
|
||||
filtered_ingredients.into_iter().collect(),
|
||||
modified_amts.into_iter().collect(),
|
||||
extra_items,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn fetch_inventory_data(
|
||||
&self,
|
||||
) -> Result<
|
||||
|
@ -38,6 +38,7 @@ pub struct AppState {
|
||||
pub modified_amts: BTreeMap<IngredientKey, String>,
|
||||
pub auth: Option<UserData>,
|
||||
pub plan_dates: BTreeSet<NaiveDate>,
|
||||
pub selected_plan_date: Option<NaiveDate>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
@ -52,6 +53,7 @@ impl AppState {
|
||||
modified_amts: BTreeMap::new(),
|
||||
auth: None,
|
||||
plan_dates: BTreeSet::new(),
|
||||
selected_plan_date: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,6 +75,7 @@ pub enum Message {
|
||||
SaveState(Option<Box<dyn FnOnce()>>),
|
||||
LoadState(Option<Box<dyn FnOnce()>>),
|
||||
UpdateStaples(String, Option<Box<dyn FnOnce()>>),
|
||||
SelectPlanDate(NaiveDate),
|
||||
}
|
||||
|
||||
impl Debug for Message {
|
||||
@ -113,6 +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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -189,8 +193,15 @@ impl StateMachine {
|
||||
debug!(?plan_dates, "meal plan list");
|
||||
state.plan_dates = BTreeSet::from_iter(plan_dates.drain(0..));
|
||||
}
|
||||
|
||||
info!("Synchronizing meal plan");
|
||||
let plan = store.fetch_plan().await?;
|
||||
let plan = if let Some(cached_plan_date) = local_store.get_plan_date() {
|
||||
let plan = store.fetch_plan_for_date(&cached_plan_date).await?;
|
||||
state.selected_plan_date = Some(cached_plan_date);
|
||||
plan
|
||||
} else {
|
||||
store.fetch_plan().await?
|
||||
};
|
||||
if let Some(plan) = plan {
|
||||
// set the counts.
|
||||
let mut plan_map = BTreeMap::new();
|
||||
@ -242,8 +253,13 @@ impl StateMachine {
|
||||
error!("{:?}", e);
|
||||
}
|
||||
}
|
||||
let inventory_data = if let Some(cached_plan_date) = &state.selected_plan_date {
|
||||
store.fetch_inventory_for_date(cached_plan_date).await
|
||||
} else {
|
||||
store.fetch_inventory_data().await
|
||||
};
|
||||
info!("Synchronizing inventory data");
|
||||
match store.fetch_inventory_data().await {
|
||||
match inventory_data {
|
||||
Ok((filtered_ingredients, modified_amts, extra_items)) => {
|
||||
local_store.set_inventory_data((
|
||||
&filtered_ingredients,
|
||||
@ -436,6 +452,36 @@ impl MessageMapper<Message, AppState> for StateMachine {
|
||||
});
|
||||
return;
|
||||
}
|
||||
Message::SelectPlanDate(date) => {
|
||||
let store = self.store.clone();
|
||||
let local_store = self.local_store.clone();
|
||||
spawn_local_scoped(cx, async move {
|
||||
if let Some(mut plan) = store
|
||||
.fetch_plan_for_date(&date)
|
||||
.await
|
||||
.expect("Failed to fetch plan for date")
|
||||
{
|
||||
// Note(jwall): This is a little unusual but because this
|
||||
// is async code we can't rely on the set below.
|
||||
original_copy.recipe_counts =
|
||||
BTreeMap::from_iter(plan.drain(0..).map(|(k, v)| (k, v as usize)));
|
||||
}
|
||||
let (filtered, modified, extras) = store
|
||||
.fetch_inventory_for_date(&date)
|
||||
.await
|
||||
.expect("Failed to fetch inventory_data for date");
|
||||
original_copy.modified_amts = modified;
|
||||
original_copy.filtered_ingredients = filtered;
|
||||
original_copy.extras = extras;
|
||||
local_store.set_plan_date(&date);
|
||||
|
||||
original.set(original_copy);
|
||||
});
|
||||
// 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
|
||||
// the original signal.
|
||||
return;
|
||||
}
|
||||
}
|
||||
original.set(original_copy);
|
||||
}
|
||||
|
@ -17,10 +17,12 @@ use sycamore::prelude::*;
|
||||
pub mod cook;
|
||||
pub mod inventory;
|
||||
pub mod plan;
|
||||
pub mod select;
|
||||
|
||||
pub use cook::*;
|
||||
pub use inventory::*;
|
||||
pub use plan::*;
|
||||
pub use select::*;
|
||||
|
||||
#[derive(Props)]
|
||||
pub struct PageState<'a, G: Html> {
|
||||
@ -33,6 +35,7 @@ pub fn PlanningPage<'a, G: Html>(cx: Scope<'a>, state: PageState<'a, G>) -> View
|
||||
let PageState { children, selected } = state;
|
||||
let children = children.call(cx);
|
||||
let planning_tabs: Vec<(String, &'static str)> = vec![
|
||||
("/ui/planning/select".to_owned(), "Select"),
|
||||
("/ui/planning/plan".to_owned(), "Plan"),
|
||||
("/ui/planning/inventory".to_owned(), "Inventory"),
|
||||
("/ui/planning/cook".to_owned(), "Cook"),
|
||||
|
26
web/src/pages/planning/select.rs
Normal file
26
web/src/pages/planning/select.rs
Normal file
@ -0,0 +1,26 @@
|
||||
// Copyright 2023 Jeremy Wall (Jeremy@marzhilsltudios.com)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
use super::PlanningPage;
|
||||
use crate::app_state::StateHandler;
|
||||
|
||||
use sycamore::prelude::*;
|
||||
|
||||
#[component]
|
||||
pub fn SelectPage<'ctx, G: Html>(cx: Scope<'ctx>, sh: StateHandler<'ctx>) -> View<G> {
|
||||
view! {cx,
|
||||
PlanningPage(
|
||||
selected=Some("Select".to_owned()),
|
||||
) { "TODO(jwall)" }
|
||||
}
|
||||
}
|
@ -62,6 +62,8 @@ pub enum ManageRoutes {
|
||||
|
||||
#[derive(Route, Debug)]
|
||||
pub enum PlanningRoutes {
|
||||
#[to("/select")]
|
||||
Select,
|
||||
#[to("/plan")]
|
||||
Plan,
|
||||
#[to("/inventory")]
|
||||
@ -83,6 +85,9 @@ fn route_switch<'ctx, G: Html>(route: &Routes, cx: Scope<'ctx>, sh: StateHandler
|
||||
use ManageRoutes::*;
|
||||
use PlanningRoutes::*;
|
||||
match route {
|
||||
Routes::Planning(Select) => view! {cx,
|
||||
SelectPage(sh)
|
||||
},
|
||||
Routes::Planning(Plan) => view! {cx,
|
||||
PlanPage(sh)
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user