Files
tables_qa/tab_drill.cpp
T
egutierrez b15106fc09 chore: auto-commit (23 archivos)
- CMakeLists.txt
- app.md
- appicon.ico
- main.cpp
- perf_tests.cpp
- perf_tests.h
- qa_panel.cpp
- qa_panel.h
- qa_state.cpp
- qa_state.h
- ...

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

204 lines
7.3 KiB
C++

// tab_drill — drill-down stages + breadcrumb demo (issue 0108 fase 2).
// Muestra tabla de ventas jerarquica (region/country/product/quarter/revenue).
// Stage 0: raw. Stage 1: agrupado por region (Sum revenue).
// La UI arranca en stage 1. Right-click en celda de region -> popup nativo
// "Drill into" del modulo -> crea stage 2 con filter region == valor.
// Breadcrumb (<>) navega back/forward entre stages.
#include "tabs.h"
#include "qa_state.h"
#include "data_table/data_table.h"
#include <imgui.h>
#include <cstdio> // snprintf
namespace tables_qa::tabs {
namespace {
// ---------------------------------------------------------------------------
// Datos: 30 filas x 5 columnas (region, country, product, quarter, revenue)
// 3 regiones x 3 paises x (Q1..Q4 repartidos) x 3 productos = 30 filas aprox.
// ---------------------------------------------------------------------------
struct Row {
const char* region;
const char* country;
const char* product;
const char* quarter;
const char* revenue;
};
constexpr int kRows = 30;
constexpr int kCols = 5;
// Revenues: mezcla de 1000..50000 con variacion por region/producto.
static const Row kSalesData[kRows] = {
// EMEA
{"EMEA", "Germany", "A", "Q1", "12500"},
{"EMEA", "Germany", "B", "Q2", "18300"},
{"EMEA", "Germany", "C", "Q3", "9700"},
{"EMEA", "France", "A", "Q4", "21000"},
{"EMEA", "France", "B", "Q1", "14200"},
{"EMEA", "France", "C", "Q2", "7800"},
{"EMEA", "Spain", "A", "Q3", "11500"},
{"EMEA", "Spain", "B", "Q4", "16800"},
{"EMEA", "Spain", "C", "Q1", "5400"},
{"EMEA", "Germany", "A", "Q2", "19900"},
// AMER
{"AMER", "USA", "A", "Q1", "45000"},
{"AMER", "USA", "B", "Q2", "38500"},
{"AMER", "USA", "C", "Q3", "22000"},
{"AMER", "Canada", "A", "Q4", "17600"},
{"AMER", "Canada", "B", "Q1", "13200"},
{"AMER", "Canada", "C", "Q2", "8900"},
{"AMER", "Brazil", "A", "Q3", "27400"},
{"AMER", "Brazil", "B", "Q4", "31000"},
{"AMER", "Brazil", "C", "Q1", "11100"},
{"AMER", "USA", "A", "Q2", "49800"},
// APAC
{"APAC", "Japan", "A", "Q1", "32000"},
{"APAC", "Japan", "B", "Q2", "25600"},
{"APAC", "Japan", "C", "Q3", "14300"},
{"APAC", "Australia", "A", "Q4", "18700"},
{"APAC", "Australia", "B", "Q1", "12400"},
{"APAC", "Australia", "C", "Q2", "6100"},
{"APAC", "Singapore", "A", "Q3", "23800"},
{"APAC", "Singapore", "B", "Q4", "29500"},
{"APAC", "Singapore", "C", "Q1", "9200"},
{"APAC", "Japan", "A", "Q2", "35400"},
};
// Backing strings (flat row-major) + ptrs (rebuilt by seed).
static std::string g_back[kRows * kCols];
static const char* g_ptrs[kRows * kCols];
// data_table state — persistent across frames.
static data_table::State g_dt;
static bool g_seeded = false;
// ---------------------------------------------------------------------------
// seed — fill g_back + g_ptrs, preset stages, set active_stage = 1.
// ---------------------------------------------------------------------------
void seed() {
for (int r = 0; r < kRows; ++r) {
int base = r * kCols;
g_back[base + 0] = kSalesData[r].region;
g_back[base + 1] = kSalesData[r].country;
g_back[base + 2] = kSalesData[r].product;
g_back[base + 3] = kSalesData[r].quarter;
g_back[base + 4] = kSalesData[r].revenue;
}
for (int i = 0; i < kRows * kCols; ++i)
g_ptrs[i] = g_back[i].c_str();
// ------------------------------------------------------------------
// Preset stages:
// stages[0] = raw (empty Stage — no breakouts, no aggregations).
// stages[1] = breakout by region + Sum of revenue.
// ------------------------------------------------------------------
g_dt.stages.clear();
g_dt.stages.emplace_back(); // Stage 0: raw
data_table::Stage agg;
agg.breakouts = {"region"};
{
data_table::Aggregation a;
a.fn = data_table::AggFn::Sum;
a.col = "revenue";
a.alias = "revenue_sum";
agg.aggregations.push_back(a);
}
g_dt.stages.push_back(agg); // Stage 1: group by region + sum revenue
// Start on the aggregated view.
g_dt.active_stage = 1;
// Clear drill history.
g_dt.drill_back.clear();
g_dt.drill_forward.clear();
g_seeded = true;
}
} // anon
// ---------------------------------------------------------------------------
// render_drill
// ---------------------------------------------------------------------------
void render_drill() {
if (!g_seeded) seed();
// ------------------------------------------------------------------
// UI controls above the table.
// ------------------------------------------------------------------
if (ImGui::SmallButton("Reset to grouped (stage 1)")) {
// Keep only stage 0 + stage 1; drop any drill stages.
if (g_dt.stages.size() > 2) {
g_dt.stages.resize(2);
}
g_dt.active_stage = 1;
g_dt.drill_back.clear();
g_dt.drill_forward.clear();
}
ImGui::SameLine();
if (ImGui::SmallButton("Reset to raw (stage 0)")) {
g_dt.active_stage = 0;
g_dt.drill_back.clear();
g_dt.drill_forward.clear();
}
ImGui::SameLine();
ImGui::TextDisabled(
"Right-click any region cell -> 'Drill into' to filter down. "
"Use breadcrumb < > to navigate back/forward.");
// ------------------------------------------------------------------
// Build TableInput.
// ------------------------------------------------------------------
data_table::TableInput tbl;
tbl.name = "sales";
tbl.headers = {"region", "country", "product", "quarter", "revenue"};
tbl.types = {
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::String,
data_table::ColumnType::Float,
};
tbl.cells = g_ptrs;
tbl.rows = kRows;
tbl.cols = kCols;
// Renderers: CategoricalChip on region, ColorScale on revenue.
tbl.column_specs.resize(kCols);
for (int i = 0; i < kCols; i++) tbl.column_specs[i].id = tbl.headers[i];
// region: CategoricalChip — one dot per region for fast scanning.
{
auto& cs = tbl.column_specs[0];
cs.renderer = data_table::CellRenderer::CategoricalChip;
cs.chips = {
{"EMEA", "#3b82f6"}, // blue
{"AMER", "#22c55e"}, // green
{"APAC", "#f59e0b"}, // amber
};
}
// revenue: ColorScale green→amber→red over [0, 50000].
{
auto& cs = tbl.column_specs[4];
cs.renderer = data_table::CellRenderer::ColorScale;
cs.range_min = 0.0;
cs.range_max = 50000.0;
cs.range_alpha = 0.30f;
// Default 3-stop gradient (green→amber→red) via empty range_stops.
}
// ------------------------------------------------------------------
// Render.
// ------------------------------------------------------------------
ImGui::BeginChild("##drill_host", ImVec2(-1, -1));
data_table::render("##drill_tbl", {tbl}, g_dt, nullptr,
qa::toggles().show_chrome);
ImGui::EndChild();
}
} // namespace tables_qa::tabs