255e8dcf71
Frontend C++ ImGui (main.cpp + 4 paneles) + backend Go (HTTP + SQLite + fsnotify + SSE). Reusa parse/scan/watch funcs del registry (issue 0130a).
108 lines
3.2 KiB
C++
108 lines
3.2 KiB
C++
#include <imgui.h>
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <cstdio>
|
|
#include <cstring>
|
|
#include <string>
|
|
#include <thread>
|
|
|
|
#include "app_base.h"
|
|
#include "core/panel_menu.h"
|
|
#include "core/icons_tabler.h"
|
|
#include "core/logger.h"
|
|
|
|
#include "panels.h"
|
|
#include "data.h"
|
|
|
|
static bool g_show_board = true;
|
|
static bool g_show_flows = true;
|
|
static bool g_show_filters = true;
|
|
static bool g_show_detail = true;
|
|
|
|
static std::atomic<bool> g_refresh_thread_alive{false};
|
|
static std::thread g_refresh_thread;
|
|
|
|
static void start_refresh_thread() {
|
|
g_refresh_thread_alive = true;
|
|
g_refresh_thread = std::thread([]() {
|
|
kanban::refresh_meta();
|
|
kanban::refresh_issues();
|
|
kanban::refresh_flows();
|
|
kanban::start_sse();
|
|
while (g_refresh_thread_alive) {
|
|
std::this_thread::sleep_for(std::chrono::seconds(30));
|
|
if (!g_refresh_thread_alive) break;
|
|
kanban::refresh_issues();
|
|
kanban::refresh_flows();
|
|
}
|
|
kanban::stop_sse();
|
|
});
|
|
}
|
|
|
|
static void stop_refresh_thread() {
|
|
g_refresh_thread_alive = false;
|
|
if (g_refresh_thread.joinable()) g_refresh_thread.join();
|
|
}
|
|
|
|
static void render() {
|
|
if (g_show_board) kanban::draw_board();
|
|
if (g_show_flows) kanban::draw_flows();
|
|
if (g_show_filters) kanban::draw_filters();
|
|
if (g_show_detail) kanban::draw_detail();
|
|
}
|
|
|
|
static int run_self_test() {
|
|
kanban::State& s = kanban::state();
|
|
s.backend_url = "http://127.0.0.1:1";
|
|
bool ok_issues = kanban::refresh_issues();
|
|
bool ok_flows = kanban::refresh_flows();
|
|
if (ok_issues || ok_flows) {
|
|
std::fprintf(stderr, "[self-test] expected refresh to fail on unreachable backend\n");
|
|
return 1;
|
|
}
|
|
kanban::Issue dummy;
|
|
dummy.status = "pendiente";
|
|
dummy.priority = "media";
|
|
if (!kanban::passes_filters(dummy)) {
|
|
std::fprintf(stderr, "[self-test] empty filters should let issue pass\n");
|
|
return 2;
|
|
}
|
|
s.filters.priorities.insert("alta");
|
|
if (kanban::passes_filters(dummy)) {
|
|
std::fprintf(stderr, "[self-test] priority filter should reject media when alta required\n");
|
|
return 3;
|
|
}
|
|
std::fprintf(stdout, "[self-test] ok\n");
|
|
return 0;
|
|
}
|
|
|
|
int main(int argc, char** argv) {
|
|
for (int i = 1; i < argc; ++i) {
|
|
if (std::strcmp(argv[i], "--self-test") == 0) {
|
|
return run_self_test();
|
|
}
|
|
if (std::strcmp(argv[i], "--backend") == 0 && i + 1 < argc) {
|
|
kanban::state().backend_url = argv[++i];
|
|
}
|
|
}
|
|
|
|
static fn_ui::PanelToggle panels[] = {
|
|
{ "Board", nullptr, &g_show_board },
|
|
{ "Flows", nullptr, &g_show_flows },
|
|
{ "Filters", nullptr, &g_show_filters },
|
|
{ "Detail", nullptr, &g_show_detail },
|
|
};
|
|
|
|
fn::AppConfig cfg;
|
|
cfg.title = "kanban_cpp v2 — dev/issues + dev/flows";
|
|
cfg.about = { "kanban_cpp", "0.2.0", "Kanban C++ v2 — gestor de dev/issues y dev/flows del registry" };
|
|
cfg.log = { "kanban_cpp.log", 1 };
|
|
cfg.panels = panels;
|
|
cfg.panel_count = sizeof(panels) / sizeof(panels[0]);
|
|
|
|
start_refresh_thread();
|
|
int rc = fn::run_app(cfg, render);
|
|
stop_refresh_thread();
|
|
return rc;
|
|
}
|