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> {
let records = sqlx::query!(
r#"SELECT
t.id as "id!: Uuid",
t.project_id as "project_id!: Uuid",
t.id AS "id!: Uuid",
t.project_id AS "project_id!: Uuid",
t.title,
t.description,
t.status as "status!: TaskStatus",
t.created_at as "created_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 WHEN merged_attempts.task_id IS NOT NULL THEN true ELSE false END as "has_merged_attempt!"
t.status AS "status!: TaskStatus",
t.created_at AS "created_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
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
FROM task_attempts ta
INNER JOIN execution_processes ep ON ta.id = ep.task_attempt_id
INNER JOIN (
SELECT execution_process_id, MAX(created_at) as latest_created_at
JOIN execution_processes ep
ON ta.id = ep.task_attempt_id
JOIN (
-- pick exactly one “latest” activity per process,
-- tiebreaking so that runningstates are lower priority
SELECT execution_process_id, status
FROM (
SELECT
execution_process_id,
status,
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
GROUP BY execution_process_id
) latest_activity ON ep.id = latest_activity.execution_process_id
INNER JOIN task_attempt_activities taa ON ep.id = taa.execution_process_id
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
) 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
) merged_attempts
ON t.id = merged_attempts.task_id
WHERE t.project_id = $1
ORDER BY t.created_at DESC"#,
ORDER BY t.created_at DESC;
"#,
project_id
)
.fetch_all(pool)

View File

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