add homekeeper stack

This commit is contained in:
2026-07-06 19:25:38 +02:00
commit 58983e6855
90 changed files with 6816 additions and 0 deletions
+180
View File
@@ -0,0 +1,180 @@
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)
})
})
}