From d0263ce46160cd4152c67381fab2ee557f3aa483 Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Tue, 30 Sep 2025 09:15:56 +0200 Subject: feat(treewide): Switch to tailwindcss and add more components --- src/components/async_fetch.rs | 50 +++++++++++++++++++++++++++ src/components/container.rs | 47 +++++++++++++------------ src/components/icon_p.rs | 18 ++++++++++ src/components/inventory.rs | 55 +++++++++++++++++++++++++++++ src/components/mod.rs | 6 +++- src/components/product_overview.rs | 71 +++++++++++++++++--------------------- src/components/recipies.rs | 12 +++++++ src/components/side_header.rs | 22 ------------ src/components/site_header.rs | 29 ++++++++++++++++ 9 files changed, 225 insertions(+), 85 deletions(-) create mode 100644 src/components/async_fetch.rs create mode 100644 src/components/icon_p.rs create mode 100644 src/components/inventory.rs create mode 100644 src/components/recipies.rs delete mode 100644 src/components/side_header.rs create mode 100644 src/components/site_header.rs (limited to 'src/components') 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! { + "Loading..."

} + }> + {move || Suspend::new(async move { + let resource = { LocalResource::new(move || $fetcher) }; + resource + .await + .map(|$bound_variable| $producer) + })} +
+ } + }}; +} +pub(crate) use AsyncFetch; + +// #[component] +// pub fn AsyncFetch( +// fetcher: impl Fn() -> Fut + 'static + Send + Sync, +// producer: P, +// ) -> impl IntoView +// where +// V: IntoView + 'static, +// P: Fn(T) -> V + 'static + Send + Sync, +// Fut: Future> + 'static, +// T: 'static, +// LocalResource>: IntoFuture> + Send, +// { +// view! { +// "Loading..."

} +// }> +// { || Suspend::new(async { +// let value_resource = LocalResource::new( || fetcher()); +// value_resource +// .await +// .map(|value| { +// producer(value) +// }) +// })} +//
+// } +// } 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! { - - -
-

{header}

+
+

{header}

{children()} + +
    + {buttons + .into_iter() + .map(|(name, path)| { + view! { +
  • + +
  • + } + }) + .collect::>()} +
} } 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, text: &'static str) -> impl IntoView { + view! { +
+
+ +
+

{text}

+
+ } +} 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! { + + +
    + { + 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! { +
      +
    • {product.name}
    • +
    • + + { + format!( + "{} {}", + amount.amount.value, + unit.short_name + ) + } + +
    • +
    + } + } + }} + } + }) + .collect::>() + } + } + } +
+ } +} 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) -> 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! { - -

{read_status}

+ }, "inventory"), + (view! { }, "consume"), + (view! { }, "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! { +

+ {format!( + "You have {products_num} product{plural_s} \ + in stock with a value \ + of {products_value} {products_currency}.", + )} +

+ } + } + )}
} } 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! { + +

"You have 0 recipies."

+
+ } +} 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! { - - - -

"Rocie"

-
-
- } -} 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, +) -> impl IntoView { + let navigate = use_navigate(); + + view! { +
+
+ +
+

{name}

+
{menu}
+
+ } +} -- cgit 1.4.1