Move stop button (#29)

* Task attempt e2bfa5ea-1e87-4b4d-a7a0-7a523ce3f49d - Final changes

* Task attempt e2bfa5ea-1e87-4b4d-a7a0-7a523ce3f49d - Final changes

* Fix activity timestamp issue
This commit is contained in:
Louis Knight-Webb
2025-06-30 23:46:08 +01:00
committed by GitHub
parent 9bb3411390
commit a16ae05350
4 changed files with 142 additions and 123 deletions

View File

@@ -1,68 +0,0 @@
{
"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<Utc>\", \n t.updated_at as \"updated_at!: DateTime<Utc>\",\n CASE WHEN in_progress_attempts.task_id IS NOT NULL THEN true ELSE false END as \"has_in_progress_attempt!: i64\",\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 execution_processes ep ON ta.id = ep.task_attempt_id\n INNER JOIN (\n SELECT execution_process_id, MAX(created_at) as latest_created_at\n FROM task_attempt_activities\n GROUP BY execution_process_id\n ) latest_activity ON ep.id = latest_activity.execution_process_id\n INNER JOIN task_attempt_activities taa ON ep.id = taa.execution_process_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": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "project_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "title",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "description",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "status!: TaskStatus",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "has_in_progress_attempt!: i64",
"ordinal": 7,
"type_info": "Integer"
},
{
"name": "has_merged_attempt!",
"ordinal": 8,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
true,
false,
false,
false,
false,
false
]
},
"hash": "4333e38d5175a00d1206b929b8f492c17c4f57b31908f6973ce4f4a53785f5f0"
}

View File

@@ -0,0 +1,68 @@
{
"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<Utc>\", \n t.updated_at AS \"updated_at!: DateTime<Utc>\",\n CASE \n WHEN in_progress_attempts.task_id IS NOT NULL THEN true \n ELSE false \n END AS \"has_in_progress_attempt!: i64\",\n CASE \n WHEN merged_attempts.task_id IS NOT NULL THEN true \n ELSE false \n END AS \"has_merged_attempt!\"\n FROM tasks t\n LEFT JOIN (\n SELECT DISTINCT ta.task_id\n FROM task_attempts ta\n JOIN execution_processes ep \n ON ta.id = ep.task_attempt_id\n JOIN (\n -- pick exactly one “latest” activity per process,\n -- tiebreaking so that runningstates are lower priority\n SELECT execution_process_id, status\n FROM (\n SELECT\n execution_process_id,\n status,\n ROW_NUMBER() OVER (\n PARTITION BY execution_process_id\n ORDER BY\n created_at DESC,\n CASE \n WHEN status IN ('setuprunning','executorrunning') THEN 1 \n ELSE 0 \n END\n ) AS rn\n FROM task_attempt_activities\n ) sub\n WHERE rn = 1\n ) latest_act \n ON ep.id = latest_act.execution_process_id\n WHERE latest_act.status IN ('setuprunning','executorrunning')\n ) in_progress_attempts \n 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 \n ON t.id = merged_attempts.task_id\n WHERE t.project_id = $1\n ORDER BY t.created_at DESC;\n ",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "project_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "title",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "description",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "status!: TaskStatus",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "has_in_progress_attempt!: i64",
"ordinal": 7,
"type_info": "Integer"
},
{
"name": "has_merged_attempt!",
"ordinal": 8,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
true,
false,
false,
false,
false,
false
]
},
"hash": "5303664388a91c378ade88cad68fcb1bce32a4be1269820bf2031dd6b7e13be8"
}

View File

@@ -74,36 +74,61 @@ impl Task {
) -> Result<Vec<TaskWithAttemptStatus>, sqlx::Error> { ) -> Result<Vec<TaskWithAttemptStatus>, sqlx::Error> {
let records = sqlx::query!( let records = sqlx::query!(
r#"SELECT r#"SELECT
t.id as "id!: Uuid", t.id AS "id!: Uuid",
t.project_id as "project_id!: Uuid", t.project_id AS "project_id!: Uuid",
t.title, t.title,
t.description, t.description,
t.status as "status!: TaskStatus", t.status AS "status!: TaskStatus",
t.created_at as "created_at!: DateTime<Utc>", t.created_at AS "created_at!: DateTime<Utc>",
t.updated_at as "updated_at!: DateTime<Utc>", t.updated_at AS "updated_at!: DateTime<Utc>",
CASE WHEN in_progress_attempts.task_id IS NOT NULL THEN true ELSE false END as "has_in_progress_attempt!: i64", CASE
CASE WHEN merged_attempts.task_id IS NOT NULL THEN true ELSE false END as "has_merged_attempt!" WHEN in_progress_attempts.task_id IS NOT NULL THEN true
FROM tasks t ELSE false
LEFT JOIN ( END AS "has_in_progress_attempt!: i64",
SELECT DISTINCT ta.task_id CASE
FROM task_attempts ta WHEN merged_attempts.task_id IS NOT NULL THEN true
INNER JOIN execution_processes ep ON ta.id = ep.task_attempt_id ELSE false
INNER JOIN ( END AS "has_merged_attempt!"
SELECT execution_process_id, MAX(created_at) as latest_created_at FROM tasks t
FROM task_attempt_activities LEFT JOIN (
GROUP BY execution_process_id SELECT DISTINCT ta.task_id
) latest_activity ON ep.id = latest_activity.execution_process_id FROM task_attempts ta
INNER JOIN task_attempt_activities taa ON ep.id = taa.execution_process_id JOIN execution_processes ep
AND taa.created_at = latest_activity.latest_created_at ON ta.id = ep.task_attempt_id
WHERE taa.status IN ('setuprunning', 'executorrunning') JOIN (
) in_progress_attempts ON t.id = in_progress_attempts.task_id -- pick exactly one “latest” activity per process,
LEFT JOIN ( -- tiebreaking so that runningstates are lower priority
SELECT DISTINCT ta.task_id SELECT execution_process_id, status
FROM task_attempts ta FROM (
WHERE ta.merge_commit IS NOT NULL SELECT
) merged_attempts ON t.id = merged_attempts.task_id execution_process_id,
WHERE t.project_id = $1 status,
ORDER BY t.created_at DESC"#, ROW_NUMBER() OVER (
PARTITION BY execution_process_id
ORDER BY
created_at DESC,
CASE
WHEN status IN ('setuprunning','executorrunning') THEN 1
ELSE 0
END
) AS rn
FROM task_attempt_activities
) sub
WHERE rn = 1
) latest_act
ON ep.id = latest_act.execution_process_id
WHERE latest_act.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 project_id
) )
.fetch_all(pool) .fetch_all(pool)

View File

@@ -565,7 +565,18 @@ export function TaskDetailsToolbar({
</DropdownMenu> </DropdownMenu>
)} )}
{!isAttemptRunning && !isStopping && ( {isStopping || isAttemptRunning ? (
<Button
variant="destructive"
size="sm"
onClick={onStopAllExecutions}
disabled={isStopping}
className="gap-2"
>
<StopCircle className="h-4 w-4" />
{isStopping ? 'Stopping...' : 'Stop Attempt'}
</Button>
) : (
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
@@ -593,33 +604,16 @@ export function TaskDetailsToolbar({
</div> </div>
{/* Special Actions */} {/* Special Actions */}
{(isAttemptRunning || {!selectedAttempt && !isAttemptRunning && !isStopping && (
isStopping ||
(!selectedAttempt && !isAttemptRunning && !isStopping)) && (
<div className="space-y-2 pt-3 border-t"> <div className="space-y-2 pt-3 border-t">
{(isAttemptRunning || isStopping) && ( <Button
<Button onClick={handleEnterCreateAttemptMode}
variant="destructive" size="sm"
size="sm" className="w-full gap-2"
onClick={onStopAllExecutions} >
disabled={isStopping} <Play className="h-4 w-4" />
className="w-full gap-2" Start Attempt
> </Button>
<StopCircle className="h-4 w-4" />
{isStopping ? 'Stopping...' : 'Stop Attempt'}
</Button>
)}
{!selectedAttempt && !isAttemptRunning && !isStopping && (
<Button
onClick={handleEnterCreateAttemptMode}
size="sm"
className="w-full gap-2"
>
<Play className="h-4 w-4" />
Start Attempt
</Button>
)}
</div> </div>
)} )}
</div> </div>