diff --git a/src-tauri/src/db.rs b/src-tauri/src/db.rs index 35245fb..4ff950b 100644 --- a/src-tauri/src/db.rs +++ b/src-tauri/src/db.rs @@ -169,6 +169,25 @@ pub struct Impairment { pub updated_at: Option, } +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct HuDraft { + pub id: String, + pub initiative_id: Option, + pub code: Option, + pub title: String, + pub description: Option, + pub acceptance_criteria: Option, + pub item_type: Option, + pub hierarchy_path: Option, + pub story_points: Option, + pub sprint: Option, + pub assigned_to: Option, + pub sync_status: String, + pub kappa_id: Option, + pub created_at: Option, + pub updated_at: Option, +} + async fn get_conn(db_path: &str) -> Result { let db = libsql::Builder::new_local(db_path) .build() @@ -299,6 +318,24 @@ async fn get_conn(db_path: &str) -> Result { created_at TEXT, updated_at TEXT, FOREIGN KEY (hu_id) REFERENCES user_stories(id) + ); + + CREATE TABLE IF NOT EXISTS hu_drafts ( + id TEXT PRIMARY KEY, + initiative_id INTEGER, + code TEXT, + title TEXT NOT NULL, + description TEXT, + acceptance_criteria TEXT, + item_type TEXT DEFAULT 'U', + hierarchy_path TEXT, + story_points REAL, + sprint TEXT, + assigned_to TEXT, + sync_status TEXT DEFAULT 'draft', + kappa_id INTEGER, + created_at TEXT DEFAULT (datetime('now')), + updated_at TEXT DEFAULT (datetime('now')) );" ) .await @@ -1089,4 +1126,81 @@ pub mod commands { Ok(()) } + + // ──────────────────────────────────────────── + // HU Drafts (local, pre-KAPPA) + // ──────────────────────────────────────────── + + #[tauri::command] + pub async fn get_hu_drafts(state: State<'_, Mutex>, initiative_id: Option) -> Result, String> { + let db_path = state.lock().map_err(|e| e.to_string())?.clone(); + let conn = get_conn(&db_path).await?; + + let query = if initiative_id.is_some() { + "SELECT id, initiative_id, code, title, description, acceptance_criteria, item_type, hierarchy_path, story_points, sprint, assigned_to, sync_status, kappa_id, created_at, updated_at FROM hu_drafts WHERE initiative_id = ?1 ORDER BY created_at DESC" + } else { + "SELECT id, initiative_id, code, title, description, acceptance_criteria, item_type, hierarchy_path, story_points, sprint, assigned_to, sync_status, kappa_id, created_at, updated_at FROM hu_drafts ORDER BY created_at DESC" + }; + + let mut rows = if let Some(pid) = initiative_id { + conn.query(query, libsql::params![pid]).await.map_err(|e| format!("Query: {e}"))? + } else { + conn.query(query, ()).await.map_err(|e| format!("Query: {e}"))? + }; + + let mut drafts = Vec::new(); + while let Some(row) = rows.next().await.map_err(|e| format!("Row: {e}"))? { + drafts.push(HuDraft { + id: row.get::(0).unwrap_or_default(), + initiative_id: row.get::(1).ok(), + code: row.get::(2).ok(), + title: row.get::(3).unwrap_or_default(), + description: row.get::(4).ok(), + acceptance_criteria: row.get::(5).ok(), + item_type: row.get::(6).ok(), + hierarchy_path: row.get::(7).ok(), + story_points: row.get::(8).ok(), + sprint: row.get::(9).ok(), + assigned_to: row.get::(10).ok(), + sync_status: row.get::(11).unwrap_or_else(|_| "draft".into()), + kappa_id: row.get::(12).ok(), + created_at: row.get::(13).ok(), + updated_at: row.get::(14).ok(), + }); + } + Ok(drafts) + } + + #[tauri::command] + pub async fn save_hu_draft(state: State<'_, Mutex>, draft: HuDraft) -> Result<(), String> { + let db_path = state.lock().map_err(|e| e.to_string())?.clone(); + let conn = get_conn(&db_path).await?; + + conn.execute( + "INSERT OR REPLACE INTO hu_drafts (id, initiative_id, code, title, description, acceptance_criteria, item_type, hierarchy_path, story_points, sprint, assigned_to, sync_status, kappa_id, updated_at) + VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12, ?13, datetime('now'))", + libsql::params![ + draft.id, draft.initiative_id, draft.code, draft.title, + draft.description, draft.acceptance_criteria, draft.item_type, + draft.hierarchy_path, draft.story_points, draft.sprint, + draft.assigned_to, draft.sync_status, draft.kappa_id, + ], + ) + .await + .map_err(|e| format!("Insert: {e}"))?; + + Ok(()) + } + + #[tauri::command] + pub async fn delete_hu_draft(state: State<'_, Mutex>, id: String) -> Result<(), String> { + let db_path = state.lock().map_err(|e| e.to_string())?.clone(); + let conn = get_conn(&db_path).await?; + + conn.execute("DELETE FROM hu_drafts WHERE id = ?1", libsql::params![id]) + .await + .map_err(|e| format!("Delete: {e}"))?; + + Ok(()) + } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 83aa44b..7d8cd73 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -50,6 +50,9 @@ fn main() { commands::get_impairments, commands::save_impairment, commands::delete_impairment, + commands::get_hu_drafts, + commands::save_hu_draft, + commands::delete_hu_draft, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src/services/tauri-db.ts b/src/services/tauri-db.ts index 468e8d0..685c585 100644 --- a/src/services/tauri-db.ts +++ b/src/services/tauri-db.ts @@ -73,34 +73,22 @@ export interface ImpairmentRecord { updated_at: string | null } -export interface WorkItemRecord { - id: number - project_id: number +export interface HuDraftRecord { + id: string + initiative_id: number | null code: string | null title: string description: string | null - type: string - status: string - priority: string -} - -export interface AlphaUserRecord { - id: number - email: string - first_name: string - last_name: string - role: string | null - seniority: string | null - cell_id: number | null - is_active: boolean - created_at: string | null -} - -export interface CellRecord { - id: number - name: string - description: string | null + acceptance_criteria: string | null + item_type: string | null + hierarchy_path: string | null + story_points: number | null + sprint: string | null + assigned_to: string | null + sync_status: string + kappa_id: number | null created_at: string | null + updated_at: string | null } export interface ProjectMemberRecord { @@ -153,6 +141,36 @@ export interface PerformanceRecord { calculated_at: string | null } +export interface WorkItemRecord { + id: number + project_id: number + code: string | null + title: string + description: string | null + type: string + status: string + priority: string +} + +export interface AlphaUserRecord { + id: number + email: string + first_name: string + last_name: string + role: string | null + seniority: string | null + cell_id: number | null + is_active: boolean + created_at: string | null +} + +export interface CellRecord { + id: number + name: string + description: string | null + created_at: string | null +} + export const tauriDb = { // Projects getProjects(): Promise { @@ -265,4 +283,15 @@ export const tauriDb = { deleteImpairment(id: number): Promise { return safeInvoke('delete_impairment', { id }) }, + + // HU Drafts + getHuDrafts(initiativeId?: number): Promise { + return safeInvoke('get_hu_drafts', { initiativeId: initiativeId ?? null }) + }, + saveHuDraft(draft: HuDraftRecord): Promise { + return safeInvoke('save_hu_draft', { draft }) + }, + deleteHuDraft(id: string): Promise { + return safeInvoke('delete_hu_draft', { id }) + }, }