meal categories
ci/woodpecker/push/e2e Pipeline was successful
ci/woodpecker/push/fmt Pipeline failed
ci/woodpecker/push/test Pipeline was successful

This commit is contained in:
2026-08-03 21:29:33 -04:00
parent cf6853d71e
commit 3695fc68d6
11 changed files with 680 additions and 67 deletions
+58 -4
View File
@@ -104,6 +104,8 @@ pub fn build_router(state: AppState) -> Router {
.route("/invitations", post(create_invitation))
.route("/meals", get(meals_page).post(create_meal))
.route("/meals/new", get(new_meal_page))
.route("/meals/categories", post(create_meal_category))
.route("/meals/categories/{category_id}/delete", post(delete_meal_category))
.route("/meals/{meal_id}", get(meal_page))
.route("/meals/{meal_id}/edit", post(edit_meal))
.route("/meals/{meal_id}/delete", post(delete_meal))
@@ -288,6 +290,8 @@ struct MealForm {
name: String,
#[serde(default)]
description: String,
#[serde(default)]
category_id: Option<String>,
csrf: String,
}
@@ -721,26 +725,65 @@ async fn create_category(
Ok(Redirect::to("/lists").into_response())
}
async fn create_meal_category(
State(state): State<AppState>,
user: CurrentUser,
LoggedForm(form): LoggedForm<CategoryForm>,
) -> Result<Response, AppError> {
verify_csrf(&user, &form.csrf)?;
let name = form.name.trim().to_owned();
if name.is_empty() || name.chars().count() > 60 {
return Err(AppError::BadRequest(
"Category names must be between 1 and 60 characters.".into(),
));
}
state.meals.create_meal_category(name).await?;
Ok(Redirect::to("/meals").into_response())
}
async fn delete_meal_category(
State(state): State<AppState>,
user: CurrentUser,
Path(category_id): Path<i64>,
LoggedForm(form): LoggedForm<CsrfForm>,
) -> Result<Response, AppError> {
verify_csrf(&user, &form.csrf)?;
state.meals.delete_meal_category(category_id).await?;
Ok(Redirect::to("/meals").into_response())
}
async fn meals_page(
State(state): State<AppState>,
user: CurrentUser,
Query(query): Query<MealPickerQuery>,
) -> Result<Response, AppError> {
let meals = state.meals.list_meals().await?;
let meal_categories = state.meals.list_meal_categories().await?;
if let Some(list_id) = query.picker {
return Ok(html_response(views::meal_picker(
&meals,
&meal_categories,
list_id,
&user.session.csrf_token,
)));
}
Ok(html_response(views::meals_page(&user.session.user, &meals)))
Ok(html_response(views::meals_page(
&user.session.user,
&meals,
&meal_categories,
&user.session.csrf_token,
)))
}
async fn new_meal_page(user: CurrentUser) -> Result<Response, AppError> {
async fn new_meal_page(
State(state): State<AppState>,
user: CurrentUser,
) -> Result<Response, AppError> {
let meal_categories = state.meals.list_meal_categories().await?;
Ok(html_response(views::meal_form_page(
&user.session.user,
None,
&meal_categories,
&user.session.csrf_token,
)))
}
@@ -759,7 +802,11 @@ async fn create_meal(
}
let meal = state
.meals
.create_meal(name, form.description.trim().to_owned())
.create_meal(
name,
form.description.trim().to_owned(),
parse_category_id(form.category_id),
)
.await?;
Ok(Redirect::to(&format!("/meals/{}", meal.id)).into_response())
}
@@ -775,10 +822,12 @@ async fn meal_page(
.await?
.ok_or(AppError::NotFound)?;
let categories = state.lists.categories().await?;
let meal_categories = state.meals.list_meal_categories().await?;
Ok(html_response(views::meal_page(
&user.session.user,
&meal,
&categories,
&meal_categories,
&user.session.csrf_token,
)))
}
@@ -798,7 +847,12 @@ async fn edit_meal(
}
state
.meals
.update_meal(meal_id, name, form.description.trim().to_owned())
.update_meal(
meal_id,
name,
form.description.trim().to_owned(),
parse_category_id(form.category_id),
)
.await?;
Ok(Redirect::to(&format!("/meals/{meal_id}")).into_response())
}