feat: timesheet row persistence backend

This commit is contained in:
Your Name
2026-02-20 15:15:50 +02:00
parent 54f75c15ed
commit a3ea37baa1
3 changed files with 81 additions and 0 deletions

View File

@@ -2407,6 +2407,73 @@ pub fn delete_entry_template(state: State<AppState>, id: i64) -> Result<(), Stri
Ok(())
}
#[tauri::command]
pub fn get_timesheet_rows(state: State<AppState>, week_start: String) -> Result<Vec<serde_json::Value>, String> {
let conn = state.db.lock().map_err(|e| e.to_string())?;
let mut stmt = conn.prepare(
"SELECT id, week_start, project_id, task_id, sort_order FROM timesheet_rows WHERE week_start = ?1 ORDER BY sort_order"
).map_err(|e| e.to_string())?;
let rows = stmt.query_map(params![week_start], |row| {
Ok(serde_json::json!({
"id": row.get::<_, i64>(0)?,
"week_start": row.get::<_, String>(1)?,
"project_id": row.get::<_, i64>(2)?,
"task_id": row.get::<_, Option<i64>>(3)?,
"sort_order": row.get::<_, i64>(4)?,
}))
}).map_err(|e| e.to_string())?;
Ok(rows.filter_map(|r| r.ok()).collect())
}
#[tauri::command]
pub fn save_timesheet_rows(state: State<AppState>, week_start: String, rows: Vec<serde_json::Value>) -> Result<(), String> {
let conn = state.db.lock().map_err(|e| e.to_string())?;
conn.execute("BEGIN TRANSACTION", []).map_err(|e| e.to_string())?;
if let Err(e) = conn.execute("DELETE FROM timesheet_rows WHERE week_start = ?1", params![week_start]) {
let _ = conn.execute("ROLLBACK", []);
return Err(e.to_string());
}
for (i, row) in rows.iter().enumerate() {
let project_id = row.get("project_id").and_then(|v| v.as_i64()).unwrap_or(0);
let task_id = row.get("task_id").and_then(|v| v.as_i64());
if let Err(e) = conn.execute(
"INSERT INTO timesheet_rows (week_start, project_id, task_id, sort_order) VALUES (?1, ?2, ?3, ?4)",
params![week_start, project_id, task_id, i as i64],
) {
let _ = conn.execute("ROLLBACK", []);
return Err(e.to_string());
}
}
conn.execute("COMMIT", []).map_err(|e| e.to_string())?;
Ok(())
}
#[tauri::command]
pub fn get_previous_week_structure(state: State<AppState>, current_week_start: String) -> Result<Vec<serde_json::Value>, String> {
let conn = state.db.lock().map_err(|e| e.to_string())?;
let current = chrono::NaiveDate::parse_from_str(&current_week_start, "%Y-%m-%d")
.map_err(|e| e.to_string())?;
let prev = current - chrono::Duration::days(7);
let prev_str = prev.format("%Y-%m-%d").to_string();
let mut stmt = conn.prepare(
"SELECT id, week_start, project_id, task_id, sort_order FROM timesheet_rows WHERE week_start = ?1 ORDER BY sort_order"
).map_err(|e| e.to_string())?;
let rows = stmt.query_map(params![prev_str], |row| {
Ok(serde_json::json!({
"id": row.get::<_, i64>(0)?,
"week_start": row.get::<_, String>(1)?,
"project_id": row.get::<_, i64>(2)?,
"task_id": row.get::<_, Option<i64>>(3)?,
"sort_order": row.get::<_, i64>(4)?,
}))
}).map_err(|e| e.to_string())?;
Ok(rows.filter_map(|r| r.ok()).collect())
}
fn format_seconds_as_time(secs: i64) -> String {
let h = secs / 3600;
let m = (secs % 3600) / 60;