diff options
| author | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-09-30 09:15:56 +0200 |
|---|---|---|
| committer | Benedikt Peetz <benedikt.peetz@b-peetz.de> | 2025-09-30 09:15:56 +0200 |
| commit | d0263ce46160cd4152c67381fab2ee557f3aa483 (patch) | |
| tree | b24a725d71b984e466ff478fbb4449111beeacac /src/components | |
| parent | chore: Second version (diff) | |
| download | web-client-d0263ce46160cd4152c67381fab2ee557f3aa483.zip | |
feat(treewide): Switch to tailwindcss and add more components
Diffstat (limited to 'src/components')
| -rw-r--r-- | src/components/async_fetch.rs | 50 | ||||
| -rw-r--r-- | src/components/container.rs | 47 | ||||
| -rw-r--r-- | src/components/icon_p.rs | 18 | ||||
| -rw-r--r-- | src/components/inventory.rs | 55 | ||||
| -rw-r--r-- | src/components/mod.rs | 6 | ||||
| -rw-r--r-- | src/components/product_overview.rs | 71 | ||||
| -rw-r--r-- | src/components/recipies.rs | 12 | ||||
| -rw-r--r-- | src/components/side_header.rs | 22 | ||||
| -rw-r--r-- | src/components/site_header.rs | 29 |
9 files changed, 225 insertions, 85 deletions
diff --git a/src/components/async_fetch.rs b/src/components/async_fetch.rs new file mode 100644 index 0000000..7105c6f --- /dev/null +++ b/src/components/async_fetch.rs @@ -0,0 +1,50 @@ +macro_rules! AsyncFetch { + (fetcher = $fetcher:block producer = |$bound_variable:pat_param| $producer:block) => {{ + use leptos::{ + prelude::{ElementChild, LocalResource, Suspend, Transition}, + view, + }; + + view! { + <Transition fallback=|| { + view! { <p>"Loading..."</p> } + }> + {move || Suspend::new(async move { + let resource = { LocalResource::new(move || $fetcher) }; + resource + .await + .map(|$bound_variable| $producer) + })} + </Transition> + } + }}; +} +pub(crate) use AsyncFetch; + +// #[component] +// pub fn AsyncFetch<P, V, T, Fut>( +// fetcher: impl Fn() -> Fut + 'static + Send + Sync, +// producer: P, +// ) -> impl IntoView +// where +// V: IntoView + 'static, +// P: Fn(T) -> V + 'static + Send + Sync, +// Fut: Future<Output = Result<T, Error>> + 'static, +// T: 'static, +// LocalResource<Result<T, Error>>: IntoFuture<Output = Result<T, Error>> + Send, +// { +// view! { +// <Transition fallback=|| { +// view! { <p>"Loading..."</p> } +// }> +// { || Suspend::new(async { +// let value_resource = LocalResource::new( || fetcher()); +// value_resource +// .await +// .map(|value| { +// producer(value) +// }) +// })} +// </Transition> +// } +// } diff --git a/src/components/container.rs b/src/components/container.rs index cf7aa5a..7a4a64f 100644 --- a/src/components/container.rs +++ b/src/components/container.rs @@ -1,34 +1,35 @@ use leptos::{ IntoView, component, - prelude::{Children, ClassAttribute, ElementChild}, + prelude::{Children, ClassAttribute, ElementChild, OnAttribute}, view, }; -use leptos_meta::Style; +use leptos_router::{NavigateOptions, hooks::use_navigate}; #[component] -pub fn Container(header: impl IntoView, children: Children) -> impl IntoView { +pub fn Container( + header: impl IntoView, + buttons: Vec<(impl IntoView, &'static str)>, + children: Children, +) -> impl IntoView { view! { - <Style> - " - .rocie-container { - border-width: 0.1rem; - border-style: solid; - border-color: gray; - border-radius: 15%; - - padding: 0.2rem; - - text-align: left; - } - .rocie-container-header { - font-size: 1.5rem; - } - " - </Style> - - <div class="rocie-container"> - <p class="rocie-container-header">{header}</p> + <div class="p-4 mt-4 mr-4 ml-4 md-2 rounded-lg border-gray-600 border"> + <p class="text-lg text-bold">{header}</p> {children()} + + <ul class="flex flex-row gap-1 pt-2 overflow-x-auto"> + {buttons + .into_iter() + .map(|(name, path)| { + view! { + <li class="bg-green-400/40 p-2 first:rounded-l-full last:rounded-r-full"> + <button on:click=move |_| { + use_navigate()(path, NavigateOptions::default()); + }>{name}</button> + </li> + } + }) + .collect::<Vec<_>>()} + </ul> </div> } } diff --git a/src/components/icon_p.rs b/src/components/icon_p.rs new file mode 100644 index 0000000..372e280 --- /dev/null +++ b/src/components/icon_p.rs @@ -0,0 +1,18 @@ +use leptos::{ + IntoView, component, + prelude::{ClassAttribute, ElementChild, Signal}, + view, +}; +use leptos_icons::Icon; + +#[component] +pub fn IconP(#[prop(into)] icon: Signal<icondata_core::Icon>, text: &'static str) -> impl IntoView { + view! { + <div class="flex justify-evenly items-center"> + <div class="mr-1"> + <Icon icon=icon /> + </div> + <p>{text}</p> + </div> + } +} diff --git a/src/components/inventory.rs b/src/components/inventory.rs new file mode 100644 index 0000000..5855b33 --- /dev/null +++ b/src/components/inventory.rs @@ -0,0 +1,55 @@ +use leptos::{ + IntoView, component, + prelude::{ClassAttribute, ElementChild}, + view, +}; + +use crate::{ + api::{get_full_product_by_id, get_products}, + components::{async_fetch::AsyncFetch, site_header::SiteHeader}, +}; + +#[component] +pub fn Inventory() -> impl IntoView { + view! { + <SiteHeader logo=icondata_io::IoArrowBack back_location="/" name="Inventory" /> + + <ul class="flex flex-col p-2 m-2"> + { + AsyncFetch! { + fetcher = {get_products()} + producer = |products| { + products + .into_iter() + .map(|product| { + view! { + {AsyncFetch! { + fetcher = {get_full_product_by_id(product.id)} + producer = |(product, amount, unit)| { + view! { + <ul class="my-3"> + <li class="m-2">{product.name}</li> + <li class="m-2"> + <span class="bg-gray-200 p-1 px-2 rounded-lg"> + { + format!( + "{} {}", + amount.amount.value, + unit.short_name + ) + } + </span> + </li> + </ul> + } + } + }} + } + }) + .collect::<Vec<_>>() + } + } + } + </ul> + } +} diff --git a/src/components/mod.rs b/src/components/mod.rs index 85f9671..7174ad8 100644 --- a/src/components/mod.rs +++ b/src/components/mod.rs @@ -1,6 +1,10 @@ // Generic +pub mod async_fetch; pub mod container; +pub mod icon_p; // Specific +pub mod inventory; pub mod product_overview; -pub mod side_header; +pub mod recipies; +pub mod site_header; diff --git a/src/components/product_overview.rs b/src/components/product_overview.rs index 4e95335..ae2eaf2 100644 --- a/src/components/product_overview.rs +++ b/src/components/product_overview.rs @@ -1,47 +1,40 @@ -use std::sync::Arc; +use leptos::{IntoView, component, view}; -use leptos::{ - IntoView, component, - prelude::{ElementChild, Set, signal}, - task::spawn_local, - view, +use crate::{ + api::get_products, + components::{async_fetch::AsyncFetch, container::Container, icon_p::IconP}, }; -use rocie_client::apis::{api_get_product_api::products, configuration::Configuration}; - -use crate::components::container::Container; #[component] -pub fn ProductOverview(config: Arc<Configuration>) -> impl IntoView { - let (read_status, write_status) = signal("Loading..".to_owned()); - - { - let local_config = Arc::clone(&config); - - spawn_local(async move { - let products = products(&local_config).await; - - write_status.set( - products - .as_ref() - .map(move |products| { - let products_num = products.len(); - let plural_s = if products_num == 1 { "" } else { "s" }; - let products_value = 2; - let products_currency = "EUR"; - format!( - "You have {products_num} product{plural_s} \ - in stock with a value \ - of {products_value} {products_currency}.", - ) - }) - .unwrap(), - ); - }); - } - +pub fn ProductOverview() -> impl IntoView { view! { - <Container header="Inventory"> - <p>{read_status}</p> + <Container + header="Inventory" + buttons=vec![ + (view! { <IconP icon=icondata_io::IoClipboardOutline text="Inventory" /> }, "inventory"), + (view! { <IconP icon=icondata_io::IoPricetags text="Consume" /> }, "consume"), + (view! { <IconP icon=icondata_io::IoStorefront text="Buy" /> }, "buy"), + ] + > + {AsyncFetch!( + fetcher = {get_products()} + producer = |products| { + let products_num = products.len(); + let plural_s = if products_num == 1 { "" } else { "s" }; + let products_value = 2; + let products_currency = "EUR"; + + view! { + <p> + {format!( + "You have {products_num} product{plural_s} \ + in stock with a value \ + of {products_value} {products_currency}.", + )} + </p> + } + } + )} </Container> } } diff --git a/src/components/recipies.rs b/src/components/recipies.rs new file mode 100644 index 0000000..1bd3a0d --- /dev/null +++ b/src/components/recipies.rs @@ -0,0 +1,12 @@ +use leptos::{IntoView, component, prelude::ElementChild, view}; + +use crate::components::container::Container; + +#[component] +pub fn Recipies() -> impl IntoView { + view! { + <Container header="Recipies" buttons=vec![("Mealplan", "mealplan")]> + <p>"You have 0 recipies."</p> + </Container> + } +} diff --git a/src/components/side_header.rs b/src/components/side_header.rs deleted file mode 100644 index 9cd6777..0000000 --- a/src/components/side_header.rs +++ /dev/null @@ -1,22 +0,0 @@ -use leptos::prelude::{AddAnyAttr, ElementChild, IntoView, StyleAttribute, component, view}; -use leptos_router::{NavigateOptions, hooks::use_navigate}; -use thaw::{Flex, FlexJustify, LayoutHeader}; - -#[component] -pub fn SiteHeader() -> impl IntoView { - let navigate = use_navigate(); - - view! { - <LayoutHeader - attr:style="padding: 20px;" - on:click=move |_| { - navigate("/", NavigateOptions::default()); - } - > - <Flex justify=FlexJustify::SpaceAround> - <img src="/logo.svg" style="width: 36px" /> - <h3>"Rocie"</h3> - </Flex> - </LayoutHeader> - } -} diff --git a/src/components/site_header.rs b/src/components/site_header.rs new file mode 100644 index 0000000..65f7137 --- /dev/null +++ b/src/components/site_header.rs @@ -0,0 +1,29 @@ +use icondata_core::Icon as DataIcon; +use leptos::prelude::{ClassAttribute, ElementChild, IntoView, OnAttribute, component, view}; +use leptos_icons::Icon; +use leptos_router::{NavigateOptions, hooks::use_navigate}; + +#[component] +pub fn SiteHeader( + logo: DataIcon, + back_location: &'static str, + name: &'static str, + #[prop(default = None)] menu: Option<String>, +) -> impl IntoView { + let navigate = use_navigate(); + + view! { + <div + class="flex flex-row justify-between items-center p-2 px-4 w-full h-1/12 bg-gray-100" + on:click=move |_| { + navigate(back_location, NavigateOptions::default()); + } + > + <div class="max-w-14"> + <Icon icon=logo /> + </div> + <p class="text-xl">{name}</p> + <div>{menu}</div> + </div> + } +} |
