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 3b4e44a..488ffb3 100644 Binary files a/appicon.ico and b/appicon.ico differ 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(); } }