[[package]]
name = "bytes"
-version = "1.9.0"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b"
+checksum = "f61dac84819c6588b558454b194026eb1f09c293b9036ae9b159e74e73ab6cf9"
[[package]]
name = "cc"
[[package]]
name = "lettre"
-version = "0.11.11"
+version = "0.11.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab4c9a167ff73df98a5ecc07e8bf5ce90b583665da3d1762eb1f775ad4d0d6f5"
+checksum = "e882e1489810a45919477602194312b1a7df0e5acc30a6188be7b520268f63f8"
dependencies = [
"async-trait",
"base64 0.22.1",
[[package]]
name = "pin-project"
-version = "1.1.8"
+version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916"
+checksum = "dfe2e71e1471fe07709406bf725f710b02927c9c54b2b5b2ec0e8087d97c327d"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
-version = "1.1.8"
+version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb"
+checksum = "f6e859e6e5bd50440ab63c47e3ebabc90f26251f7c73c3d3e837b74a1cc3fa67"
dependencies = [
"proc-macro2",
"quote",
pub async fn load_user(&self, user_id: i64) -> Result<Option<model::User>> {
sqlx::query_as(
- "SELECT [id], [email], [name], [lang], [is_admin] FROM [User] WHERE [id] = $1",
+ "SELECT [id], [email], [name], [default_servings], [lang], [is_admin] FROM [User] WHERE [id] = $1",
)
.bind(user_id)
.fetch_optional(&self.pool)
user_id: i64,
new_email: Option<&str>,
new_name: Option<&str>,
+ new_default_servings: Option<u32>,
new_password: Option<&str>,
) -> Result<UpdateUserResult> {
let mut tx = self.tx().await?;
let hashed_new_password = new_password.map(|p| hash(p).unwrap());
- let (email, name, hashed_password) = sqlx::query_as::<_, (String, String, String)>(
- "SELECT [email], [name], [password] FROM [User] WHERE [id] = $1",
+ let (email, name, default_servings, hashed_password) = sqlx::query_as::<
+ _,
+ (String, String, u32, String),
+ >(
+ "SELECT [email], [name], [default_servings], [password] FROM [User] WHERE [id] = $1",
)
.bind(user_id)
.fetch_one(&mut *tx)
sqlx::query(
r#"
UPDATE [User]
-SET [email] = $2, [name] = $3, [password] = $4
+SET [email] = $2, [name] = $3, [default_servings] = $4, [password] = $5
WHERE [id] = $1
"#,
)
.bind(user_id)
.bind(new_email.unwrap_or(&email))
.bind(new_name.map(str::trim).unwrap_or(&name))
+ .bind(new_default_servings.unwrap_or(default_servings))
.bind(hashed_new_password.unwrap_or(hashed_password))
.execute(&mut *tx)
.await?;
pub id: i64,
pub name: String,
pub email: String,
+ pub default_servings: u32,
pub lang: String,
pub is_admin: bool,
}
pub username: &'a str,
pub email: &'a str,
+ pub default_servings: u32,
pub message: &'a str,
pub message_email: &'a str,
pub message_password: &'a str,
)
// Recipes.
.route("/recipe/new", get(services::recipe::create))
- .route("/recipe/edit/{id}", get(services::recipe::edit_recipe))
+ .route("/recipe/edit/{id}", get(services::recipe::edit))
.route("/recipe/view/{id}", get(services::recipe::view))
// User.
.route(
}
#[debug_handler]
-pub async fn edit_recipe(
+pub async fn edit(
State(connection): State<db::Connection>,
Extension(user): Extension<Option<model::User>>,
Extension(tr): Extension<translation::Tr>,
ProfileTemplate {
username: &user.name,
email: &user.email,
+ default_servings: user.default_servings,
message: "",
message_email: "",
message_password: "",
pub struct EditUserForm {
name: String,
email: String,
+ default_servings: u32,
password_1: String,
password_2: String,
}
user: Some(user),
username: &form_data.name,
email: &form_data.email,
+ default_servings: form_data.default_servings,
message_email: match error {
ProfileUpdateError::InvalidEmail => tr.t(Sentence::InvalidEmail),
ProfileUpdateError::EmailAlreadyTaken => tr.t(Sentence::EmailAlreadyTaken),
user.id,
Some(email_trimmed),
Some(&form_data.name),
+ Some(form_data.default_servings),
new_password,
)
.await
user,
username: &form_data.name,
email: &form_data.email,
+ default_servings: form_data.default_servings,
message,
message_email: "",
message_password: "",
// Profile
ProfileTitle,
ProfileEmail,
+ ProfileDefaultServings,
ProfileNewPassword,
ProfileFollowEmailLink,
ProfileEmailSent,
{% block main_container %}
-{% match user %}
-{% when Some with (user) %}
+{% if let Some(user) = user %}
<div class="content" id="user-edit">
<h1>{{ tr.t(Sentence::ProfileTitle) }}</h1>
{{ message_email }}
+ <label for="input-servings">{{ tr.t(Sentence::ProfileDefaultServings) }}</label>
+ <input
+ id="input-servings"
+ type="number"
+ step="1" min="1" max="100"
+ name="default_servings"
+ value="{{ default_servings }}"/>
+
<label for="input-password-1">{{ tr.tp(Sentence::ProfileNewPassword, [Box::new(common::consts::MIN_PASSWORD_SIZE)]) }}</label>
<input id="input-password-1" type="password" name="password_1" autocomplete="new-password" />
{{ message }}
</div>
-{% when None %}
-{% endmatch %}
+{% endif %}
{% endblock %}
\ No newline at end of file
{% endfor %}
<div id="hidden-templates">
- {% include "calendar.html" %}
+ {# To create a modal dialog to choose a date and and servings #}
+ {% if let Some(user) = user %}
+ <div class="date-and-servings" >
+ {% include "calendar.html" %}
+ <label for="input-servings">{{ tr.t(Sentence::RecipeServings) }}</label>
+ <input
+ id="input-servings"
+ type="number"
+ step="1" min="1" max="100"
+ value="{{ user.default_servings }}"/>
+ </div>
+ {% endif %}
</div>
</div>
(ProfileTitle, "Profile"),
(ProfileEmail, "Email (need to be revalidated if changed)"),
+ (ProfileDefaultServings, "Default servings"),
(ProfileNewPassword, "New password (minimum {} characters)"),
(ProfileFollowEmailLink, "Follow this link to validate this email address, {}"),
(ProfileEmailSent, "An email has been sent, follow the link to validate your new email"),
(ProfileTitle, "Profile"),
(ProfileEmail, "Email (doit être revalidé si changé)"),
+ (ProfileDefaultServings, "Nombre de portions par défaut"),
(ProfileNewPassword, "Nouveau mot de passe (minimum {} caractères)"),
(ProfileFollowEmailLink, "Suivez ce lien pour valider l'adresse email, {}"),
(ProfileEmailSent, "Un email a été envoyé, suivez le lien pour valider la nouvelle adresse email"),
(RecipeNotFound, "Recette non-trouvée"),
(RecipeTitle, "Titre"),
(RecipeDescription, "Description"),
- (RecipeServings, "Nombre de personnes"),
+ (RecipeServings, "Nombre de portions"),
(RecipeEstimatedTime, "Temps estimé"),
(RecipeDifficulty, "Difficulté"),
(RecipeDifficultyEasy, "Facile"),
-use std::sync::{Arc, Mutex};
+use std::{cell::RefCell, rc::Rc};
use chrono::{offset::Local, DateTime, Datelike, Days, Months, Weekday};
use common::ron_api;
#[derive(Clone)]
struct CalendarState {
- internal_state: Arc<Mutex<CalendarStateInternal>>,
+ internal_state: Rc<RefCell<CalendarStateInternal>>,
}
impl CalendarState {
pub fn new() -> Self {
let current_date = Local::now();
Self {
- internal_state: Arc::new(Mutex::new(CalendarStateInternal {
+ internal_state: Rc::new(RefCell::new(CalendarStateInternal {
displayed_date: current_date,
selected_date: current_date,
})),
}
pub fn displayed_date_next_month(&self) {
- let mut locker = self.internal_state.lock().unwrap();
- locker.displayed_date = locker.displayed_date + Months::new(1);
+ let mut state_borrowed = self.internal_state.borrow_mut();
+ state_borrowed.displayed_date = state_borrowed.displayed_date + Months::new(1);
}
pub fn displayed_date_previous_month(&self) {
- let mut locker = self.internal_state.lock().unwrap();
- locker.displayed_date = locker.displayed_date - Months::new(1);
+ let mut state_borrowed = self.internal_state.borrow_mut();
+ state_borrowed.displayed_date = state_borrowed.displayed_date - Months::new(1);
}
pub fn get_displayed_date(&self) -> DateTime<Local> {
- self.internal_state.lock().unwrap().displayed_date
+ self.internal_state.borrow().displayed_date
}
pub fn get_selected_date(&self) -> DateTime<Local> {
- self.internal_state.lock().unwrap().selected_date
+ self.internal_state.borrow().selected_date
}
pub fn set_selected_date(&self, date: DateTime<Local>) {
- self.internal_state.lock().unwrap().selected_date = date;
+ self.internal_state.borrow_mut().selected_date = date;
}
}
.unwrap();
for recipe in scheduled_recipes.recipes {
- log!(recipe.1);
+ // log!(recipe.1);
+ // TODO
}
});
}
let add_to_planner: Element = selector("#recipe-view .add-to-planner");
EventListener::new(&add_to_planner, "click", move |_event| {
spawn_local(async move {
- modal_dialog::show_and_initialize("#hidden-templates .calendar", async |element| {
- calendar::setup(element);
- })
+ modal_dialog::show_and_initialize(
+ "#hidden-templates .date-and-servings",
+ async |element| {
+ calendar::setup(element.selector(".calendar"));
+ },
+ )
.await;
});
})