//! Dönem notları: //! //! | Yöntem | Yol | İş | //! |---|---|---| //! | GET | `/api/periods/{id}/notes?context=planning` | notlar (sayfa filtresi isteğe bağlı) | //! | POST | `/api/periods/{id}/notes` | not ekle | //! | PATCH | `/api/notes/{id}` | düzenle | //! | DELETE | `/api/notes/{id}` | sil | use axum::{ Json, extract::{Path, Query, State}, http::StatusCode, }; use mongodb::bson::Uuid; use serde::{Deserialize, Serialize}; use ts_rs::TS; use validator::Validate; use crate::{ auth::AuthUser, error::{AppError, AppResult}, extract::ValidJson, models::note::{Note, NoteContext}, services::note, state::AppState, }; #[derive(Deserialize)] pub struct ListQuery { context: Option, } #[derive(Deserialize, Validate, TS)] #[ts(export, rename = "NoteCreateRequest")] pub struct CreateRequest { context: NoteContext, title: String, /// Markdown #[serde(default)] body_md: String, } #[derive(Deserialize, Validate, TS)] #[ts(export, rename = "NoteUpdateRequest", optional_fields)] pub struct UpdateRequest { title: Option, body_md: Option, version: Option, } #[derive(Serialize, TS)] #[ts(export, rename = "Note")] pub struct NoteDto { id: String, context: NoteContext, title: String, body_md: String, created_by: String, /// ISO 8601 updated_at: String, version: i32, } impl From for NoteDto { fn from(n: Note) -> Self { Self { id: n.id.to_string(), context: n.context, title: n.title, body_md: n.body_md, created_by: n.created_by.to_string(), updated_at: n.updated_at.try_to_rfc3339_string().unwrap_or_default(), version: n.version, } } } fn parse_id(id: &str) -> AppResult { Uuid::parse_str(id).map_err(|_| AppError::NotFound) } pub async fn list( State(s): State, auth: AuthUser, Path(period_id): Path, Query(q): Query, ) -> AppResult>> { let notes = note::list(&s.db, auth.household_id, parse_id(&period_id)?, q.context).await?; Ok(Json(notes.into_iter().map(NoteDto::from).collect())) } pub async fn create( State(s): State, auth: AuthUser, Path(period_id): Path, ValidJson(req): ValidJson, ) -> AppResult<(StatusCode, Json)> { let n = note::create( &s.db, auth.household_id, auth.user_id, parse_id(&period_id)?, req.context, &req.title, &req.body_md, ) .await?; Ok((StatusCode::CREATED, Json(n.into()))) } pub async fn update( State(s): State, auth: AuthUser, Path(id): Path, ValidJson(req): ValidJson, ) -> AppResult> { let n = note::update( &s.db, auth.household_id, parse_id(&id)?, req.title.as_deref(), req.body_md.as_deref(), req.version, ) .await?; Ok(Json(n.into())) } pub async fn delete( State(s): State, auth: AuthUser, Path(id): Path, ) -> AppResult { note::delete(&s.db, auth.household_id, parse_id(&id)?).await?; Ok(StatusCode::NO_CONTENT) }