Add a calendar to schedule a recipe to a chosen date (WIP)
This commit is contained in:
parent
d9449de02b
commit
9d3f9e9c60
15 changed files with 441 additions and 62 deletions
19
Cargo.lock
generated
19
Cargo.lock
generated
|
|
@ -149,9 +149,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.8.2"
|
version = "0.8.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "efea76243612a2436fb4074ba0cf3ba9ea29efdeb72645d8fc63f116462be1de"
|
checksum = "6d6fd624c75e18b3b4c6b9caf42b1afe24437daaee904069137d8bab077be8b8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum-core",
|
"axum-core",
|
||||||
"axum-macros",
|
"axum-macros",
|
||||||
|
|
@ -184,12 +184,12 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.5.1"
|
version = "0.5.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "eab1b0df7cded837c40dacaa2e1c33aa17c84fc3356ae67b5645f1e83190753e"
|
checksum = "df1362f362fd16024ae199c1970ce98f9661bf5ef94b9808fee734bc3698b733"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-core",
|
"futures-util",
|
||||||
"http 1.2.0",
|
"http 1.2.0",
|
||||||
"http-body",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
|
|
@ -204,9 +204,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-extra"
|
name = "axum-extra"
|
||||||
version = "0.11.0"
|
version = "0.10.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "543f0799d22486525744f06a3580b64f3e51d97aba73ea0e09040969c0034722"
|
checksum = "460fc6f625a1f7705c6cf62d0d070794e94668988b1c38111baeec177c715f7b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"axum",
|
"axum",
|
||||||
"axum-core",
|
"axum-core",
|
||||||
|
|
@ -700,6 +700,7 @@ dependencies = [
|
||||||
name = "frontend"
|
name = "frontend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"chrono",
|
||||||
"common",
|
"common",
|
||||||
"console_error_panic_hook",
|
"console_error_panic_hook",
|
||||||
"futures",
|
"futures",
|
||||||
|
|
@ -2067,9 +2068,9 @@ checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.43"
|
version = "0.38.44"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
|
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"errno",
|
"errno",
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ edition = "2021"
|
||||||
common = { path = "../common" }
|
common = { path = "../common" }
|
||||||
|
|
||||||
axum = { version = "0.8", features = ["macros"] }
|
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"] }
|
tokio = { version = "1", features = ["full"] }
|
||||||
tower = { version = "0.5", features = ["util"] }
|
tower = { version = "0.5", features = ["util"] }
|
||||||
tower-http = { version = "0.6", features = ["fs", "trace"] }
|
tower-http = { version = "0.6", features = ["fs", "trace"] }
|
||||||
|
|
|
||||||
46
backend/scss/calendar.scss
Normal file
46
backend/scss/calendar.scss
Normal file
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
#modal-dialog {
|
#modal-dialog {
|
||||||
// visibility: hidden;
|
// visibility: hidden;
|
||||||
color: white;
|
color: white;
|
||||||
max-width: 300px;
|
width: 500px;
|
||||||
margin-left: -125px;
|
margin-left: -250px;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
@use 'toast.scss';
|
@use 'toast.scss';
|
||||||
@use 'modal-dialog.scss';
|
@use 'modal-dialog.scss';
|
||||||
|
@use 'calendar.scss';
|
||||||
|
|
||||||
$color-1: #B29B89;
|
$color-1: #B29B89;
|
||||||
$color-2: #89B29B;
|
$color-2: #89B29B;
|
||||||
|
|
@ -123,6 +124,10 @@ body {
|
||||||
h1 {
|
h1 {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#hidden-templates {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#recipe-edit {
|
#recipe-edit {
|
||||||
|
|
@ -163,10 +168,6 @@ body {
|
||||||
background-color: red;
|
background-color: red;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#hidden-templates {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
form {
|
form {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{fs::File, sync::LazyLock};
|
use std::{borrow::Borrow, fs::File, sync::LazyLock};
|
||||||
|
|
||||||
use ron::de::from_reader;
|
use ron::de::from_reader;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
@ -114,6 +114,27 @@ pub enum Sentence {
|
||||||
RecipeOneServing,
|
RecipeOneServing,
|
||||||
RecipeSomeServings,
|
RecipeSomeServings,
|
||||||
RecipeEstimatedTimeMinAbbreviation,
|
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";
|
pub const DEFAULT_LANGUAGE_CODE: &str = "en";
|
||||||
|
|
@ -131,7 +152,10 @@ impl Tr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn t(&self, sentence: Sentence) -> &'static str {
|
pub fn t<T>(&self, sentence: T) -> &'static str
|
||||||
|
where
|
||||||
|
T: Borrow<Sentence>,
|
||||||
|
{
|
||||||
self.lang.get(sentence)
|
self.lang.get(sentence)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,10 +220,15 @@ impl Language {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get(&'static self, sentence: Sentence) -> &'static str {
|
pub fn get<T>(&'static self, sentence: T) -> &'static str
|
||||||
|
where
|
||||||
|
T: Borrow<Sentence>,
|
||||||
|
{
|
||||||
|
let sentence_cloned: Sentence = sentence.borrow().clone();
|
||||||
|
|
||||||
let text: &str = self
|
let text: &str = self
|
||||||
.translation
|
.translation
|
||||||
.get(sentence.clone() as usize)
|
.get(sentence_cloned as usize)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.as_ref();
|
.as_ref();
|
||||||
if text.is_empty() && self.code != DEFAULT_LANGUAGE_CODE {
|
if text.is_empty() && self.code != DEFAULT_LANGUAGE_CODE {
|
||||||
|
|
|
||||||
45
backend/templates/calendar.html
Normal file
45
backend/templates/calendar.html
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<div class="calendar">
|
||||||
|
<div class="month-selector">
|
||||||
|
<span class="prev">PREV</span>
|
||||||
|
<span class="year" ></span>
|
||||||
|
|
||||||
|
{% 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,
|
||||||
|
] %}
|
||||||
|
<span class="month">{{ tr.t(*month) }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<span class="next">NEXT</span>
|
||||||
|
</div>
|
||||||
|
<ul class="weekdays">
|
||||||
|
{% for day in [
|
||||||
|
Sentence::CalendarMondayAbbreviation,
|
||||||
|
Sentence::CalendarTuesdayAbbreviation,
|
||||||
|
Sentence::CalendarWednesdayAbbreviation,
|
||||||
|
Sentence::CalendarThursdayAbbreviation,
|
||||||
|
Sentence::CalendarFridayAbbreviation,
|
||||||
|
Sentence::CalendarSaturdayAbbreviation,
|
||||||
|
Sentence::CalendarSundayAbbreviation,
|
||||||
|
] %}
|
||||||
|
<li class="weekday">{{ tr.t(*day) }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<ul class="days">
|
||||||
|
{% for i in 0..7 %}
|
||||||
|
{% for j in 0..5 %}
|
||||||
|
<li id="day-{{i}}{{j}}"></li>
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
@ -5,8 +5,11 @@
|
||||||
<div class="content" id="recipe-view">
|
<div class="content" id="recipe-view">
|
||||||
<h2 class="recipe-title" >{{ recipe.title }}</h2>
|
<h2 class="recipe-title" >{{ recipe.title }}</h2>
|
||||||
|
|
||||||
{% if user.is_some() && crate::data::model::can_user_edit_recipe(&user.as_ref().unwrap(), &recipe) %}
|
{% if let Some(user) = user %}
|
||||||
<a class="edit-recipe" href="/recipe/edit/{{ recipe.id }}" >Edit</a>
|
{% if crate::data::model::can_user_edit_recipe(user, recipe) %}
|
||||||
|
<a class="edit-recipe" href="/recipe/edit/{{ recipe.id }}" >Edit</a>
|
||||||
|
{% endif %}
|
||||||
|
<span class="add-to-planner">Add to planner</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="tags">
|
<div class="tags">
|
||||||
|
|
@ -27,7 +30,6 @@
|
||||||
{% else %}
|
{% else %}
|
||||||
{% endmatch %}
|
{% endmatch %}
|
||||||
|
|
||||||
|
|
||||||
{% match recipe.estimated_time %}
|
{% match recipe.estimated_time %}
|
||||||
{% when Some(time) %}
|
{% when Some(time) %}
|
||||||
{{ time +}} {{+ tr.t(Sentence::RecipeEstimatedTimeMinAbbreviation) }}
|
{{ time +}} {{+ tr.t(Sentence::RecipeEstimatedTimeMinAbbreviation) }}
|
||||||
|
|
@ -76,6 +78,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
<div id="hidden-templates">
|
||||||
|
{% include "calendar.html" %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
@ -99,6 +99,26 @@
|
||||||
(RecipeOneServing, "1 serving"),
|
(RecipeOneServing, "1 serving"),
|
||||||
(RecipeSomeServings, "{} servings"),
|
(RecipeSomeServings, "{} servings"),
|
||||||
(RecipeEstimatedTimeMinAbbreviation, "min"),
|
(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"),
|
(RecipeOneServing, "pour 1 personne"),
|
||||||
(RecipeSomeServings, "pour {} personnes"),
|
(RecipeSomeServings, "pour {} personnes"),
|
||||||
(RecipeEstimatedTimeMinAbbreviation, "min"),
|
(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"),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
|
|
@ -13,6 +13,8 @@ default = ["console_error_panic_hook"]
|
||||||
[dependencies]
|
[dependencies]
|
||||||
common = { path = "../common" }
|
common = { path = "../common" }
|
||||||
|
|
||||||
|
chrono = "0.4"
|
||||||
|
|
||||||
ron = "0.8"
|
ron = "0.8"
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
thiserror = "2"
|
thiserror = "2"
|
||||||
|
|
|
||||||
121
frontend/src/calendar.rs
Normal file
121
frontend/src/calendar.rs
Normal file
|
|
@ -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::<Element>(".year")
|
||||||
|
.set_inner_html(&year.to_string());
|
||||||
|
|
||||||
|
for (i, m) in calendar
|
||||||
|
.selector_all::<Element>(".month")
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
if i as u32 + 1 == month {
|
||||||
|
m.set_class_name("month current");
|
||||||
|
} else {
|
||||||
|
m.set_class_name("month");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// calendar
|
||||||
|
// .selector::<Element>(".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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
|
mod calendar;
|
||||||
mod modal_dialog;
|
mod modal_dialog;
|
||||||
mod on_click;
|
mod on_click;
|
||||||
mod recipe_edit;
|
mod recipe_edit;
|
||||||
|
mod recipe_view;
|
||||||
mod request;
|
mod request;
|
||||||
mod toast;
|
mod toast;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
@ -20,16 +22,24 @@ pub fn main() -> Result<(), JsValue> {
|
||||||
let location = window().location().pathname()?;
|
let location = window().location().pathname()?;
|
||||||
let path: Vec<&str> = location.split('/').skip(1).collect();
|
let path: Vec<&str> = location.split('/').skip(1).collect();
|
||||||
|
|
||||||
if let ["recipe", "edit", id] = path[..] {
|
// if let ["recipe", "edit", id] = path[..] {
|
||||||
let id = id.parse::<i64>().unwrap(); // TODO: remove unwrap.
|
match path[..] {
|
||||||
if let Err(error) = recipe_edit::setup_page(id) {
|
["recipe", "edit", id] => {
|
||||||
log!(error);
|
let id = id.parse::<i64>().unwrap(); // TODO: remove unwrap.
|
||||||
|
if let Err(error) = recipe_edit::setup_page(id) {
|
||||||
|
log!(error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
["recipe", "view", id] => {
|
||||||
// Disable: user editing data are now submitted as classic form data.
|
let id = id.parse::<i64>().unwrap(); // TODO: remove unwrap.
|
||||||
// ["user", "edit"] => {
|
if let Err(error) = recipe_view::setup_page(id) {
|
||||||
// handles::user_edit(document)?;
|
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");
|
let select_language: HtmlSelectElement = by_id("select-website-language");
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,39 @@
|
||||||
use futures::{future::FutureExt, pin_mut, select};
|
use futures::{future::FutureExt, pin_mut, select};
|
||||||
use web_sys::{Element, HtmlDialogElement};
|
use web_sys::{Element, HtmlDialogElement};
|
||||||
|
|
||||||
use crate::utils::{by_id, SelectorExt};
|
use crate::{
|
||||||
|
on_click,
|
||||||
|
utils::{by_id, selector_and_clone, SelectorExt},
|
||||||
|
};
|
||||||
|
|
||||||
use crate::on_click;
|
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<T>(content: DialogContent<'_, T>) -> bool
|
||||||
|
where
|
||||||
|
T: Fn(&Element),
|
||||||
|
{
|
||||||
let dialog: HtmlDialogElement = by_id("modal-dialog");
|
let dialog: HtmlDialogElement = by_id("modal-dialog");
|
||||||
|
|
||||||
let input_ok: Element = dialog.selector(".ok");
|
let input_ok: Element = dialog.selector(".ok");
|
||||||
let input_cancel: Element = dialog.selector(".cancel");
|
let input_cancel: Element = dialog.selector(".cancel");
|
||||||
|
|
||||||
dialog.selector::<Element>(".content").set_inner_html(message);
|
let content_element = dialog.selector::<Element>(".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();
|
dialog.show_modal().unwrap();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,22 +20,6 @@ use crate::{
|
||||||
utils::{by_id, selector, selector_and_clone, SelectorExt},
|
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> {
|
pub fn setup_page(recipe_id: i64) -> Result<(), JsValue> {
|
||||||
// Title.
|
// Title.
|
||||||
{
|
{
|
||||||
|
|
@ -266,10 +250,10 @@ pub fn setup_page(recipe_id: i64) -> Result<(), JsValue> {
|
||||||
EventListener::new(&delete_button, "click", move |_event| {
|
EventListener::new(&delete_button, "click", move |_event| {
|
||||||
let title: HtmlInputElement = by_id("input-title");
|
let title: HtmlInputElement = by_id("input-title");
|
||||||
spawn_local(async move {
|
spawn_local(async move {
|
||||||
if modal_dialog::show(&format!(
|
if modal_dialog::show(modal_dialog::DialogContent::<fn(&Element)>::Text(&format!(
|
||||||
"Are you sure to delete the recipe '{}'",
|
"Are you sure to delete the recipe '{}'",
|
||||||
title.value()
|
title.value()
|
||||||
))
|
)))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
let body = ron_api::Id { id: recipe_id };
|
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.
|
// Add a new group.
|
||||||
{
|
{
|
||||||
let button_add_group: HtmlInputElement = by_id("input-add-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 };
|
let body = ron_api::Id { id: recipe_id };
|
||||||
spawn_local(async move {
|
spawn_local(async move {
|
||||||
let response: ron_api::Id = request::post("recipe/add_group", body).await.unwrap();
|
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![],
|
steps: vec![],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
})
|
||||||
on_click_add_group.forget();
|
.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
@ -397,7 +381,12 @@ fn create_group_element(group: &ron_api::Group) -> Element {
|
||||||
.selector::<HtmlInputElement>(".input-group-name")
|
.selector::<HtmlInputElement>(".input-group-name")
|
||||||
.value();
|
.value();
|
||||||
spawn_local(async move {
|
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::<fn(&Element)>::Text(&format!(
|
||||||
|
"Are you sure to delete the group '{}'",
|
||||||
|
name
|
||||||
|
)))
|
||||||
|
.await
|
||||||
|
{
|
||||||
let body = ron_api::Id { id: group_id };
|
let body = ron_api::Id { id: group_id };
|
||||||
let _ = request::delete::<(), _>("recipe/remove_group", body).await;
|
let _ = request::delete::<(), _>("recipe/remove_group", body).await;
|
||||||
let group_element = by_id::<Element>(&format!("group-{}", group_id));
|
let group_element = by_id::<Element>(&format!("group-{}", group_id));
|
||||||
|
|
@ -530,7 +519,12 @@ fn create_step_element(group_element: &Element, step: &ron_api::Step) -> Element
|
||||||
.selector::<HtmlTextAreaElement>(".text-area-step-action")
|
.selector::<HtmlTextAreaElement>(".text-area-step-action")
|
||||||
.value();
|
.value();
|
||||||
spawn_local(async move {
|
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::<fn(&Element)>::Text(&format!(
|
||||||
|
"Are you sure to delete the step '{}'",
|
||||||
|
action
|
||||||
|
)))
|
||||||
|
.await
|
||||||
|
{
|
||||||
let body = ron_api::Id { id: step_id };
|
let body = ron_api::Id { id: step_id };
|
||||||
let _ = request::delete::<(), _>("recipe/remove_step", body).await;
|
let _ = request::delete::<(), _>("recipe/remove_step", body).await;
|
||||||
let step_element = by_id::<Element>(&format!("step-{}", step_id));
|
let step_element = by_id::<Element>(&format!("step-{}", step_id));
|
||||||
|
|
@ -675,12 +669,17 @@ fn create_ingredient_element(step_element: &Element, ingredient: &ron_api::Ingre
|
||||||
.selector::<HtmlInputElement>(".input-ingredient-name")
|
.selector::<HtmlInputElement>(".input-ingredient-name")
|
||||||
.value();
|
.value();
|
||||||
spawn_local(async move {
|
spawn_local(async move {
|
||||||
if modal_dialog::show(&format!("Are you sure to delete the ingredient '{}'", name))
|
if modal_dialog::show(modal_dialog::DialogContent::<fn(&Element)>::Text(&format!(
|
||||||
.await
|
"Are you sure to delete the ingredient '{}'",
|
||||||
|
name
|
||||||
|
)))
|
||||||
|
.await
|
||||||
{
|
{
|
||||||
let body = ron_api::Id { id: ingredient_id };
|
let body = ron_api::Id { id: ingredient_id };
|
||||||
let _ = request::delete::<(), _>("recipe/remove_ingredient", body).await;
|
let _ = request::delete::<(), _>("recipe/remove_ingredient", body).await;
|
||||||
by_id::<Element>(&format!("ingredient-{}", ingredient_id)).remove();
|
let ingredient_element = by_id::<Element>(&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
|
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 {
|
enum CursorPosition {
|
||||||
UpperPart,
|
UpperPart,
|
||||||
LowerPart,
|
LowerPart,
|
||||||
|
|
|
||||||
40
frontend/src/recipe_view.rs
Normal file
40
frontend/src/recipe_view.rs
Normal file
|
|
@ -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(())
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue