225 lines
7.6 KiB
C++
225 lines
7.6 KiB
C++
#include "views.h"
|
|
#include "imgui.h"
|
|
#include "implot.h"
|
|
|
|
#include "viz/kpi_card.h"
|
|
#include "viz/bar_chart.h"
|
|
#include "viz/pie_chart.h"
|
|
#include "viz/table_view.h"
|
|
#include "viz/sparkline.h"
|
|
#include "core/dashboard_panel.h"
|
|
#include "core/dashboard_grid.h"
|
|
#include "core/fps_overlay.h"
|
|
#include "core/fullscreen_window.h"
|
|
|
|
#include <vector>
|
|
|
|
static std::vector<const char*> to_cstr(const std::vector<std::string>& v) {
|
|
std::vector<const char*> out;
|
|
out.reserve(v.size());
|
|
for (auto& s : v) out.push_back(s.c_str());
|
|
return out;
|
|
}
|
|
|
|
void draw_kpi_row(const RegistryStats& 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
|
|
? 100.0f * stats.pure_functions / stats.total_functions
|
|
: 0.0f;
|
|
|
|
dashboard_grid_begin(6, 12.0f);
|
|
|
|
kpi_card("Functions", static_cast<float>(stats.total_functions), 0, nullptr, 0, "%.0f");
|
|
dashboard_grid_next();
|
|
kpi_card("Types", static_cast<float>(stats.total_types), 0, nullptr, 0, "%.0f");
|
|
dashboard_grid_next();
|
|
kpi_card("Apps", static_cast<float>(stats.total_apps), 0, nullptr, 0, "%.0f");
|
|
dashboard_grid_next();
|
|
kpi_card("Analysis", static_cast<float>(stats.total_analysis), 0, nullptr, 0, "%.0f");
|
|
dashboard_grid_next();
|
|
kpi_card("Tested %", tested_pct, 0, nullptr, 0, "%.1f%%");
|
|
dashboard_grid_next();
|
|
kpi_card("Pure %", pure_pct, 0, nullptr, 0, "%.1f%%");
|
|
|
|
dashboard_grid_end();
|
|
}
|
|
|
|
void draw_charts(RegistryData& data) {
|
|
dashboard_grid_begin(2, 12.0f);
|
|
|
|
if (dashboard_panel_begin("Functions by Language", 300, 280)) {
|
|
auto labels = to_cstr(data.lang_labels);
|
|
if (!labels.empty())
|
|
bar_chart("##lang", labels.data(), data.lang_values.data(), static_cast<int>(labels.size()));
|
|
}
|
|
dashboard_panel_end();
|
|
dashboard_grid_next();
|
|
|
|
if (dashboard_panel_begin("Functions by Domain", 300, 280)) {
|
|
auto labels = to_cstr(data.domain_labels);
|
|
if (!labels.empty())
|
|
bar_chart("##domain", labels.data(), data.domain_values.data(), static_cast<int>(labels.size()));
|
|
}
|
|
dashboard_panel_end();
|
|
dashboard_grid_next();
|
|
|
|
if (dashboard_panel_begin("Purity", 250, 280)) {
|
|
const char* labels[] = {"Pure", "Impure"};
|
|
float values[] = {static_cast<float>(data.stats.pure_functions), static_cast<float>(data.stats.impure_functions)};
|
|
pie_chart("##purity", labels, values, 2);
|
|
}
|
|
dashboard_panel_end();
|
|
dashboard_grid_next();
|
|
|
|
if (dashboard_panel_begin("Kind", 250, 280)) {
|
|
auto labels = to_cstr(data.kind_labels);
|
|
if (!labels.empty())
|
|
pie_chart("##kind", labels.data(), data.kind_values.data(), static_cast<int>(labels.size()));
|
|
}
|
|
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();
|
|
}
|
|
|
|
void draw_recent_functions(const std::vector<FunctionRow>& funcs) {
|
|
const char* headers[] = {"Name", "Lang", "Domain", "Kind", "Purity", "Tested", "Created"};
|
|
constexpr int cols = 7;
|
|
std::vector<std::string> cell_strings;
|
|
cell_strings.reserve(funcs.size() * cols);
|
|
for (auto& f : funcs) {
|
|
cell_strings.push_back(f.name);
|
|
cell_strings.push_back(f.lang);
|
|
cell_strings.push_back(f.domain);
|
|
cell_strings.push_back(f.kind);
|
|
cell_strings.push_back(f.purity);
|
|
cell_strings.push_back(f.tested ? "yes" : "no");
|
|
cell_strings.push_back(f.created_at.substr(0, 10));
|
|
}
|
|
auto cells = to_cstr(cell_strings);
|
|
table_view("##recent", headers, cols, cells.data(), static_cast<int>(funcs.size()));
|
|
}
|
|
|
|
void draw_apps_list(const std::vector<AppRow>& apps) {
|
|
const char* headers[] = {"Name", "Lang", "Domain", "Framework", "Description"};
|
|
constexpr int cols = 5;
|
|
std::vector<std::string> cell_strings;
|
|
cell_strings.reserve(apps.size() * cols);
|
|
for (auto& a : apps) {
|
|
cell_strings.push_back(a.name);
|
|
cell_strings.push_back(a.lang);
|
|
cell_strings.push_back(a.domain);
|
|
cell_strings.push_back(a.framework);
|
|
cell_strings.push_back(a.description);
|
|
}
|
|
auto cells = to_cstr(cell_strings);
|
|
table_view("##apps", headers, cols, cells.data(), static_cast<int>(apps.size()));
|
|
}
|
|
|
|
void draw_analysis_list(const std::vector<AnalysisRow>& analyses) {
|
|
const char* headers[] = {"Name", "Domain", "Description"};
|
|
constexpr int cols = 3;
|
|
std::vector<std::string> cell_strings;
|
|
cell_strings.reserve(analyses.size() * cols);
|
|
for (auto& a : analyses) {
|
|
cell_strings.push_back(a.name);
|
|
cell_strings.push_back(a.domain);
|
|
cell_strings.push_back(a.description);
|
|
}
|
|
auto cells = to_cstr(cell_strings);
|
|
table_view("##analysis", headers, cols, cells.data(), static_cast<int>(analyses.size()));
|
|
}
|
|
|
|
void draw_types_list(const std::vector<TypeRow>& types) {
|
|
const char* headers[] = {"Name", "Lang", "Domain", "Algebraic", "Description"};
|
|
constexpr int cols = 5;
|
|
std::vector<std::string> cell_strings;
|
|
cell_strings.reserve(types.size() * cols);
|
|
for (auto& t : types) {
|
|
cell_strings.push_back(t.name);
|
|
cell_strings.push_back(t.lang);
|
|
cell_strings.push_back(t.domain);
|
|
cell_strings.push_back(t.algebraic);
|
|
cell_strings.push_back(t.description);
|
|
}
|
|
auto cells = to_cstr(cell_strings);
|
|
table_view("##types", headers, cols, cells.data(), static_cast<int>(types.size()));
|
|
}
|
|
|
|
void draw_dashboard(RegistryData& data) {
|
|
fps_overlay();
|
|
|
|
fullscreen_window_begin("##dashboard");
|
|
|
|
// Header
|
|
ImGui::Text("fn_registry Dashboard");
|
|
ImGui::SameLine(ImGui::GetWindowWidth() - 100);
|
|
if (ImGui::Button("Reload")) {
|
|
ImGui::GetIO().UserData = reinterpret_cast<void*>(1);
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
// KPIs
|
|
draw_kpi_row(data.stats);
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
// Charts
|
|
draw_charts(data);
|
|
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::Spacing();
|
|
|
|
// Tables in tabs
|
|
if (ImGui::BeginTabBar("##tables")) {
|
|
if (ImGui::BeginTabItem("Recent Functions")) {
|
|
draw_recent_functions(data.recent_funcs);
|
|
ImGui::EndTabItem();
|
|
}
|
|
if (ImGui::BeginTabItem("Apps")) {
|
|
draw_apps_list(data.apps);
|
|
ImGui::EndTabItem();
|
|
}
|
|
if (ImGui::BeginTabItem("Analysis")) {
|
|
draw_analysis_list(data.analyses);
|
|
ImGui::EndTabItem();
|
|
}
|
|
if (ImGui::BeginTabItem("Types")) {
|
|
draw_types_list(data.types);
|
|
ImGui::EndTabItem();
|
|
}
|
|
ImGui::EndTabBar();
|
|
}
|
|
|
|
fullscreen_window_end();
|
|
}
|