Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 123 additions & 0 deletions WWW/stepper.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/* ==========================================================================
Workflow Stepper — Clean, minimal text-based design
========================================================================== */

.workflow-stepper {
padding: 10px 24px 8px;
margin: 0;
background: #ffffff;
border-bottom: 1px solid #e8e8e8;
}

.workflow-stepper.stepper-hidden {
display: none;
}

/* Header: section name + step count */
.stepper-header {
display: flex;
justify-content: space-between;
align-items: baseline;
margin-bottom: 8px;
}

.stepper-section-name {
font-size: 13px;
font-weight: 600;
color: #083B3F;
letter-spacing: 0.2px;
}

.stepper-step-count {
font-size: 11px;
font-weight: 500;
color: #999;
}

/* Step items row */
.stepper-items {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 4px 0;
}

/* Individual step */
.stepper-item {
display: inline-flex;
align-items: center;
cursor: pointer;
padding: 3px 8px;
border-radius: 4px;
transition: background 0.2s ease;
font-size: 12.5px;
white-space: nowrap;
}

.stepper-item:hover {
background: #f0f7f0;
}

/* Separator dot between steps */
.stepper-sep {
color: #d0d0d0;
font-size: 10px;
margin: 0 2px;
user-select: none;
}

/* ---- States ---- */

/* Completed */
.stepper-item.completed {
color: #00BFC4;
font-weight: 500;
}

.stepper-item.completed .stepper-icon {
color: #00BFC4;
margin-right: 4px;
font-size: 12px;
}

/* Active */
.stepper-item.active {
color: #083B3F;
font-weight: 700;
background: #eef8ee;
border: 1px solid #7BC148;
}

.stepper-item.active .stepper-icon {
color: #7BC148;
margin-right: 4px;
font-size: 12px;
}

/* Inactive / future */
.stepper-item.inactive {
color: #aaa;
font-weight: 400;
}

.stepper-item.inactive .stepper-icon {
color: #ccc;
margin-right: 4px;
font-size: 10px;
}

/* ---- Responsive ---- */
@media (max-width: 768px) {
.stepper-items {
gap: 2px 0;
}

.stepper-item {
font-size: 11px;
padding: 2px 5px;
}

.stepper-section-name {
font-size: 12px;
}
}
24 changes: 18 additions & 6 deletions server.R
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ library(Rautoml)
options(shiny.maxRequestSize=3000000*1024^2)

function(input, output, session){
waiter_show(
html = spin_loaders(id = 2, style="width:56px;height:56px;color:#7BC148;"),
waiter::waiter_show(
html = waiter::spin_loaders(id = 2, style="width:56px;height:56px;color:#7BC148;"),
color = "#FFF"
)

Expand All @@ -15,8 +15,8 @@ function(input, output, session){

observeEvent(USER$logged_in, {
req(isTRUE(USER$logged_in))
waiter_show(
html = spin_loaders(id = 3, style="width:56px;height:56px;color:#7BC148;"),
waiter::waiter_show(
html = waiter::spin_loaders(id = 3, style="width:56px;height:56px;color:#7BC148;"),
color = "#FFF"
)

Expand Down Expand Up @@ -305,6 +305,18 @@ function(input, output, session){

menu_translation()

#### ---- Workflow Stepper (reactive rendering) ------------------------------
output$workflow_stepper_bar <- renderUI({
active_tab <- input$tabs
current_step <- get_step_for_tab(active_tab)
render_stepper_html(current_step)
})

## Stepper click navigation
observeEvent(input$stepper_nav, {
shinydashboard::updateTabItems(session, "tabs", selected = input$stepper_nav)
}, ignoreInit = TRUE)

#### ---- Change language ----------------------------------------------------
output$change_language = change_language

Expand Down Expand Up @@ -738,9 +750,9 @@ function(input, output, session){
iv_url$enable()
iv_ml$enable()

waiter_hide()
waiter::waiter_hide()
}, ignoreInit = FALSE)

waiter_hide()
waiter::waiter_hide()

}
2 changes: 1 addition & 1 deletion server/auth.R
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ user_auth <- function(input, output, session) {
"last_name" = "Last Name"
),
cookie_name = "aphrc",
cookie_password = "aphrcpass1"
salt = "aphrcpass1"
)

# Show name somewhere if you want (e.g. in header)
Expand Down
5 changes: 5 additions & 0 deletions ui/dashboard_body.R
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## Load UI files
source("ui/stepper_ui.R")
source("ui/sourcedata_ui.R")
source("ui/overview_ui.R")
source("ui/explore_ui.R")
Expand Down Expand Up @@ -75,6 +76,10 @@ aphrcBody <- dashboardBody(
});
"))
),
## ---- Stepper CSS ----
tags$head(tags$link(rel = "stylesheet", type = "text/css", href = "stepper.css")),
## ---- Workflow Stepper (above tab content) ----
stepper_ui(),

tabItems(
tabItem(tabName = "homePage"
Expand Down
95 changes: 95 additions & 0 deletions ui/stepper_ui.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# ui/stepper_ui.R
# ============================================================================
# Workflow Stepper — clean text-based progress indicator
# White background, checkmark text, section header with step count
# ============================================================================

workflow_steps <- list(
list(num = 1, label = "Upload", section = "Data Upload", tabs = c("sourcedata")),
list(num = 2, label = "Explore", section = "Data Exploration", tabs = c("Overview", "Explore")),
list(num = 3, label = "Transform", section = "Data Transformation", tabs = c("Transform", "combineData")),
list(num = 4, label = "Visualize", section = "Visualization", tabs = c("visualizeData", "summarizeAutomatic", "summarizeCustom")),
list(num = 5, label = "Model Setup", section = "Machine Learning / Model Setup", tabs = c("setupModels", "featureEngineering")),
list(num = 6, label = "Train", section = "Model Training", tabs = c("trainModel")),
list(num = 7, label = "Deploy", section = "Validate & Deploy", tabs = c("validateDeployModel", "predictClassify"))
)

non_workflow_tabs <- c("homePage", "researchQuestions", "deeplearning", "cnndeep",
"evidenceQuality", "achilles", "omop_visualizations",
"CohortConstructor", "FeatureExtraction", "addResources")

get_step_for_tab <- function(tab_name) {
if (is.null(tab_name) || tab_name %in% non_workflow_tabs) return(0)
for (step in workflow_steps) {
if (tab_name %in% step$tabs) return(step$num)
}
return(0)
}

stepper_ui <- function() {
tags$div(
id = "workflow-stepper-container",
uiOutput("workflow_stepper_bar")
)
}

render_stepper_html <- function(active_step) {
if (active_step == 0) {
return(tags$div(class = "workflow-stepper stepper-hidden"))
}

# Get current section name
current_section <- workflow_steps[[active_step]]$section
total_steps <- length(workflow_steps)

# Build step items
step_elements <- list()

for (i in seq_along(workflow_steps)) {
step <- workflow_steps[[i]]

if (i < active_step) {
# Completed
el <- tags$span(
class = "stepper-item completed",
onclick = sprintf("Shiny.setInputValue('stepper_nav','%s',{priority:'event'});", step$tabs[1]),
tags$span(class = "stepper-icon", HTML("&#10004;")),
step$label
)
} else if (i == active_step) {
# Active
el <- tags$span(
class = "stepper-item active",
tags$span(class = "stepper-icon", HTML("&#9658;")),
step$label
)
} else {
# Inactive
el <- tags$span(
class = "stepper-item inactive",
onclick = sprintf("Shiny.setInputValue('stepper_nav','%s',{priority:'event'});", step$tabs[1]),
tags$span(class = "stepper-icon", HTML("&#9675;")),
step$label
)
}

step_elements <- c(step_elements, list(el))

# Add separator (except after last)
if (i < total_steps) {
step_elements <- c(step_elements, list(tags$span(class = "stepper-sep", HTML("&middot;"))))
}
}

tags$div(
class = "workflow-stepper",
# Header row
tags$div(
class = "stepper-header",
tags$span(class = "stepper-section-name", current_section),
tags$span(class = "stepper-step-count", paste0("Step ", active_step, " of ", total_steps))
),
# Steps row
tags$div(class = "stepper-items", step_elements)
)
}