---
title: "Integrating {shiny.telemetry} with bidux"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Integrating {shiny.telemetry} with bidux}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
```

## Overview

The `{bidux}` package provides powerful tools to analyze real user behavior data from telemetry sources and automatically identify UX friction points. Think of it as A/B testing for UX design—you can measure the impact of design changes on user behavior with the same rigor you apply to data analysis.

**The power of telemetry + BID**: Instead of guessing what users struggle with, you can systematically identify friction points from real usage data, then apply behavioral science principles to fix them.

This creates a powerful feedback loop where actual usage patterns drive design improvements through the BID framework.

**New in 0.3.1:** Enhanced telemetry workflow with hybrid objects and streamlined API for better integration with tidy data workflows.

## Choosing a Telemetry Source

Bidux supports two telemetry sources:

1. **`{shiny.telemetry}`** - Dedicated package for Shiny app telemetry (covered in this vignette)
2. **Shiny Native OpenTelemetry** - Built into Shiny 1.12+ with rich performance data (NEW!)

Both work seamlessly with `bid_telemetry()` using automatic format detection.

### When to Use Each

**Use `{shiny.telemetry}` when:**

- Using Shiny versions < 1.12.0
- Need simple event tracking without additional dependencies
- Have an established `{shiny.telemetry}` workflow
- Want lightweight telemetry with minimal overhead

**Use Shiny OpenTelemetry when:**

- Using Shiny >= 1.12.0
- Want performance insights (render times, reactive latency) in addition to interactions
- Integrating with observability platforms (Jaeger, Grafana, Datadog)
- Need distributed tracing across services

See `vignette("otel-integration")` for complete OpenTelemetry setup guide including:
- Detailed span-to-event conversion mappings
- Column schema and ID extraction logic
- OTEL configuration and export formats
- Comparison with `{shiny.telemetry}`

### Automatic Format Detection

Bidux automatically detects the telemetry source, so the analysis workflow is identical:

```{r auto_detect, eval=FALSE}
# Works with {shiny.telemetry}
issues <- bid_telemetry("telemetry.sqlite")

# Works with Shiny OTEL
issues <- bid_telemetry("otel_spans.json")

# Same analysis workflow for both!
critical <- issues |>
  dplyr::filter(severity == "critical")
```

## Modern vs Legacy API

The package provides two complementary approaches to telemetry analysis, both actively supported:

### Modern API (0.3.1+): `bid_telemetry()`

The modern `bid_telemetry()` function returns a clean tibble (class `bid_issues_tbl`) for tidy data workflows:

```{r modern_api, eval=FALSE}
library(bidux)
library(dplyr)

# Modern approach: get tidy issues tibble
issues <- bid_telemetry("telemetry.sqlite")

# Print method shows prioritized triage view
print(issues)
#> # BID Telemetry Issues Summary
#> Found 8 issues from 847 sessions
#> Critical: 2 issues
#> High: 3 issues
#> Medium: 2 issues
#> Low: 1 issue

# Use tidy data operations for filtering
critical <- issues |>
  filter(severity == "critical") |>
  arrange(desc(impact_rate))

# Access as regular tibble columns
high_impact <- issues |>
  filter(impact_rate > 0.2) |>
  select(issue_id, issue_type, severity, problem)
```

**Key characteristics of `bid_telemetry()` output:**
- Returns a tibble (data frame) with one row per issue
- Class: `c("bid_issues_tbl", "tbl_df", "tbl", "data.frame")`
- Columns: `issue_id`, `issue_type`, `severity`, `affected_sessions`, `impact_rate`, `problem`, `evidence`, `theory`, `stage`, `created_at`
- Supports standard dplyr operations: `filter()`, `arrange()`, `select()`, etc.
- Access telemetry flags via `bid_flags(issues)`

### Legacy API (Backward Compatible): `bid_ingest_telemetry()`

The legacy `bid_ingest_telemetry()` function returns a hybrid object that maintains full backward compatibility while providing enhanced functionality:

```{r legacy_compat, eval=FALSE}
# Legacy approach returns hybrid object
legacy_result <- bid_ingest_telemetry("telemetry.sqlite")

# 1. LEGACY LIST INTERFACE (backward compatible)
length(legacy_result) # Number of issues as named list
names(legacy_result) # Issue IDs: "unused_input_region", "error_1", etc.
legacy_result[[1]] # Access individual bid_stage Notice objects
legacy_result$unused_input_region # Access by name

# 2. ENHANCED TIBBLE INTERFACE (new in 0.3.1)
as_tibble(legacy_result) # Convert to tidy tibble view
print(legacy_result) # Pretty-printed triage summary

# 3. FLAGS EXTRACTION (new in 0.3.1)
flags <- bid_flags(legacy_result) # Extract global telemetry flags
flags$has_critical_issues # Boolean flag
flags$has_navigation_issues # Boolean flag
flags$session_count # Integer metadata
```

**Key characteristics of `bid_ingest_telemetry()` output:**
- Returns a hybrid object with class: `c("bid_issues", "list")`
- **List interface**: Named list of `bid_stage` Notice objects (legacy behavior)
- **Attributes**:
  - `issues_tbl`: Tidy tibble (accessed via `as_tibble()`)
  - `flags`: Named list of telemetry flags (accessed via `bid_flags()`)
  - `created_at`: Timestamp when analysis was performed
- Fully backward compatible with existing code using list operations

### When to Use Each

**Use `bid_telemetry()` for:**
- New code and tidy data workflows
- When you want direct tibble output
- Filtering, sorting, and data manipulation with dplyr
- Integration with ggplot2 for visualization
- Reporting and analysis

**Use `bid_ingest_telemetry()` for:**
- Existing code that expects list-based output
- When you need backward compatibility
- Direct access to individual `bid_stage` Notice objects
- Legacy workflows and scripts

**Both functions share the same parameters and analysis logic**, so you can easily switch between them based on your workflow needs.

## Prerequisites

First, ensure you have `{shiny.telemetry}` set up in your Shiny application:

```{r setup, eval=FALSE}
library(shiny)
library(shiny.telemetry)

# Initialize telemetry
telemetry <- Telemetry$new()

ui <- fluidPage(
  use_telemetry(), # Add telemetry JavaScript

  titlePanel("Sales Dashboard"),
  sidebarLayout(
    sidebarPanel(
      selectInput(
        "region",
        "Region:",
        choices = c("North", "South", "East", "West")
      ),
      dateRangeInput("date_range", "Date Range:"),
      selectInput(
        "product_category",
        "Product Category:",
        choices = c("All", "Electronics", "Clothing", "Food")
      ),
      actionButton("refresh", "Refresh Data")
    ),
    mainPanel(
      tabsetPanel(
        tabPanel("Overview", plotOutput("overview_plot")),
        tabPanel("Details", dataTableOutput("details_table")),
        tabPanel("Settings", uiOutput("settings_ui"))
      )
    )
  )
)

server <- function(input, output, session) {
  # Start telemetry tracking
  telemetry$start_session()

  # Your app logic here...
}

shinyApp(ui, server)
```

## Analyzing Telemetry Data

After collecting telemetry data from your users, use either function to identify UX issues:

```{r basic_usage, eval=FALSE}
library(bidux)

# Modern approach: returns tibble
issues <- bid_telemetry("telemetry.sqlite")

# Legacy approach: returns hybrid object
issues_legacy <- bid_ingest_telemetry("telemetry.sqlite")

# Or from JSON log file (both functions support this)
issues <- bid_telemetry("telemetry.log", format = "json")

# Review identified issues
nrow(issues) # Modern: tibble row count
print(issues) # Both show formatted triage summary
```

## Configuring Analysis Sensitivity with Presets

The `bid_telemetry_presets()` function provides pre-configured threshold sets for different use cases. This makes it easy to adjust how aggressively the analysis identifies issues without manually setting each threshold.

### Available Presets

```{r presets_overview, eval=FALSE}
# Get preset configurations
strict_thresholds <- bid_telemetry_presets("strict")
moderate_thresholds <- bid_telemetry_presets("moderate")
relaxed_thresholds <- bid_telemetry_presets("relaxed")

# View what each preset contains
str(strict_thresholds)
#> List of 6
#>  $ unused_input_threshold: num 0.02
#>  $ delay_threshold_secs  : num 20
#>  $ error_rate_threshold  : num 0.05
#>  $ navigation_threshold  : num 0.1
#>  $ rapid_change_window   : num 15
#>  $ rapid_change_count    : num 4
```

### Strict Preset

**Use when:** You want to catch even minor issues. Ideal for critical applications, new dashboards, or high-stakes environments where every friction point matters.

```{r preset_strict, eval=FALSE}
# Strict preset detects issues early
strict_issues <- bid_telemetry(
  "telemetry.sqlite",
  thresholds = bid_telemetry_presets("strict")
)

# What makes it strict:
# - Flags inputs used by < 2% of sessions (vs 5% moderate)
# - Flags delays > 20 seconds (vs 30 seconds moderate)
# - Flags errors in > 5% of sessions (vs 10% moderate)
# - Flags pages visited by < 10% (vs 20% moderate)
# - More sensitive to confusion patterns (4 changes in 15s vs 5 in 10s)
```

### Moderate Preset (Default)

**Use when:** You want balanced detection suitable for most applications. This is the default if you don't specify thresholds.

```{r preset_moderate, eval=FALSE}
# Moderate preset is the default
moderate_issues <- bid_telemetry("telemetry.sqlite")

# Or explicitly specify it
moderate_issues <- bid_telemetry(
  "telemetry.sqlite",
  thresholds = bid_telemetry_presets("moderate")
)

# Balanced thresholds:
# - Flags inputs used by < 5% of sessions
# - Flags delays > 30 seconds
# - Flags errors in > 10% of sessions
# - Flags pages visited by < 20%
# - Standard confusion detection (5 changes in 10s)
```

### Relaxed Preset

**Use when:** You only want to detect major issues. Ideal for mature, stable dashboards where you're looking for significant problems only.

```{r preset_relaxed, eval=FALSE}
# Relaxed preset for mature applications
relaxed_issues <- bid_telemetry(
  "telemetry.sqlite",
  thresholds = bid_telemetry_presets("relaxed")
)

# Only flags major issues:
# - Flags inputs used by < 10% of sessions (vs 5% moderate)
# - Flags delays > 60 seconds (vs 30 seconds moderate)
# - Flags errors in > 20% of sessions (vs 10% moderate)
# - Flags pages visited by < 30% (vs 20% moderate)
# - Less sensitive to confusion (7 changes in 5s vs 5 in 10s)
```

### Custom Thresholds

You can also mix presets with custom overrides:

```{r custom_thresholds, eval=FALSE}
# Start with moderate preset but customize specific thresholds
custom_issues <- bid_telemetry(
  "telemetry.sqlite",
  thresholds = c(
    bid_telemetry_presets("moderate"),
    list(
      unused_input_threshold = 0.03, # Override: flag if < 3% use
      error_rate_threshold = 0.15 # Override: flag if > 15% errors
    )
  )
)

# Or build completely custom thresholds
fully_custom <- bid_telemetry(
  "telemetry.sqlite",
  thresholds = list(
    unused_input_threshold = 0.1, # Flag if <10% of sessions use input
    delay_threshold_secs = 60, # Flag if >60s before first interaction
    error_rate_threshold = 0.05, # Flag if >5% of sessions have errors
    navigation_threshold = 0.3, # Flag if <30% visit a page
    rapid_change_window = 5, # Look for changes within 5 seconds
    rapid_change_count = 3 # Flag if 3+ changes in window
  )
)
```

### Comparing Presets

Different presets will identify different numbers of issues:

```{r preset_comparison, eval=FALSE}
# Analyze same data with all three presets
strict <- bid_telemetry("telemetry.sqlite",
  thresholds = bid_telemetry_presets("strict")
)
moderate <- bid_telemetry("telemetry.sqlite",
  thresholds = bid_telemetry_presets("moderate")
)
relaxed <- bid_telemetry("telemetry.sqlite",
  thresholds = bid_telemetry_presets("relaxed")
)

# Compare issue counts
comparison <- data.frame(
  preset = c("Strict", "Moderate", "Relaxed"),
  issues_found = c(nrow(strict), nrow(moderate), nrow(relaxed)),
  critical = c(
    sum(strict$severity == "critical"),
    sum(moderate$severity == "critical"),
    sum(relaxed$severity == "critical")
  )
)

print(comparison)
#>     preset issues_found critical
#> 1   Strict           12        3
#> 2 Moderate            8        2
#> 3  Relaxed            4        1
```

## Understanding the Analysis

The function analyzes five key friction indicators:

### 1. Unused or Under-used Inputs

Identifies UI controls that users rarely or never interact with:

```{r unused_inputs_example, eval=FALSE}
# Using modern API
issues <- bid_telemetry("telemetry.sqlite")

# Filter for unused input issues
unused <- issues |> filter(issue_type == "unused_input")

# Example issue details
unused[1, ]
#> issue_id: unused_input_region
#> severity: high
#> problem: Users are not interacting with the 'region' input control
#> evidence: Only 25 out of 847 sessions (3.0%) interacted with 'region'
#> affected_sessions: 822
#> impact_rate: 0.97
```

This suggests the region filter might be:
- Hidden or hard to find
- Not relevant to users' tasks
- Confusing or intimidating

### 2. Delayed First Interactions

Detects when users take too long to start using the dashboard:

```{r delayed_interaction_example, eval=FALSE}
issues <- bid_telemetry("telemetry.sqlite")

delayed <- issues |> filter(issue_type == "delayed_interaction")

delayed[1, ]
#> issue_id: delayed_interaction
#> severity: critical
#> problem: Users take a long time before making their first interaction
#> evidence: Median time to first input is 47 seconds, and 12% had no interactions
#> affected_sessions: 254
#> impact_rate: 0.30
```

This indicates users might be:
- Overwhelmed by the initial view
- Unsure where to start
- Looking for information that's not readily apparent

### 3. Frequent Errors

Identifies systematic errors that disrupt user experience:

```{r error_patterns_example, eval=FALSE}
issues <- bid_telemetry("telemetry.sqlite")

errors <- issues |> filter(issue_type == "error_pattern")

errors[1, ]
#> issue_id: error_1
#> severity: high
#> problem: Users encounter errors when using the dashboard
#> evidence: Error 'Data query failed' occurred 127 times in 15.0% of sessions
#> affected_sessions: 127
#> impact_rate: 0.15
```

This reveals:
- Reliability issues with specific features
- Input validation problems
- Performance bottlenecks

### 4. Navigation Drop-offs

Finds pages or tabs that users rarely visit:

```{r navigation_dropoff_example, eval=FALSE}
issues <- bid_telemetry("telemetry.sqlite")

nav_issues <- issues |> filter(issue_type == "navigation_dropoff")

nav_issues[1, ]
#> issue_id: navigation_settings_tab
#> severity: medium
#> problem: The 'settings_tab' page/tab is rarely visited by users
#> evidence: Only 42 sessions (5.0%) visited 'settings_tab'
#> affected_sessions: 805
#> impact_rate: 0.95
```

Low visit rates suggest:
- Poor information scent
- Hidden or unclear navigation
- Irrelevant content

### 5. Confusion Patterns

Detects rapid repeated changes indicating user confusion:

```{r confusion_pattern_example, eval=FALSE}
issues <- bid_telemetry("telemetry.sqlite")

confusion <- issues |> filter(issue_type == "confusion_pattern")

confusion[1, ]
#> issue_id: confusion_date_range
#> severity: medium
#> problem: Users show signs of confusion when interacting with 'date_range'
#> evidence: 8 sessions showed rapid repeated changes (avg 6 changes in 7.5s)
#> affected_sessions: 8
#> impact_rate: 0.01
```

This suggests:
- Unclear feedback when values change
- Unexpected behavior
- Poor affordances

## Bridge Functions for BID Integration

The modern API provides bridge functions to seamlessly convert telemetry issues into BID stages:

### Converting Individual Issues: `bid_notice_issue()`

Convert a single telemetry issue into a Notice stage:

```{r bridge_individual, eval=FALSE}
# Get telemetry issues
issues <- bid_telemetry("telemetry.sqlite")

# Create interpret stage
interpret_result <- bid_interpret(
  central_question = "How can we reduce user friction?"
)

# Convert highest impact issue to Notice
priority_issue <- issues |>
  filter(severity == "critical") |>
  arrange(desc(impact_rate)) |>
  slice_head(n = 1)

notice_result <- bid_notice_issue(
  issue = priority_issue,
  previous_stage = interpret_result
)

# The Notice stage now contains the telemetry problem and evidence
print(notice_result)

# Optional: Override specific fields
notice_custom <- bid_notice_issue(
  issue = priority_issue,
  previous_stage = interpret_result,
  override = list(
    problem = "Custom problem description based on deeper analysis"
  )
)
```

### Converting Multiple Issues: `bid_notices()`

Convert multiple telemetry issues into Notice stages:

```{r bridge_batch, eval=FALSE}
# Get high-priority issues
issues <- bid_telemetry("telemetry.sqlite")
high_priority <- issues |> filter(severity %in% c("critical", "high"))

interpret_result <- bid_interpret(
  central_question = "How can we systematically address UX issues?"
)

# Convert all high-priority issues to Notice stages
notice_list <- bid_notices(
  issues = high_priority,
  previous_stage = interpret_result,
  max_issues = 3 # Limit to top 3 issues
)

# Result is a named list of bid_stage objects
length(notice_list) # Number of Notice stages created
notice_list[[1]] # Access individual Notice stage

# Continue BID workflow with first issue
anticipate_result <- bid_anticipate(
  previous_stage = notice_list[[1]],
  bias_mitigations = list(
    choice_overload = "Simplify interface",
    anchoring = "Set appropriate defaults"
  )
)
```

### Quick Issue Addressing: `bid_address()`

Convenience function for single-issue workflows:

```{r bridge_sugar, eval=FALSE}
# Quick single-issue addressing
issues <- bid_telemetry("telemetry.sqlite")
interpret <- bid_interpret("How can we improve user experience?")

# Address the highest impact issue
top_issue <- issues[which.max(issues$impact_rate), ]
notice <- bid_address(top_issue, interpret)

# Equivalent to bid_notice_issue()
# but more concise for quick workflows
```

### Pipeline Processing: `bid_pipeline()`

Create a pipeline for systematically addressing top issues:

```{r bridge_pipeline, eval=FALSE}
issues <- bid_telemetry("telemetry.sqlite")
interpret <- bid_interpret("How can we systematically improve UX?")

# Create pipeline for top 3 issues (sorted by severity then impact)
notice_pipeline <- bid_pipeline(issues, interpret, max = 3)

# Process each issue through the BID framework
for (i in seq_along(notice_pipeline)) {
  cli::cli_h2("Addressing Issue {i}")

  notice <- notice_pipeline[[i]]

  anticipate <- bid_anticipate(
    previous_stage = notice,
    bias_mitigations = list(
      confirmation_bias = "Show contradicting data",
      anchoring = "Provide context"
    )
  )

  structure <- bid_structure(previous_stage = anticipate)

  validate <- bid_validate(
    previous_stage = structure,
    next_steps = c("Implement changes", "Collect new telemetry")
  )
}
```

## Using Telemetry Flags for Layout Optimization

Extract global flags to inform structure decisions:

```{r telemetry_flags, eval=FALSE}
# Analyze telemetry
issues <- bid_telemetry("telemetry.sqlite")

# Extract flags
flags <- bid_flags(issues)

# Available flags:
str(flags)
#> List of 11
#>  $ has_issues             : logi TRUE
#>  $ has_critical_issues    : logi TRUE
#>  $ has_input_issues       : logi TRUE
#>  $ has_navigation_issues  : logi TRUE
#>  $ has_error_patterns     : logi TRUE
#>  $ has_confusion_patterns : logi FALSE
#>  $ has_delay_issues       : logi TRUE
#>  $ session_count          : int 847
#>  $ analysis_timestamp     : POSIXct
#>  $ unused_input_threshold : num 0.05
#>  $ delay_threshold_seconds: num 30

# Use flags to inform BID workflow
if (flags$has_navigation_issues) {
  cli::cli_alert_warning("Navigation issues detected - avoid tab-heavy layouts")
}

# Pass flags to structure stage
structure_result <- bid_structure(
  previous_stage = anticipate_result,
  telemetry_flags = flags # Influences layout selection and suggestions
)

# Flags also work with legacy hybrid objects
legacy_result <- bid_ingest_telemetry("telemetry.sqlite")
legacy_flags <- bid_flags(legacy_result) # Same flags interface
```

## Understanding the Hybrid Object

The `bid_ingest_telemetry()` hybrid object provides dual interfaces:

```{r hybrid_demo, eval=FALSE}
# Create hybrid object
hybrid <- bid_ingest_telemetry("telemetry.sqlite")

# INTERFACE 1: Legacy List (backward compatible)
# - Behaves exactly like pre-0.3.1 versions
class(hybrid)
#> [1] "bid_issues" "list"

length(hybrid) # Number of issues
#> [1] 8

names(hybrid) # Issue IDs
#> [1] "unused_input_region"      "delayed_interaction"
#> [3] "error_1"                  "navigation_settings_tab"

hybrid[[1]] # First issue as bid_stage object
#> BID Framework - Notice Stage
#> Problem: Users are not interacting with the 'region' input control
#> Evidence: Only 25 out of 847 sessions (3.0%) interacted with 'region'

hybrid$error_1 # Access by name
#> BID Framework - Notice Stage
#> Problem: Users encounter errors when using the dashboard

# INTERFACE 2: Tibble View (enhanced in 0.3.1)
# - Convert to tibble for tidy operations
issues_tbl <- as_tibble(hybrid)
class(issues_tbl)
#> [1] "tbl_df"     "tbl"        "data.frame"

nrow(issues_tbl) # Same count as length(hybrid)
#> [1] 8

# Filter and manipulate as tibble
critical <- issues_tbl |> filter(severity == "critical")

# INTERFACE 3: Flags Extraction
flags <- bid_flags(hybrid)
flags$has_critical_issues
#> [1] TRUE

# INTERFACE 4: Pretty Printing
print(hybrid)
#> # BID Telemetry Issues Summary
#> Found 8 issues from 847 sessions
#> Critical: 2 issues
#> High: 3 issues
#> ...

# All interfaces work on the same object!
```

## Integrating with BID Workflow

Use the identified issues to drive your BID process:

```{r bid_workflow, eval=FALSE}
# Analyze telemetry
issues <- bid_telemetry("telemetry.sqlite")

# Get top critical issue
critical_issue <- issues |>
  filter(severity == "critical") |>
  slice_head(n = 1)

# Start BID workflow
interpret_result <- bid_interpret(
  central_question = "How can we prevent data query errors?",
  data_story = list(
    hook = "15% of users encounter errors",
    context = "Errors occur after date range changes",
    tension = "Users lose trust when queries fail",
    resolution = "Implement robust error handling and loading states"
  )
)

# Convert telemetry issue to Notice stage
notice_result <- bid_notice_issue(
  issue = critical_issue,
  previous_stage = interpret_result
)

# Or use the problem/evidence directly
notice_result_manual <- bid_notice(
  previous_stage = interpret_result,
  problem = critical_issue$problem,
  evidence = critical_issue$evidence
)

# Continue through BID stages
anticipate_result <- bid_anticipate(
  previous_stage = notice_result,
  bias_mitigations = list(
    anchoring = "Show loading states to set proper expectations",
    confirmation_bias = "Display error context to help users understand issues"
  )
)

# Extract flags for structure decisions
flags <- bid_flags(issues)

structure_result <- bid_structure(
  previous_stage = anticipate_result,
  telemetry_flags = flags
)

validate_result <- bid_validate(
  previous_stage = structure_result,
  summary_panel = "Error handling improvements with clear user feedback",
  next_steps = c(
    "Implement loading states",
    "Add error context",
    "Test with users",
    "Re-run telemetry analysis"
  )
)
```

## Real-World Example: E-commerce Dashboard Optimization

Let's walk through a complete example of using telemetry data to improve an e-commerce dashboard.

### The Scenario

You've built an e-commerce analytics dashboard for business stakeholders. After 3 months in production, users are complaining it's "hard to use" but can't be specific about what's wrong.

### Step 1: Diagnose with Telemetry Data

```{r telemetry-diagnosis, eval=FALSE}
# Analyze 3 months of user behavior data
issues <- bid_telemetry("ecommerce_dashboard_telemetry.sqlite")

# Review the systematic analysis
print(issues)
#> # BID Telemetry Issues Summary
#> Found 8 issues from 847 sessions
#>
#> Critical: 2 issues
#> High: 3 issues
#> Medium: 2 issues
#> Low: 1 issue
#>
#> Top Priority Issues:
#> ! delayed_interaction: 30.0% impact (254 sessions)
#>    Problem: Users take a long time before making their first interaction
#> ! unused_input_advanced_filters: 97.0% impact (822 sessions)
#>    Problem: Users are not interacting with the 'advanced_filters' input
#> ! error_1: 15.0% impact (127 sessions)
#>    Problem: Users encounter errors when using the dashboard

# Examine the most critical issue in detail
critical_issue <- issues |>
  filter(severity == "critical") |>
  arrange(desc(impact_rate)) |>
  slice_head(n = 1)

print(critical_issue)
#> # A tibble: 1 × 10
#>   issue_id    issue_type  severity affected_sessions impact_rate problem
#>   <chr>       <chr>       <chr>                <int>       <dbl> <chr>
#> 1 delayed_in… delayed_in… critical               254        0.30 Users take…
```

### Step 2: Apply BID Framework Systematically

```{r ecommerce-bid-analysis, eval=FALSE}
# Start with interpretation of the business context
interpret_stage <- bid_interpret(
  central_question = "How can we make e-commerce insights more accessible?",
  data_story = list(
    hook = "Business teams struggle to get quick insights from our dashboard",
    context = "Stakeholders have 10-15 minutes between meetings to check performance",
    tension = "Current interface requires 47+ seconds just to orient and start using",
    resolution = "Provide immediate value with progressive disclosure"
  ),
  user_personas = list(
    list(
      name = "Marketing Manager",
      goals = "Quick campaign performance insights",
      pain_points = "Too much information, unclear where to start",
      technical_level = "Intermediate"
    ),
    list(
      name = "Executive",
      goals = "High-level business health check",
      pain_points = "Gets lost in technical details",
      technical_level = "Basic"
    )
  )
)

# Convert critical issue to Notice stage
notice_stage <- bid_notice_issue(
  issue = critical_issue,
  previous_stage = interpret_stage
)

# Apply behavioral science
anticipate_stage <- bid_anticipate(
  previous_stage = notice_stage,
  bias_mitigations = list(
    choice_overload = "Reduce initial options, use progressive disclosure",
    attention_bias = "Use visual hierarchy to guide user focus",
    anchoring = "Lead with most important business metric"
  )
)

# Use telemetry flags to inform structure
flags <- bid_flags(issues)
structure_stage <- bid_structure(
  previous_stage = anticipate_stage,
  telemetry_flags = flags
)

# Define validation
validate_stage <- bid_validate(
  previous_stage = structure_stage,
  summary_panel = "Executive summary with key insights and trend indicators",
  next_steps = c(
    "Implement simplified landing page with key metrics",
    "Add progressive disclosure for detailed analytics",
    "Create role-based views for different user types",
    "Set up telemetry tracking to measure improvement"
  )
)
```

### Step 3: Implement Evidence-Based Improvements

```{r ecommerce-improvements, eval=FALSE}
# Before: Information overload (what telemetry revealed)
ui_before <- dashboardPage(
  dashboardHeader(title = "E-commerce Analytics"),
  dashboardSidebar(
    # 15+ filter options immediately visible
    selectInput("date_range", "Date Range", choices = date_options),
    selectInput("product_category", "Category", choices = categories, multiple = TRUE),
    selectInput("channel", "Sales Channel", choices = channels, multiple = TRUE),
    # ... 12 more filters
    actionButton("apply_filters", "Apply Filters")
  ),
  dashboardBody(
    # 12 value boxes competing for attention
    fluidRow(
      valueBoxOutput("revenue"), valueBoxOutput("orders"),
      valueBoxOutput("aov"), valueBoxOutput("conversion"),
      # ... 8 more value boxes
    ),
    # Multiple complex charts
    fluidRow(
      plotOutput("revenue_trend"), plotOutput("category_performance"),
      plotOutput("channel_analysis"), plotOutput("customer_segments")
    )
  )
)

# After: BID-informed design addressing telemetry insights
ui_after <- page_fillable(
  theme = bs_theme(version = 5, preset = "bootstrap"),

  # Address delayed interaction: Immediate value on landing
  layout_columns(
    col_widths = c(8, 4),

    # Primary business health (addresses anchoring bias)
    card(
      card_header("Business Performance Today", class = "bg-primary text-white"),
      layout_columns(
        col_widths = c(6, 6),
        value_box(
          "Revenue Today",
          "$47.2K",
          "vs. $43.1K yesterday (+9.5%)",
          showcase = bs_icon("graph-up"),
          theme = "success"
        ),
        div(
          h5("Key Insights", style = "margin-bottom: 15px;"),
          tags$ul(
            tags$li("Mobile traffic up 15%"),
            tags$li("Electronics category leading"),
            tags$li("Cart abandonment rate increased")
          ),
          actionButton("investigate_abandonment", "Investigate",
            class = "btn btn-warning btn-sm"
          )
        )
      )
    ),

    # Quick actions (addresses choice overload)
    card(
      card_header("Quick Actions"),
      div(
        actionButton(
          "todays_performance",
          "Today's Performance",
          class = "btn btn-primary btn-block mb-2"
        ),
        actionButton(
          "weekly_trends",
          "Weekly Trends",
          class = "btn btn-secondary btn-block mb-2"
        ),
        actionButton(
          "campaign_results",
          "Campaign Results",
          class = "btn btn-info btn-block mb-2"
        ),
        hr(),
        p("Need something specific?", style = "font-size: 0.9em; color: #666;"),
        actionButton(
          "show_filters",
          "Custom Analysis",
          class = "btn btn-outline-secondary btn-sm"
        )
      )
    )
  ),

  # Advanced options hidden by default (progressive disclosure)
  conditionalPanel(
    condition = "input.show_filters",
    card(
      card_header("Custom Analysis"),
      layout_columns(
        col_widths = c(3, 3, 3, 3),
        selectInput("time_period", "Time Period",
          choices = c("Today", "This Week", "This Month")
        ),
        selectInput("focus_area", "Focus Area",
          choices = c("Revenue", "Traffic", "Conversions")
        ),
        selectInput("comparison", "Compare To",
          choices = c("Previous Period", "Same Period Last Year")
        ),
        actionButton("apply_custom", "Analyze", class = "btn btn-primary")
      )
    )
  )
)
```

### Step 4: Measure the Impact

```{r measure-impact, eval=FALSE}
# After implementing changes, collect new telemetry
issues_after <- bid_telemetry(
  "ecommerce_dashboard_telemetry_after_changes.sqlite"
)

# Compare metrics
improvement_metrics <- tibble::tibble(
  metric = c(
    "Time to first interaction",
    "Session abandonment rate",
    "Critical issues found",
    "User satisfaction score"
  ),
  before = c("47 seconds", "12%", "2 issues", "6.2/10"),
  after = c("8 seconds", "3%", "0 issues", "8.1/10"),
  improvement = c("-83%", "-75%", "-100%", "+31%")
)

print(improvement_metrics)
```

## Complete Modern Workflow Example

Here's a complete workflow using the streamlined modern API:

```{r complete_example, eval=FALSE}
library(bidux)
library(dplyr)

# 1. Analyze telemetry with appropriate sensitivity
issues <- bid_telemetry(
  "telemetry.sqlite",
  thresholds = bid_telemetry_presets("moderate")
)

# 2. Review and triage issues
print(issues)

# Focus on high-impact issues
critical_issues <- issues |>
  filter(severity %in% c("critical", "high")) |>
  arrange(desc(impact_rate))

# 3. Address top issue through BID framework
if (nrow(critical_issues) > 0) {
  top_issue <- critical_issues |> slice_head(n = 1)

  # Interpret
  interpret_stage <- bid_interpret(
    central_question = "How can we address the most critical UX issue?",
    data_story = list(
      hook = "Critical issues identified from user behavior data",
      context = "Telemetry reveals specific friction points",
      tension = "Users struggling with core functionality",
      resolution = "Data-driven improvements to user experience"
    )
  )

  # Notice (using bridge function)
  notice_stage <- bid_notice_issue(
    issue = top_issue,
    previous_stage = interpret_stage
  )

  # Anticipate
  anticipate_stage <- bid_anticipate(
    previous_stage = notice_stage,
    bias_mitigations = list(
      choice_overload = "Hide advanced filters until needed",
      default_effect = "Pre-select most common filter values"
    )
  )

  # Structure (with telemetry flags)
  flags <- bid_flags(issues)
  structure_stage <- bid_structure(
    previous_stage = anticipate_stage,
    telemetry_flags = flags
  )

  # Validate
  validate_stage <- bid_validate(
    previous_stage = structure_stage,
    summary_panel = "Simplified filtering with progressive disclosure",
    next_steps = c(
      "Remove unused filters",
      "Implement progressive disclosure",
      "Add contextual help",
      "Re-test with telemetry after changes"
    )
  )

  # 4. Generate report
  improvement_report <- bid_report(validate_stage, format = "html")
}
```

## Best Practices

1. **Collect Sufficient Data**: Ensure you have telemetry from at least 50-100 sessions before analysis for reliable patterns.

2. **Choose Appropriate Sensitivity**: Use presets to match your context:
   - `strict`: New or critical applications
   - `moderate`: Most production applications (default)
   - `relaxed`: Mature, stable dashboards

3. **Regular Analysis**: Run telemetry analysis periodically (e.g., monthly) to catch emerging issues.

4. **Combine with Qualitative Data**: Use telemetry insights alongside user interviews and usability testing.

5. **Track Improvements**: After implementing changes, collect new telemetry to verify improvements:

```{r verify_improvements, eval=FALSE}
# Before changes
issues_before <- bid_telemetry(
  "telemetry_before.sqlite",
  thresholds = bid_telemetry_presets("moderate")
)

# After implementing improvements
issues_after <- bid_telemetry(
  "telemetry_after.sqlite",
  thresholds = bid_telemetry_presets("moderate") # Use same thresholds!
)

# Compare issue counts
comparison <- tibble::tibble(
  period = c("Before", "After"),
  total_issues = c(nrow(issues_before), nrow(issues_after)),
  critical = c(
    sum(issues_before$severity == "critical"),
    sum(issues_after$severity == "critical")
  ),
  high = c(
    sum(issues_before$severity == "high"),
    sum(issues_after$severity == "high")
  )
)

print(comparison)
```

6. **Document Patterns**: Build a knowledge base of common patterns in your domain:

```{r document_patterns, eval=FALSE}
# Save recurring patterns for future reference
telemetry_patterns <- tibble::tibble(
  pattern = c(
    "date_filter_confusion",
    "tab_discovery",
    "error_recovery"
  ),
  description = c(
    "Users often struggle with date range inputs",
    "Secondary tabs have low discovery",
    "Users abandon after errors"
  ),
  solution = c(
    "Use date presets (Today, This Week, This Month)",
    "Improve visual hierarchy and navigation cues",
    "Implement graceful error handling with recovery options"
  )
)
```

## Conclusion

The telemetry integration in `{bidux}` bridges the gap between user behavior data and design decisions. By automatically identifying friction points from real usage patterns, it provides concrete, evidence-based starting points for the BID framework.

**Key takeaways:**
- Use `bid_telemetry()` for modern tidy workflows (returns tibble)
- Use `bid_ingest_telemetry()` for backward compatibility (returns hybrid object)
- Configure sensitivity with `bid_telemetry_presets()`
- Bridge functions (`bid_notice_issue()`, `bid_notices()`) seamlessly integrate telemetry into BID workflow
- Extract global flags with `bid_flags()` to inform layout decisions
- Measure impact by comparing telemetry before and after changes

This data-driven approach ultimately leads to more user-friendly Shiny applications grounded in actual user behavior rather than assumptions.
