Files
kanban_cpp/panel_board.cpp
T
agent 255e8dcf71 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).
2026-05-22 22:19:47 +02:00

151 lines
4.5 KiB
C++

#include "panels.h"
#include "data.h"
#include <imgui.h>
#include <imgui_internal.h>
#include <mutex>
#include <string>
#include <vector>
#include "core/icons_tabler.h"
namespace kanban {
static const char* k_columns[] = {"pendiente", "in-progress", "bloqueado", "completado"};
static const int k_n_columns = sizeof(k_columns) / sizeof(k_columns[0]);
static ImU32 priority_color(const std::string& p) {
if (p == "critica") return IM_COL32(255, 80, 80, 255);
if (p == "alta") return IM_COL32(255, 165, 0, 255);
if (p == "media") return IM_COL32(120, 170, 255, 255);
if (p == "baja") return IM_COL32(140, 140, 140, 255);
return IM_COL32(180, 180, 180, 255);
}
static void draw_card(const Issue& iss) {
ImGui::PushID(iss.id.c_str());
ImVec2 size(0.0f, 64.0f);
ImGui::BeginChild("card", size, true, ImGuiWindowFlags_NoScrollbar);
// Header: id + priority badge
ImGui::TextColored(ImColor(180, 180, 180, 255), "%s", iss.id.c_str());
ImGui::SameLine();
{
ImVec2 cur = ImGui::GetCursorScreenPos();
ImU32 c = priority_color(iss.priority);
ImDrawList* dl = ImGui::GetWindowDrawList();
float r = 5.0f;
dl->AddCircleFilled(ImVec2(cur.x + r, cur.y + r + 2), r, c);
ImGui::Dummy(ImVec2(2 * r + 4, 2 * r));
ImGui::SameLine();
ImGui::TextUnformatted(iss.priority.c_str());
}
// Title (truncate to ~60 chars)
std::string t = iss.title;
if (t.size() > 70) t = t.substr(0, 67) + "...";
ImGui::TextWrapped("%s", t.c_str());
// First domain chip
if (!iss.domain.empty()) {
ImGui::TextColored(ImColor(140, 200, 255, 255), TI_TAG " %s", iss.domain[0].c_str());
}
ImGui::EndChild();
if (ImGui::IsItemClicked()) {
refresh_issue_detail(iss.id);
}
// Drag source
if (ImGui::BeginDragDropSource(ImGuiDragDropFlags_None)) {
ImGui::SetDragDropPayload("KANBAN_ISSUE", iss.id.c_str(), iss.id.size() + 1);
ImGui::Text("%s %s", iss.id.c_str(), t.c_str());
ImGui::EndDragDropSource();
}
ImGui::PopID();
}
static void draw_column(const char* status_key, std::vector<const Issue*>& items) {
ImGui::TableNextColumn();
// Column header with count
ImGui::PushFont(nullptr);
ImGui::TextColored(ImColor(220, 220, 220, 255), "%s (%zu)", status_key, items.size());
ImGui::PopFont();
ImGui::Separator();
// Drop target on the whole column
ImVec2 region = ImGui::GetContentRegionAvail();
ImGui::BeginChild((std::string("col_") + status_key).c_str(), region, false);
for (const auto* iss : items) {
draw_card(*iss);
}
// Bottom drop zone
ImGui::InvisibleButton("col_drop", ImVec2(-1, 16));
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* p = ImGui::AcceptDragDropPayload("KANBAN_ISSUE")) {
std::string id((const char*)p->Data, p->DataSize - 1);
patch_issue_status(id, status_key);
}
ImGui::EndDragDropTarget();
}
ImGui::EndChild();
if (ImGui::BeginDragDropTarget()) {
if (const ImGuiPayload* p = ImGui::AcceptDragDropPayload("KANBAN_ISSUE")) {
std::string id((const char*)p->Data, p->DataSize - 1);
patch_issue_status(id, status_key);
}
ImGui::EndDragDropTarget();
}
}
void draw_board() {
if (!ImGui::Begin(TI_LAYOUT_KANBAN " Board")) {
ImGui::End();
return;
}
// Snapshot under lock
std::vector<Issue> snapshot;
{
std::lock_guard<std::mutex> g(state().mu);
snapshot = state().issues;
}
// Bucket issues by status
std::vector<std::vector<const Issue*>> buckets(k_n_columns);
int filtered_out = 0;
for (const auto& iss : snapshot) {
if (!passes_filters(iss)) {
filtered_out++;
continue;
}
for (int c = 0; c < k_n_columns; ++c) {
if (iss.status == k_columns[c]) {
buckets[c].push_back(&iss);
break;
}
}
}
ImGui::Text("%zu total — %d filtered out", snapshot.size(), filtered_out);
ImGui::Separator();
if (ImGui::BeginTable("board_table", k_n_columns,
ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_Resizable | ImGuiTableFlags_SizingStretchSame)) {
ImGui::TableNextRow();
for (int c = 0; c < k_n_columns; ++c) {
draw_column(k_columns[c], buckets[c]);
}
ImGui::EndTable();
}
ImGui::End();
}
} // namespace kanban