summaryrefslogtreecommitdiffstats
path: root/src/components
diff options
context:
space:
mode:
Diffstat (limited to 'src/components')
-rw-r--r--src/components/async_fetch.rs32
-rw-r--r--src/components/banner.rs6
-rw-r--r--src/components/buy.rs193
-rw-r--r--src/components/container.rs47
-rw-r--r--src/components/input_placeholder.rs15
-rw-r--r--src/components/inventory.rs55
-rw-r--r--src/components/mod.rs4
-rw-r--r--src/components/product_overview.rs38
-rw-r--r--src/components/recipies.rs10
-rw-r--r--src/components/select_placeholder.rs22
10 files changed, 85 insertions, 337 deletions
diff --git a/src/components/async_fetch.rs b/src/components/async_fetch.rs
index f24e3a5..7bf44a0 100644
--- a/src/components/async_fetch.rs
+++ b/src/components/async_fetch.rs
@@ -15,31 +15,21 @@ pub(crate) use AsyncResource;
macro_rules! AsyncFetch {
(
- fetcher = $fetcher:block
- producer = |$bound_variable:pat_param| $producer:block
+ @map_error_in_producer
+ fetcher = $fetcher:expr,
+ producer = $producer:expr $(,)?
) => {{
- 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>
+ AsyncFetch! {
+ @map_error_in_producer
+ from_resource = leptos::prelude::LocalResource::new(move || $fetcher),
+ producer = $producer
}
}};
+
(
@map_error_in_producer
- from_resource = $resource:ident
- producer = |$bound_variable:pat_param| $producer:block
+ from_resource = $resource:expr,
+ producer = $producer:expr $(,)?
) => {{
use leptos::prelude::{ElementChild, Suspend, Transition};
@@ -50,7 +40,7 @@ macro_rules! AsyncFetch {
{move || Suspend::new(async move {
$resource
.await
- .map(|$bound_variable| $producer)
+ .map($producer)
})}
</Transition>
}
diff --git a/src/components/banner.rs b/src/components/banner.rs
index acaaf62..3244a07 100644
--- a/src/components/banner.rs
+++ b/src/components/banner.rs
@@ -9,9 +9,5 @@ pub fn Banner<T>(mut text: T) -> impl IntoView
where
T: FnMut() -> String + Send + 'static,
{
- view! {
- <p class="text-white rounded-lg m-2 p-2 bg-red-600">
- {move || text()}
- </p>
- }
+ view! { <p class="text-white rounded-lg m-2 p-2 bg-red-600">{move || text()}</p> }
}
diff --git a/src/components/buy.rs b/src/components/buy.rs
deleted file mode 100644
index cb4cff4..0000000
--- a/src/components/buy.rs
+++ /dev/null
@@ -1,193 +0,0 @@
-use leptos::{
- IntoView, component,
- prelude::{Get, Read, Show, WriteSignal, expect_context, signal},
- task::spawn_local,
- view,
-};
-use leptos_router::{NavigateOptions, hooks::use_navigate};
-use log::info;
-use reactive_stores::Store;
-use rocie_client::{
- apis::Error,
- models::{Product, Unit},
-};
-use uuid::Uuid;
-
-use crate::{
- ConfigState, ConfigStateStoreFields,
- api::{
- buy_barcode_wrapper, get_product_by_name, get_product_unit_by_id,
- get_products_by_part_name, get_unit_by_id,
- },
- components::{async_fetch::AsyncResource, banner::Banner, form::Form, site_header::SiteHeader},
-};
-
-#[component]
-pub fn Buy() -> impl IntoView {
- let (on_submit_errored, on_submit_errored_set) = signal(None);
-
- view! {
- <SiteHeader logo=icondata_io::IoPricetag back_location="/" name="Buy" />
-
- <Show when=move || on_submit_errored.get().is_some()>
- <Banner text=move || on_submit_errored.get().expect("Should be some") />
- </Show>
-
- {
- Form! {
- on_submit = |barcode_number, amount| {
- let config = expect_context::<Store<ConfigState>>();
- let config = config.config().read();
-
- spawn_local(async move {
- if let Err(err) = buy_barcode_wrapper(&config, barcode_number).await {
- let error = format!("Error in form on-submit for barcode `{barcode_number}`: {err}");
- on_submit_errored_set.set(Some(error));
- } else {
- on_submit_errored_set.set(None);
-
- info!("Bought barcode {barcode_number} {amount} times");
- }
-
- });
- };
-
- <Input
- name=barcode_number,
- rust_type=u32,
- html_type="number",
- label="Barcode Number",
- />
-
- <Input
- name=amount,
- rust_type=u16,
- html_type="number",
- label="Amount"
- />
- }
- }
- }
-}
-
-#[component]
-pub fn AssociateBarcode() -> impl IntoView {
- let product_name_signal;
-
- let (show_units, show_units_set) = signal(false);
-
- view! {
- <SiteHeader logo=icondata_io::IoPricetag back_location="/" name="Buy" />
-
- {
- Form! {
- on_submit = |product_name, amount, unit_id| {
- spawn_local(async move {
- let navigate = use_navigate();
-
- info!("Got product barcode: {product_name} with amount: {amount}, and {unit_id}");
-
- navigate("/", NavigateOptions::default());
- });
- };
-
- <Input
- name=product_name,
- rust_type=String,
- html_type="text",
- label="Product Name",
- reactive=product_name_signal
- auto_complete=generate_suggest_products
- />
-
- <Show
- when=move || show_units.get(),
- >
- <Select
- name=unit_id,
- rust_type=Uuid,
- label="Unit",
- options=AsyncResource! {
- (
- product_name: Option<String> = product_name_signal(),
- show_units_set: WriteSignal<bool> = show_units_set
- ) -> Result<Vec<(String, String)>, leptos::error::Error> {
- let units = product_unit_fetcher(product_name).await?;
-
- show_units_set.set(units.is_some());
- if let Some(units) = units {
- Ok(
- units
- .into_iter()
- .map(|unit| (unit.full_name_singular, unit.id.to_string()))
- .collect()
- )
- } else {
- Ok(vec![])
- }
- }
- },
- />
- </Show>
-
- <Input
- name=amount,
- rust_type=u16,
- html_type="number",
- label="Amount"
- />
- }
- }
- }
-}
-
-async fn generate_suggest_products(
- optional_product_name: Option<String>,
-) -> Result<Option<Vec<String>>, leptos::error::Error> {
- if let Some(product_name) = optional_product_name
- && !product_name.is_empty()
- {
- let products = get_products_by_part_name(product_name).await?;
- Ok(Some(products.into_iter().map(|prod| prod.name).collect()))
- } else {
- Ok(None)
- }
-}
-
-async fn product_unit_fetcher(
- optinal_product_name: Option<String>,
-) -> Result<Option<Vec<Unit>>, leptos::error::Error> {
- if let Some(product_name) = optinal_product_name
- && !product_name.is_empty()
- {
- let value: Option<Product> = {
- match get_product_by_name(product_name).await {
- Ok(ok) => Ok::<_, leptos::error::Error>(Some(ok)),
- Err(err) => match err {
- Error::ResponseError(ref response_content) => {
- match response_content.status.as_u16() {
- 404 => Ok(None),
- _ => Err(err.into()),
- }
- }
- err => Err(err.into()),
- },
- }?
- };
-
- if let Some(value) = value {
- let (_, unit_property) = get_product_unit_by_id(value.id).await?;
-
- let mut units = Vec::with_capacity(unit_property.units.len());
- for unit_id in unit_property.units {
- units.push(get_unit_by_id(unit_id).await?);
- }
-
- Ok(Some(units))
- } else {
- Ok(None)
- }
- } else {
- Ok(None)
- }
-}
diff --git a/src/components/container.rs b/src/components/container.rs
index 7a4a64f..83b9584 100644
--- a/src/components/container.rs
+++ b/src/components/container.rs
@@ -11,25 +11,36 @@ pub fn Container(
buttons: Vec<(impl IntoView, &'static str)>,
children: Children,
) -> impl IntoView {
+ assert!(!buttons.is_empty());
+
+ let first_button_path = buttons.first().expect("Should have at least on button").1;
+
view! {
- <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()}
+ <button
+ type="button"
+ on:click=|_| {
+ use_navigate()(first_button_path, NavigateOptions::default());
+ }
+ >
+ <div class="p-4 mt-4 mr-4 ml-4 md-2 text-justify 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>
+ <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>
+ </button>
}
}
diff --git a/src/components/input_placeholder.rs b/src/components/input_placeholder.rs
index 99b3196..0589363 100644
--- a/src/components/input_placeholder.rs
+++ b/src/components/input_placeholder.rs
@@ -96,12 +96,7 @@ pub fn InputPlaceholder(
{label}
</label>
- <Show
- when=move || {
- !autocomplete_signal.get().is_empty()
- }
- fallback=move || ()
- >
+ <Show when=move || { !autocomplete_signal.get().is_empty() } fallback=move || ()>
<div class="\
absolute \
top-0 \
@@ -139,7 +134,7 @@ pub fn InputPlaceholder(
provide_auto_completion(
auto_complete,
autocomplete_set,
- reactive
+ reactive,
)
})
}}
@@ -178,10 +173,10 @@ fn provide_auto_completion(
autocomplete_set.set(item2.clone());
reactive
.expect(
- "Should be set, \
- when autocomplete is used")
+ "Should be set, \
+ when autocomplete is used",
+ )
.set(Some(item2.clone()));
-
info!("Set autocomplete to {item2}.");
}
>
diff --git a/src/components/inventory.rs b/src/components/inventory.rs
deleted file mode 100644
index 5855b33..0000000
--- a/src/components/inventory.rs
+++ /dev/null
@@ -1,55 +0,0 @@
-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 ca2ac10..efc4842 100644
--- a/src/components/mod.rs
+++ b/src/components/mod.rs
@@ -2,16 +2,14 @@ use std::sync::atomic::{AtomicU32, Ordering};
// Generic
pub mod async_fetch;
+pub mod banner;
pub mod container;
pub mod form;
pub mod icon_p;
pub mod input_placeholder;
pub mod select_placeholder;
-pub mod banner;
// Specific
-pub mod buy;
-pub mod inventory;
pub mod product_overview;
pub mod recipies;
pub mod site_header;
diff --git a/src/components/product_overview.rs b/src/components/product_overview.rs
index 777baef..5413dc1 100644
--- a/src/components/product_overview.rs
+++ b/src/components/product_overview.rs
@@ -11,33 +11,31 @@ pub fn ProductOverview() -> impl IntoView {
<Container
header="Inventory"
buttons=vec![
- (
- view! { <IconP icon=icondata_io::IoClipboardOutline text="Inventory" /> },
- "inventory",
- ),
+ (view! { <IconP icon=icondata_io::IoClipboard 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";
+ @map_error_in_producer
+ 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>
- }
- }
+ 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
index 1bd3a0d..f7903e4 100644
--- a/src/components/recipies.rs
+++ b/src/components/recipies.rs
@@ -1,11 +1,17 @@
use leptos::{IntoView, component, prelude::ElementChild, view};
-use crate::components::container::Container;
+use crate::components::{container::Container, icon_p::IconP};
#[component]
pub fn Recipies() -> impl IntoView {
view! {
- <Container header="Recipies" buttons=vec![("Mealplan", "mealplan")]>
+ <Container
+ header="Recipies"
+ buttons=vec![
+ (view! { <IconP icon=icondata_io::IoFastFood text="Recipies" /> }, "recipies"),
+ (view! { <IconP icon=icondata_io::IoCalendarSharp text="Mealplan" /> }, "mealplan"),
+ ]
+ >
<p>"You have 0 recipies."</p>
</Container>
}
diff --git a/src/components/select_placeholder.rs b/src/components/select_placeholder.rs
index 2e0f783..7289793 100644
--- a/src/components/select_placeholder.rs
+++ b/src/components/select_placeholder.rs
@@ -41,16 +41,18 @@ pub fn SelectPlaceholder(
"
node_ref=node_ref
>
- {move || AsyncFetch! {
- @map_error_in_producer
- from_resource = options
- producer = |options| {
- options
- .into_iter()
- .map(|(label, value)| {
- view! { <option value=value>{label}</option> }
- })
- .collect_view()
+ {move || {
+ AsyncFetch! {
+ @map_error_in_producer
+ from_resource = options,
+ producer = |options| {
+ options
+ .into_iter()
+ .map(|(label, value)| {
+ view! { <option value=value>{label}</option> }
+ })
+ .collect_view()
+ }
}
}}
</select>