//! Daily review note commands. use chrono::NaiveDate; use serde::{Deserialize, Serialize}; use std::sync::Arc; use tauri::State; use tracing::instrument; use goingson_core::DailyNote; use crate::state::{AppState, DESKTOP_USER_ID}; use super::ApiError; // ============ Response Type ============ #[derive(Debug, Serialize)] #[serde(rename_all = "camelCase")] pub struct DailyNoteResponse { pub id: String, pub note_date: String, pub went_well: String, pub could_improve: String, pub is_reviewed: bool, pub reviewed_at: Option, } impl From for DailyNoteResponse { fn from(note: DailyNote) -> Self { Self { id: note.id.to_string(), note_date: note.note_date.format("%Y-%m-%d").to_string(), went_well: note.went_well, could_improve: note.could_improve, is_reviewed: note.is_reviewed, reviewed_at: note.reviewed_at.map(|dt| dt.to_rfc3339()), } } } // ============ Input Types ============ #[derive(Debug, Deserialize)] #[serde(rename_all = "camelCase")] pub struct UpsertDailyNoteInput { pub note_date: String, pub went_well: String, pub could_improve: String, pub is_reviewed: bool, } // ============ Commands ============ #[tauri::command] #[instrument(skip_all)] pub async fn get_daily_note( state: State<'_, Arc>, date: String, ) -> Result, ApiError> { let date = NaiveDate::parse_from_str(&date, "%Y-%m-%d") .map_err(|_| ApiError::validation("date", "Invalid date format, expected YYYY-MM-DD"))?; let note = state.daily_notes.get_by_date(DESKTOP_USER_ID, date).await?; Ok(note.map(DailyNoteResponse::from)) } #[tauri::command] #[instrument(skip_all)] pub async fn upsert_daily_note( state: State<'_, Arc>, input: UpsertDailyNoteInput, ) -> Result { let date = NaiveDate::parse_from_str(&input.note_date, "%Y-%m-%d") .map_err(|_| ApiError::validation("date", "Invalid date format, expected YYYY-MM-DD"))?; let note = state.daily_notes.upsert( DESKTOP_USER_ID, date, &input.went_well, &input.could_improve, input.is_reviewed, ).await?; Ok(DailyNoteResponse::from(note)) }