feat: layout responsivo y KPIs adicionales en dashboard
Grid de charts pasa de 2 a 4 columnas con altura dinamica (35% del espacio disponible). Se eliminan los paneles Activity y Extras, moviendo Unit Tests y Proposals a la fila de KPIs (ahora 8 cards). Se reduce spacing entre secciones para aprovechar mejor la pantalla. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -23,13 +23,11 @@ static std::vector<const char*> to_cstr(const std::vector<std::string>& v) {
|
|||||||
|
|
||||||
void draw_kpi_row(const RegistryStats& stats) {
|
void draw_kpi_row(const RegistryStats& stats) {
|
||||||
float tested_pct = stats.total_functions > 0
|
float tested_pct = stats.total_functions > 0
|
||||||
? 100.0f * stats.tested_functions / stats.total_functions
|
? 100.0f * stats.tested_functions / stats.total_functions : 0.0f;
|
||||||
: 0.0f;
|
|
||||||
float pure_pct = stats.total_functions > 0
|
float pure_pct = stats.total_functions > 0
|
||||||
? 100.0f * stats.pure_functions / stats.total_functions
|
? 100.0f * stats.pure_functions / stats.total_functions : 0.0f;
|
||||||
: 0.0f;
|
|
||||||
|
|
||||||
dashboard_grid_begin(6, 12.0f);
|
dashboard_grid_begin(8, 8.0f);
|
||||||
|
|
||||||
kpi_card("Functions", static_cast<float>(stats.total_functions), 0, nullptr, 0, "%.0f");
|
kpi_card("Functions", static_cast<float>(stats.total_functions), 0, nullptr, 0, "%.0f");
|
||||||
dashboard_grid_next();
|
dashboard_grid_next();
|
||||||
@@ -39,17 +37,21 @@ void draw_kpi_row(const RegistryStats& stats) {
|
|||||||
dashboard_grid_next();
|
dashboard_grid_next();
|
||||||
kpi_card("Analysis", static_cast<float>(stats.total_analysis), 0, nullptr, 0, "%.0f");
|
kpi_card("Analysis", static_cast<float>(stats.total_analysis), 0, nullptr, 0, "%.0f");
|
||||||
dashboard_grid_next();
|
dashboard_grid_next();
|
||||||
kpi_card("Tested %", tested_pct, 0, nullptr, 0, "%.1f%%");
|
kpi_card("Unit Tests", static_cast<float>(stats.total_unit_tests), 0, nullptr, 0, "%.0f");
|
||||||
dashboard_grid_next();
|
dashboard_grid_next();
|
||||||
kpi_card("Pure %", pure_pct, 0, nullptr, 0, "%.1f%%");
|
kpi_card("Proposals", static_cast<float>(stats.total_proposals), 0, nullptr, 0, "%.0f");
|
||||||
|
dashboard_grid_next();
|
||||||
|
kpi_card("Tested", tested_pct, 0, nullptr, 0, "%.0f%%");
|
||||||
|
dashboard_grid_next();
|
||||||
|
kpi_card("Pure", pure_pct, 0, nullptr, 0, "%.0f%%");
|
||||||
|
|
||||||
dashboard_grid_end();
|
dashboard_grid_end();
|
||||||
}
|
}
|
||||||
|
|
||||||
void draw_charts(RegistryData& data) {
|
void draw_charts(RegistryData& data, float height) {
|
||||||
dashboard_grid_begin(2, 12.0f);
|
dashboard_grid_begin(4, 8.0f);
|
||||||
|
|
||||||
if (dashboard_panel_begin("Functions by Language", 300, 280)) {
|
if (dashboard_panel_begin("By Language", 0, height)) {
|
||||||
auto labels = to_cstr(data.lang_labels);
|
auto labels = to_cstr(data.lang_labels);
|
||||||
if (!labels.empty())
|
if (!labels.empty())
|
||||||
bar_chart("##lang", labels.data(), data.lang_values.data(), static_cast<int>(labels.size()));
|
bar_chart("##lang", labels.data(), data.lang_values.data(), static_cast<int>(labels.size()));
|
||||||
@@ -57,7 +59,7 @@ void draw_charts(RegistryData& data) {
|
|||||||
dashboard_panel_end();
|
dashboard_panel_end();
|
||||||
dashboard_grid_next();
|
dashboard_grid_next();
|
||||||
|
|
||||||
if (dashboard_panel_begin("Functions by Domain", 300, 280)) {
|
if (dashboard_panel_begin("By Domain", 0, height)) {
|
||||||
auto labels = to_cstr(data.domain_labels);
|
auto labels = to_cstr(data.domain_labels);
|
||||||
if (!labels.empty())
|
if (!labels.empty())
|
||||||
bar_chart("##domain", labels.data(), data.domain_values.data(), static_cast<int>(labels.size()));
|
bar_chart("##domain", labels.data(), data.domain_values.data(), static_cast<int>(labels.size()));
|
||||||
@@ -65,43 +67,21 @@ void draw_charts(RegistryData& data) {
|
|||||||
dashboard_panel_end();
|
dashboard_panel_end();
|
||||||
dashboard_grid_next();
|
dashboard_grid_next();
|
||||||
|
|
||||||
if (dashboard_panel_begin("Purity", 250, 280)) {
|
if (dashboard_panel_begin("Purity", 0, height)) {
|
||||||
const char* labels[] = {"Pure", "Impure"};
|
const char* labels[] = {"Pure", "Impure"};
|
||||||
float values[] = {static_cast<float>(data.stats.pure_functions), static_cast<float>(data.stats.impure_functions)};
|
float values[] = {static_cast<float>(data.stats.pure_functions),
|
||||||
|
static_cast<float>(data.stats.impure_functions)};
|
||||||
pie_chart("##purity", labels, values, 2);
|
pie_chart("##purity", labels, values, 2);
|
||||||
}
|
}
|
||||||
dashboard_panel_end();
|
dashboard_panel_end();
|
||||||
dashboard_grid_next();
|
dashboard_grid_next();
|
||||||
|
|
||||||
if (dashboard_panel_begin("Kind", 250, 280)) {
|
if (dashboard_panel_begin("Kind", 0, height)) {
|
||||||
auto labels = to_cstr(data.kind_labels);
|
auto labels = to_cstr(data.kind_labels);
|
||||||
if (!labels.empty())
|
if (!labels.empty())
|
||||||
pie_chart("##kind", labels.data(), data.kind_values.data(), static_cast<int>(labels.size()));
|
pie_chart("##kind", labels.data(), data.kind_values.data(), static_cast<int>(labels.size()));
|
||||||
}
|
}
|
||||||
dashboard_panel_end();
|
dashboard_panel_end();
|
||||||
dashboard_grid_next();
|
|
||||||
|
|
||||||
if (dashboard_panel_begin("Activity (last 30 days)", 600, 280)) {
|
|
||||||
auto labels = to_cstr(data.date_labels);
|
|
||||||
if (!labels.empty())
|
|
||||||
bar_chart("##dates", labels.data(), data.date_values.data(), static_cast<int>(labels.size()));
|
|
||||||
else
|
|
||||||
ImGui::TextDisabled("No functions created in the last 30 days");
|
|
||||||
}
|
|
||||||
dashboard_panel_end();
|
|
||||||
dashboard_grid_next();
|
|
||||||
|
|
||||||
if (dashboard_panel_begin("Extras", 250, 280)) {
|
|
||||||
kpi_card("Unit Tests", static_cast<float>(data.stats.total_unit_tests), 0, nullptr, 0, "%.0f");
|
|
||||||
ImGui::Spacing();
|
|
||||||
kpi_card("Proposals", static_cast<float>(data.stats.total_proposals), 0, nullptr, 0, "%.0f");
|
|
||||||
ImGui::Spacing();
|
|
||||||
const char* t_labels[] = {"Tested", "Untested"};
|
|
||||||
float t_values[] = {static_cast<float>(data.stats.tested_functions),
|
|
||||||
static_cast<float>(data.stats.total_functions - data.stats.tested_functions)};
|
|
||||||
pie_chart("##tested", t_labels, t_values, 2);
|
|
||||||
}
|
|
||||||
dashboard_panel_end();
|
|
||||||
|
|
||||||
dashboard_grid_end();
|
dashboard_grid_end();
|
||||||
}
|
}
|
||||||
@@ -172,34 +152,30 @@ void draw_types_list(const std::vector<TypeRow>& types) {
|
|||||||
|
|
||||||
void draw_dashboard(RegistryData& data) {
|
void draw_dashboard(RegistryData& data) {
|
||||||
fps_overlay();
|
fps_overlay();
|
||||||
|
|
||||||
fullscreen_window_begin("##dashboard");
|
fullscreen_window_begin("##dashboard");
|
||||||
|
|
||||||
// Header
|
float win_h = ImGui::GetContentRegionAvail().y;
|
||||||
|
|
||||||
|
// Row 1: Header + KPIs (~80px)
|
||||||
ImGui::Text("fn_registry Dashboard");
|
ImGui::Text("fn_registry Dashboard");
|
||||||
ImGui::SameLine(ImGui::GetWindowWidth() - 100);
|
ImGui::SameLine(ImGui::GetWindowWidth() - 100);
|
||||||
if (ImGui::Button("Reload")) {
|
if (ImGui::Button("Reload")) {
|
||||||
ImGui::GetIO().UserData = reinterpret_cast<void*>(1);
|
ImGui::GetIO().UserData = reinterpret_cast<void*>(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Spacing();
|
|
||||||
|
|
||||||
// KPIs
|
|
||||||
draw_kpi_row(data.stats);
|
draw_kpi_row(data.stats);
|
||||||
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Spacing();
|
|
||||||
|
|
||||||
// Charts
|
// Row 2: Charts — take 35% of remaining height
|
||||||
draw_charts(data);
|
float remaining = ImGui::GetContentRegionAvail().y;
|
||||||
|
float chart_h = remaining * 0.35f;
|
||||||
|
if (chart_h < 150.0f) chart_h = 150.0f;
|
||||||
|
|
||||||
ImGui::Spacing();
|
draw_charts(data, chart_h);
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
ImGui::Spacing();
|
|
||||||
|
|
||||||
// Tables in tabs
|
// Row 3: Tables — fill the rest
|
||||||
if (ImGui::BeginTabBar("##tables")) {
|
if (ImGui::BeginTabBar("##tables")) {
|
||||||
if (ImGui::BeginTabItem("Recent Functions")) {
|
if (ImGui::BeginTabItem("Recent Functions")) {
|
||||||
draw_recent_functions(data.recent_funcs);
|
draw_recent_functions(data.recent_funcs);
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ void draw_dashboard(RegistryData& data);
|
|||||||
|
|
||||||
// Individual views (called by draw_dashboard)
|
// Individual views (called by draw_dashboard)
|
||||||
void draw_kpi_row(const RegistryStats& stats);
|
void draw_kpi_row(const RegistryStats& stats);
|
||||||
void draw_charts(RegistryData& data);
|
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);
|
||||||
void draw_analysis_list(const std::vector<AnalysisRow>& analyses);
|
void draw_analysis_list(const std::vector<AnalysisRow>& analyses);
|
||||||
|
|||||||
Reference in New Issue
Block a user