From ff67e4e069e6dec5957d2091fa195d3ff6d3c697 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Tue, 19 May 2026 00:31:34 +0200 Subject: [PATCH] chore: auto-commit (4 archivos) - app.md - appicon.ico - views.cpp - work_tab.cpp Co-Authored-By: Claude Opus 4.7 (1M context) --- app.md | 23 +++---- appicon.ico | Bin 11953 -> 13804 bytes views.cpp | 5 ++ work_tab.cpp | 187 ++++++++++++++++++++++++++++++++++----------------- 4 files changed, 143 insertions(+), 72 deletions(-) diff --git a/app.md b/app.md index 27362fe..26d17e8 100644 --- a/app.md +++ b/app.md @@ -2,6 +2,7 @@ name: registry_dashboard lang: cpp domain: tui +version: 0.1.0 description: "Dashboard ImGui para visualizar el estado del fn_registry. Consume datos via sqlite_api HTTP (fallback a SQLite directo). KPIs, charts, tablas, desglose por lenguaje/dominio/pureza." tags: [dashboard, imgui, visualization, registry, http] uses_functions: @@ -11,18 +12,6 @@ uses_functions: - pie_chart_cpp_viz - table_view_cpp_viz - sparkline_cpp_viz - # data_table stack (issue 0081-J) - - data_table_cpp_viz - - viz_render_cpp_viz - - compute_stage_cpp_core - - compute_pipeline_cpp_core - - tql_emit_cpp_core - - tql_apply_cpp_core - - tql_to_sql_cpp_core - - lua_engine_cpp_core - - join_tables_cpp_core - - auto_detect_type_cpp_core - - compute_column_stats_cpp_core # core (dashboard primitives) - dashboard_panel_cpp_core - dashboard_grid_cpp_core @@ -214,3 +203,13 @@ Decisiones de scope: - `fn_ui::app_menubar` reemplaza el item plano `Settings...` por un `BeginMenu("Settings")` con dos subitems: `Settings...` (existente) y `About...` (nuevo modulo `app_about_cpp_core`). El registry_dashboard cablea la info via `fn_ui::about_window_set_info("fn_registry Dashboard", "0.2.0", "Dashboard ImGui...")` antes de `fn::run_app`. - Tabla `Apps` gana columna **Git**: `remote` si `repo_url` esta poblado en `apps.repo_url`, `local` si existe `/.git/`, `-` si nada. `AppRow` extendido con `repo_url` y `dir_path`; SELECT en `data.cpp` y `data_http.cpp` ampliado a 8 columnas. - Build OK: `cmake --build build --target registry_dashboard` (Linux). La columna "Git" se ve sin reindexar. + + +## Capability growth log + +Una linea por bump SemVer. Bump-type segun `.claude/commands/version.md`: +- `major`: breaking observable (CLI args, schema BBDD propia, formato wire). +- `minor`: feature aditiva (nuevo panel, endpoint, opcion). +- `patch`: bugfix sin cambio observable. + +- v0.1.0 (2026-05-18) — baseline. diff --git a/appicon.ico b/appicon.ico index 3b4e44ac15fabfb5ca3792b9793562335acb3d7f..488ffb3d7c7af87cd42d6929738f6062a298b10c 100644 GIT binary patch literal 13804 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 diff --git a/views.cpp b/views.cpp index a14715d..d9050d6 100644 --- a/views.cpp +++ b/views.cpp @@ -377,6 +377,7 @@ void draw_kpi_row(const RegistryData& data) { const float* spark_data = data.date_values.empty() ? nullptr : data.date_values.data(); const int spark_count = static_cast(data.date_values.size()); + // LAYOUT-TABLE — KPI/form/splitter, no data; keep BeginTable inline. if (ImGui::BeginTable("##kpi_grid", 4, flags)) { struct KPI { const char* label; float value; const char* fmt; const char* icon; }; const KPI cards[8] = { @@ -433,6 +434,7 @@ void draw_charts(RegistryData& data, float height) { | ImGuiTableFlags_NoPadOuterX; const float plot_h = height - 48.0f; + // LAYOUT-TABLE — KPI/form/splitter, no data; keep BeginTable inline. if (ImGui::BeginTable("##chart_grid", 4, flags)) { ImGui::TableNextRow(); @@ -644,6 +646,7 @@ void draw_monitor(RegistryData& data) { // 7 KPI cards: Calls / MCP / Reg% / Errors / Violations / Copies / Versions // "MCP" = calls Claude lanza via tools registry-aware (mcp / fn_cli_run / // heredoc). "Reg %" = porcentaje del total con function_id no vacio. + // LAYOUT-TABLE — KPI/form/splitter, no data; keep BeginTable inline. const ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_NoPadOuterX; if (ImGui::BeginTable("##monitor_kpi", 7, flags)) { struct KPI { const char* label; float value; const char* icon; const char* fmt; }; @@ -1105,6 +1108,7 @@ void draw_projects_list(RegistryData& data) { } // Dos columnas: izquierda arbol, derecha detalle. + // LAYOUT-TABLE — KPI/form/splitter, no data; keep BeginTable inline. const ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingStretchProp; if (!ImGui::BeginTable("##proj_layout", 2, flags)) return; @@ -1443,6 +1447,7 @@ void draw_functions_explorer() { return; } + // LAYOUT-TABLE — KPI/form/splitter, no data; keep BeginTable inline. const ImGuiTableFlags flags = ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingStretchProp; if (!ImGui::BeginTable("##explorer_layout", 2, flags)) return; diff --git a/work_tab.cpp b/work_tab.cpp index 2aa7172..1f5d02f 100644 --- a/work_tab.cpp +++ b/work_tab.cpp @@ -8,6 +8,8 @@ #include "core/page_header.h" #include "core/empty_state.h" #include "core/badge.h" +#include "data_table/data_table.h" +#include "core/data_table_types.h" #include "nlohmann/json.hpp" #include @@ -231,37 +233,64 @@ void draw_work_tab() { ImGui::Separator(); ImGui::Spacing(); - // Flows table + // Flows table — migrado a data_table::render (issue 0107g) ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::text_muted); ImGui::TextUnformatted("Flows"); ImGui::PopStyleColor(); - if (ImGui::BeginTable("##flows_work", 8, - ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | - ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_Resizable)) { - ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 50.0f); - ImGui::TableSetupColumn("Name"); - ImGui::TableSetupColumn("Pattern", ImGuiTableColumnFlags_WidthFixed, 110.0f); - ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("Risk", ImGuiTableColumnFlags_WidthFixed, 70.0f); - ImGui::TableSetupColumn("Accept", ImGuiTableColumnFlags_WidthFixed, 60.0f); - ImGui::TableSetupColumn("DoD", ImGuiTableColumnFlags_WidthFixed, 60.0f); - ImGui::TableSetupColumn("UserFace", ImGuiTableColumnFlags_WidthFixed, 70.0f); - ImGui::TableHeadersRow(); + { + static data_table::State g_st_flows; + static std::vector g_back_flows; + static std::vector g_ptrs_flows; - std::string buf; - for (auto& f : g_data.flows) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); ImGui::TextUnformatted(f.id.c_str()); - ImGui::TableNextColumn(); ImGui::TextUnformatted(f.name.c_str()); - ImGui::TableNextColumn(); ImGui::TextUnformatted(f.pattern.empty() ? "-" : f.pattern.c_str()); - ImGui::TableNextColumn(); ImGui::TextUnformatted(f.status.c_str()); - ImGui::TableNextColumn(); ImGui::TextUnformatted(f.risk.c_str()); - ImGui::TableNextColumn(); ImGui::Text("%d%%", f.acceptance_pct); - ImGui::TableNextColumn(); ImGui::Text("%d%%", f.dod_pct); - ImGui::TableNextColumn(); ImGui::Text("%d%%", f.user_facing_pct); + g_back_flows.clear(); + std::string tmp_buf; + for (const auto& f : g_data.flows) { + g_back_flows.push_back(f.id); + g_back_flows.push_back(f.name); + g_back_flows.push_back(f.pattern.empty() ? "-" : f.pattern); + g_back_flows.push_back(f.status); + g_back_flows.push_back(f.risk); + g_back_flows.push_back(std::to_string(f.acceptance_pct) + "%"); + g_back_flows.push_back(std::to_string(f.dod_pct) + "%"); + g_back_flows.push_back(std::to_string(f.user_facing_pct) + "%"); } - ImGui::EndTable(); + g_ptrs_flows.clear(); + for (const auto& s : g_back_flows) g_ptrs_flows.push_back(s.c_str()); + + data_table::TableInput tbl; + tbl.name = "flows_work"; + tbl.headers = {"ID", "Name", "Pattern", "Status", "Risk", "Accept", "DoD", "UserFace"}; + tbl.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, data_table::ColumnType::String, + }; + tbl.cells = g_ptrs_flows.empty() ? nullptr : g_ptrs_flows.data(); + tbl.rows = (int)g_data.flows.size(); + tbl.cols = 8; + + tbl.column_specs.resize(tbl.cols); + for (int i = 0; i < tbl.cols; i++) tbl.column_specs[i].id = tbl.headers[i]; + // Status → CategoricalChip + tbl.column_specs[3].renderer = data_table::CellRenderer::CategoricalChip; + tbl.column_specs[3].chips = { + {"activo", "#22c55e"}, {"done", "#22c55e"}, + {"in-progress","#f59e0b"}, {"bloqueado","#ef4444"}, + {"pendiente", "#a3a3a3"}, + }; + // Risk → CategoricalChip + tbl.column_specs[4].renderer = data_table::CellRenderer::CategoricalChip; + tbl.column_specs[4].chips = { + {"alta", "#ef4444"}, {"high", "#ef4444"}, + {"media", "#f59e0b"}, {"medium", "#f59e0b"}, + {"baja", "#22c55e"}, {"low", "#22c55e"}, + }; + + ImGui::BeginChild("##flows_work_host", ImVec2(-1, 220)); + data_table::render("##flows_work_dt", {tbl}, g_st_flows, nullptr); + ImGui::EndChild(); } ImGui::Spacing(); @@ -269,43 +298,81 @@ void draw_work_tab() { ImGui::TextUnformatted("Top issues (priority alta, not done)"); ImGui::PopStyleColor(); - if (ImGui::BeginTable("##top_issues_work", 7, - ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders | - ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_Resizable)) { - ImGui::TableSetupColumn("ID", ImGuiTableColumnFlags_WidthFixed, 70.0f); - ImGui::TableSetupColumn("Title"); - ImGui::TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 80.0f); - ImGui::TableSetupColumn("Domain", ImGuiTableColumnFlags_WidthFixed, 140.0f); - ImGui::TableSetupColumn("Status", ImGuiTableColumnFlags_WidthFixed, 90.0f); - ImGui::TableSetupColumn("Deps", ImGuiTableColumnFlags_WidthFixed, 110.0f); - ImGui::TableSetupColumn("Prio", ImGuiTableColumnFlags_WidthFixed, 60.0f); - ImGui::TableHeadersRow(); + // Top issues table — migrado a data_table::render (issue 0107g) + // Nota: la columna Deps original tenia colores condicionales (verde/amber segun deps_resolved). + // Mapeado a CategoricalChip: "-" (gris), "OK" (verde), "blocked" (amber). + { + static data_table::State g_st_issues; + static std::vector g_back_issues; + static std::vector g_ptrs_issues; - std::string buf; - for (auto& i : g_data.top_issues) { - ImGui::TableNextRow(); - ImGui::TableNextColumn(); ImGui::TextUnformatted(i.id.c_str()); - ImGui::TableNextColumn(); ImGui::TextUnformatted(i.title.c_str()); - ImGui::TableNextColumn(); ImGui::TextUnformatted(i.type.c_str()); - ImGui::TableNextColumn(); ImGui::TextUnformatted(join_domain(i.domain, buf)); - ImGui::TableNextColumn(); ImGui::TextUnformatted(i.status.c_str()); - ImGui::TableNextColumn(); - if (i.depends.empty()) { - ImGui::TextUnformatted("-"); - } else if (i.deps_resolved) { - ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::success); - ImGui::TextUnformatted("OK"); - ImGui::PopStyleColor(); - } else { - ImGui::PushStyleColor(ImGuiCol_Text, fn_tokens::colors::warning); - ImGui::Text("blocked"); - ImGui::PopStyleColor(); + g_back_issues.clear(); + std::string dom_buf; + for (const auto& iss : g_data.top_issues) { + g_back_issues.push_back(iss.id); + g_back_issues.push_back(iss.title); + g_back_issues.push_back(iss.type); + // domain: join con coma + dom_buf.clear(); + for (size_t k = 0; k < iss.domain.size(); ++k) { + if (k) dom_buf += ","; + dom_buf += iss.domain[k]; } - ImGui::TableNextColumn(); - ImGui::PushStyleColor(ImGuiCol_Text, prio_color(i.priority)); - ImGui::TextUnformatted(i.priority.c_str()); - ImGui::PopStyleColor(); + g_back_issues.push_back(dom_buf); + g_back_issues.push_back(iss.status); + // deps: mostrar "-" / "OK" / "blocked" + if (iss.depends.empty()) { + g_back_issues.push_back("-"); + } else if (iss.deps_resolved) { + g_back_issues.push_back("OK"); + } else { + g_back_issues.push_back("blocked"); + } + g_back_issues.push_back(iss.priority); } - ImGui::EndTable(); + g_ptrs_issues.clear(); + for (const auto& s : g_back_issues) g_ptrs_issues.push_back(s.c_str()); + + data_table::TableInput tbl; + tbl.name = "top_issues_work"; + tbl.headers = {"ID", "Title", "Type", "Domain", "Status", "Deps", "Prio"}; + tbl.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, + }; + tbl.cells = g_ptrs_issues.empty() ? nullptr : g_ptrs_issues.data(); + tbl.rows = (int)g_data.top_issues.size(); + tbl.cols = 7; + + tbl.column_specs.resize(tbl.cols); + for (int i = 0; i < tbl.cols; i++) tbl.column_specs[i].id = tbl.headers[i]; + // Status → CategoricalChip + tbl.column_specs[4].renderer = data_table::CellRenderer::CategoricalChip; + tbl.column_specs[4].chips = { + {"pendiente", "#a3a3a3"}, + {"in-progress", "#f59e0b"}, + {"bloqueado", "#ef4444"}, + {"completado", "#22c55e"}, + }; + // Deps → CategoricalChip (verde OK / amber blocked / gris -) + tbl.column_specs[5].renderer = data_table::CellRenderer::CategoricalChip; + tbl.column_specs[5].chips = { + {"OK", "#22c55e"}, + {"blocked", "#f59e0b"}, + {"-", "#a3a3a3"}, + }; + // Prio → CategoricalChip + tbl.column_specs[6].renderer = data_table::CellRenderer::CategoricalChip; + tbl.column_specs[6].chips = { + {"alta", "#ef4444"}, {"high", "#ef4444"}, + {"media", "#f59e0b"}, {"medium", "#f59e0b"}, + {"baja", "#22c55e"}, {"low", "#22c55e"}, + }; + + ImGui::BeginChild("##top_issues_work_host", ImVec2(-1, -1)); + data_table::render("##top_issues_work_dt", {tbl}, g_st_issues, nullptr); + ImGui::EndChild(); } }