From 2180cc4154367fd8cd4fb33e34ad6d72d933fb78 Mon Sep 17 00:00:00 2001 From: Louis Knight-Webb Date: Fri, 20 Jun 2025 17:49:01 +0100 Subject: [PATCH] Task attempt 9e09ecb7-e7c2-4a60-9561-2e3e34dbc60c - Final changes --- ...198be10ecfbcc10970bba5d9f41fd5018c858fd784cb2.json} | 10 ++++++++-- backend/src/models/task.rs | 10 +++++++++- frontend/src/components/tasks/TaskCard.tsx | 6 +++++- shared/types.ts | 2 +- 4 files changed, 23 insertions(+), 5 deletions(-) rename backend/.sqlx/{query-0291d620d1e6c461b135c62e08621c41c22130a86d8664701bc390bc72fb4d4b.json => query-ffd6ac9bbe361cdb172198be10ecfbcc10970bba5d9f41fd5018c858fd784cb2.json} (51%) diff --git a/backend/.sqlx/query-0291d620d1e6c461b135c62e08621c41c22130a86d8664701bc390bc72fb4d4b.json b/backend/.sqlx/query-ffd6ac9bbe361cdb172198be10ecfbcc10970bba5d9f41fd5018c858fd784cb2.json similarity index 51% rename from backend/.sqlx/query-0291d620d1e6c461b135c62e08621c41c22130a86d8664701bc390bc72fb4d4b.json rename to backend/.sqlx/query-ffd6ac9bbe361cdb172198be10ecfbcc10970bba5d9f41fd5018c858fd784cb2.json index 32e1ce6e..d10e46b1 100644 --- a/backend/.sqlx/query-0291d620d1e6c461b135c62e08621c41c22130a86d8664701bc390bc72fb4d4b.json +++ b/backend/.sqlx/query-ffd6ac9bbe361cdb172198be10ecfbcc10970bba5d9f41fd5018c858fd784cb2.json @@ -1,6 +1,6 @@ { "db_name": "SQLite", - "query": "SELECT \n t.id as \"id!: Uuid\", \n t.project_id as \"project_id!: Uuid\", \n t.title, \n t.description, \n t.status as \"status!: TaskStatus\", \n t.created_at as \"created_at!: DateTime\", \n t.updated_at as \"updated_at!: DateTime\",\n CASE WHEN in_progress_attempts.task_id IS NOT NULL THEN true ELSE false END as \"has_in_progress_attempt!\"\n FROM tasks t\n LEFT JOIN (\n SELECT DISTINCT ta.task_id \n FROM task_attempts ta\n INNER JOIN (\n SELECT task_attempt_id, MAX(created_at) as latest_created_at\n FROM task_attempt_activities\n GROUP BY task_attempt_id\n ) latest_activity ON ta.id = latest_activity.task_attempt_id\n INNER JOIN task_attempt_activities taa ON ta.id = taa.task_attempt_id \n AND taa.created_at = latest_activity.latest_created_at\n WHERE taa.status IN ('setuprunning', 'executorrunning')\n ) in_progress_attempts ON t.id = in_progress_attempts.task_id\n WHERE t.project_id = $1 \n ORDER BY t.created_at DESC", + "query": "SELECT \n t.id as \"id!: Uuid\", \n t.project_id as \"project_id!: Uuid\", \n t.title, \n t.description, \n t.status as \"status!: TaskStatus\", \n t.created_at as \"created_at!: DateTime\", \n t.updated_at as \"updated_at!: DateTime\",\n CASE WHEN in_progress_attempts.task_id IS NOT NULL THEN true ELSE false END as \"has_in_progress_attempt!\",\n CASE WHEN merged_attempts.task_id IS NOT NULL THEN true ELSE false END as \"has_merged_attempt!\"\n FROM tasks t\n LEFT JOIN (\n SELECT DISTINCT ta.task_id \n FROM task_attempts ta\n INNER JOIN (\n SELECT task_attempt_id, MAX(created_at) as latest_created_at\n FROM task_attempt_activities\n GROUP BY task_attempt_id\n ) latest_activity ON ta.id = latest_activity.task_attempt_id\n INNER JOIN task_attempt_activities taa ON ta.id = taa.task_attempt_id \n AND taa.created_at = latest_activity.latest_created_at\n WHERE taa.status IN ('setuprunning', 'executorrunning')\n ) in_progress_attempts ON t.id = in_progress_attempts.task_id\n LEFT JOIN (\n SELECT DISTINCT ta.task_id \n FROM task_attempts ta\n WHERE ta.merge_commit IS NOT NULL\n ) merged_attempts ON t.id = merged_attempts.task_id\n WHERE t.project_id = $1 \n ORDER BY t.created_at DESC", "describe": { "columns": [ { @@ -42,6 +42,11 @@ "name": "has_in_progress_attempt!", "ordinal": 7, "type_info": "Int" + }, + { + "name": "has_merged_attempt!", + "ordinal": 8, + "type_info": "Int" } ], "parameters": { @@ -55,8 +60,9 @@ false, false, false, + false, false ] }, - "hash": "0291d620d1e6c461b135c62e08621c41c22130a86d8664701bc390bc72fb4d4b" + "hash": "ffd6ac9bbe361cdb172198be10ecfbcc10970bba5d9f41fd5018c858fd784cb2" } diff --git a/backend/src/models/task.rs b/backend/src/models/task.rs index 01e9711b..5b26f557 100644 --- a/backend/src/models/task.rs +++ b/backend/src/models/task.rs @@ -39,6 +39,7 @@ pub struct TaskWithAttemptStatus { pub created_at: DateTime, pub updated_at: DateTime, pub has_in_progress_attempt: bool, + pub has_merged_attempt: bool, } #[derive(Debug, Deserialize, TS)] @@ -87,7 +88,8 @@ impl Task { t.status as "status!: TaskStatus", t.created_at as "created_at!: DateTime", t.updated_at as "updated_at!: DateTime", - CASE WHEN in_progress_attempts.task_id IS NOT NULL THEN true ELSE false END as "has_in_progress_attempt!" + CASE WHEN in_progress_attempts.task_id IS NOT NULL THEN true ELSE false END as "has_in_progress_attempt!", + CASE WHEN merged_attempts.task_id IS NOT NULL THEN true ELSE false END as "has_merged_attempt!" FROM tasks t LEFT JOIN ( SELECT DISTINCT ta.task_id @@ -101,6 +103,11 @@ impl Task { AND taa.created_at = latest_activity.latest_created_at WHERE taa.status IN ('setuprunning', 'executorrunning') ) in_progress_attempts ON t.id = in_progress_attempts.task_id + LEFT JOIN ( + SELECT DISTINCT ta.task_id + FROM task_attempts ta + WHERE ta.merge_commit IS NOT NULL + ) merged_attempts ON t.id = merged_attempts.task_id WHERE t.project_id = $1 ORDER BY t.created_at DESC"#, project_id @@ -119,6 +126,7 @@ impl Task { created_at: record.created_at, updated_at: record.updated_at, has_in_progress_attempt: record.has_in_progress_attempt != 0, + has_merged_attempt: record.has_merged_attempt != 0, }) .collect(); diff --git a/frontend/src/components/tasks/TaskCard.tsx b/frontend/src/components/tasks/TaskCard.tsx index 81a827ad..940d6cfe 100644 --- a/frontend/src/components/tasks/TaskCard.tsx +++ b/frontend/src/components/tasks/TaskCard.tsx @@ -6,7 +6,7 @@ import { DropdownMenuTrigger } from '@/components/ui/dropdown-menu' import { KanbanCard } from '@/components/ui/shadcn-io/kanban' -import { MoreHorizontal, Trash2, Edit, Loader2 } from 'lucide-react' +import { MoreHorizontal, Trash2, Edit, Loader2, CheckCircle } from 'lucide-react' import type { TaskWithAttemptStatus } from 'shared/types' type Task = TaskWithAttemptStatus @@ -42,6 +42,10 @@ export function TaskCard({ task, index, status, onEdit, onDelete, onViewDetails {task.has_in_progress_attempt && ( )} + {/* Merged Indicator */} + {task.has_merged_attempt && ( + + )} {/* Actions Menu */}
e.stopPropagation()} diff --git a/shared/types.ts b/shared/types.ts index 3b3e73a7..3de50ba7 100644 --- a/shared/types.ts +++ b/shared/types.ts @@ -25,7 +25,7 @@ export type TaskStatus = "todo" | "inprogress" | "inreview" | "done" | "cancelle export type Task = { id: string, project_id: string, title: string, description: string | null, status: TaskStatus, created_at: string, updated_at: string, }; -export type TaskWithAttemptStatus = { id: string, project_id: string, title: string, description: string | null, status: TaskStatus, created_at: string, updated_at: string, has_in_progress_attempt: boolean, }; +export type TaskWithAttemptStatus = { id: string, project_id: string, title: string, description: string | null, status: TaskStatus, created_at: string, updated_at: string, has_in_progress_attempt: boolean, has_merged_attempt: boolean, }; export type UpdateTask = { title: string | null, description: string | null, status: TaskStatus | null, };