feat(dashboard): v0.3.0 — Status panel, dashboard_panel/grid, AppConfig pattern
- 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).
This commit is contained in:
@@ -41,6 +41,7 @@ add_imgui_app(registry_dashboard
|
|||||||
${CMAKE_SOURCE_DIR}/functions/core/select.cpp
|
${CMAKE_SOURCE_DIR}/functions/core/select.cpp
|
||||||
${CMAKE_SOURCE_DIR}/functions/core/toast.cpp
|
${CMAKE_SOURCE_DIR}/functions/core/toast.cpp
|
||||||
${CMAKE_SOURCE_DIR}/functions/core/process_runner.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
|
${CMAKE_SOURCE_DIR}/functions/core/tree_view.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@
|
|||||||
#include "core/fullscreen_window.h"
|
#include "core/fullscreen_window.h"
|
||||||
#include "core/app_menubar.h"
|
#include "core/app_menubar.h"
|
||||||
#include "core/app_about.h"
|
#include "core/app_about.h"
|
||||||
|
#include "core/app_settings.h"
|
||||||
|
#include "core/tokens.h"
|
||||||
#include "data.h"
|
#include "data.h"
|
||||||
#include "data_http.h"
|
#include "data_http.h"
|
||||||
#include "views.h"
|
#include "views.h"
|
||||||
@@ -118,12 +120,51 @@ int main(int argc, char** argv) {
|
|||||||
// Info de la ventana About (submenu Settings → About...)
|
// Info de la ventana About (submenu Settings → About...)
|
||||||
fn_ui::about_window_set_info(
|
fn_ui::about_window_set_info(
|
||||||
"fn_registry Dashboard",
|
"fn_registry Dashboard",
|
||||||
"0.2.0",
|
"0.3.0",
|
||||||
"Dashboard ImGui para visualizar el estado del fn_registry. "
|
"Dashboard ImGui para visualizar el estado del fn_registry. "
|
||||||
"Consume datos via sqlite_api HTTP (fallback a SQLite directo). "
|
"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<void*>(1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
reload_data();
|
reload_data();
|
||||||
|
|
||||||
return fn::run_app(
|
return fn::run_app(
|
||||||
|
|||||||
@@ -9,9 +9,9 @@
|
|||||||
#include "viz/pie_chart.h"
|
#include "viz/pie_chart.h"
|
||||||
#include "viz/table_view.h"
|
#include "viz/table_view.h"
|
||||||
#include "viz/sparkline.h"
|
#include "viz/sparkline.h"
|
||||||
|
#include "core/icons_tabler.h"
|
||||||
#include "core/dashboard_panel.h"
|
#include "core/dashboard_panel.h"
|
||||||
#include "core/dashboard_grid.h"
|
#include "core/dashboard_grid.h"
|
||||||
#include "core/fps_overlay.h"
|
|
||||||
#include "core/fullscreen_window.h"
|
#include "core/fullscreen_window.h"
|
||||||
#include "core/tokens.h"
|
#include "core/tokens.h"
|
||||||
#include "core/page_header.h"
|
#include "core/page_header.h"
|
||||||
@@ -68,7 +68,8 @@ static void trigger_reload() {
|
|||||||
// KPI row
|
// 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
|
float tested_pct = stats.total_functions > 0
|
||||||
? 100.0f * stats.tested_functions / stats.total_functions : 0.0f;
|
? 100.0f * stats.tested_functions / stats.total_functions : 0.0f;
|
||||||
float pure_pct = stats.total_functions > 0
|
float pure_pct = stats.total_functions > 0
|
||||||
@@ -77,22 +78,30 @@ void draw_kpi_row(const RegistryStats& stats) {
|
|||||||
const ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame
|
const ImGuiTableFlags flags = ImGuiTableFlags_SizingStretchSame
|
||||||
| ImGuiTableFlags_NoPadOuterX;
|
| 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<int>(data.date_values.size());
|
||||||
|
|
||||||
if (ImGui::BeginTable("##kpi_grid", 4, flags)) {
|
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] = {
|
const KPI cards[8] = {
|
||||||
{"Functions", static_cast<float>(stats.total_functions), "%.0f"},
|
{"Functions", static_cast<float>(stats.total_functions), "%.0f", TI_FUNCTION},
|
||||||
{"Types", static_cast<float>(stats.total_types), "%.0f"},
|
{"Types", static_cast<float>(stats.total_types), "%.0f", TI_HEXAGON},
|
||||||
{"Apps", static_cast<float>(stats.total_apps), "%.0f"},
|
{"Apps", static_cast<float>(stats.total_apps), "%.0f", TI_APPS},
|
||||||
{"Analysis", static_cast<float>(stats.total_analysis), "%.0f"},
|
{"Analysis", static_cast<float>(stats.total_analysis), "%.0f", TI_FLASK},
|
||||||
{"Unit Tests", static_cast<float>(stats.total_unit_tests), "%.0f"},
|
{"Unit Tests", static_cast<float>(stats.total_unit_tests), "%.0f", TI_TEST_PIPE},
|
||||||
{"Proposals", static_cast<float>(stats.total_proposals), "%.0f"},
|
{"Proposals", static_cast<float>(stats.total_proposals), "%.0f", TI_BULB},
|
||||||
{"Tested", tested_pct, "%.0f%%"},
|
{"Tested", tested_pct, "%.0f%%", TI_CIRCLE_CHECK},
|
||||||
{"Pure", pure_pct, "%.0f%%"},
|
{"Pure", pure_pct, "%.0f%%", TI_HEART},
|
||||||
};
|
};
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
if (i % 4 == 0) ImGui::TableNextRow();
|
if (i % 4 == 0) ImGui::TableNextRow();
|
||||||
ImGui::TableSetColumnIndex(i % 4);
|
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();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
@@ -587,8 +596,8 @@ static void draw_actions_bar() {
|
|||||||
|
|
||||||
void draw_dashboard(RegistryData& data) {
|
void draw_dashboard(RegistryData& data) {
|
||||||
// Tema aplicado por fn::run_app() (app_base.h, ThemeMode::FnDark default).
|
// 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");
|
fullscreen_window_begin("##dashboard");
|
||||||
|
|
||||||
char subtitle[128];
|
char subtitle[128];
|
||||||
@@ -604,10 +613,16 @@ void draw_dashboard(RegistryData& data) {
|
|||||||
draw_actions_bar();
|
draw_actions_bar();
|
||||||
ImGui::Dummy(ImVec2(0, fn_tokens::spacing::sm));
|
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));
|
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);
|
draw_charts(data, chart_h);
|
||||||
ImGui::Dummy(ImVec2(0, fn_tokens::spacing::md));
|
ImGui::Dummy(ImVec2(0, fn_tokens::spacing::md));
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ void draw_dashboard(RegistryData& data);
|
|||||||
void views_set_api_url(const std::string& url);
|
void views_set_api_url(const std::string& url);
|
||||||
|
|
||||||
// Individual views (called by draw_dashboard)
|
// 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_charts(RegistryData& data, float height = 250.0f);
|
||||||
void draw_recent_functions(const std::vector<FunctionRow>& funcs);
|
void draw_recent_functions(const std::vector<FunctionRow>& funcs);
|
||||||
void draw_apps_list(const std::vector<AppRow>& apps);
|
void draw_apps_list(const std::vector<AppRow>& apps);
|
||||||
|
|||||||
Reference in New Issue
Block a user