Files
fn_registry/cpp/functions/viz/data_table_drill.cpp
T
egutierrez 7eb7b3d0c8 chore: snapshot WIP previo + flow 0008 + 7 sub-issues (0112-0119)
Snapshot de WIP acumulado de sesiones previas antes de merge wave 1
del flow 0008 (kanban_cpp + agent_runner_api + DoD schema).

Incluye:
- dev/flows/0008-kanban-cpp-and-agent-workflows.md
- dev/issues/0112-0119*.md (7 sub-issues)
- WIP previo en cmd/fn/doctor.go, registry/*, modules/, cpp/, etc.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 18:17:08 +02:00

205 lines
8.3 KiB
C++

// data_table_drill — drill-down stack + breadcrumb de stages.
// Sub-funcion extraida de modules/data_table/data_table.cpp (issue 0107c).
//
// Rangos de lineas del fuente original:
// - make_drill_filter : lineas 699-706
// - apply_drill_step : lineas 708-718
// - undo_drill_step : lineas 720-730
// - drill_up : lineas 732-737
// - draw_stage_breadcrumb : lineas 1386-1483
// - drill_into : lineas 2898-2919
#include "viz/data_table_drill.h"
#include "core/data_table_types.h"
#include "imgui.h"
#include <cstdio>
#include <string>
#include <vector>
namespace data_table {
// ---------------------------------------------------------------------------
// make_drill_filter: crea un Filter Op::Eq para col_idx con el valor dado.
// ---------------------------------------------------------------------------
Filter make_drill_filter(int col_idx, const std::string& value) {
Filter f;
f.col = col_idx;
f.op = Op::Eq;
f.value = value;
return f;
}
// ---------------------------------------------------------------------------
// apply_drill_step: inserta step.added en el stage en step.target_stage y
// actualiza st.active_stage. Retorna true si el step se aplico correctamente.
// ---------------------------------------------------------------------------
bool apply_drill_step(State& st, const DrillStep& step) {
if (step.target_stage < 0 || step.target_stage >= (int)st.stages.size()) return false;
Stage& s = st.stages[step.target_stage];
int pos = step.filter_pos;
if (pos < 0 || pos > (int)s.filters.size()) return false;
s.filters.insert(s.filters.begin() + pos, step.added);
st.active_stage = step.target_stage;
return true;
}
// ---------------------------------------------------------------------------
// undo_drill_step: elimina el filter insertado por apply_drill_step y restaura
// st.active_stage a step.prev_active_stage. Retorna true si se deshizo.
// ---------------------------------------------------------------------------
bool undo_drill_step(State& st, const DrillStep& step) {
if (step.target_stage < 0 || step.target_stage >= (int)st.stages.size()) return false;
Stage& s = st.stages[step.target_stage];
int pos = step.filter_pos;
if (pos < 0 || pos >= (int)s.filters.size()) return false;
s.filters.erase(s.filters.begin() + pos);
if (step.prev_active_stage >= 0 && step.prev_active_stage < (int)st.stages.size())
st.active_stage = step.prev_active_stage;
return true;
}
// ---------------------------------------------------------------------------
// drill_up: retrocede st.active_stage en 1 si hay stages previos.
// Retorna true si se pudo retroceder.
// ---------------------------------------------------------------------------
bool drill_up(State& st) {
if (st.stages.empty()) return false;
if (st.active_stage <= 0) return false;
st.active_stage -= 1;
return true;
}
// ---------------------------------------------------------------------------
// draw_stage_breadcrumb: barra de navegacion de drill con botones < > ^ y
// selector de stages. Mutates st.drill_back/forward y st.active_stage.
// ---------------------------------------------------------------------------
void draw_stage_breadcrumb(State& st) {
st.ensure_stage0();
// Drill history back/forward (fase 10). Botones al inicio.
{
bool can_back = !st.drill_back.empty();
ImGui::BeginDisabled(!can_back);
if (ImGui::SmallButton("<##drill_back")) {
DrillStep s = st.drill_back.back();
st.drill_back.pop_back();
if (undo_drill_step(st, s)) {
st.drill_forward.push_back(s);
}
}
ImGui::EndDisabled();
if (can_back && ImGui::IsItemHovered())
ImGui::SetTooltip("Drill back (%zu)", st.drill_back.size());
ImGui::SameLine();
bool can_fwd = !st.drill_forward.empty();
ImGui::BeginDisabled(!can_fwd);
if (ImGui::SmallButton(">##drill_fwd")) {
DrillStep s = st.drill_forward.back();
st.drill_forward.pop_back();
if (apply_drill_step(st, s)) {
st.drill_back.push_back(s);
}
}
ImGui::EndDisabled();
if (can_fwd && ImGui::IsItemHovered())
ImGui::SetTooltip("Drill forward (%zu)", st.drill_forward.size());
ImGui::SameLine();
bool can_up = (st.active_stage > 0);
ImGui::BeginDisabled(!can_up);
if (ImGui::SmallButton("^##drill_up")) drill_up(st);
ImGui::EndDisabled();
if (can_up && ImGui::IsItemHovered())
ImGui::SetTooltip("Drill up (stage previo, sin perder filters)");
ImGui::SameLine();
ImGui::TextDisabled("|");
ImGui::SameLine();
}
for (int si = 0; si < (int)st.stages.size(); ++si) {
if (si > 0) { ImGui::SameLine(); ImGui::TextDisabled(">"); ImGui::SameLine(); }
bool active = (si == st.active_stage);
if (active) {
ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32( 80, 140, 200, 240));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32(100, 160, 220, 240));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, IM_COL32( 60, 120, 180, 240));
} else {
ImGui::PushStyleColor(ImGuiCol_Button, IM_COL32( 70, 70, 90, 200));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, IM_COL32( 90, 90, 120, 220));
ImGui::PushStyleColor(ImGuiCol_ButtonActive, IM_COL32( 55, 55, 75, 220));
}
char label[256];
if (si == 0) {
std::snprintf(label, sizeof(label), "Raw##stage%d", si);
} else {
const Stage& s = st.stages[si];
std::string desc;
for (size_t i = 0; i < s.breakouts.size() && i < 2; ++i) {
if (i > 0) desc += ", ";
desc += s.breakouts[i];
}
if (s.breakouts.size() > 2) desc += "...";
if (desc.empty())
std::snprintf(label, sizeof(label), "Stage %d##s%d", si, si);
else
std::snprintf(label, sizeof(label), "Stage %d: by %s##s%d",
si, desc.c_str(), si);
}
if (ImGui::Button(label)) st.active_stage = si;
ImGui::PopStyleColor(3);
if (si > 0) {
ImGui::SameLine();
char xlbl[32];
std::snprintf(xlbl, sizeof(xlbl), "x##rm_s%d", si);
if (ImGui::SmallButton(xlbl)) {
// borra ese stage y sucesores
while ((int)st.stages.size() > si) st.stages.pop_back();
if (st.active_stage >= (int)st.stages.size())
st.active_stage = (int)st.stages.size() - 1;
if (st.active_stage < 0) st.active_stage = 0;
break;
}
}
}
ImGui::SameLine();
ImGui::TextDisabled(">");
ImGui::SameLine();
if (ImGui::SmallButton("+ Stage##add_stage")) {
st.stages.push_back(Stage{});
st.active_stage = (int)st.stages.size() - 1;
}
}
// ---------------------------------------------------------------------------
// drill_into: API publica. Anade un filter Op::Eq sobre col_name=value al
// stage (from_stage - 1) y cambia st.active_stage a ese stage previo.
// Graba el step en st.drill_back y limpia st.drill_forward (rama nueva).
// ---------------------------------------------------------------------------
void drill_into(State& st, int from_stage,
const std::string& col_name, const std::string& value,
const std::vector<std::string>& prev_input_headers)
{
if (from_stage <= 0 || from_stage >= (int)st.stages.size()) return;
int target = from_stage - 1;
int ci = -1;
for (size_t i = 0; i < prev_input_headers.size(); ++i) {
if (prev_input_headers[i] == col_name) { ci = (int)i; break; }
}
if (ci < 0) return;
// Fase 10: graba step en drill_back, limpia forward (rama nueva).
DrillStep step;
step.target_stage = target;
step.filter_pos = (int)st.stages[target].filters.size();
step.prev_active_stage = st.active_stage;
step.added = make_drill_filter(ci, value);
apply_drill_step(st, step);
st.drill_back.push_back(step);
st.drill_forward.clear();
}
} // namespace data_table