feat: 5 puntitos de status en DAG List (R1..R5 columnas Badge)

- data_http: parsea last_runs[] del /api/dags y guarda d.last_runs_status (max 5, mas reciente primero).
- tabs.cpp DAG List: 5 columnas R1..R5 con CellRenderer::Badge + BadgeRule por status (success=verde, failed=rojo, running=amarillo, pending/cancelled=gris, "-"=tenue).
- main.cpp: g_refresh_pending. WS auto-trigger refresh /api/dags cuando ve un run con status terminal -> last_runs se actualiza sin pulsar nada.
- main + tabs: extern "C" dag_list_request_refresh() para el boton Refresh manual.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-15 17:07:09 +02:00
parent 7c7923ac6a
commit 49fc908fb4
4 changed files with 77 additions and 11 deletions
+52 -9
View File
@@ -75,6 +75,9 @@ static std::string status_for_dag(const std::string& dag_name,
// DAG List
// ---------------------------------------------------------------------------
// Forward decl — main.cpp owns the cache and the refresh trigger.
extern "C" void dag_list_request_refresh();
void draw_dag_list(const std::string& api_url,
const std::vector<dag_ui::DagInfo>& dags,
const std::vector<dag_ui::DagRunRow>& live_runs)
@@ -84,6 +87,12 @@ void draw_dag_list(const std::string& api_url,
return;
}
if (ImGui::Button(TI_REFRESH " Refresh##dag_list")) {
dag_list_request_refresh();
}
ImGui::SameLine();
ImGui::TextDisabled("Double-click row -> inspect. R1..R5 = last 5 runs.");
if (dags.empty()) {
empty_state("( no DAGs )", "Empty registry",
"Place a YAML in apps/dag_engine/dags_migrated/ and reload the server.");
@@ -91,27 +100,62 @@ void draw_dag_list(const std::string& api_url,
return;
}
// Build TableInput
// Build TableInput. Columnas R1..R5 muestran status de las ultimas 5 runs
// como badges coloreadas (verde/rojo/amarillo/gris) — issue 0095.
data_table::TableInput ti;
ti.name = "dags";
ti.headers = {"Name", "Schedule", "Last Status", "Tags", "Valid", "File"};
ti.headers = {"Name", "Schedule", "Last Status",
"R1", "R2", "R3", "R4", "R5",
"Tags", "Valid"};
ti.types = {
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::String, // Name
data_table::ColumnType::String, // Schedule
data_table::ColumnType::String, // Last Status
data_table::ColumnType::String, // R1
data_table::ColumnType::String, // R2
data_table::ColumnType::String, // R3
data_table::ColumnType::String, // R4
data_table::ColumnType::String, // R5
data_table::ColumnType::String, // Tags
data_table::ColumnType::String, // Valid
};
ti.rows = static_cast<int>(dags.size());
ti.cols = static_cast<int>(ti.headers.size());
// BadgeRule por status: misma config para R1..R5.
auto run_status_badges = [](){
std::vector<data_table::BadgeRule> rules;
rules.push_back({"success", "#22c55e", ""}); // verde
rules.push_back({"failed", "#ef4444", ""}); // rojo
rules.push_back({"running", "#eab308", ""}); // amarillo
rules.push_back({"pending", "#94a3b8", ""}); // gris azulado
rules.push_back({"cancelled", "#6b7280", ""}); // gris
rules.push_back({"-", "#1f2937", "·"}); // dot tenue cuando no hay run
return rules;
};
// ColumnSpec por columna. Solo R1..R5 (indices 3..7) son Badge.
ti.column_specs.resize(ti.cols);
for (int i = 0; i < ti.cols; i++) ti.column_specs[i].id = ti.headers[i];
for (int i = 3; i <= 7; i++) {
ti.column_specs[i].renderer = data_table::CellRenderer::Badge;
ti.column_specs[i].badges = run_status_badges();
}
g_back_dag_list.clear();
g_back_dag_list.reserve(dags.size() * ti.cols);
for (auto& d : dags) {
g_back_dag_list.push_back(d.name);
g_back_dag_list.push_back(d.schedule.empty() ? "-" : d.schedule[0]);
g_back_dag_list.push_back(status_for_dag(d.name, d, live_runs));
// R1..R5 — most recent first; "-" si menos de 5 runs.
for (int i = 0; i < 5; i++) {
if (i < static_cast<int>(d.last_runs_status.size())) {
g_back_dag_list.push_back(d.last_runs_status[i]);
} else {
g_back_dag_list.push_back("-");
}
}
std::string tags_csv;
for (size_t i = 0; i < d.tags.size(); i++) {
if (i) tags_csv += ",";
@@ -119,7 +163,6 @@ void draw_dag_list(const std::string& api_url,
}
g_back_dag_list.push_back(tags_csv);
g_back_dag_list.push_back(d.valid ? "yes" : "no");
g_back_dag_list.push_back(d.file_path);
}
cells_to_ptrs(g_back_dag_list, g_ptrs_dag_list);
ti.cells = g_ptrs_dag_list.data();