From 00a19d8632d264a84193fdc4530c071fa6756884 Mon Sep 17 00:00:00 2001 From: Egutierrez Date: Wed, 29 Apr 2026 00:58:17 +0200 Subject: [PATCH] =?UTF-8?q?feat(dashboard):=20v0.3.0=20=E2=80=94=20Status?= =?UTF-8?q?=20panel,=20dashboard=5Fpanel/grid,=20AppConfig=20pattern?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - main.cpp: bump 0.2.0 → 0.3.0; añadir Status section en Settings via settings_window_add_section (fuente API/SQLite, URL, DB path, Reload). - views.cpp: chart_panel manual + BeginChild custom → dashboard_panel/grid del registry. KPI cards con sparkline a la derecha, altura responsive. - views.h: signature de draw_kpi_row pasa RegistryData (para sparkline). - CMakeLists.txt: añadir process_state_machine.cpp (dependencia de process_runner tras issue 0045). --- CMakeLists.txt | 1 + main.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++-- views.cpp | 45 ++++++++++++++++++++++++++++++--------------- views.h | 2 +- 4 files changed, 75 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d52419e..f3e583d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,7 @@ add_imgui_app(registry_dashboard ${CMAKE_SOURCE_DIR}/functions/core/select.cpp ${CMAKE_SOURCE_DIR}/functions/core/toast.cpp ${CMAKE_SOURCE_DIR}/functions/core/process_runner.cpp + ${CMAKE_SOURCE_DIR}/functions/core/process_state_machine.cpp ${CMAKE_SOURCE_DIR}/functions/core/tree_view.cpp ) diff --git a/main.cpp b/main.cpp index c6a88ca..14d5dfb 100644 --- a/main.cpp +++ b/main.cpp @@ -3,6 +3,8 @@ #include "core/fullscreen_window.h" #include "core/app_menubar.h" #include "core/app_about.h" +#include "core/app_settings.h" +#include "core/tokens.h" #include "data.h" #include "data_http.h" #include "views.h" @@ -118,12 +120,51 @@ int main(int argc, char** argv) { // Info de la ventana About (submenu Settings → About...) fn_ui::about_window_set_info( "fn_registry Dashboard", - "0.2.0", + "0.3.0", "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." + "KPIs con sparkline, charts con leyenda, tablas, altura responsive, " + "Status panel en Settings, multi-viewport, dashboard_panel en views." ); + // Seccion Status dentro de la ventana Settings (submenu Settings → Settings...). + // Muestra fuente activa de datos, URL del API y path SQLite fallback. + fn_ui::settings_window_add_section("status", "Status", []{ + using namespace fn_tokens; + ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted); + ImGui::TextUnformatted("Source:"); + ImGui::PopStyleColor(); + ImGui::SameLine(); + if (g_loaded) { + ImGui::PushStyleColor(ImGuiCol_Text, colors::success); + ImGui::TextUnformatted(g_using_http ? "HTTP API (connected)" : "SQLite (direct)"); + ImGui::PopStyleColor(); + } else { + ImGui::PushStyleColor(ImGuiCol_Text, colors::error); + ImGui::TextUnformatted("not connected"); + ImGui::PopStyleColor(); + } + + if (!g_api_url.empty()) { + ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted); + ImGui::TextUnformatted("API:"); + ImGui::PopStyleColor(); + ImGui::SameLine(); + ImGui::TextUnformatted(g_api_url.c_str()); + } + if (!g_db_path.empty()) { + ImGui::PushStyleColor(ImGuiCol_Text, colors::text_muted); + ImGui::TextUnformatted("DB:"); + ImGui::PopStyleColor(); + ImGui::SameLine(); + ImGui::TextUnformatted(g_db_path.c_str()); + } + ImGui::Spacing(); + if (ImGui::Button("Reload")) { + ImGui::GetIO().UserData = reinterpret_cast(1); + } + }); + reload_data(); return fn::run_app( diff --git a/views.cpp b/views.cpp index a955f5a..ed278b3 100644 --- a/views.cpp +++ b/views.cpp @@ -9,9 +9,9 @@ #include "viz/pie_chart.h" #include "viz/table_view.h" #include "viz/sparkline.h" +#include "core/icons_tabler.h" #include "core/dashboard_panel.h" #include "core/dashboard_grid.h" -#include "core/fps_overlay.h" #include "core/fullscreen_window.h" #include "core/tokens.h" #include "core/page_header.h" @@ -68,7 +68,8 @@ static void trigger_reload() { // KPI row // --------------------------------------------------------------------------- -void draw_kpi_row(const RegistryStats& stats) { +void draw_kpi_row(const RegistryData& data) { + const RegistryStats& stats = data.stats; float tested_pct = stats.total_functions > 0 ? 100.0f * stats.tested_functions / stats.total_functions : 0.0f; float pure_pct = stats.total_functions > 0 @@ -77,22 +78,30 @@ void draw_kpi_row(const RegistryStats& stats) { const ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_NoPadOuterX; + // Sparkline shared: ultimos 30 dias de creacion de funciones (timeline real + // del registry). Si no hay datos cargados, queda vacio y el card mostrara + // solo valor + delta placeholder. + const float* spark_data = data.date_values.empty() ? nullptr : data.date_values.data(); + const int spark_count = static_cast(data.date_values.size()); + if (ImGui::BeginTable("##kpi_grid", 4, flags)) { - struct KPI { const char* label; float value; const char* fmt; }; + struct KPI { const char* label; float value; const char* fmt; const char* icon; }; const KPI cards[8] = { - {"Functions", static_cast(stats.total_functions), "%.0f"}, - {"Types", static_cast(stats.total_types), "%.0f"}, - {"Apps", static_cast(stats.total_apps), "%.0f"}, - {"Analysis", static_cast(stats.total_analysis), "%.0f"}, - {"Unit Tests", static_cast(stats.total_unit_tests), "%.0f"}, - {"Proposals", static_cast(stats.total_proposals), "%.0f"}, - {"Tested", tested_pct, "%.0f%%"}, - {"Pure", pure_pct, "%.0f%%"}, + {"Functions", static_cast(stats.total_functions), "%.0f", TI_FUNCTION}, + {"Types", static_cast(stats.total_types), "%.0f", TI_HEXAGON}, + {"Apps", static_cast(stats.total_apps), "%.0f", TI_APPS}, + {"Analysis", static_cast(stats.total_analysis), "%.0f", TI_FLASK}, + {"Unit Tests", static_cast(stats.total_unit_tests), "%.0f", TI_TEST_PIPE}, + {"Proposals", static_cast(stats.total_proposals), "%.0f", TI_BULB}, + {"Tested", tested_pct, "%.0f%%", TI_CIRCLE_CHECK}, + {"Pure", pure_pct, "%.0f%%", TI_HEART}, }; for (int i = 0; i < 8; i++) { if (i % 4 == 0) ImGui::TableNextRow(); ImGui::TableSetColumnIndex(i % 4); - kpi_card(cards[i].label, cards[i].value, 0.0f, nullptr, 0, cards[i].fmt); + kpi_card(cards[i].label, cards[i].value, 0.0f, + spark_data, spark_count, + cards[i].fmt, cards[i].icon); } ImGui::EndTable(); } @@ -587,8 +596,8 @@ static void draw_actions_bar() { void draw_dashboard(RegistryData& data) { // Tema aplicado por fn::run_app() (app_base.h, ThemeMode::FnDark default). + // FPS overlay lo dibuja app_base.cpp segun settings().show_fps — no llamarlo aqui. - fps_overlay(); fullscreen_window_begin("##dashboard"); char subtitle[128]; @@ -604,10 +613,16 @@ void draw_dashboard(RegistryData& data) { draw_actions_bar(); ImGui::Dummy(ImVec2(0, fn_tokens::spacing::sm)); - draw_kpi_row(data.stats); + draw_kpi_row(data); ImGui::Dummy(ImVec2(0, fn_tokens::spacing::md)); - constexpr float chart_h = 260.0f; + // Altura del bloque de charts proporcional al espacio disponible: ~28% del + // espacio restante despues de KPIs/header/tabs (clamped a [200, 360] px + // para que ni se aplaste ni domine en pantallas grandes). + const float remaining_h = ImGui::GetContentRegionAvail().y; + float chart_h = remaining_h * 0.32f; + if (chart_h < 200.0f) chart_h = 200.0f; + if (chart_h > 360.0f) chart_h = 360.0f; draw_charts(data, chart_h); ImGui::Dummy(ImVec2(0, fn_tokens::spacing::md)); diff --git a/views.h b/views.h index 572e800..125bd95 100644 --- a/views.h +++ b/views.h @@ -11,7 +11,7 @@ void draw_dashboard(RegistryData& data); void views_set_api_url(const std::string& url); // Individual views (called by draw_dashboard) -void draw_kpi_row(const RegistryStats& stats); +void draw_kpi_row(const RegistryData& data); void draw_charts(RegistryData& data, float height = 250.0f); void draw_recent_functions(const std::vector& funcs); void draw_apps_list(const std::vector& apps);