feat: initial scaffold of kanban_cpp v2 (issue 0130)
Frontend C++ ImGui (main.cpp + 4 paneles) + backend Go (HTTP + SQLite + fsnotify + SSE). Reusa parse/scan/watch funcs del registry (issue 0130a).
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
#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;
|
||||
}
|
||||
Reference in New Issue
Block a user