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
+181
View File
@@ -0,0 +1,181 @@
VARROA_METHODS_DISPLAY <- c(
"Nat. Totenfall" = "natuerlicher_totenfall",
"Alkoholwäsche" = "alkoholwaesche",
"Puderzucker" = "puderzucker"
)
screen_varroa_ui <- function(id) {
ns <- NS(id)
bslib::navset_card_tab(
bslib::nav_panel("Kontrolle",
shiny::div(class = "p-2",
section_card(NULL,
shiny::div(class = "row g-2 py-2",
shiny::div(class = "col-4",
shiny::dateInput(ns("count_date_from"), "Von", value = Sys.Date() - 3L, language = "de", weekstart = 1)
),
shiny::div(class = "col-4",
shiny::dateInput(ns("count_date_to"), "Bis", value = Sys.Date(), language = "de", weekstart = 1)
),
shiny::div(class = "col-4",
shiny::textInput(ns("count_time"), "Uhrzeit",
value = format(Sys.time(), "%H:%M"), placeholder = "HH:MM")
)
)
),
btn_group_row(ns, "count_method", "Methode", VARROA_METHODS_DISPLAY),
stepper_row(ns, "count_val", "Anzahl / Wert"),
shiny::uiOutput(ns("milben_pro_tag_ui")),
section_card(NULL,
shiny::div(class = "py-2",
shiny::div(
class = "bk-check d-flex justify-content-between align-items-center py-2 border-bottom",
shiny::tags$label(class = "fw-semibold", `for` = ns("is_ctrl"), "Nachkontrolle zu Behandlung?"),
shiny::checkboxInput(ns("is_ctrl"), NULL, value = FALSE)
),
shiny::uiOutput(ns("ctrl_extra_ui")),
shiny::textAreaInput(ns("count_notes"), "Notizen", rows = 2, width = "100%")
)
),
shiny::div(class = "d-grid mt-2",
shiny::actionButton(ns("save_count"), "Kontrolle speichern", class = "btn btn-primary btn-lg")
)
)
),
bslib::nav_panel("Behandlung",
shiny::div(class = "p-2",
section_card(NULL,
shiny::div(class = "row g-2 py-2",
shiny::div(class = "col-6",
shiny::dateInput(ns("treat_date"), "Datum", value = Sys.Date(), language = "de", weekstart = 1)
),
shiny::div(class = "col-6",
shiny::textInput(ns("treat_time"), "Uhrzeit",
value = format(Sys.time(), "%H:%M"), placeholder = "HH:MM")
),
shiny::div(class = "col-12",
shiny::selectizeInput(ns("treat_product"), "Mittel *",
choices = c("Mittel wählen…" = "", VARROA_PRODUCTS),
options = list(create = TRUE, placeholder = "Mittel wählen…")
)
)
)
),
section_card(NULL,
shiny::div(class = "py-2",
shiny::uiOutput(ns("treat_method_ui")),
shiny::textInput(ns("treat_dosage"), "Dosierung", placeholder = "2,3 g / Volk"),
shiny::textAreaInput(ns("treat_notes"), "Notizen", rows = 2, width = "100%")
)
),
shiny::div(class = "d-grid mt-2",
shiny::actionButton(ns("save_treat"), "Behandlung speichern", class = "btn btn-warning btn-lg")
)
)
)
)
}
screen_varroa_server <- function(id, db, nav) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
count_method <- btn_group_server("count_method", ns, input, output,
VARROA_METHODS_DISPLAY, default = "natuerlicher_totenfall")
count_val <- stepper_server("count_val", input, output)
observeEvent(nav$screen, {
if (nav$screen == "varroa") {
count_val(NA_integer_)
shiny::updateCheckboxInput(session, "is_ctrl", value = FALSE)
shiny::updateDateInput(session, "count_date_from", value = Sys.Date() - 3L)
shiny::updateDateInput(session, "count_date_to", value = Sys.Date())
shiny::updateTextInput(session, "count_time", value = format(Sys.time(), "%H:%M"))
shiny::updateTextInput(session, "treat_time", value = format(Sys.time(), "%H:%M"))
}
}, ignoreInit = TRUE)
output$milben_pro_tag_ui <- renderUI({
v <- count_val()
d_von <- input$count_date_from
d_bis <- input$count_date_to
if (is.na(v) || is.null(d_von) || is.null(d_bis)) return(NULL)
days <- as.numeric(as.Date(d_bis) - as.Date(d_von))
if (days < 0) return(shiny::p(class = "text-danger small py-1", "Bis-Datum liegt vor Von-Datum."))
mpt <- round(v / max(1, days), 1)
cls <- if (mpt < 1) "text-success" else if (mpt < 3) "text-warning" else "text-danger"
shiny::div(
class = paste("text-center py-2 fw-bold fs-4", cls),
paste0(mpt, " Milben/Tag"),
shiny::tags$div(class = "text-muted fw-normal small",
paste0(v, " Milben über ", days, " Tage"))
)
})
output$ctrl_extra_ui <- renderUI({
req(isTRUE(input$is_ctrl), nav$colony_id)
treats <- db_varroa_treatments_list(db, nav$colony_id)
if (nrow(treats) == 0)
return(shiny::p(class = "text-muted small py-2", "Keine Behandlungen erfasst."))
shiny::tagList(
shiny::selectInput(ns("ctrl_treatment"), "Zur Behandlung",
choices = setNames(treats$id, paste(treats$date, treats$product))
),
shiny::textInput(ns("ctrl_result"), "Ergebnis / Befund")
)
})
output$treat_method_ui <- renderUI({
product <- input$treat_product
methods <- if (!is.null(product) && product != "") VARROA_APPLICATION_METHODS[[product]] else NULL
if (!is.null(methods)) {
shiny::selectizeInput(ns("treat_method"), "Anwendung",
choices = c("Anwendung wählen…" = "", methods),
options = list(create = TRUE, placeholder = "Anwendung wählen…")
)
} else {
shiny::textInput(ns("treat_method"), "Anwendung", placeholder = "z. B. Träufeln")
}
})
observeEvent(input$save_count, {
req(nav$colony_id, !is.na(count_val()))
db_varroa_counts_insert(db,
colony_id = nav$colony_id,
date_from = input$count_date_from,
date = input$count_date_to,
method = count_method() %||% "natuerlicher_totenfall",
count = count_val(),
notes = input$count_notes,
time = input$count_time %||% NA
)
if (isTRUE(input$is_ctrl) && !is.null(input$ctrl_treatment)) {
db_varroa_controls_insert(db,
treatment_id = as.integer(input$ctrl_treatment),
colony_id = nav$colony_id,
date = input$count_date,
result = input$ctrl_result %||% "",
notes = input$count_notes
)
}
count_val(NA_integer_)
shiny::updateTextAreaInput(session, "count_notes", value = "")
shiny::updateCheckboxInput(session, "is_ctrl", value = FALSE)
nav$screen <- "colony"
})
observeEvent(input$save_treat, {
req(nav$colony_id, nchar(trimws(input$treat_product)) > 0)
db_varroa_treatments_insert(db,
colony_id = nav$colony_id,
date = input$treat_date,
product = trimws(input$treat_product),
method = input$treat_method,
dosage = input$treat_dosage,
notes = input$treat_notes,
time = input$treat_time %||% NA
)
nav$screen <- "colony"
})
})
}