Recipe can now be scheduled
This commit is contained in:
parent
ae6da1a5ae
commit
fbef990022
18 changed files with 233 additions and 51 deletions
|
|
@ -15,7 +15,7 @@
|
|||
|
||||
#toast.show {
|
||||
visibility: visible;
|
||||
animation: fadein 0.5s, fadeout 0.5s 3.6s;
|
||||
animation: fadein 0.5s, fadeout 0.5s 9.6s;
|
||||
animation-iteration-count: 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,6 @@
|
|||
-- Datetimes are stored as 'ISO 8601' text format.
|
||||
-- For example: '2025-01-07T10:41:05.697884837+00:00'.
|
||||
|
||||
-- Version 1 is the initial structure.
|
||||
CREATE TABLE [Version] (
|
||||
[id] INTEGER PRIMARY KEY,
|
||||
|
|
@ -165,9 +168,11 @@ CREATE TABLE [RecipeScheduled] (
|
|||
[id] INTEGER PRIMARY KEY,
|
||||
[user_id] INTEGER NOT NULL,
|
||||
[recipe_id] INTEGER NOT NULL,
|
||||
[date] TEXT NOT NULL,
|
||||
[date] TEXT NOT NULL, -- In form of 'YYYY-MM-DD'.
|
||||
[servings] INTEGER, -- If NULL use [recipe].[servings].
|
||||
|
||||
UNIQUE([user_id], [recipe_id], [date]),
|
||||
|
||||
FOREIGN KEY([user_id]) REFERENCES [User]([id]) ON DELETE CASCADE,
|
||||
FOREIGN KEY([recipe_id]) REFERENCES [Recipe]([id]) ON DELETE CASCADE
|
||||
);
|
||||
|
|
|
|||
|
|
@ -752,7 +752,7 @@ VALUES ($1, $2)
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn add_schedule_recipe(
|
||||
pub async fn add_scheduled_recipe(
|
||||
&self,
|
||||
user_id: i64,
|
||||
recipe_id: i64,
|
||||
|
|
@ -775,7 +775,7 @@ VALUES ($1, $2, $3, $4)
|
|||
.map_err(DBError::from)
|
||||
}
|
||||
|
||||
pub async fn remove_scheduled_recipe(
|
||||
pub async fn rm_scheduled_recipe(
|
||||
&self,
|
||||
user_id: i64,
|
||||
recipe_id: i64,
|
||||
|
|
@ -964,13 +964,13 @@ VALUES
|
|||
let tomorrow = today + Days::new(1);
|
||||
|
||||
connection
|
||||
.add_schedule_recipe(user_id, recipe_id_1, today, 4)
|
||||
.add_scheduled_recipe(user_id, recipe_id_1, today, 4)
|
||||
.await?;
|
||||
connection
|
||||
.add_schedule_recipe(user_id, recipe_id_2, yesterday, 4)
|
||||
.add_scheduled_recipe(user_id, recipe_id_2, yesterday, 4)
|
||||
.await?;
|
||||
connection
|
||||
.add_schedule_recipe(user_id, recipe_id_1, tomorrow, 4)
|
||||
.add_scheduled_recipe(user_id, recipe_id_1, tomorrow, 4)
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
|
|
@ -1008,13 +1008,13 @@ VALUES
|
|||
);
|
||||
|
||||
connection
|
||||
.remove_scheduled_recipe(user_id, recipe_id_1, today)
|
||||
.rm_scheduled_recipe(user_id, recipe_id_1, today)
|
||||
.await?;
|
||||
connection
|
||||
.remove_scheduled_recipe(user_id, recipe_id_2, yesterday)
|
||||
.rm_scheduled_recipe(user_id, recipe_id_2, yesterday)
|
||||
.await?;
|
||||
connection
|
||||
.remove_scheduled_recipe(user_id, recipe_id_1, tomorrow)
|
||||
.rm_scheduled_recipe(user_id, recipe_id_1, tomorrow)
|
||||
.await?;
|
||||
|
||||
assert_eq!(
|
||||
|
|
|
|||
|
|
@ -181,6 +181,14 @@ async fn main() {
|
|||
"/calendar/get_scheduled_recipes",
|
||||
get(services::ron::get_scheduled_recipes),
|
||||
)
|
||||
.route(
|
||||
"/calendar/schedule_recipe",
|
||||
post(services::ron::schedule_recipe),
|
||||
)
|
||||
.route(
|
||||
"/calendar/remove_scheduled_recipe",
|
||||
delete(services::ron::rm_scheduled_recipe),
|
||||
)
|
||||
.fallback(services::ron::not_found);
|
||||
|
||||
let fragments_routes = Router::new().route(
|
||||
|
|
|
|||
|
|
@ -631,6 +631,36 @@ pub async fn get_scheduled_recipes(
|
|||
}
|
||||
}
|
||||
|
||||
#[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> {
|
||||
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?;
|
||||
}
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
#[debug_handler]
|
||||
pub async fn rm_scheduled_recipe(
|
||||
State(connection): State<db::Connection>,
|
||||
Extension(user): Extension<Option<model::User>>,
|
||||
ExtractRon(ron): ExtractRon<common::ron_api::ScheduledRecipe>,
|
||||
) -> Result<impl IntoResponse> {
|
||||
check_user_rights_recipe(&connection, &user, ron.recipe_id).await?;
|
||||
if let Some(user) = user {
|
||||
connection
|
||||
.rm_scheduled_recipe(user.id, ron.recipe_id, ron.date)
|
||||
.await?;
|
||||
}
|
||||
Ok(StatusCode::OK)
|
||||
}
|
||||
|
||||
/// 404 ///
|
||||
|
||||
#[debug_handler]
|
||||
|
|
|
|||
|
|
@ -141,6 +141,9 @@ pub enum Sentence {
|
|||
CalendarOctober,
|
||||
CalendarNovember,
|
||||
CalendarDecember,
|
||||
CalendarAddToPlanner,
|
||||
CalendarAddToPlannerSuccess,
|
||||
CalendarDateFormat, // See https://docs.rs/chrono/latest/chrono/format/strftime/index.html.
|
||||
}
|
||||
|
||||
pub const DEFAULT_LANGUAGE_CODE: &str = "en";
|
||||
|
|
@ -181,6 +184,10 @@ impl Tr {
|
|||
pub fn current_lang_code(&self) -> &str {
|
||||
&self.lang.code
|
||||
}
|
||||
|
||||
pub fn current_lang_and_territory_code(&self) -> String {
|
||||
format!("{}-{}", self.lang.code, self.lang.territory)
|
||||
}
|
||||
}
|
||||
|
||||
// #[macro_export]
|
||||
|
|
@ -200,6 +207,7 @@ impl Tr {
|
|||
#[derive(Debug, Deserialize)]
|
||||
struct StoredLanguage {
|
||||
code: String,
|
||||
territory: String,
|
||||
name: String,
|
||||
translation: Vec<(Sentence, String)>,
|
||||
}
|
||||
|
|
@ -207,6 +215,7 @@ struct StoredLanguage {
|
|||
#[derive(Debug)]
|
||||
struct Language {
|
||||
code: String,
|
||||
territory: String,
|
||||
name: String,
|
||||
translation: Vec<String>,
|
||||
}
|
||||
|
|
@ -215,6 +224,7 @@ impl Language {
|
|||
pub fn from_stored_language(stored_language: StoredLanguage) -> Self {
|
||||
Self {
|
||||
code: stored_language.code,
|
||||
territory: stored_language.territory,
|
||||
name: stored_language.name,
|
||||
translation: {
|
||||
let mut translation = vec![String::new(); Sentence::COUNT];
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="{{ tr.current_lang_and_territory_code() }}">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
{% 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>
|
||||
<span class="add-to-planner">{{ tr.t(Sentence::CalendarAddToPlanner) }}</span>
|
||||
{% endif %}
|
||||
|
||||
<div class="tags">
|
||||
|
|
@ -92,6 +92,9 @@
|
|||
value="{{ user.default_servings }}"/>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<span class="calendar-add-to-planner-success">{{ tr.t(Sentence::CalendarAddToPlannerSuccess) }}</span>
|
||||
<span class="calendar-date-format">{{ tr.t(Sentence::CalendarDateFormat) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
[
|
||||
(
|
||||
code: "en",
|
||||
territory: "US",
|
||||
name: "English",
|
||||
translation: [
|
||||
(MainTitle, "Cooking Recipes"),
|
||||
|
|
@ -124,10 +125,14 @@
|
|||
(CalendarOctober, "October"),
|
||||
(CalendarNovember, "November"),
|
||||
(CalendarDecember, "December"),
|
||||
(CalendarAddToPlanner, "Add to planner"),
|
||||
(CalendarAddToPlannerSuccess, "Recipe {} has been scheduled for {}"),
|
||||
(CalendarDateFormat, "%A, %-d %B, %C%y"),
|
||||
]
|
||||
),
|
||||
(
|
||||
code: "fr",
|
||||
territory: "FR",
|
||||
name: "Français",
|
||||
translation: [
|
||||
(MainTitle, "Recettes de Cuisine"),
|
||||
|
|
@ -251,6 +256,9 @@
|
|||
(CalendarOctober, "Octobre"),
|
||||
(CalendarNovember, "Novembre"),
|
||||
(CalendarDecember, "Décembre"),
|
||||
(CalendarAddToPlanner, "Ajouter au planificateur"),
|
||||
(CalendarAddToPlannerSuccess, "La recette {} a été agendée pour le {}"),
|
||||
(CalendarDateFormat, "%A %-d %B %C%y"),
|
||||
]
|
||||
)
|
||||
]
|
||||
Loading…
Add table
Add a link
Reference in a new issue