screen_checklists_ui <- function(id) { ns <- NS(id) bslib::navset_card_tab( bslib::nav_panel("Ausführen", shiny::div(class = "p-2", shiny::uiOutput(ns("checklist_sel")), shiny::uiOutput(ns("run_items")) ) ), bslib::nav_panel("Verwalten", shiny::div(class = "p-2", shiny::div(class = "d-flex gap-2 mb-3", shiny::textInput(ns("new_name"), NULL, placeholder = "Neue Checkliste..."), shiny::selectInput(ns("new_scope"), NULL, choices = c("Volk" = "colony", "Stand" = "apiary"), width = "120px"), shiny::actionButton(ns("add_list"), shiny::icon("plus"), class = "btn btn-primary") ), shiny::uiOutput(ns("manage_list")) ) ) ) } screen_checklists_server <- function(id, db, nav) { moduleServer(id, function(input, output, session) { ns <- session$ns reload <- reactiveVal(0) sel_list <- reactiveVal(NULL) checklists <- reactive({ reload(); db_checklists_list(db) }) output$checklist_sel <- renderUI({ cls <- checklists() if (nrow(cls) == 0) return(shiny::p(class = "text-muted", "Noch keine Checklisten. Im Tab 'Verwalten' anlegen.")) shiny::selectInput(ns("run_list"), "Checkliste", choices = c("Bitte wählen" = "", setNames(cls$id, paste0("[", cls$scope, "] ", cls$name))) ) }) output$run_items <- renderUI({ req(input$run_list, input$run_list != "", nav$colony_id) lid <- as.integer(input$run_list) scope_id <- nav$colony_id date <- as.character(Sys.Date()) items <- db_checklist_items_list(db, lid) if (nrow(items) == 0) return(shiny::p(class = "text-muted", "Keine Einträge.")) done <- db_checklist_completions_list(db, lid, scope_id, date) shiny::div(class = "list-group", lapply(seq_len(nrow(items)), function(i) { item <- items[i, ] completed <- item$id %in% done$item_id shiny::div( class = paste("list-group-item d-flex align-items-center gap-3 py-3", if (completed) "list-group-item-success" else ""), shiny::tags$input( class = "form-check-input flex-shrink-0", type = "checkbox", checked = if (completed) NA else NULL, style = "width:1.3em;height:1.3em;cursor:pointer", onclick = sprintf( "Shiny.setInputValue('%s',{id:%d}, {priority:'event'})", ns("toggle"), item$id ) ), shiny::span(item$text, style = if (completed) "text-decoration:line-through;opacity:.6" else "") ) }) ) }) observeEvent(input$toggle, { req(input$run_list, nav$colony_id) db_checklist_completions_toggle(db, checklist_id = as.integer(input$run_list), item_id = as.integer(input$toggle$id), scope_id = nav$colony_id, date = as.character(Sys.Date()) ) }) # ---- Manage tab --------------------------------------------------------- output$manage_list <- renderUI({ cls <- checklists() if (nrow(cls) == 0) return(shiny::p(class = "text-muted", "Keine Checklisten.")) lapply(seq_len(nrow(cls)), function(i) { row <- cls[i, ] shiny::div(class = "card mb-2", bslib::card_body( class = "py-2", shiny::div(class = "d-flex justify-content-between align-items-center", shiny::div( shiny::tags$strong(row$name), shiny::tags$small(class = "text-muted ms-2", paste0("[", row$scope, "]")) ), shiny::div( shiny::actionButton(ns(paste0("sel_", row$id)), "Einträge", class = "btn btn-sm btn-outline-secondary me-1"), shiny::actionButton(ns(paste0("del_list_", row$id)), shiny::icon("trash"), class = "btn btn-sm btn-outline-danger") ) ), shiny::uiOutput(ns(paste0("items_", row$id))) ) ) }) }) observe({ cls <- checklists() lapply(cls$id, function(lid) { local({ llid <- lid observeEvent(input[[paste0("sel_", llid)]], sel_list(llid), ignoreInit = TRUE) observeEvent(input[[paste0("del_list_", llid)]], { db_checklists_delete(db, llid) if (identical(sel_list(), llid)) sel_list(NULL) reload(reload() + 1L) }, ignoreInit = TRUE) }) }) }) observe({ lid <- sel_list() req(lid) items <- db_checklist_items_list(db, lid) output[[paste0("items_", lid)]] <- renderUI({ shiny::div(class = "mt-2", if (nrow(items) > 0) shiny::tags$ul(class = "list-unstyled mb-2", lapply(seq_len(nrow(items)), function(i) { it <- items[i, ] shiny::tags$li(class = "d-flex justify-content-between align-items-center small py-1 border-bottom", it$text, shiny::actionButton(ns(paste0("del_item_", it$id)), "×", class = "btn btn-sm btn-link text-danger p-0", onclick = "event.stopPropagation()" ) ) }) ), shiny::div(class = "d-flex gap-2", shiny::textInput(ns(paste0("item_txt_", lid)), NULL, placeholder = "Neuer Eintrag..."), shiny::actionButton(ns(paste0("add_item_", lid)), shiny::icon("plus"), class = "btn btn-outline-primary") ) ) }) lapply(items$id, function(iid) { local({ liid <- iid observeEvent(input[[paste0("del_item_", liid)]], { db_checklist_items_delete(db, liid) reload(reload() + 1L) }, ignoreInit = TRUE) }) }) observeEvent(input[[paste0("add_item_", lid)]], { txt <- input[[paste0("item_txt_", lid)]] req(nchar(trimws(txt)) > 0) its <- db_checklist_items_list(db, lid) pos <- if (nrow(its) > 0) max(its$position) + 1L else 1L db_checklist_items_insert(db, lid, trimws(txt), pos) shiny::updateTextInput(session, paste0("item_txt_", lid), value = "") reload(reload() + 1L) }, ignoreInit = TRUE) }) observeEvent(input$add_list, { req(nchar(trimws(input$new_name)) > 0) db_checklists_insert(db, trimws(input$new_name), input$new_scope) shiny::updateTextInput(session, "new_name", value = "") reload(reload() + 1L) }) }) }