Toast message when scheduling a recipe

This commit is contained in:
Greg Burri 2025-02-05 15:44:48 +01:00
parent fbef990022
commit ccb1248da3
11 changed files with 168 additions and 66 deletions

View file

@ -1,9 +1,15 @@
use chrono::{prelude::*, Days};
use chrono::prelude::*;
use common::ron_api::Difficulty;
use itertools::Itertools;
use sqlx::Error;
use super::{Connection, DBError, Result};
use crate::{data::model, user_authentication};
use crate::data::model;
pub enum AddScheduledRecipeResult {
Ok,
RecipeAlreadyScheduledAtThisDate,
}
impl Connection {
/// Returns all the recipe titles where recipe is written in the given language.
@ -758,8 +764,8 @@ VALUES ($1, $2)
recipe_id: i64,
date: NaiveDate,
servings: u32,
) -> Result<()> {
sqlx::query(
) -> Result<AddScheduledRecipeResult> {
match sqlx::query(
r#"
INSERT INTO [RecipeScheduled] (user_id, recipe_id, date, servings)
VALUES ($1, $2, $3, $4)
@ -771,8 +777,15 @@ VALUES ($1, $2, $3, $4)
.bind(servings)
.execute(&self.pool)
.await
.map(|_| ())
.map_err(DBError::from)
{
Err(Error::Database(error))
if error.code() == Some(std::borrow::Cow::Borrowed("2067"))
&& error.message() == "UNIQUE constraint failed: RecipeScheduled.user_id, RecipeScheduled.recipe_id, RecipeScheduled.date" =>
{
Ok(AddScheduledRecipeResult::RecipeAlreadyScheduledAtThisDate)
}
_ => Ok(AddScheduledRecipeResult::Ok),
}
}
pub async fn rm_scheduled_recipe(
@ -783,9 +796,9 @@ VALUES ($1, $2, $3, $4)
) -> Result<()> {
sqlx::query(
r#"
DELETE FROM [RecipeScheduled]
WHERE [user_id] = $1 AND [recipe_id] = $2 AND [date] = $3
"#,
DELETE FROM [RecipeScheduled]
WHERE [user_id] = $1 AND [recipe_id] = $2 AND [date] = $3
"#,
)
.bind(user_id)
.bind(recipe_id)
@ -823,6 +836,7 @@ ORDER BY [date]
#[cfg(test)]
mod tests {
use super::*;
use chrono::Days;
#[tokio::test]
async fn create_a_new_recipe_then_update_its_title() -> Result<()> {
@ -1007,6 +1021,14 @@ VALUES
]
);
// Recipe scheduled at the same date is forbidden.
let Ok(AddScheduledRecipeResult::RecipeAlreadyScheduledAtThisDate) = connection
.add_scheduled_recipe(user_id, recipe_id_1, today, 4)
.await
else {
panic!("DBError::RecipeAlreadyScheduledAtThisDate must be returned");
};
connection
.rm_scheduled_recipe(user_id, recipe_id_1, today)
.await?;

View file

@ -945,6 +945,7 @@ VALUES
1,
Some("muaddib@fremen.com"),
Some("muaddib"),
None,
Some("Chani"),
)
.await?

View file

@ -42,6 +42,13 @@ pub fn ron_error(status: StatusCode, message: &str) -> impl IntoResponse {
)
}
pub fn ron_response_ok<T>(ron: T) -> impl IntoResponse
where
T: Serialize,
{
ron_response(StatusCode::OK, ron)
}
pub fn ron_response<T>(status: StatusCode, ron: T) -> impl IntoResponse
where
T: Serialize,

View file

@ -2,7 +2,7 @@ use axum::{
debug_handler,
extract::{Extension, Query, State},
http::{HeaderMap, StatusCode},
response::{ErrorResponse, IntoResponse, Result},
response::{ErrorResponse, IntoResponse, Response, Result},
};
use axum_extra::extract::cookie::{Cookie, CookieJar};
use chrono::NaiveDate;
@ -11,10 +11,10 @@ use serde::Deserialize;
use crate::{
consts,
data::db,
data::{self, db},
model,
ron_extractor::ExtractRon,
ron_utils::{ron_error, ron_response},
ron_utils::{ron_error, ron_response_ok},
};
const NOT_AUTHORIZED_MESSAGE: &str = "Action not authorized";
@ -257,13 +257,10 @@ pub async fn get_tags(
State(connection): State<db::Connection>,
recipe_id: Query<RecipeId>,
) -> Result<impl IntoResponse> {
Ok(ron_response(
StatusCode::OK,
common::ron_api::Tags {
recipe_id: recipe_id.id,
tags: connection.get_recipes_tags(recipe_id.id).await?,
},
))
Ok(ron_response_ok(common::ron_api::Tags {
recipe_id: recipe_id.id,
tags: connection.get_recipes_tags(recipe_id.id).await?,
}))
}
#[debug_handler]
@ -401,8 +398,7 @@ pub async fn get_groups(
recipe_id: Query<RecipeId>,
) -> Result<impl IntoResponse> {
// Here we don't check user rights on purpose.
Ok(ron_response(
StatusCode::OK,
Ok(ron_response_ok(
connection
.get_groups(recipe_id.id)
.await?
@ -421,7 +417,7 @@ pub async fn add_group(
check_user_rights_recipe(&connection, &user, ron.id).await?;
let id = connection.add_recipe_group(ron.id).await?;
Ok(ron_response(StatusCode::OK, common::ron_api::Id { id }))
Ok(ron_response_ok(common::ron_api::Id { id }))
}
#[debug_handler]
@ -479,7 +475,7 @@ pub async fn add_step(
check_user_rights_recipe_group(&connection, &user, ron.id).await?;
let id = connection.add_recipe_step(ron.id).await?;
Ok(ron_response(StatusCode::OK, common::ron_api::Id { id }))
Ok(ron_response_ok(common::ron_api::Id { id }))
}
#[debug_handler]
@ -523,7 +519,7 @@ pub async fn add_ingredient(
) -> Result<impl IntoResponse> {
check_user_rights_recipe_step(&connection, &user, ron.id).await?;
let id = connection.add_recipe_ingredient(ron.id).await?;
Ok(ron_response(StatusCode::OK, common::ron_api::Id { id }))
Ok(ron_response_ok(common::ron_api::Id { id }))
}
#[debug_handler]
@ -615,14 +611,11 @@ pub async fn get_scheduled_recipes(
date_range: Query<DateRange>,
) -> Result<impl IntoResponse> {
if let Some(user) = user {
Ok(ron_response(
StatusCode::OK,
common::ron_api::ScheduledRecipes {
recipes: connection
.get_scheduled_recipes(user.id, date_range.start_date, date_range.end_date)
.await?,
},
))
Ok(ron_response_ok(common::ron_api::ScheduledRecipes {
recipes: connection
.get_scheduled_recipes(user.id, date_range.start_date, date_range.end_date)
.await?,
}))
} else {
Err(ErrorResponse::from(ron_error(
StatusCode::UNAUTHORIZED,
@ -631,19 +624,35 @@ pub async fn get_scheduled_recipes(
}
}
impl From<data::db::recipe::AddScheduledRecipeResult> for common::ron_api::ScheduleRecipeResult {
fn from(db_res: data::db::recipe::AddScheduledRecipeResult) -> Self {
match db_res {
db::recipe::AddScheduledRecipeResult::Ok => Self::Ok,
db::recipe::AddScheduledRecipeResult::RecipeAlreadyScheduledAtThisDate => {
Self::RecipeAlreadyScheduledAtThisDate
}
}
}
}
#[debug_handler]
pub async fn schedule_recipe(
State(connection): State<db::Connection>,
Extension(user): Extension<Option<model::User>>,
ExtractRon(ron): ExtractRon<common::ron_api::ScheduleRecipe>,
) -> Result<impl IntoResponse> {
) -> Result<Response> {
check_user_rights_recipe(&connection, &user, ron.recipe_id).await?;
if let Some(user) = user {
connection
.add_scheduled_recipe(user.id, ron.recipe_id, ron.date, ron.servings)
.await?;
.await
.map(|res| {
ron_response_ok(common::ron_api::ScheduleRecipeResult::from(res)).into_response()
})
.map_err(ErrorResponse::from)
} else {
Ok(StatusCode::OK.into_response())
}
Ok(StatusCode::OK)
}
#[debug_handler]

View file

@ -143,6 +143,7 @@ pub enum Sentence {
CalendarDecember,
CalendarAddToPlanner,
CalendarAddToPlannerSuccess,
CalendarAddToPlannerAlreadyExists,
CalendarDateFormat, // See https://docs.rs/chrono/latest/chrono/format/strftime/index.html.
}