chore: auto-commit (4 archivos)
- app.md - appicon.ico - views.cpp - work_tab.cpp Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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 `<dir_path>/.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.
|
||||
|
||||
BIN
Binary file not shown.
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 14 KiB |
@@ -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<int>(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;
|
||||
|
||||
+127
-60
@@ -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 <chrono>
|
||||
@@ -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<std::string> g_back_flows;
|
||||
static std::vector<const char*> 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<std::string> g_back_issues;
|
||||
static std::vector<const char*> 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();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user