screen_apiaries_ui <- function(id) { ns <- NS(id) shiny::tagList( shiny::div(class = "mb-3", style = "border-radius:8px;overflow:hidden;box-shadow:0 1px 4px rgba(0,0,0,.15)", leaflet::leafletOutput(ns("overview_map"), height = "200px", width = "100%") ), shiny::uiOutput(ns("cards")), shiny::br(), shiny::actionButton(ns("add_btn"), shiny::tagList(shiny::icon("plus"), " Neuer Stand"), class = "add-dashed btn", style = "width:100%;min-height:80px" ) ) } screen_apiaries_server <- function(id, db, nav) { moduleServer(id, function(input, output, session) { ns <- session$ns reload <- reactiveVal(0) edit_id <- reactiveVal(NULL) add_coords <- reactiveValues(lat = NA_real_, lng = NA_real_) edit_coords <- reactiveValues(lat = NA_real_, lng = NA_real_) apiaries <- reactive({ reload(); db_apiaries_list(db) }) output$cards <- renderUI({ ap <- apiaries() if (nrow(ap) == 0) return(shiny::p(class = "text-muted", "Noch keine Bienenstände angelegt.")) lapply(seq_len(nrow(ap)), function(i) { row <- ap[i, ] shiny::div( class = "card tap-card mb-3", onclick = sprintf("Shiny.setInputValue('%s', %d, {priority:'event'})", ns("select"), row$id), bslib::card_body( class = "d-flex justify-content-between align-items-start", shiny::div( shiny::tags$strong(class = "fs-5", row$name), shiny::tags$p(class = "text-muted mb-0 small", if (!is.na(row$location_text) && nchar(row$location_text) > 0) row$location_text ) ), shiny::div( class = "d-flex flex-column align-items-end gap-1", shiny::tags$span(class = "badge bg-secondary", paste(row$colony_count, "Völker")), shiny::div( shiny::actionButton(ns(paste0("edit_", row$id)), shiny::icon("pencil"), class = "btn btn-sm btn-link p-0 text-muted", onclick = "event.stopPropagation()" ) ) ) ) ) }) }) observeEvent(input$select, { nav$apiary_id <- input$select nav$screen <- "colonies" }) # ---- Overview map ------------------------------------------------------- output$overview_map <- leaflet::renderLeaflet({ ap <- apiaries() m <- leaflet::leaflet(options = leaflet::leafletOptions(zoomControl = FALSE, attributionControl = FALSE)) |> leaflet::addTiles(group = "Karte") |> leaflet::addProviderTiles("Esri.WorldImagery", group = "Satellit") |> leaflet::addLayersControl(baseGroups = c("Karte", "Satellit"), options = leaflet::layersControlOptions(collapsed = TRUE)) with_coords <- ap[!is.na(ap$lat) & !is.na(ap$lng), ] if (nrow(with_coords) > 0) { m <- leaflet::addMarkers(m, lng = with_coords$lng, lat = with_coords$lat, label = with_coords$name, layerId = as.character(with_coords$id), clusterOptions = leaflet::markerClusterOptions() ) } else { m <- leaflet::setView(m, lng = 10.45, lat = 51.17, zoom = 6) } m }) observeEvent(input$overview_map_marker_click, { click <- input$overview_map_marker_click if (!is.null(click$id)) { nav$apiary_id <- as.integer(click$id) nav$screen <- "colonies" } }) # ---- Add modal ----------------------------------------------------------- output$add_map <- leaflet::renderLeaflet({ leaflet::leaflet() |> leaflet::addTiles(group = "Karte") |> leaflet::addProviderTiles("Esri.WorldImagery", group = "Satellit") |> leaflet::addLayersControl(baseGroups = c("Karte", "Satellit"), options = leaflet::layersControlOptions(collapsed = TRUE)) |> leaflet::setView(lng = 10.45, lat = 51.17, zoom = 6) }) observeEvent(input$add_map_click, { cl <- input$add_map_click add_coords$lat <- cl$lat; add_coords$lng <- cl$lng p <- leaflet::leafletProxy("add_map", session) leaflet::clearMarkers(p) leaflet::addMarkers(p, lng = cl$lng, lat = cl$lat) }) observeEvent(input$add_btn, { add_coords$lat <- NA_real_; add_coords$lng <- NA_real_ shiny::showModal(shiny::modalDialog( title = "Neuer Bienenstand", size = "l", footer = shiny::tagList( shiny::modalButton("Abbrechen"), shiny::actionButton(ns("confirm_add"), "Anlegen", class = "btn-primary") ), shiny::textInput(ns("new_name"), "Name *"), shiny::textInput(ns("new_loc"), "Standortbeschreibung"), shiny::tags$p(class = "text-muted small mt-3 mb-1", shiny::tags$em("Standort auf Karte setzen (optional):")), leaflet::leafletOutput(ns("add_map"), height = "280px"), shiny::uiOutput(ns("add_coords_lbl")), shiny::textAreaInput(ns("new_notes"), "Notizen", rows = 2) )) }) output$add_coords_lbl <- renderUI({ req(!is.na(add_coords$lat)) shiny::tags$p(class = "text-muted small mt-1", sprintf("%.5f, %.5f", add_coords$lat, add_coords$lng)) }) observeEvent(input$confirm_add, { req(nchar(trimws(input$new_name)) > 0) db_apiaries_insert(db, name = trimws(input$new_name), location_text = input$new_loc, lat = add_coords$lat, lng = add_coords$lng, notes = input$new_notes ) shiny::removeModal(); reload(reload() + 1L) }) # ---- Edit modal ---------------------------------------------------------- output$edit_map <- leaflet::renderLeaflet({ lat <- if (!is.na(edit_coords$lat)) edit_coords$lat else 51.17 lng <- if (!is.na(edit_coords$lng)) edit_coords$lng else 10.45 zoom <- if (!is.na(edit_coords$lat)) 13L else 6L m <- leaflet::leaflet() |> leaflet::addTiles(group = "Karte") |> leaflet::addProviderTiles("Esri.WorldImagery", group = "Satellit") |> leaflet::addLayersControl(baseGroups = c("Karte", "Satellit"), options = leaflet::layersControlOptions(collapsed = TRUE)) |> leaflet::setView(lng = lng, lat = lat, zoom = zoom) if (!is.na(edit_coords$lat)) m <- leaflet::addMarkers(m, lng = lng, lat = lat) m }) observeEvent(input$edit_map_click, { cl <- input$edit_map_click edit_coords$lat <- cl$lat; edit_coords$lng <- cl$lng p <- leaflet::leafletProxy("edit_map", session) leaflet::clearMarkers(p) leaflet::addMarkers(p, lng = cl$lng, lat = cl$lat) }) observe({ ap <- apiaries() lapply(ap$id, function(aid) { local({ lid <- aid observeEvent(input[[paste0("edit_", lid)]], { row <- db_apiaries_get(db, lid) edit_id(lid) edit_coords$lat <- row$lat %||% NA_real_ edit_coords$lng <- row$lng %||% NA_real_ shiny::showModal(shiny::modalDialog( title = "Stand bearbeiten", size = "l", footer = shiny::tagList( shiny::modalButton("Abbrechen"), shiny::actionButton(ns("confirm_edit"), "Speichern", class = "btn-primary"), shiny::actionButton(ns("confirm_delete"), "Löschen", class = "btn-danger ms-auto") ), shiny::textInput(ns("edit_name"), "Name *", value = row$name), shiny::textInput(ns("edit_loc"), "Standortbeschreibung", value = row$location_text %||% ""), shiny::tags$p(class = "text-muted small mt-3 mb-1", shiny::tags$em("Standort auf Karte setzen:")), leaflet::leafletOutput(ns("edit_map"), height = "280px"), shiny::uiOutput(ns("edit_coords_lbl")), shiny::textAreaInput(ns("edit_notes"), "Notizen", rows = 2, value = row$notes %||% "") )) }, ignoreInit = TRUE) }) }) }) output$edit_coords_lbl <- renderUI({ req(!is.na(edit_coords$lat)) shiny::tags$p(class = "text-muted small mt-1", sprintf("%.5f, %.5f", edit_coords$lat, edit_coords$lng)) }) observeEvent(input$confirm_edit, { req(edit_id()) db_apiaries_update(db, id = edit_id(), name = trimws(input$edit_name), location_text = input$edit_loc, lat = edit_coords$lat, lng = edit_coords$lng, notes = input$edit_notes ) shiny::removeModal(); reload(reload() + 1L) }) observeEvent(input$confirm_delete, { req(edit_id()) db_apiaries_delete(db, edit_id()) shiny::removeModal(); reload(reload() + 1L) }) }) }