use axum::{ extract::{Path, State}, http::StatusCode, Json, }; use sea_orm::{prelude::Decimal, DatabaseConnection}; use serde::Deserialize; use utoipa::ToSchema; use crate::models::category::Model as CategoryModel; use crate::services::CategoryService; #[derive(Debug, Deserialize, ToSchema)] #[schema(example = json!({"name": "Groceries", "limit_amount": 5000.00}))] pub struct CreateCategoryRequest { pub name: String, pub limit_amount: Decimal, } #[derive(Debug, Deserialize, ToSchema)] #[schema(example = json!({"name": "Monthly Groceries", "limit_amount": 6000.00}))] pub struct UpdateCategoryRequest { pub name: Option, pub limit_amount: Option, } #[utoipa::path( post, path = "/families/{family_id}/categories", tag = "categories", params( ("family_id" = i32, Path, description = "Family ID") ), request_body = CreateCategoryRequest, responses( (status = 200, description = "Category created successfully", body = CategoryModel), (status = 500, description = "Internal server error") ) )] pub async fn create_category( State(db): State, Path(family_id): Path, Json(payload): Json, ) -> Result, StatusCode> { CategoryService::create(&db, family_id, payload.name, payload.limit_amount) .await .map(Json) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) } #[utoipa::path( get, path = "/families/{family_id}/categories/{category_id}", tag = "categories", params( ("family_id" = i32, Path, description = "Family ID"), ("category_id" = i32, Path, description = "Category ID") ), responses( (status = 200, description = "Category found", body = CategoryModel), (status = 404, description = "Category not found"), (status = 500, description = "Internal server error") ) )] pub async fn get_category( State(db): State, Path((family_id, category_id)): Path<(i32, i32)>, ) -> Result, StatusCode> { let category = CategoryService::find_by_id(&db, category_id) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? .ok_or(StatusCode::NOT_FOUND)?; if category.family_id != family_id { return Err(StatusCode::NOT_FOUND); } Ok(Json(category)) } #[utoipa::path( get, path = "/families/{family_id}/categories", tag = "categories", params( ("family_id" = i32, Path, description = "Family ID") ), responses( (status = 200, description = "List of categories for the family", body = Vec), (status = 500, description = "Internal server error") ) )] pub async fn get_categories_by_family( State(db): State, Path(family_id): Path, ) -> Result>, StatusCode> { CategoryService::find_by_family_id(&db, family_id) .await .map(Json) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) } #[utoipa::path( put, path = "/families/{family_id}/categories/{category_id}", tag = "categories", params( ("family_id" = i32, Path, description = "Family ID"), ("category_id" = i32, Path, description = "Category ID") ), request_body = UpdateCategoryRequest, responses( (status = 200, description = "Category updated successfully", body = CategoryModel), (status = 404, description = "Category not found"), (status = 500, description = "Internal server error") ) )] pub async fn update_category( State(db): State, Path((family_id, category_id)): Path<(i32, i32)>, Json(payload): Json, ) -> Result, StatusCode> { let category = CategoryService::find_by_id(&db, category_id) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? .ok_or(StatusCode::NOT_FOUND)?; if category.family_id != family_id { return Err(StatusCode::NOT_FOUND); } CategoryService::update(&db, category_id, payload.name, payload.limit_amount) .await .map(Json) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) } #[utoipa::path( delete, path = "/families/{family_id}/categories/{category_id}", tag = "categories", params( ("family_id" = i32, Path, description = "Family ID"), ("category_id" = i32, Path, description = "Category ID") ), responses( (status = 204, description = "Category deleted successfully"), (status = 404, description = "Category not found"), (status = 500, description = "Internal server error") ) )] pub async fn delete_category( State(db): State, Path((family_id, category_id)): Path<(i32, i32)>, ) -> Result { let category = CategoryService::find_by_id(&db, category_id) .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)? .ok_or(StatusCode::NOT_FOUND)?; if category.family_id != family_id { return Err(StatusCode::NOT_FOUND); } CategoryService::delete(&db, category_id) .await .map(|_| StatusCode::NO_CONTENT) .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR) }