feat(tasks): B2a part 1 — archive schema + task_lists scaffolding
Migration v16 adds tasks.archived (default 0) + tasks.archived_at + idx_tasks_archived. TaskRow extended with both fields; list_tasks and list_subtasks default-filter archived rows; complete_subtask_and_check_parent SELECT lists updated to round-trip the new columns. TaskListRow + ImportSummary structs land in preparation for the task_lists CRUD that part 2 wires up. All 61 existing tests still pass. (SQL splitter is naive about ';' inside -- comments, so the v16 comment block was rewritten to avoid a bare semicolon mid-comment that the splitter was treating as a statement boundary.) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -328,8 +328,9 @@ pub async fn insert_task(
|
||||
pub async fn list_tasks(pool: &SqlitePool) -> Result<Vec<TaskRow>> {
|
||||
let rows = sqlx::query(
|
||||
"SELECT id, text, bucket, list_id, effort, notes, done, done_at, created_at, \
|
||||
source_transcript_id, parent_task_id, energy \
|
||||
FROM tasks WHERE parent_task_id IS NULL ORDER BY created_at DESC",
|
||||
source_transcript_id, parent_task_id, energy, archived, archived_at \
|
||||
FROM tasks WHERE parent_task_id IS NULL AND archived = 0 \
|
||||
ORDER BY created_at DESC",
|
||||
)
|
||||
.fetch_all(pool)
|
||||
.await
|
||||
@@ -341,7 +342,8 @@ pub async fn list_tasks(pool: &SqlitePool) -> Result<Vec<TaskRow>> {
|
||||
pub async fn get_task_by_id(pool: &SqlitePool, id: &str) -> Result<Option<TaskRow>> {
|
||||
let row = sqlx::query(
|
||||
"SELECT id, text, bucket, list_id, effort, notes, done, done_at, created_at, \
|
||||
source_transcript_id, parent_task_id, energy FROM tasks WHERE id = ?",
|
||||
source_transcript_id, parent_task_id, energy, archived, archived_at \
|
||||
FROM tasks WHERE id = ?",
|
||||
)
|
||||
.bind(id)
|
||||
.fetch_optional(pool)
|
||||
@@ -431,8 +433,8 @@ pub async fn insert_subtask(
|
||||
pub async fn list_subtasks(pool: &SqlitePool, parent_id: &str) -> Result<Vec<TaskRow>> {
|
||||
let rows = sqlx::query(
|
||||
"SELECT id, text, bucket, list_id, effort, notes, done, done_at, created_at, \
|
||||
source_transcript_id, parent_task_id, energy \
|
||||
FROM tasks WHERE parent_task_id = ? ORDER BY created_at ASC",
|
||||
source_transcript_id, parent_task_id, energy, archived, archived_at \
|
||||
FROM tasks WHERE parent_task_id = ? AND archived = 0 ORDER BY created_at ASC",
|
||||
)
|
||||
.bind(parent_id)
|
||||
.fetch_all(pool)
|
||||
@@ -499,7 +501,8 @@ pub async fn complete_subtask_and_check_parent(
|
||||
}
|
||||
|
||||
let select_sql = "SELECT id, text, bucket, list_id, effort, notes, done, done_at, \
|
||||
created_at, source_transcript_id, parent_task_id, energy FROM tasks WHERE id = ?";
|
||||
created_at, source_transcript_id, parent_task_id, energy, archived, archived_at \
|
||||
FROM tasks WHERE id = ?";
|
||||
|
||||
let updated_subtask = sqlx::query(select_sql)
|
||||
.bind(subtask_id)
|
||||
@@ -872,6 +875,34 @@ pub struct TaskRow {
|
||||
/// migration v11). Unset is the expected normal case — the match-my-
|
||||
/// energy sort treats unset as Medium-equivalent.
|
||||
pub energy: Option<String>,
|
||||
/// B2a — Inbox-only archive flag (migration v16). `true` means the
|
||||
/// row is archived and excluded from the default `list_tasks` /
|
||||
/// `list_subtasks` query. `archived_at` stamps the archive time so
|
||||
/// the Archived view can sort newest-first.
|
||||
pub archived: bool,
|
||||
pub archived_at: Option<String>,
|
||||
}
|
||||
|
||||
/// B2a — task_lists row. Mirrors the `task_lists` table that has lived in
|
||||
/// migration v1 since the start; this struct + the surrounding CRUD wires
|
||||
/// the table that the frontend was previously stashing in localStorage.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct TaskListRow {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub built_in: bool,
|
||||
pub profile_id: Option<String>,
|
||||
pub created_at: String,
|
||||
}
|
||||
|
||||
/// Result of `import_task_lists` — how many rows landed and how many were
|
||||
/// skipped because their `id` already exists. The whole batch runs in one
|
||||
/// transaction so a duplicate inside the batch (rather than against the
|
||||
/// existing table) is a hard failure that rolls everything back.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ImportSummary {
|
||||
pub imported: usize,
|
||||
pub skipped: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
@@ -948,6 +979,18 @@ fn task_row_from(r: sqlx::sqlite::SqliteRow) -> TaskRow {
|
||||
source_transcript_id: r.get("source_transcript_id"),
|
||||
parent_task_id: r.get("parent_task_id"),
|
||||
energy: r.get("energy"),
|
||||
archived: r.get::<i64, _>("archived") != 0,
|
||||
archived_at: r.get("archived_at"),
|
||||
}
|
||||
}
|
||||
|
||||
fn task_list_row_from(r: &sqlx::sqlite::SqliteRow) -> TaskListRow {
|
||||
TaskListRow {
|
||||
id: r.get("id"),
|
||||
name: r.get("name"),
|
||||
built_in: r.get::<i64, _>("built_in") != 0,
|
||||
profile_id: r.get("profile_id"),
|
||||
created_at: r.get("created_at"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user