---
title: "Shiny Apps"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{a04_shiny_apps}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse   = TRUE,
  comment    = "#>",
  warning    = FALSE,
  message    = FALSE,
  out.width  = "95%",  # figures occupy ~95% of document width
  out.height = "auto",
  dpi        = 320,    # ensure figure quality
  fig.width  = 6,      # default aspect ratio (can be overridden per-figure)
  fig.height = 3
)
options(rmarkdown.html_vignette.check_title = FALSE)
```

This vignette shows how to render **interactive plots** and **tabular results** in Shiny using the visOmopResults package and functions that build on it. Specifically, we will demonstrate:

- **Small/static tables** → `gt`, for compact, fixed results such as cohort counts or characteristics.

- **Large/dynamic tables** → `DT` or `reactable`, for large results or those that require sorting/filtering to interpret.

- **Plots** → build with `ggplot2` and (optionally) wrap with `plotly::ggplotly()` for interactivity in shiny.

# Set up

Load packages and mock data.

```{r}
library(shiny)
library(bslib)
library(sortable)
library(shinyWidgets)
library(gt)
library(DT)
library(reactable)
library(plotly)
library(dplyr)
library(visOmopResults)
library(IncidencePrevalence)
library(CohortCharacteristics)
library(shinycssloaders)

# Mock results in visOmopResults
data <- visOmopResults::data

# Remove global options (just in case we have them from previous work)
setGlobalPlotOptions(style = NULL, type = NULL)
setGlobalTableOptions(style = NULL, type = NULL)
```

## Mock data

We will use 3 different mock results, which are: 

- **Baseline characteristics** from the `CohortCharacteristics` package

- **Incidence** results from the `IncidencePrevalence` package

- **Large scale characterisation**, which is not a `<summarised_result>`


# Shiny App: User Inteface

The Shiny app has three panels, one for each result. All allow filtering by sex strata and provide panel-specific visualization options:


**- Baseline characteristics:** shows a `gt` table with controls for headers, groups, and hidden columns..

**- Large Scale characteristics:** renders as a `datatable` or `reactable`, with options to group or hide columns. 

**- Incidence:** displays a static `ggplot` or interactive `plotly` plot, with options for colouring, faceting, and ribbons.


## Example UI Code

```{r, eval=FALSE}
ui <- bslib::page_navbar(
  title = "visOmopResults for Shiny",
  window_title = "visOmopResults • Shiny",
  collapsible = TRUE,
  # Baseline Characteristics (GT table)
  bslib::nav_panel(
    title = "Baseline Characteristics",
    icon = icon("users-gear"),
    bslib::layout_sidebar(
      sidebar = bslib::sidebar(
        title = "Filters",
        shinyWidgets::pickerInput(
          inputId = "summarise_characteristics_sex",
          label = "Sex",
          choices = c("overall", "Male", "Female"),
          selected = "overall",
          multiple = TRUE
        ),
        width = 320,
        position = "left",
        open = TRUE
      ),
      bslib::card(
        full_screen = TRUE,
        bslib::card_header("Table layout"),
        bslib::layout_sidebar(
          sidebar = bslib::sidebar(
            title = "Arrange columns",
            sortable::bucket_list(
              header = NULL,
              group_name = "col-buckets",
              orientation = "horizontal",
              add_rank_list(
                text = "None",
                labels = c("variable_name", "variable_level", "estimate_name"),
                input_id = "summarise_characteristics_table_none"
              ),
              add_rank_list(
                text = "Header",
                labels = c("sex"),
                input_id = "summarise_characteristics_table_header"
              ),
              add_rank_list(
                text = "Group columns",
                labels = c("cdm_name", "cohort_name"),
                input_id = "summarise_characteristics_table_group_column"
              ),
              add_rank_list(
                text = "Hide",
                labels = "table_name",
                input_id = "summarise_characteristics_table_hide"
              )
            ),
            position = "right",
            width = 400,
            open = FALSE
          ),
          # GT output
          gt::gt_output("summarise_characteristics_table") |>
            shinycssloaders::withSpinner(type = 4)
        )
      )
    )
  ),
  # Large Scale Characterisation (DT / reactable)
  bslib::nav_panel(
    title = "Large Scale Characterisation",
    icon = icon("table"),
    bslib::layout_sidebar(
      sidebar = bslib::sidebar(
        # title = "Display options",
        shinyWidgets::pickerInput(
          inputId = "large_scale_sex",
          label = "Sex",
          choices = c("overall", "Male", "Female"),
          selected = "overall",
          multiple = TRUE
        ),
        radioButtons(
          "large_engine",
          "Renderer",
          choices = c("DT", "reactable"),
          inline = TRUE
        ),
        sortable::bucket_list(
          header = NULL,
          group_name = "col-buckets",
          orientation = "horizontal",
          add_rank_list(
            text = "None",
            labels = c("variable_name", "variable_level", "estimate_name"),
            input_id = "large_scale_none"
          ),
          add_rank_list(
            text = "Group columns",
            labels = c("cdm_name", "cohort_name"),
            input_id = "large_scale_group_column"
          ),
          add_rank_list(
            text = "Hide",
            labels = character(),
            input_id = "large_scale_hide"
          )
        ),
        width = 320
      ),
      bslib::card(
        full_screen = TRUE,
        bslib::card_header("Cohort characteristics (large-scale)"),
        conditionalPanel(
          "input.large_engine == 'DT'",
          DTOutput("large_dt") |> shinycssloaders::withSpinner(type = 4)
        ),
        conditionalPanel(
          "input.large_engine == 'reactable'",
          reactableOutput("large_reactable") |> shinycssloaders::withSpinner(type = 4)
        )
      )
    )
  ),
  # Incidence (ggplot → plotly)
  bslib::nav_panel(
    title = "Incidence",
    icon = icon("chart-line"),
    bslib::layout_sidebar(
      sidebar = bslib::sidebar(
        title = "Plot options",
        shinyWidgets::pickerInput(
          "incidence_sex",
          "Sex strata",
          choices = c("overall", "Male", "Female"),
          selected = "overall",
          multiple = TRUE
        ),
        shinyWidgets::pickerInput(
          inputId = "facet",
          label = "Facet",
          selected = "sex",
          multiple = TRUE,
          choices = c("cdm_name", "incidence_start_date", "sex", "outcome_cohort_name"),
        ),
        shinyWidgets::pickerInput(
          inputId = "colour",
          label = "Colour",
          selected = "outcome_cohort_name",
          multiple = TRUE,
          choices = c("cdm_name", "incidence_start_date",  "sex", "outcome_cohort_name")
        ),
        checkboxInput("inc_ribbon", "Show ribbon (CI)", TRUE),
        checkboxInput("interactive", "Interactive Plot", TRUE),
        width = 320
      ),
      bslib::card(
        full_screen = TRUE,
        bslib::card_header("Incidence over time"),
        uiOutput("incidence_plot", height = "520px") |> shinycssloaders::withSpinner(type = 4)
      )
    )
  )
)
```


# Shiny App: Server

### 1) Baseline characteristics

The server filters results by the selected sex and creates a `gt` table using the `tableCharacteristics()` function from the `CohortCharacteristics` package. This function is built on **visOmopResults**, which ensures consistent styling and supports arguments to define headers, group columns, and hide columns.  

If you have your own `<summarised_result>` table, which don't has a dedicated table function, you can instead use `visOmopTable()` to generate a `gt` table in Shiny. This allows you to group estimates and configure header, group, and hidden column options in a similar way.


### 2) Large Scale characteristics

These results are not in `<summarised_result>` format, as shown below:

```{r}
data$large_scale_characteristics
```


In this case, we use `visTable()` to generate tables as either a `datatable` or a `reactable`, depending on the user’s choice in the UI. The table type is specified with the type argument.

For both table types, we pass the UI-selected columns to `groupColumn` and `hide.` We do not generate a header for this result, as it would require restructuring the estimates into a single "estimate_value" column.

The look and behaviour of the tables can be customised through the style argument. Available options can be explored with:

- `tableStyle("datatable")`

- `tableStyle("reactable")`

In this vignette, we modify the `datatable` style in the server code so filters appear at the top of the table instead of the default bottom.


### 3) Incidence

For incidence results, we use the `plotIncidence()` function from the `IncidencePrevalence` package. This function creates a `ggplot` object, which can be rendered as a static plot with `plotOutput` or as an interactive plot with `plotlyOutput`. Users can also select which columns to use for colouring and faceting, and whether to display confidence interval ribbons.

For other results—whether `<summarised_reuslt>` class or not—you can generate plots in a similar way by using the plotting functions available in `visOmopResults.`


## Example Server Code

> Note: Both `CohortCharacteristics` and `IncidencePrevalence` functions for plotting and tabulation are built on `visOmopResults`, which means they share a consistent interface and style.


```{r, eval=FALSE}
server <- function(input, output, session) {
  # Baseline (GT)
  output$summarise_characteristics_table <- gt::render_gt({
    data$summarised_characteristics |>
      # filter results by sex
      filterStrata(sex %in% input$summarise_characteristics_sex) |>
      # create GT table
      CohortCharacteristics::tableCharacteristics(
        header = input$summarise_characteristics_table_header,
        groupColumn = input$summarise_characteristics_table_group_column,
        hide = input$summarise_characteristics_table_hide,
        type = "gt"
      )
  })

  # Large scale characteristics
  getLargeScaleResults <- reactive({
    data$large_scale_characteristics |>
      filter(.data$sex %in% input$large_scale_sex)
  })
  # To render as DT
  output$large_dt <- renderDT({
    getLargeScaleResults() |>
      visTable(
        hide = input$large_scale_hide,
        groupColumn = input$large_scale_group_column,
        type = "datatable",
        style = list(
          filter = "top",
          searchHighlight = TRUE,
          rownames = FALSE
        )
      )
  })
  # To render as reactable
  output$large_reactable <- reactable::renderReactable({
    getLargeScaleResults() |>
      visTable(
        hide = input$large_scale_hide,
        groupColumn = input$large_scale_group_column,
        type = "reactable",
        style = "default"
      )
  })

  # Incidence
  getIncidencePlot <- reactive({
    data$incidence |>
      filterStrata(sex %in% input$incidence_sex) |>
      plotIncidence(
        colour = input$colour,
        facet = input$facet,
        ribbon = input$inc_ribbon
      ) +
      theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust = 1))
  })
  output$incidence_plot <- renderUI({
    plt <- getIncidencePlot()
    if (input$interactive) {
      ggplotly(plt)
    } else {
      renderPlot(plt)
    }
  })
}
```

# Run App

To run the Shiny app, copy the code chunks provided in this vignette into a script named **`app.R`**, and add the following line at the end:

```{r, eval=FALSE}
shinyApp(ui, server)
```


You can find the complete code run the ShinyApp [here](https://github.com/darwin-eu/visOmopResults/tree/main/inst/app.R). 
