app: Allow to list recipes
This commit is contained in:
parent
80e3d7ee86
commit
f3788e31a9
3 changed files with 136 additions and 6 deletions
|
|
@ -22,6 +22,7 @@ mod bootstrap;
|
||||||
mod ingredients;
|
mod ingredients;
|
||||||
mod recipe_creator;
|
mod recipe_creator;
|
||||||
mod sidebar;
|
mod sidebar;
|
||||||
|
mod recipe;
|
||||||
|
|
||||||
const API_ROUTE: &str = match option_env!("REGALADE_API_SERVER_BASE") {
|
const API_ROUTE: &str = match option_env!("REGALADE_API_SERVER_BASE") {
|
||||||
None => "http://localhost:8085",
|
None => "http://localhost:8085",
|
||||||
|
|
@ -50,11 +51,45 @@ enum Route {
|
||||||
NewRecipe,
|
NewRecipe,
|
||||||
#[at("/recipe/:id")]
|
#[at("/recipe/:id")]
|
||||||
Recipe { id: i64 },
|
Recipe { id: i64 },
|
||||||
|
#[at("/recipe")]
|
||||||
|
SearchRecipe,
|
||||||
#[at("/404")]
|
#[at("/404")]
|
||||||
#[not_found]
|
#[not_found]
|
||||||
NotFound,
|
NotFound,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
|
enum RouteKind {
|
||||||
|
Index,
|
||||||
|
Ingredients,
|
||||||
|
NewRecipe,
|
||||||
|
Recipe,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Route {
|
||||||
|
fn kind(&self) -> Option<RouteKind> {
|
||||||
|
match self {
|
||||||
|
Route::Index => Some(RouteKind::Index),
|
||||||
|
Route::Ingredients => Some(RouteKind::Ingredients),
|
||||||
|
Route::NewRecipe => Some(RouteKind::NewRecipe),
|
||||||
|
Route::Recipe { .. } => Some(RouteKind::Recipe),
|
||||||
|
Route::SearchRecipe => Some(RouteKind::Recipe),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RouteKind {
|
||||||
|
fn redirect_to(&self) -> Route {
|
||||||
|
match self {
|
||||||
|
RouteKind::Index => Route::Index,
|
||||||
|
RouteKind::Ingredients => Route::Ingredients,
|
||||||
|
RouteKind::NewRecipe => Route::NewRecipe,
|
||||||
|
RouteKind::Recipe => Route::SearchRecipe,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[function_component]
|
#[function_component]
|
||||||
fn App() -> Html {
|
fn App() -> Html {
|
||||||
html! {
|
html! {
|
||||||
|
|
@ -483,6 +518,11 @@ fn switch(route: Route) -> Html {
|
||||||
{format!("RECIPE {id}")}
|
{format!("RECIPE {id}")}
|
||||||
</GlobalStateRedirector>
|
</GlobalStateRedirector>
|
||||||
},
|
},
|
||||||
|
Route::SearchRecipe => html!{
|
||||||
|
<GlobalStateRedirector {route}>
|
||||||
|
<recipe::RecipeList />
|
||||||
|
</GlobalStateRedirector>
|
||||||
|
},
|
||||||
Route::NotFound => html! {
|
Route::NotFound => html! {
|
||||||
"Page not found"
|
"Page not found"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
84
app/src/recipe.rs
Normal file
84
app/src/recipe.rs
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
use uuid::Uuid;
|
||||||
|
use yew::{prelude::*, suspense::use_future};
|
||||||
|
use yew_router::prelude::*;
|
||||||
|
|
||||||
|
use crate::{api, RegaladeGlobalState, Route};
|
||||||
|
|
||||||
|
async fn get_all_recipes(
|
||||||
|
token: String,
|
||||||
|
household: Uuid,
|
||||||
|
) -> anyhow::Result<api::ListRecipesResponse> {
|
||||||
|
let rsp = gloo_net::http::Request::get(api!("household/{household}/recipe"))
|
||||||
|
.header("Authorization", &format!("Bearer {token}"))
|
||||||
|
.send()
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if !rsp.ok() {
|
||||||
|
let body = rsp.text().await.unwrap_or_default();
|
||||||
|
anyhow::bail!("Could not get recipes (code={}): {body}", rsp.status());
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(rsp.json().await?)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq, Properties)]
|
||||||
|
struct RecipeListProps {
|
||||||
|
token: String,
|
||||||
|
household: Uuid,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component]
|
||||||
|
fn RecipeListInner(props: &RecipeListProps) -> HtmlResult {
|
||||||
|
let state = use_future(|| get_all_recipes(props.token.clone(), props.household))?;
|
||||||
|
|
||||||
|
Ok(match &*state {
|
||||||
|
Ok(l) => html! {
|
||||||
|
<div class="container text-center">
|
||||||
|
<div class="row row-cols-2 row-cols-sm-2 row-cols-md-4 g-2 mb-1">
|
||||||
|
{for l.recipes.iter().map(|(id, name)| html!{
|
||||||
|
<div class="col" key={*id}>
|
||||||
|
<div class="p-3 border rounded border-light-subtle h-100">
|
||||||
|
<Link<Route>
|
||||||
|
classes={classes!(
|
||||||
|
"link-light",
|
||||||
|
"link-offset-2",
|
||||||
|
"link-underline-opacity-25",
|
||||||
|
"link-underline-opacity-100-hover",
|
||||||
|
)}
|
||||||
|
to={Route::Recipe { id: *id }}
|
||||||
|
>
|
||||||
|
{name}
|
||||||
|
</Link<Route>>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
},
|
||||||
|
Err(e) => html! {
|
||||||
|
<div class={classes!("alert", "alert-danger")} role="alert">
|
||||||
|
{format!("Error fetching recipes: {e}")}
|
||||||
|
</div>
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[function_component]
|
||||||
|
pub fn RecipeList() -> Html {
|
||||||
|
let fallback = html! {"Loading ..."};
|
||||||
|
let global_state = use_state(RegaladeGlobalState::get);
|
||||||
|
|
||||||
|
html! {
|
||||||
|
<div class="d-flex align-items-center justify-content-center w-100">
|
||||||
|
<div class={classes!("container", "text-center", "rounded", "border", "pt-2", "m-2")}>
|
||||||
|
<h2>{"Recipes"}</h2>
|
||||||
|
<Suspense {fallback}>
|
||||||
|
<RecipeListInner
|
||||||
|
token={global_state.token.token.clone()}
|
||||||
|
household={global_state.household.id}
|
||||||
|
/>
|
||||||
|
</Suspense>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,13 +10,14 @@ use crate::{
|
||||||
api,
|
api,
|
||||||
bootstrap::{bs, ConfirmDangerModal, FormModal},
|
bootstrap::{bs, ConfirmDangerModal, FormModal},
|
||||||
do_add_user_to_household, do_resolve_user, HouseholdInfo, RegaladeGlobalState, Route,
|
do_add_user_to_household, do_resolve_user, HouseholdInfo, RegaladeGlobalState, Route,
|
||||||
|
RouteKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
struct MenuEntry {
|
struct MenuEntry {
|
||||||
icon: &'static str,
|
icon: &'static str,
|
||||||
label: &'static str,
|
label: &'static str,
|
||||||
page: Route,
|
page: RouteKind,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Properties, PartialEq)]
|
#[derive(Properties, PartialEq)]
|
||||||
|
|
@ -298,7 +299,7 @@ fn Sidebar(props: &SidebarProps) -> Html {
|
||||||
)}>
|
)}>
|
||||||
{
|
{
|
||||||
for props.entries.iter().map(|e| {
|
for props.entries.iter().map(|e| {
|
||||||
let active = if e.page == props.current {
|
let active = if Some(e.page) == props.current.kind() {
|
||||||
Some("active")
|
Some("active")
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
|
|
@ -311,7 +312,7 @@ fn Sidebar(props: &SidebarProps) -> Html {
|
||||||
"text-white",
|
"text-white",
|
||||||
active,
|
active,
|
||||||
)}
|
)}
|
||||||
to={e.page}
|
to={e.page.redirect_to()}
|
||||||
>
|
>
|
||||||
<i class={classes!("fs-4", e.icon)}></i>
|
<i class={classes!("fs-4", e.icon)}></i>
|
||||||
<span class={classes!("ms-2", "d-none", "d-sm-inline")}>
|
<span class={classes!("ms-2", "d-none", "d-sm-inline")}>
|
||||||
|
|
@ -433,17 +434,22 @@ pub(crate) fn RegaladeSidebar(props: &RegaladeSidebarProps) -> Html {
|
||||||
MenuEntry {
|
MenuEntry {
|
||||||
label: "Home",
|
label: "Home",
|
||||||
icon: "bi-house",
|
icon: "bi-house",
|
||||||
page: Route::Index,
|
page: RouteKind::Index,
|
||||||
|
},
|
||||||
|
MenuEntry {
|
||||||
|
label: "Recipes",
|
||||||
|
icon: "bi-book",
|
||||||
|
page: RouteKind::Recipe,
|
||||||
},
|
},
|
||||||
MenuEntry {
|
MenuEntry {
|
||||||
label: "Ingredients",
|
label: "Ingredients",
|
||||||
icon: "bi-egg-fill",
|
icon: "bi-egg-fill",
|
||||||
page: Route::Ingredients,
|
page: RouteKind::Ingredients,
|
||||||
},
|
},
|
||||||
MenuEntry {
|
MenuEntry {
|
||||||
label: "New Recipe",
|
label: "New Recipe",
|
||||||
icon: "bi-clipboard2-plus-fill",
|
icon: "bi-clipboard2-plus-fill",
|
||||||
page: Route::NewRecipe,
|
page: RouteKind::NewRecipe,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue