From d757a61cdb108f08cac4fc957ef5c3751850b343 Mon Sep 17 00:00:00 2001 From: Christian Oswald Date: Thu, 19 Mar 2026 14:28:46 +0100 Subject: [PATCH] =?UTF-8?q?anzeige=20der=20neuen=20DS=20hinzugef=C3=BCgt?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- R/_funktionen/f_table.R | 2 +- R/_funktionen/scroll_to_Row.R | 19 ++++ R/buchungen_mod.R | 196 ++++++++++++++++++---------------- global.R | 1 + 4 files changed, 124 insertions(+), 94 deletions(-) create mode 100644 R/_funktionen/scroll_to_Row.R diff --git a/R/_funktionen/f_table.R b/R/_funktionen/f_table.R index e53b4ea..805de9c 100644 --- a/R/_funktionen/f_table.R +++ b/R/_funktionen/f_table.R @@ -16,7 +16,7 @@ f_reactable <- function(daten, coldefs = NULL, selection = "single", defaultSele theme = reactableTheme( highlightColor = "#e6f7ff", # Etwas dezenter als knallgrün, optional # borderColor = "#dfe2e5", - rowSelectedStyle = list(backgroundColor = "#98F5FF") + rowSelectedStyle = list(backgroundColor = "#98F5FF")# ), rowStyle = list(cursor = "pointer"), onClick = "select", diff --git a/R/_funktionen/scroll_to_Row.R b/R/_funktionen/scroll_to_Row.R new file mode 100644 index 0000000..451d97a --- /dev/null +++ b/R/_funktionen/scroll_to_Row.R @@ -0,0 +1,19 @@ +## +## Datum : 2026-03-19_14-26 +## Name : Christian Oswald +## Datei : scroll_to_Row.R +## Projekt : gemfin-shiny +## Kommentar: Springt zur gewünschten Reactable Zeile +## + +scroll_to_row <- function(table_id, row_idx, delay_ms = 200) { + shinyjs::delay(delay_ms, { + runjs(sprintf(" + const rows = document.querySelectorAll('#%s .rt-tr-group'); + const idx = %d - 1; + if (rows[idx]) { + rows[idx].scrollIntoView({behavior: 'smooth', block: 'center'}); + } + ", table_id, row_idx)) + }) +} \ No newline at end of file diff --git a/R/buchungen_mod.R b/R/buchungen_mod.R index 7a2e770..1709c3d 100644 --- a/R/buchungen_mod.R +++ b/R/buchungen_mod.R @@ -3,6 +3,7 @@ buchungenUI <- function(id) { ns <- NS(id) tagList( + useShinyjs(), h3("Hauptbuchungen"), reactableOutput(ns("buchungen_table")), hr(), @@ -12,7 +13,7 @@ buchungenUI <- function(id) { actionBttn(ns("add_trans"), "Transaktion hinzu", size = "sm", style = "material-flat", color = "warning"), actionBttn(ns("del_trans"), "Transaktion Löschen", size = "sm", style = "material-flat", color = "danger"), actionBttn(ns("add_detail"), "Detail hinzu", size = "sm", style = "material-flat", color = "success"), - + # Das UI-Element des Moduls (auch wenn es leer ist) postingModuleUI(ns("posting_modal")) ) @@ -21,106 +22,115 @@ buchungenUI <- function(id) { buchungenServer <- function(id, conn) { moduleServer(id, function(input, output, session) { ns <- session$ns + # * Reactive Variablen ---- - postings_data <- reactiveVal(read_buch_tabelle(conn)) - details_data <- reactiveVal(NULL) - selected_trans_id <- reactiveVal(NULL) - current_main_idx <- reactiveVal(NULL) - # Trigger-Objekt für das Modal: enthält post_id und einen Counter - # Der Counter erzwingt eine Reaktion, auch wenn die gleiche ID zweimal geklickt wird - modal_trigger <- reactiveVal(list(post_id = NULL, counter = 0)) - # * Modal starten (EINMALIG) ---- + postings_data <- reactiveVal(read_buch_tabelle(conn)) + details_data <- reactiveVal(NULL) + selected_trans_id <- reactiveVal(NULL) + current_main_idx <- reactiveVal(NULL) + reset_trigger <- reactiveVal(0) # ← neu: erzwingt Re-render mit Filter-Reset + modal_trigger <- reactiveVal(list(post_id = NULL, counter = 0)) + update_db_trigger <- postingModuleServer("posting_modal", conn, selected_trans_id, modal_trigger) - + # * Haupttabelle rendern ---- output$buchungen_table <- renderReactable({ - req(postings_data()) - f_reactable( - daten = postings_data(), coldefs = coldef_entries_tabelle, - selection = "single", - hoehe = "60vh", + reset_trigger() # Abhängigkeit – bei Increment wird neu gerendert + f_reactable( + daten = postings_data(), + coldefs = coldef_entries_tabelle, + selection = "single", + hoehe = "60vh", defaultSelected = current_main_idx() ) }) - - # * Details-Tabelle laden ---- - # sel_details <- reactive(getReactableState("buchungen_table", "selected")) - # observeEvent(sel_details(), ignoreInit = T, { - # browser() - # req(sel_details()) - # current_main_idx(getReactableState("buchungen_table", "selected")) - # t_id <- postings_data()[sel_details(), "entry_id"] %>% pull() - # selected_trans_id(t_id) - # details_data(read_buch_tabelle(conn, trans_id = t_id)) - # }) - # output$details_table <- renderReactable({ - # req(details_data()) - # f_reactable(details_data(), coldefs = coldef_entries_tabelle, selection = "single", hoehe = NULL) - # }) - + + # * Details-Tabelle laden ---- + sel_details <- reactive(getReactableState("buchungen_table", "selected")) + observeEvent(sel_details(), ignoreInit = TRUE, { + req(sel_details()) + current_main_idx(sel_details()) + t_id <- postings_data()[sel_details(), "entry_id"] |> pull() + selected_trans_id(t_id) + details_data(read_buch_tabelle(conn, trans_id = t_id)) + }) + output$details_table <- renderReactable({ + req(details_data()) + f_reactable( + details_data(), + coldefs = coldef_entries_tabelle, + selection = "single", + hoehe = NULL + ) + }) + # * Detail hinzufügen ---- - # observeEvent(input$add_detail, { - # req(selected_trans_id()) - # modal_trigger(list(post_id = NULL, counter = modal_trigger()$counter + 1)) - # }) - + observeEvent(input$add_detail, { + req(selected_trans_id()) + modal_trigger(list(post_id = NULL, counter = modal_trigger()$counter + 1)) + }) + # * Detail editieren (Klick auf Detail-Tabelle) ---- - # sel_detail <- reactive(getReactableState("details_table", "selected")) - # observeEvent(sel_detail(), ignoreInit = T, { - # p_id <- details_data()$id[sel_detail()] - # modal_trigger(list(post_id = p_id, counter = modal_trigger()$counter + 1)) - # }) - - # * Details laden ---- - # observeEvent(update_db_trigger(), ignoreInit = TRUE, { - # req(selected_trans_id()) - # # Tabellen-Daten neu aus DB ziehen - # details_data(read_buch_tabelle(conn, trans_id = selected_trans_id())) - # # Optional: Auch Haupttabelle erneuern, falls sich Summen geändert haben - # postings_data(read_buch_tabelle(conn)) - # updateReactable("buchungen_table", data = postings_data(read_buch_tabelle(conn))) - # }) - - # * Neue Transaktion ---- - # observeEvent(input$add_trans, { - # new_t_id <- max_id(conn, "entries") + 1 - # # 1. Entry anlegen - # dbxInsert(conn, "entries", data.frame(id = new_t_id)) - # # 2. Zwei Postings direkt vor-anlegen (z.B. mit Betrag 0) - # p_id1 <- max_id(conn, "postings") + 1 - # p_id2 <- p_id1 + 1 - # new_postings <- data.frame( - # id = c(p_id1, p_id2), - # entry_id = c(new_t_id, new_t_id), - # amount = c(0, 0), - # account_id = c(0, 0), - # valuta = c(Sys.Date(), Sys.Date()) - # ) - # dbxInsert(conn, "postings", new_postings) - # # 3. UI refreshen - # selected_trans_id(new_t_id) - # postings_data(read_buch_tabelle(conn)) - # neue_zeile <- which(postings_data()$id == p_id1) - # details_data(read_buch_tabelle(conn, trans_id = new_t_id)) - # daten_postings <- postings_data() - # daten_details <- details_data() - # updateReactable("buchungen_table", data = daten_postings, selected = neue_zeile, filters = NULL, sortsBy=NULL) - # }) - + sel_detail <- reactive(getReactableState("details_table", "selected")) + + observeEvent(sel_detail(), ignoreInit = TRUE, { + p_id <- details_data()$id[sel_detail()] + modal_trigger(list(post_id = p_id, counter = modal_trigger()$counter + 1)) + }) + + # * DB-Update durch Modal ---- + observeEvent(update_db_trigger(), ignoreInit = TRUE, { + req(selected_trans_id()) + details_data(read_buch_tabelle(conn, trans_id = selected_trans_id())) + postings_data(read_buch_tabelle(conn)) + # Kein Re-render nötig – Filter/Sortierung bleibt erhalten + updateReactable("buchungen_table", data = postings_data()) + }) + + # * Neue Transaktion ---- + observeEvent(input$add_trans, { + new_t_id <- max_id(conn, "entries") + 1 + dbxInsert(conn, "entries", data.frame(id = new_t_id)) + + p_id1 <- max_id(conn, "postings") + 1 + p_id2 <- p_id1 + 1 + dbxInsert(conn, "postings", data.frame( + id = c(p_id1, p_id2), + entry_id = c(new_t_id, new_t_id), + amount = c(0, 0), + account_id = c(0, 0), + valuta = c(Sys.Date(), Sys.Date()) + )) + ## ** Daten aktualisieren ---- + postings_data(read_buch_tabelle(conn)) + selected_trans_id(new_t_id) + details_data(read_buch_tabelle(conn, trans_id = new_t_id)) + ## ** Index in neuen Daten suchen ---- + neue_zeile <- which(postings_data()$id == p_id1) + current_main_idx(neue_zeile) + ## ** Re-render erzwingen → Filter wird zurückgesetzt, neue Zeile selektiert ---- + reset_trigger(isolate(reset_trigger()) + 1) + scroll_to_row(ns("buchungen_table"), neue_zeile) + showNotification( + "Filter zurückgesetzt – neue Transaktion ist jetzt sichtbar.", + type = "message", + duration = 3 + ) + }) + # * Transaktion löschen ---- - # observeEvent(input$del_trans, ignoreInit = T, { - # req(selected_trans_id()) - # print(selected_trans_id()) - # dbxDelete(conn, "postings", where = data.frame(entry_id = selected_trans_id())) - # dbxDelete(conn, "entries", where = data.frame(id = selected_trans_id())) - # postings_data(read_buch_tabelle(conn)) - # details_data(leer_df_from_table(conn, "postings")) - # daten_postings <- postings_data() - # daten_details <- details_data(NULL) - # browser() - # updateReactable("buchungen_table", data = daten_postings) - # current_main_idx(NULL) - # # updateReactable("details_table", data = daten_details) - # }) + observeEvent(input$del_trans, ignoreInit = TRUE, { + req(selected_trans_id()) + dbxDelete(conn, "postings", where = data.frame(entry_id = selected_trans_id())) + dbxDelete(conn, "entries", where = data.frame(id = selected_trans_id())) + + postings_data(read_buch_tabelle(conn)) + current_main_idx(NULL) + selected_trans_id(NULL) + details_data(NULL) + + updateReactable("buchungen_table", data = postings_data()) + # details_table re-rendert automatisch weil details_data(NULL) → req() schlägt fehl + }) }) -} +} \ No newline at end of file diff --git a/global.R b/global.R index f7d8029..88aab45 100644 --- a/global.R +++ b/global.R @@ -9,6 +9,7 @@ library("shinydashboard") library("reactable") library("conflicted") library("R.utils") +library("shinyjs") conflicts_prefer(dplyr::select) conflicts_prefer(dplyr::filter)