// 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 #include // 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