diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index fde1399..a931b71 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -6,6 +6,7 @@ pub mod hotkey; pub mod live; pub mod models; pub mod transcription; +pub mod tasks; pub mod transcripts; pub mod update; pub mod windows; diff --git a/src-tauri/src/commands/tasks.rs b/src-tauri/src/commands/tasks.rs new file mode 100644 index 0000000..ab9d659 --- /dev/null +++ b/src-tauri/src/commands/tasks.rs @@ -0,0 +1,117 @@ +// Tauri commands wrapping kon_storage task CRUD. +// Pattern mirrors transcripts.rs — TaskDto is the camelCase frontend shape, +// storage functions are aliased with db_ prefix to avoid name collisions. + +use serde::{Deserialize, Serialize}; + +use kon_storage::{ + complete_task as db_complete_task, + delete_task as db_delete_task, + insert_task as db_insert_task, + list_tasks as db_list_tasks, + TaskRow, +}; + +use crate::AppState; + +/// Frontend-facing task shape. Matches the in-memory object in page.svelte.js. +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct TaskDto { + pub id: String, + pub text: String, + pub bucket: String, + pub list_id: Option, + pub effort: Option, + pub done: bool, + pub done_at: Option, + pub created_at: String, + pub source_transcript_id: Option, + pub parent_task_id: Option, +} + +impl From for TaskDto { + fn from(r: TaskRow) -> Self { + Self { + id: r.id, + text: r.text, + bucket: r.bucket, + list_id: r.list_id, + effort: r.effort, + done: r.done, + done_at: r.done_at, + created_at: r.created_at, + source_transcript_id: r.source_transcript_id, + parent_task_id: r.parent_task_id, + } + } +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateTaskRequest { + pub id: String, + pub text: String, + pub bucket: String, + pub source_transcript_id: Option, +} + +#[tauri::command] +pub async fn create_task_cmd( + state: tauri::State<'_, AppState>, + request: CreateTaskRequest, +) -> Result<(), String> { + db_insert_task( + &state.db, + &request.id, + &request.text, + &request.bucket, + request.source_transcript_id.as_deref(), + ) + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub async fn list_tasks_cmd( + state: tauri::State<'_, AppState>, +) -> Result, String> { + db_list_tasks(&state.db) + .await + .map(|rows| rows.into_iter().map(TaskDto::from).collect()) + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub async fn complete_task_cmd( + state: tauri::State<'_, AppState>, + id: String, +) -> Result<(), String> { + db_complete_task(&state.db, &id) + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub async fn delete_task_cmd( + state: tauri::State<'_, AppState>, + id: String, +) -> Result<(), String> { + db_delete_task(&state.db, &id) + .await + .map_err(|e| e.to_string()) +} + +#[tauri::command] +pub async fn uncomplete_task_cmd( + state: tauri::State<'_, AppState>, + id: String, +) -> Result<(), String> { + sqlx::query("UPDATE tasks SET done = 0, done_at = NULL WHERE id = ?") + .bind(&id) + .execute(&state.db) + .await + .map_err(|e| format!("Uncomplete task failed: {e}")) + .map(|_| ()) +} + diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index feed2b6..7f187d5 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -252,6 +252,12 @@ pub fn run() { commands::audio::start_native_capture, commands::audio::stop_native_capture, commands::audio::list_audio_devices, + // Tasks (canonical SQLite-backed task CRUD) + commands::tasks::create_task_cmd, + commands::tasks::list_tasks_cmd, + commands::tasks::complete_task_cmd, + commands::tasks::delete_task_cmd, + commands::tasks::uncomplete_task_cmd, // Transcripts (canonical SQLite-backed history) — Day 4 commands::transcripts::add_transcript, commands::transcripts::list_transcripts,