Files
registry_dashboard/main.cpp
T
egutierrez 00a19d8632 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).
2026-04-29 00:58:17 +02:00

175 lines
5.5 KiB
C++

#include "app_base.h"
#include "imgui.h"
#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"
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string>
#include <fstream>
static RegistryData g_data;
static std::string g_db_path;
static std::string g_api_url;
static bool g_loaded = false;
static bool g_using_http = false;
static void reload_data() {
g_data = RegistryData{};
// Try HTTP API first
if (!g_api_url.empty()) {
g_loaded = load_registry_data_http(g_api_url, g_data);
if (g_loaded) {
g_using_http = true;
return;
}
fprintf(stderr, "HTTP API failed, falling back to SQLite\n");
}
// Fallback to direct SQLite
g_using_http = false;
if (!g_db_path.empty()) {
g_loaded = load_registry_data(g_db_path.c_str(), g_data);
if (!g_loaded) {
fprintf(stderr, "Failed to load registry data from: %s\n", g_db_path.c_str());
}
}
}
static void render() {
// MainMenuBar (solo Settings — el dashboard no expone paneles toggleables)
fn_ui::app_menubar(nullptr, 0, nullptr);
if (ImGui::GetIO().UserData != nullptr) {
ImGui::GetIO().UserData = nullptr;
reload_data();
}
if (!g_loaded) {
fullscreen_window_begin("##error");
ImGui::TextColored(ImVec4(1, 0.3f, 0.3f, 1),
"Could not load registry data");
ImGui::Spacing();
if (!g_api_url.empty())
ImGui::Text("API: %s (unreachable)", g_api_url.c_str());
if (!g_db_path.empty())
ImGui::Text("DB: %s", g_db_path.c_str());
ImGui::Spacing();
ImGui::TextWrapped(
"Usage: registry_dashboard [--api URL] [db_path ...]\n"
" --api URL Connect to sqlite_api (default: http://127.0.0.1:8484)\n"
" db_path Direct SQLite path(s) as fallback");
ImGui::Spacing();
if (ImGui::Button("Retry")) {
reload_data();
}
fullscreen_window_end();
return;
}
draw_dashboard(g_data);
}
int main(int argc, char** argv) {
// Parse --api flag
std::vector<std::string> db_candidates;
bool api_explicit = false;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "--api") == 0 && i + 1 < argc) {
g_api_url = argv[++i];
api_explicit = true;
} else if (strncmp(argv[i], "--api=", 6) == 0) {
g_api_url = argv[i] + 6;
api_explicit = true;
} else {
db_candidates.push_back(argv[i]);
}
}
// Default: try localhost API if no --api given
if (!api_explicit) {
g_api_url = "http://127.0.0.1:8484";
}
// Resolve SQLite fallback path
for (auto& candidate : db_candidates) {
if (std::ifstream(candidate).good()) {
g_db_path = candidate;
fprintf(stdout, "SQLite fallback: %s\n", g_db_path.c_str());
break;
}
fprintf(stderr, "Not found: %s\n", candidate.c_str());
}
if (!db_candidates.empty()) {
if (g_db_path.empty()) g_db_path = db_candidates.back();
}
// Compartir el API URL con las vistas (para reindex/add desde la toolbar)
views_set_api_url(g_api_url);
// Info de la ventana About (submenu Settings → About...)
fn_ui::about_window_set_info(
"fn_registry Dashboard",
"0.3.0",
"Dashboard ImGui para visualizar el estado del fn_registry. "
"Consume datos via sqlite_api HTTP (fallback a SQLite directo). "
"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();
return fn::run_app(
{.title = "fn_registry Dashboard", .width = 1600, .height = 1000, .viewports = true},
render
);
}