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/api/mod.rs | 50 +++++++++++++++++++++++++++
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 ++++++++++++++++
src/lib.rs | 41 ++++++++++++++++------
src/main.rs | 5 +++
src/pages/home.rs | 24 ++++++-------
13 files changed, 321 insertions(+), 109 deletions(-)
create mode 100644 src/api/mod.rs
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')
diff --git a/src/api/mod.rs b/src/api/mod.rs
new file mode 100644
index 0000000..8b9e77d
--- /dev/null
+++ b/src/api/mod.rs
@@ -0,0 +1,50 @@
+use leptos::{
+ error::Error,
+ prelude::{Read, expect_context},
+};
+use reactive_stores::Store;
+use rocie_client::{
+ apis::{
+ api_get_inventory_api::amount_by_id,
+ api_get_product_api::{product_by_id, products},
+ api_get_unit_api::unit_by_id,
+ },
+ models::{Product, ProductAmount, ProductId, Unit, UnitId},
+};
+
+use crate::{ConfigState, ConfigStateStoreFields};
+
+pub(crate) async fn get_amount_by_id(product_id: ProductId) -> Result {
+ let config = expect_context::>();
+ amount_by_id(&config.config().read(), product_id)
+ .await
+ .map_err(Into::::into)
+}
+pub(crate) async fn get_product_by_id(product_id: ProductId) -> Result {
+ let config = expect_context::>();
+ product_by_id(&config.config().read(), product_id)
+ .await
+ .map_err(Into::::into)
+}
+pub(crate) async fn get_unit_by_id(unit_id: UnitId) -> Result {
+ let config = expect_context::>();
+ unit_by_id(&config.config().read(), unit_id)
+ .await
+ .map_err(Into::::into)
+}
+pub(crate) async fn get_full_product_by_id(
+ id: ProductId,
+) -> Result<(Product, ProductAmount, Unit), Error> {
+ let amount = get_amount_by_id(id).await?;
+ let product = get_product_by_id(id).await?;
+ let unit = get_unit_by_id(amount.amount.unit).await?;
+
+ Ok::<_, Error>((product, amount, unit))
+}
+
+pub(crate) async fn get_products() -> Result, Error> {
+ let config = expect_context::>();
+ products(&config.config().read())
+ .await
+ .map_err(Into::::into)
+}
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
}
+// }>
+// { || 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}
{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! {
+
+ }
+}
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}
+
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 357ce93..a488d95 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,24 +7,38 @@
reason = "Can't add it to leptos' components"
)]
#![expect(
- clippy::needless_pass_by_value,
- reason = "Can't add it to leptos' components"
+ unused_extern_crates,
+ reason = "Deependency needed to inject the `js` feature into uuid"
)]
+extern crate uuid;
+
+// All of them are only used in the `main.rs` and not in the `lib.rs` part of this crate.
+extern crate console_error_panic_hook;
+extern crate console_log;
+extern crate log;
+mod api;
mod components;
mod pages;
-use std::sync::Arc;
-
-use leptos::prelude::{AddAnyAttr, IntoView, component, view};
+use leptos::prelude::{AddAnyAttr, IntoView, component, provide_context, view};
use leptos_meta::{Html, Meta, Title, provide_meta_context};
use leptos_router::{
components::{Route, Router, Routes},
path,
};
+use reactive_stores::Store;
use rocie_client::apis::configuration::Configuration;
-use crate::pages::{home::Home, not_found::NotFound};
+use crate::{
+ components::inventory::Inventory,
+ pages::{home::Home, not_found::NotFound},
+};
+
+#[derive(Debug, Clone, Store)]
+pub struct ConfigState {
+ config: Configuration,
+}
#[component]
pub fn App() -> impl IntoView {
@@ -37,13 +51,15 @@ pub fn App() -> impl IntoView {
config.user_agent = Some("rocie-mobile".to_owned());
"http://127.0.0.1:8080".clone_into(&mut config.base_path);
- Arc::new(config)
+ config
};
+ provide_context(Store::new(ConfigState { config }));
+
view! {
-
+
@@ -53,8 +69,13 @@ pub fn App() -> impl IntoView {
}
+ view! { }
+ }
+ />
+ }
}
/>
diff --git a/src/main.rs b/src/main.rs
index c377484..c3eaadf 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,8 @@
+#![expect(
+ unused_crate_dependencies,
+ reason = "We use them in the lib version of this crate"
+)]
+
use leptos::prelude::{mount_to_body, view};
use rocie_mobile::App;
diff --git a/src/pages/home.rs b/src/pages/home.rs
index 8749860..387562e 100644
--- a/src/pages/home.rs
+++ b/src/pages/home.rs
@@ -1,26 +1,23 @@
-use std::sync::Arc;
-
use leptos::{
IntoView, component,
error::ErrorBoundary,
- prelude::{CollectView, ElementChild, Get, GetUntracked},
+ prelude::{ClassAttribute, CollectView, ElementChild, Get, GetUntracked},
view,
};
use leptos_router::{
NavigateOptions,
hooks::{use_navigate, use_query_map},
};
-use rocie_client::apis::configuration::Configuration;
-use thaw::{Layout, LayoutPosition};
-use crate::components::{product_overview::ProductOverview, side_header::SiteHeader};
+use crate::components::{
+ product_overview::ProductOverview, recipies::Recipies, site_header::SiteHeader,
+};
#[component]
-pub fn Home(config: Arc) -> impl IntoView {
+pub fn Home() -> impl IntoView {
let query_map = use_query_map().get_untracked();
let navigate = use_navigate();
- // mobile page
if let Some(path) = query_map.get("path") {
navigate(&path, NavigateOptions::default());
}
@@ -44,12 +41,11 @@ pub fn Home(config: Arc) -> impl IntoView {
}
}>
-
-
-
-
-
-
+
}
}
--
cgit 1.4.1