From 9d3f9e9c60507ddc7a6711c74312c21fd239771b Mon Sep 17 00:00:00 2001 From: Greg Burri Date: Thu, 23 Jan 2025 03:01:15 +0100 Subject: [PATCH] Add a calendar to schedule a recipe to a chosen date (WIP) --- Cargo.lock | 19 ++--- backend/Cargo.toml | 2 +- backend/scss/calendar.scss | 46 +++++++++++ backend/scss/modal-dialog.scss | 4 +- backend/scss/style.scss | 9 ++- backend/src/translation.rs | 37 ++++++++- backend/templates/calendar.html | 45 +++++++++++ backend/templates/recipe_view.html | 12 ++- backend/translation.ron | 40 ++++++++++ frontend/Cargo.toml | 2 + frontend/src/calendar.rs | 121 +++++++++++++++++++++++++++++ frontend/src/lib.rs | 28 ++++--- frontend/src/modal_dialog.rs | 33 ++++++-- frontend/src/recipe_edit.rs | 67 +++++++++------- frontend/src/recipe_view.rs | 40 ++++++++++ 15 files changed, 442 insertions(+), 63 deletions(-) create mode 100644 backend/scss/calendar.scss create mode 100644 backend/templates/calendar.html create mode 100644 frontend/src/calendar.rs create mode 100644 frontend/src/recipe_view.rs diff --git a/Cargo.lock b/Cargo.lock index 5b745be..a53d261 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,9 +149,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] name = "axum" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efea76243612a2436fb4074ba0cf3ba9ea29efdeb72645d8fc63f116462be1de" +checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8" dependencies = [ "axum-core", "axum-macros", @@ -184,12 +184,12 @@ dependencies = [ [[package]] name = "axum-core" -version = "0.5.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eab1b0df7cded837c40dacaa2e1c33aa17c84fc3356ae67b5645f1e83190753e" +checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733" dependencies = [ "bytes", - "futures-core", + "futures-util", "http 1.2.0", "http-body", "http-body-util", @@ -204,9 +204,9 @@ dependencies = [ [[package]] name = "axum-extra" -version = "0.11.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "543f0799d22486525744f06a3580b64f3e51d97aba73ea0e09040969c0034722" +checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b" dependencies = [ "axum", "axum-core", @@ -700,6 +700,7 @@ dependencies = [ name = "frontend" version = "0.1.0" dependencies = [ + "chrono", "common", "console_error_panic_hook", "futures", @@ -2067,9 +2068,9 @@ checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" [[package]] name = "rustix" -version = "0.38.43" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ "bitflags", "errno", diff --git a/backend/Cargo.toml b/backend/Cargo.toml index a55c0e1..cc6a499 100644 --- a/backend/Cargo.toml +++ b/backend/Cargo.toml @@ -8,7 +8,7 @@ edition = "2021" common = { path = "../common" } axum = { version = "0.8", features = ["macros"] } -axum-extra = { version = "0.11", features = ["cookie"] } +axum-extra = { version = "0.10", features = ["cookie"] } tokio = { version = "1", features = ["full"] } tower = { version = "0.5", features = ["util"] } tower-http = { version = "0.6", features = ["fs", "trace"] } diff --git a/backend/scss/calendar.scss b/backend/scss/calendar.scss new file mode 100644 index 0000000..6500a85 --- /dev/null +++ b/backend/scss/calendar.scss @@ -0,0 +1,46 @@ +.calendar { + .month-selector { + width: 100%; + text-align: center; + + .prev { + float: left; + } + + .next { + float: right; + } + + .month { + display: none; + } + + .month.current { + display: inline; + } + } + + ul.weekdays { + margin: 0; + padding: 20px 0; + + li { + display: inline-block; + width: 14%; + text-align: center; + margin: 0; + } + } + + ul.days { + margin: 0; + padding: 20px 0; + + li { + display: inline-block; + width: 14%; + text-align: center; + margin: 0; + } + } +} \ No newline at end of file diff --git a/backend/scss/modal-dialog.scss b/backend/scss/modal-dialog.scss index 7ea2fb1..597d842 100644 --- a/backend/scss/modal-dialog.scss +++ b/backend/scss/modal-dialog.scss @@ -1,8 +1,8 @@ #modal-dialog { // visibility: hidden; color: white; - max-width: 300px; - margin-left: -125px; + width: 500px; + margin-left: -250px; background-color: black; text-align: center; border-radius: 2px; diff --git a/backend/scss/style.scss b/backend/scss/style.scss index 8c9f9b0..a1efb64 100644 --- a/backend/scss/style.scss +++ b/backend/scss/style.scss @@ -1,5 +1,6 @@ @use 'toast.scss'; @use 'modal-dialog.scss'; +@use 'calendar.scss'; $color-1: #B29B89; $color-2: #89B29B; @@ -123,6 +124,10 @@ body { h1 { text-align: center; } + + #hidden-templates { + display: none; + } } #recipe-edit { @@ -163,10 +168,6 @@ body { background-color: red; } } - - #hidden-templates { - display: none; - } } form { diff --git a/backend/src/translation.rs b/backend/src/translation.rs index 7fa8053..0e4383f 100644 --- a/backend/src/translation.rs +++ b/backend/src/translation.rs @@ -1,4 +1,4 @@ -use std::{fs::File, sync::LazyLock}; +use std::{borrow::Borrow, fs::File, sync::LazyLock}; use ron::de::from_reader; use serde::Deserialize; @@ -114,6 +114,27 @@ pub enum Sentence { RecipeOneServing, RecipeSomeServings, RecipeEstimatedTimeMinAbbreviation, + + // Calendar. + CalendarMondayAbbreviation, + CalendarTuesdayAbbreviation, + CalendarWednesdayAbbreviation, + CalendarThursdayAbbreviation, + CalendarFridayAbbreviation, + CalendarSaturdayAbbreviation, + CalendarSundayAbbreviation, + CalendarJanuary, + CalendarFebruary, + CalendarMarch, + CalendarApril, + CalendarMay, + CalendarJune, + CalendarJuly, + CalendarAugust, + CalendarSeptember, + CalendarOctober, + CalendarNovember, + CalendarDecember, } pub const DEFAULT_LANGUAGE_CODE: &str = "en"; @@ -131,7 +152,10 @@ impl Tr { } } - pub fn t(&self, sentence: Sentence) -> &'static str { + pub fn t(&self, sentence: T) -> &'static str + where + T: Borrow, + { self.lang.get(sentence) } @@ -196,10 +220,15 @@ impl Language { } } - pub fn get(&'static self, sentence: Sentence) -> &'static str { + pub fn get(&'static self, sentence: T) -> &'static str + where + T: Borrow, + { + let sentence_cloned: Sentence = sentence.borrow().clone(); + let text: &str = self .translation - .get(sentence.clone() as usize) + .get(sentence_cloned as usize) .unwrap() .as_ref(); if text.is_empty() && self.code != DEFAULT_LANGUAGE_CODE { diff --git a/backend/templates/calendar.html b/backend/templates/calendar.html new file mode 100644 index 0000000..5183ea5 --- /dev/null +++ b/backend/templates/calendar.html @@ -0,0 +1,45 @@ +
+
+ PREV + + + {% for month in [ + Sentence::CalendarJanuary, + Sentence::CalendarFebruary, + Sentence::CalendarMarch, + Sentence::CalendarApril, + Sentence::CalendarMay, + Sentence::CalendarJune, + Sentence::CalendarJuly, + Sentence::CalendarAugust, + Sentence::CalendarSeptember, + Sentence::CalendarOctober, + Sentence::CalendarNovember, + Sentence::CalendarDecember, + ] %} + {{ tr.t(*month) }} + {% endfor %} + + NEXT +
+
    + {% for day in [ + Sentence::CalendarMondayAbbreviation, + Sentence::CalendarTuesdayAbbreviation, + Sentence::CalendarWednesdayAbbreviation, + Sentence::CalendarThursdayAbbreviation, + Sentence::CalendarFridayAbbreviation, + Sentence::CalendarSaturdayAbbreviation, + Sentence::CalendarSundayAbbreviation, + ] %} +
  • {{ tr.t(*day) }}
  • + {% endfor %} + +
      + {% for i in 0..7 %} + {% for j in 0..5 %} +
    • + {% endfor %} + {% endfor %} +
    +
\ No newline at end of file diff --git a/backend/templates/recipe_view.html b/backend/templates/recipe_view.html index 989735e..3a2d487 100644 --- a/backend/templates/recipe_view.html +++ b/backend/templates/recipe_view.html @@ -5,8 +5,11 @@

{{ recipe.title }}

- {% if user.is_some() && crate::data::model::can_user_edit_recipe(&user.as_ref().unwrap(), &recipe) %} - Edit + {% if let Some(user) = user %} + {% if crate::data::model::can_user_edit_recipe(user, recipe) %} + Edit + {% endif %} + Add to planner {% endif %}
@@ -27,7 +30,6 @@ {% else %} {% endmatch %} - {% match recipe.estimated_time %} {% when Some(time) %} {{ time +}} {{+ tr.t(Sentence::RecipeEstimatedTimeMinAbbreviation) }} @@ -76,6 +78,10 @@
{% endfor %} + +
+ {% include "calendar.html" %} +
{% endblock %} \ No newline at end of file diff --git a/backend/translation.ron b/backend/translation.ron index 835f018..168d8de 100644 --- a/backend/translation.ron +++ b/backend/translation.ron @@ -99,6 +99,26 @@ (RecipeOneServing, "1 serving"), (RecipeSomeServings, "{} servings"), (RecipeEstimatedTimeMinAbbreviation, "min"), + + (CalendarMondayAbbreviation, "Mon"), + (CalendarTuesdayAbbreviation, "Tue"), + (CalendarWednesdayAbbreviation, "Wed"), + (CalendarThursdayAbbreviation, "Thu"), + (CalendarFridayAbbreviation, "Fri"), + (CalendarSaturdayAbbreviation, "Sat"), + (CalendarSundayAbbreviation, "Sun"), + (CalendarJanuary, "January"), + (CalendarFebruary, "February"), + (CalendarMarch, "March"), + (CalendarApril, "April"), + (CalendarMay, "May"), + (CalendarJune, "June"), + (CalendarJuly, "July"), + (CalendarAugust, "August"), + (CalendarSeptember, "September"), + (CalendarOctober, "October"), + (CalendarNovember, "November"), + (CalendarDecember, "December"), ] ), ( @@ -201,6 +221,26 @@ (RecipeOneServing, "pour 1 personne"), (RecipeSomeServings, "pour {} personnes"), (RecipeEstimatedTimeMinAbbreviation, "min"), + + (CalendarMondayAbbreviation, "Lun"), + (CalendarTuesdayAbbreviation, "Mar"), + (CalendarWednesdayAbbreviation, "Mer"), + (CalendarThursdayAbbreviation, "Jeu"), + (CalendarFridayAbbreviation, "Ven"), + (CalendarSaturdayAbbreviation, "Sam"), + (CalendarSundayAbbreviation, "Dim"), + (CalendarJanuary, "Janvier"), + (CalendarFebruary, "Février"), + (CalendarMarch, "Mars"), + (CalendarApril, "Avril"), + (CalendarMay, "Mai"), + (CalendarJune, "Juin"), + (CalendarJuly, "Juillet"), + (CalendarAugust, "Août"), + (CalendarSeptember, "Septembre"), + (CalendarOctober, "Octobre"), + (CalendarNovember, "Novembre"), + (CalendarDecember, "Décembre"), ] ) ] \ No newline at end of file diff --git a/frontend/Cargo.toml b/frontend/Cargo.toml index 7c9f8b8..4214b2f 100644 --- a/frontend/Cargo.toml +++ b/frontend/Cargo.toml @@ -13,6 +13,8 @@ default = ["console_error_panic_hook"] [dependencies] common = { path = "../common" } +chrono = "0.4" + ron = "0.8" serde = { version = "1.0", features = ["derive"] } thiserror = "2" diff --git a/frontend/src/calendar.rs b/frontend/src/calendar.rs new file mode 100644 index 0000000..7ace6d3 --- /dev/null +++ b/frontend/src/calendar.rs @@ -0,0 +1,121 @@ +use std::{ + ops::{AddAssign, SubAssign}, + sync::{ + atomic::{AtomicI32, AtomicU32, Ordering}, + Arc, + }, +}; + +use chrono::{offset::Local, Datelike, Days, NaiveDate, Weekday}; +use gloo::{console::log, events::EventListener}; +use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::spawn_local; +use web_sys::Element; + +use crate::utils::{by_id, SelectorExt}; + +pub fn setup(calendar: &Element) { + let prev: Element = calendar.selector(".prev"); + let next: Element = calendar.selector(".next"); + + let current_month = Arc::new(AtomicU32::new(Local::now().month())); + let current_year = Arc::new(AtomicI32::new(Local::now().year())); + + display_month(calendar, Local::now().year(), Local::now().month()); + + let calendar_clone = calendar.clone(); + let current_month_clone = current_month.clone(); + let current_year_clone = current_year.clone(); + EventListener::new(&prev, "click", move |_event| { + let mut m = current_month_clone.load(Ordering::Relaxed) - 1; + if m == 0 { + current_year_clone.fetch_sub(1, Ordering::Relaxed); + m = 12 + } + current_month_clone.store(m, Ordering::Relaxed); + display_month( + &calendar_clone, + current_year_clone.load(Ordering::Relaxed), + m, + ); + }) + .forget(); + + let calendar_clone = calendar.clone(); + let current_month_clone = current_month.clone(); + let current_year_clone = current_year.clone(); + EventListener::new(&next, "click", move |_event| { + let mut m = current_month_clone.load(Ordering::Relaxed) + 1; + if m == 13 { + current_year_clone.fetch_add(1, Ordering::Relaxed); + m = 1 + } + current_month_clone.store(m, Ordering::Relaxed); + display_month( + &calendar_clone, + current_year_clone.load(Ordering::Relaxed), + m, + ); + }) + .forget(); + + // now.weekday() + + // console!(now.to_string()); +} + +// fn translate_month(month: u32) -> &'static str { +// match +// } + +fn display_month(calendar: &Element, year: i32, month: u32) { + log!(year, month); + + calendar + .selector::(".year") + .set_inner_html(&year.to_string()); + + for (i, m) in calendar + .selector_all::(".month") + .into_iter() + .enumerate() + { + if i as u32 + 1 == month { + m.set_class_name("month current"); + } else { + m.set_class_name("month"); + } + } + + // calendar + // .selector::(".month") + // .set_inner_html(&month.to_string()); + + let mut current = NaiveDate::from_ymd_opt(year, month, 1).unwrap(); + + // let mut day = Local:: ; + while (current - Days::new(1)).month() == month { + current = current - Days::new(1); + } + + while current.weekday() != Weekday::Mon { + current = current - Days::new(1); + } + + for i in 0..7 { + for j in 0..5 { + let li: Element = by_id(&format!("day-{}{}", i, j)); + li.set_inner_html(¤t.day().to_string()); + + if current == Local::now().date_naive() { + li.set_class_name("current-month today"); + } else if current.month() == month { + li.set_class_name("current-month"); + } else { + li.set_class_name(""); + } + + current = current + Days::new(1); + } + } +} diff --git a/frontend/src/lib.rs b/frontend/src/lib.rs index d114c56..588be31 100644 --- a/frontend/src/lib.rs +++ b/frontend/src/lib.rs @@ -1,6 +1,8 @@ +mod calendar; mod modal_dialog; mod on_click; mod recipe_edit; +mod recipe_view; mod request; mod toast; mod utils; @@ -20,16 +22,24 @@ pub fn main() -> Result<(), JsValue> { let location = window().location().pathname()?; let path: Vec<&str> = location.split('/').skip(1).collect(); - if let ["recipe", "edit", id] = path[..] { - let id = id.parse::().unwrap(); // TODO: remove unwrap. - if let Err(error) = recipe_edit::setup_page(id) { - log!(error); + // if let ["recipe", "edit", id] = path[..] { + match path[..] { + ["recipe", "edit", id] => { + let id = id.parse::().unwrap(); // TODO: remove unwrap. + if let Err(error) = recipe_edit::setup_page(id) { + log!(error); + } } - - // Disable: user editing data are now submitted as classic form data. - // ["user", "edit"] => { - // handles::user_edit(document)?; - // } + ["recipe", "view", id] => { + let id = id.parse::().unwrap(); // TODO: remove unwrap. + if let Err(error) = recipe_view::setup_page(id) { + log!(error); + } + } + _ => (), // Disable: user editing data are now submitted as classic form data. + // ["user", "edit"] => { + // handles::user_edit(document)?; + // } } let select_language: HtmlSelectElement = by_id("select-website-language"); diff --git a/frontend/src/modal_dialog.rs b/frontend/src/modal_dialog.rs index a42a33a..ac83ab9 100644 --- a/frontend/src/modal_dialog.rs +++ b/frontend/src/modal_dialog.rs @@ -1,16 +1,39 @@ use futures::{future::FutureExt, pin_mut, select}; use web_sys::{Element, HtmlDialogElement}; -use crate::utils::{by_id, SelectorExt}; - -use crate::on_click; +use crate::{ + on_click, + utils::{by_id, selector_and_clone, SelectorExt}, +}; + +pub enum DialogContent<'a, T> +where + T: Fn(&Element), +{ + Text(&'a str), + CloneFromElement(&'a str, T), +} -pub async fn show(message: &str) -> bool { +pub async fn show(content: DialogContent<'_, T>) -> bool +where + T: Fn(&Element), +{ let dialog: HtmlDialogElement = by_id("modal-dialog"); + let input_ok: Element = dialog.selector(".ok"); let input_cancel: Element = dialog.selector(".cancel"); - dialog.selector::(".content").set_inner_html(message); + let content_element = dialog.selector::(".content"); + + match content { + DialogContent::Text(message) => content_element.set_inner_html(message), + DialogContent::CloneFromElement(element_selector, initilizer) => { + let element: Element = selector_and_clone(element_selector); + content_element.set_inner_html(""); + content_element.append_child(&element).unwrap(); + initilizer(&element); + } + } dialog.show_modal().unwrap(); diff --git a/frontend/src/recipe_edit.rs b/frontend/src/recipe_edit.rs index 5fe5d13..d1a4e4c 100644 --- a/frontend/src/recipe_edit.rs +++ b/frontend/src/recipe_edit.rs @@ -20,22 +20,6 @@ use crate::{ utils::{by_id, selector, selector_and_clone, SelectorExt}, }; -async fn reload_recipes_list(current_recipe_id: i64) { - match Request::get("/fragments/recipes_list") - .query([("current_recipe_id", current_recipe_id.to_string())]) - .send() - .await - { - Err(error) => { - toast::show(Level::Info, &format!("Internal server error: {}", error)); - } - Ok(response) => { - let list = document().get_element_by_id("recipes-list").unwrap(); - list.set_outer_html(&response.text().await.unwrap()); - } - } -} - pub fn setup_page(recipe_id: i64) -> Result<(), JsValue> { // Title. { @@ -266,10 +250,10 @@ pub fn setup_page(recipe_id: i64) -> Result<(), JsValue> { EventListener::new(&delete_button, "click", move |_event| { let title: HtmlInputElement = by_id("input-title"); spawn_local(async move { - if modal_dialog::show(&format!( + if modal_dialog::show(modal_dialog::DialogContent::::Text(&format!( "Are you sure to delete the recipe '{}'", title.value() - )) + ))) .await { let body = ron_api::Id { id: recipe_id }; @@ -314,7 +298,7 @@ pub fn setup_page(recipe_id: i64) -> Result<(), JsValue> { // Add a new group. { let button_add_group: HtmlInputElement = by_id("input-add-group"); - let on_click_add_group = EventListener::new(&button_add_group, "click", move |_event| { + EventListener::new(&button_add_group, "click", move |_event| { let body = ron_api::Id { id: recipe_id }; spawn_local(async move { let response: ron_api::Id = request::post("recipe/add_group", body).await.unwrap(); @@ -325,8 +309,8 @@ pub fn setup_page(recipe_id: i64) -> Result<(), JsValue> { steps: vec![], }); }); - }); - on_click_add_group.forget(); + }) + .forget(); } Ok(()) @@ -397,7 +381,12 @@ fn create_group_element(group: &ron_api::Group) -> Element { .selector::(".input-group-name") .value(); spawn_local(async move { - if modal_dialog::show(&format!("Are you sure to delete the group '{}'", name)).await { + if modal_dialog::show(modal_dialog::DialogContent::::Text(&format!( + "Are you sure to delete the group '{}'", + name + ))) + .await + { let body = ron_api::Id { id: group_id }; let _ = request::delete::<(), _>("recipe/remove_group", body).await; let group_element = by_id::(&format!("group-{}", group_id)); @@ -530,7 +519,12 @@ fn create_step_element(group_element: &Element, step: &ron_api::Step) -> Element .selector::(".text-area-step-action") .value(); spawn_local(async move { - if modal_dialog::show(&format!("Are you sure to delete the step '{}'", action)).await { + if modal_dialog::show(modal_dialog::DialogContent::::Text(&format!( + "Are you sure to delete the step '{}'", + action + ))) + .await + { let body = ron_api::Id { id: step_id }; let _ = request::delete::<(), _>("recipe/remove_step", body).await; let step_element = by_id::(&format!("step-{}", step_id)); @@ -675,12 +669,17 @@ fn create_ingredient_element(step_element: &Element, ingredient: &ron_api::Ingre .selector::(".input-ingredient-name") .value(); spawn_local(async move { - if modal_dialog::show(&format!("Are you sure to delete the ingredient '{}'", name)) - .await + if modal_dialog::show(modal_dialog::DialogContent::::Text(&format!( + "Are you sure to delete the ingredient '{}'", + name + ))) + .await { let body = ron_api::Id { id: ingredient_id }; let _ = request::delete::<(), _>("recipe/remove_ingredient", body).await; - by_id::(&format!("ingredient-{}", ingredient_id)).remove(); + let ingredient_element = by_id::(&format!("ingredient-{}", ingredient_id)); + ingredient_element.next_element_sibling().unwrap().remove(); + ingredient_element.remove(); } }); }) @@ -689,6 +688,22 @@ fn create_ingredient_element(step_element: &Element, ingredient: &ron_api::Ingre ingredient_element } +async fn reload_recipes_list(current_recipe_id: i64) { + match Request::get("/fragments/recipes_list") + .query([("current_recipe_id", current_recipe_id.to_string())]) + .send() + .await + { + Err(error) => { + toast::show(Level::Info, &format!("Internal server error: {}", error)); + } + Ok(response) => { + let list = document().get_element_by_id("recipes-list").unwrap(); + list.set_outer_html(&response.text().await.unwrap()); + } + } +} + enum CursorPosition { UpperPart, LowerPart, diff --git a/frontend/src/recipe_view.rs b/frontend/src/recipe_view.rs new file mode 100644 index 0000000..488e459 --- /dev/null +++ b/frontend/src/recipe_view.rs @@ -0,0 +1,40 @@ +use gloo::{ + console::console, + events::EventListener, + net::http::Request, + utils::{document, window}, +}; +use wasm_bindgen::prelude::*; +use wasm_bindgen_futures::spawn_local; +use web_sys::{ + DragEvent, Element, HtmlDivElement, HtmlInputElement, HtmlSelectElement, HtmlTextAreaElement, + KeyboardEvent, +}; + +use common::ron_api; + +use crate::{ + calendar, modal_dialog, request, + toast::{self, Level}, + utils::{by_id, selector, selector_and_clone, SelectorExt}, +}; + +pub fn setup_page(recipe_id: i64) -> Result<(), JsValue> { + let add_to_planner: Element = selector("#recipe-view .add-to-planner"); + EventListener::new(&add_to_planner, "click", move |_event| { + // console!("CLICK".to_string()); + spawn_local(async move { + modal_dialog::show(modal_dialog::DialogContent::CloneFromElement( + "#hidden-templates .calendar", + |element| { + // console!("SETUP...".to_string()); + calendar::setup(element); + }, + )) + .await; + }); + }) + .forget(); + + Ok(()) +} -- 2.49.0