|
|
@@ -0,0 +1,136 @@
|
|
|
+use actix_web::{put, web, HttpResponse};
|
|
|
+use serde::Deserialize;
|
|
|
+use serde_json::json;
|
|
|
+use sqlx::PgPool;
|
|
|
+use std::collections::HashSet;
|
|
|
+use uuid::Uuid;
|
|
|
+
|
|
|
+use crate::auth::AuthUser;
|
|
|
+use crate::models::exercise::{Exercise, ExerciseType};
|
|
|
+use crate::models::workout::Workout;
|
|
|
+
|
|
|
+#[derive(Deserialize)]
|
|
|
+pub struct ExerciseInput {
|
|
|
+ pub name: String,
|
|
|
+ pub exercise_type: ExerciseType,
|
|
|
+ pub position: i32,
|
|
|
+}
|
|
|
+
|
|
|
+#[derive(Deserialize)]
|
|
|
+pub struct UpdateWorkoutInput {
|
|
|
+ pub name: Option<String>,
|
|
|
+ pub exercises: Option<Vec<ExerciseInput>>,
|
|
|
+}
|
|
|
+
|
|
|
+#[put("/workout/{id}")]
|
|
|
+pub async fn update_workout(
|
|
|
+ pool: web::Data<PgPool>,
|
|
|
+ auth: AuthUser,
|
|
|
+ path: web::Path<Uuid>,
|
|
|
+ body: web::Json<UpdateWorkoutInput>,
|
|
|
+) -> HttpResponse {
|
|
|
+ let id = path.into_inner();
|
|
|
+
|
|
|
+ let workout = match Workout::find_by_id(pool.get_ref(), id).await {
|
|
|
+ Ok(Some(w)) => w,
|
|
|
+ Ok(None) => return HttpResponse::NotFound().json(json!({"error": "Workout not found"})),
|
|
|
+ Err(e) => {
|
|
|
+ eprintln!("update_workout find error: {e}");
|
|
|
+ return HttpResponse::InternalServerError().json(json!({"error": "An error occurred"}));
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ if workout.user_id != auth.0.id {
|
|
|
+ return HttpResponse::Forbidden()
|
|
|
+ .json(json!({"error": "You do not have permission to update this workout"}));
|
|
|
+ }
|
|
|
+
|
|
|
+ if let Some(exercises) = &body.exercises {
|
|
|
+ let positions: HashSet<i32> = exercises.iter().map(|e| e.position).collect();
|
|
|
+ if positions.len() != exercises.len() {
|
|
|
+ return HttpResponse::UnprocessableEntity().json(json!({
|
|
|
+ "error": "Two or more exercises share the same position. Each exercise must have a unique position within the workout."
|
|
|
+ }));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ let mut tx = match pool.begin().await {
|
|
|
+ Ok(t) => t,
|
|
|
+ Err(e) => {
|
|
|
+ eprintln!("update_workout begin tx error: {e}");
|
|
|
+ return HttpResponse::InternalServerError().json(json!({"error": "An error occurred"}));
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ if let Some(name) = &body.name {
|
|
|
+ if let Err(e) = sqlx::query("UPDATE workouts SET name = $1 WHERE id = $2")
|
|
|
+ .bind(name)
|
|
|
+ .bind(id)
|
|
|
+ .execute(&mut *tx)
|
|
|
+ .await
|
|
|
+ {
|
|
|
+ eprintln!("update_workout name error: {e}");
|
|
|
+ return HttpResponse::InternalServerError()
|
|
|
+ .json(json!({"error": "Failed to update workout"}));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if let Some(exercises) = &body.exercises {
|
|
|
+ if let Err(e) = sqlx::query("DELETE FROM exercises WHERE workout = $1")
|
|
|
+ .bind(id)
|
|
|
+ .execute(&mut *tx)
|
|
|
+ .await
|
|
|
+ {
|
|
|
+ eprintln!("update_workout delete exercises error: {e}");
|
|
|
+ return HttpResponse::InternalServerError()
|
|
|
+ .json(json!({"error": "Failed to update exercises"}));
|
|
|
+ }
|
|
|
+
|
|
|
+ for exercise in exercises {
|
|
|
+ if let Err(e) = sqlx::query(
|
|
|
+ "INSERT INTO exercises (workout, name, exercise_type, position) VALUES ($1, $2, $3, $4)",
|
|
|
+ )
|
|
|
+ .bind(id)
|
|
|
+ .bind(&exercise.name)
|
|
|
+ .bind(exercise.exercise_type.clone())
|
|
|
+ .bind(exercise.position)
|
|
|
+ .execute(&mut *tx)
|
|
|
+ .await
|
|
|
+ {
|
|
|
+ eprintln!("update_workout insert exercise error: {e}");
|
|
|
+ return HttpResponse::InternalServerError()
|
|
|
+ .json(json!({"error": "Failed to update exercises"}));
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if let Err(e) = tx.commit().await {
|
|
|
+ eprintln!("update_workout commit error: {e}");
|
|
|
+ return HttpResponse::InternalServerError().json(json!({"error": "An error occurred"}));
|
|
|
+ }
|
|
|
+
|
|
|
+ let workout = match Workout::find_by_id(pool.get_ref(), id).await {
|
|
|
+ Ok(Some(w)) => w,
|
|
|
+ _ => return HttpResponse::InternalServerError().json(json!({"error": "An error occurred"})),
|
|
|
+ };
|
|
|
+
|
|
|
+ let exercises = match Exercise::find_by_workout(pool.get_ref(), id).await {
|
|
|
+ Ok(e) => e,
|
|
|
+ Err(e) => {
|
|
|
+ eprintln!("update_workout fetch exercises error: {e}");
|
|
|
+ return HttpResponse::InternalServerError().json(json!({"error": "An error occurred"}));
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ HttpResponse::Ok().json(json!({
|
|
|
+ "id": workout.id,
|
|
|
+ "name": workout.name,
|
|
|
+ "exercises": exercises.iter().map(|e| json!({
|
|
|
+ "id": e.id,
|
|
|
+ "name": e.name,
|
|
|
+ "notes": e.notes,
|
|
|
+ "exercise_type": e.exercise_type,
|
|
|
+ "position": e.position,
|
|
|
+ })).collect::<Vec<_>>(),
|
|
|
+ }))
|
|
|
+}
|