From 696f2e3ac81b02aeb9050d0ffaad6dabaa5f18af Mon Sep 17 00:00:00 2001 From: madeliri Date: Thu, 23 Apr 2026 11:51:04 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B7=D0=B0=D0=B4=D0=B0=D1=87=D0=B8=20?= =?UTF-8?q?-=20=D0=B2=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D0=BE?= =?UTF-8?q?=D0=BC=20=D0=BC=D0=BE=D0=B4=D1=83=D0=BB=D0=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app.R | 369 +++--------------------------------------------- app/forms.R | 39 +++++ app/tasks.R | 303 +++++++++++++++++++++++++++++++++++++++ modules/utils.R | 2 +- 4 files changed, 359 insertions(+), 354 deletions(-) create mode 100644 app/forms.R create mode 100644 app/tasks.R diff --git a/app.R b/app.R index 1421158..4c178f0 100644 --- a/app.R +++ b/app.R @@ -30,6 +30,10 @@ box::use( modules/global_options[AUTH_ENABLED] ) +# MODULES +box::use(app/forms) +box::use(app/tasks) + # SETTINGS ================================ HEADER_TEXT <- config::get("form_name") @@ -72,7 +76,7 @@ ui <- page_sidebar( uiOutput("status_message"), textOutput("status_message2"), uiOutput("display_log"), - actionButton("tasks_shieesh", "Задачи: нет активных"), + actionButton("tasks-shieesh", "Задачи: нет активных"), position = "left", open = list(mobile = "always") ), @@ -140,6 +144,8 @@ server <- function(input, output, session) { } } + values$current_user <- ifelse(AUTH_ENABLED, res_auth$user, "anonymous") + if (showing_buttons) { tagList( br(), @@ -161,7 +167,8 @@ server <- function(input, output, session) { main_key = NULL, nested_key = NULL, nested_form_id = NULL, - tasks_id_selector = NULL + tasks_id_selector = NULL, + current_user = NULL ) scheme <- reactiveVal(enabled_schemes[1]) # наименование выбранной схемы @@ -208,40 +215,6 @@ server <- function(input, output, session) { # ОБЩИЕ ФУНКЦИИ ============================ # ========================================== - ## перенос данных из датафрейма в форму ----------------------- - load_data_to_form <- function( - df, - table_name = "main", - schm, - ns - ) { - - input_types <- unname(mhcs()$get_id_type_list(table_name)) - input_ids <- names(mhcs()$get_id_type_list(table_name)) - if (missing(ns)) ns <- NULL - - # transform df to list - # loaded_df_for_id <- as.list(df) - # loaded_df_for_id <- df[input_ids] - - # rewrite input forms - purrr::walk2( - .x = input_types, - .y = input_ids, - .f = \(x_type, x_id) { - - # updating forms with loaded data - utils$update_forms_with_data( - form_id = x_id, - form_type = x_type, - value = df[[x_id]], - scheme = mhcs()$get_scheme(table_name), - ns = ns - ) - } - ) - } - ## сохранение данных из форм в базу данных -------- save_inputs_to_db <- function( table_name, @@ -608,10 +581,10 @@ server <- function(input, output, session) { ) # загрузка данных в формы - load_data_to_form( + forms$load_data_to_form( df = df, table_name = values$nested_form_id, - mhcs(), + mhcs = mhcs, ns = NS(values$nested_form_id) ) } else { @@ -858,16 +831,17 @@ server <- function(input, output, session) { con = con ) - load_data_to_form( + forms$load_data_to_form( df = df, table_name = "main", - mhcs() + mhcs ) values$main_key <- input$load_data_key_selector main_form_is_empty(FALSE) - update_task_button_count(con) + # обновление счетичка задач + tasks$update_task_button_count(con, values, NS("tasks")) log_action_to_db("loading data", values$main_key, con = con) removeModal() @@ -1231,320 +1205,9 @@ server <- function(input, output, session) { } # TASKS --------------------------------------- - ## show button watcher ------------------------- - observeEvent(input$tasks_shieesh, { + tasks$server("tasks", values, scheme, mhcs) - con <- db$make_db_connection(scheme(),"nested_tables") - on.exit(db$close_db_connection(con, "nested_tables"), add = TRUE) - values$tasks_data <- if ("tasks" %in% DBI::dbListTables(con)) { - DBI::dbGetQuery(con, glue::glue("SELECT * FROM tasks WHERE task_main_key = '{values$main_key}'")) |> - dplyr::mutate(dplyr::across(c("task_datetime_created", "task_datetime_last_updated", "task_datetime_completed"), as.POSIXct)) - } else { - NULL - } - - values$tasks_id_selector <- NULL - show_modal_for_tasks() - - }) - - show_modal_for_tasks <- function() { - - if (!is.null(values$tasks_data)) { - - tasks_selector <- values$tasks_data |> - dplyr::filter(task_status != "completed") |> - dplyr::pull(task_id) - - tasks_selector <- unique(c(values$tasks_id_selector, tasks_selector)) - tasks_selector <- sort(tasks_selector) - - if (length(values$tasks_id_selector) == 0) { - values$tasks_id_selector <- if (length(tasks_selector) == 0) NULL else tasks_selector[[1]] - } - - } else { - tasks_selector <- NULL - } - - # ui - # очень большой костыль - subroup_scheme <- mhcs()$get_scheme("tasks") |> - dplyr::filter(form_id != "dummy") - - tab <- if (length(tasks_selector) > 0) { - bslib::nav_panel( - title = "no name provided", - purrr::pmap( - .l = dplyr::distinct(subroup_scheme, form_id, form_label, form_type), - .f = utils$render_forms, - main_scheme = subroup_scheme - ) - ) - } else { - bslib::nav_panel("", div("Нет доступных записей.", br(), "Необходимо создать новую запись.")) - } - - ui <- layout_sidebar( - sidebar = tagList( - selectizeInput("hhhhhhh", label = "what", choices = tasks_selector, selected = values$tasks_id_selector), - actionButton("tasks_create_new_task", "new_task"), - actionButton("tasks_add_autoreview", "add autotask"), - actionButton("tasks_DT_VIEW", "DT") - ), - tab - ) - - showModal(modalDialog( - ui, - size = "l", - footer = tagList( - actionButton("tasks_saving_button", "сохранить изменения") - ), - easyClose = TRUE - )) - } - - observeEvent(input$hhhhhhh, { - req(input$hhhhhhh) - req(values$tasks_id_selector) - - # выбранный ключ в форме - перемещаем в RV - values$tasks_id_selector <- input$hhhhhhh - - }) - - observeEvent(values$tasks_id_selector, { - - df <- values$tasks_data |> - dplyr::filter(task_id == values$tasks_id_selector) - - load_data_to_form( - df = df, - table_name = "tasks", - mhcs() - ) - }) - - ## saving button watcher ------------------------------ - observeEvent(input$tasks_saving_button, { - - con <- db$make_db_connection(scheme(),"tasks_saving_button") - on.exit(db$close_db_connection(con, "tasks_saving_button"), add = TRUE) - - id_and_types_list <- mhcs()$get_id_type_list("tasks") - input_types <- unname(id_and_types_list) - input_ids <- names(id_and_types_list) - - exported_values <- purrr::map2( - .x = input_ids, - .y = input_types, - .f = \(x_id, x_type) { - - input_d <- input[[x_id]] - - # return empty if 0 element - if (length(input_d) == 0) { - return(utils$get_empty_data(x_type)) - } else { - input_d - } - } - ) - - exported_df <- setNames(exported_values, input_ids) |> - as_tibble() - - df <- values$tasks_data - - df[df$task_id == values$tasks_id_selector,]$task_status <- exported_df$task_status - df[df$task_id == values$tasks_id_selector,]$task_title <- exported_df$task_title - df[df$task_id == values$tasks_id_selector,]$task_description <- exported_df$task_description - df[df$task_id == values$tasks_id_selector,]$task_due_date <- exported_df$task_due_date - df[df$task_id == values$tasks_id_selector,]$task_user_last_updated <- ifelse(AUTH_ENABLED, res_auth$user, "anonymous") - df[df$task_id == values$tasks_id_selector,]$task_datetime_last_updated <- Sys.time() - - if (exported_df$task_status == "completed") { - df[df$task_id == values$tasks_id_selector,]$task_user_completed <- ifelse(AUTH_ENABLED, res_auth$user, "anonymous") - df[df$task_id == values$tasks_id_selector,]$task_datetime_completed <- Sys.time() - } - - values$tasks_data <- df - - if ("tasks" %in% DBI::dbListTables(con)) { - query <- glue::glue(" - DELETE - FROM tasks - WHERE task_main_key = '{values$main_key}' - ") - DBI::dbExecute(con, query) - } - - DBI::dbWriteTable(con, "tasks", df, append = TRUE) - - update_task_button_count(con) - showNotification("Задача успешно создана/обновлена", type = "message") - - tasks_selector <- values$tasks_data |> - dplyr::filter(task_status != "completed") |> - dplyr::pull(task_id) - - selector <- ifelse(!values$tasks_id_selector %in% tasks_selector, tasks_selector[1], values$tasks_id_selector) - - updateSelectInput(inputId = "hhhhhhh", choices = tasks_selector, selected = selector) - - }) - - observeEvent(input$tasks_DT_VIEW, { - - output$dt_tasks <- DT::renderDataTable( - DT::datatable( - values$tasks_data, - caption = 'Table 1: This is a simple caption for the table.', - rownames = FALSE, - # colnames = col_types |> dplyr::pull(form_id, form_label), - extensions = c('KeyTable', "FixedColumns"), - # editable = 'cell', - selection = "none", - options = list( - dom = 'tip', - scrollX = TRUE, - fixedColumns = list(leftColumns = 1), - keys = TRUE - ) - ) |> - DT::formatDate(c("task_datetime_created", "task_datetime_completed", "task_datetime_last_updated"), "toLocaleDateString", params = list('ru-RU')) - ) - - showModal(modalDialog( - DT::dataTableOutput("dt_tasks"), - size = "xl", - # footer = tagList( - # actionButton("nested_form_dt_save", "сохранить изменения") - # ), - easyClose = TRUE - )) - - }) - - ## functions ---------------------------------- - get_default_task <- function() { - - tibble::tibble( - task_id = paste0(format(Sys.time(), "%Y%m%d%H%M%S"), "_", values$main_key), - task_main_key = values$main_key, - task_status = "active", - task_title = "new task", - task_description = "description", - task_due_date = NA, - task_user_created = ifelse(AUTH_ENABLED, res_auth$user, "anonymous"), - task_datetime_created = Sys.time(), - task_user_last_updated = NA, - task_datetime_last_updated = NA, - task_user_completed = NA, - task_datetime_completed = NA - ) - } - - observeEvent(input$tasks_create_new_task, { - new_task <- get_default_task() - - values$tasks_data <- rbind(values$tasks_data, new_task) - values$tasks_id_selector <- new_task$task_id - - tasks_selector <- values$tasks_data |> - dplyr::filter(task_status != "completed") |> - dplyr::pull(task_id) - - updateSelectInput(inputId = "hhhhhhh", choices = tasks_selector, selected = values$tasks_id_selector) - removeModal() - show_modal_for_tasks() - }) - - observeEvent(input$tasks_add_autoreview, { - new_task <- get_default_task() - - new_task$task_title <- "autoreview" - new_task$task_description <- "напоминание об актуализации данных" - new_task$task_due_date <- Sys.Date() + 28 - - values$tasks_data <- rbind(values$tasks_data, new_task) - values$tasks_id_selector <- new_task$task_id - - tasks_selector <- values$tasks_data |> - dplyr::filter(task_status != "completed") |> - dplyr::pull(task_id) - - updateSelectInput(inputId = "hhhhhhh", choices = tasks_selector, selected = values$tasks_id_selector) - removeModal() - show_modal_for_tasks() - }) - - update_task_button_count <- function(con) { - if ("tasks" %in% DBI::dbListTables(con)) { - tasks_num <- DBI::dbGetQuery(con, glue::glue("SELECT COUNT ('task_id') FROM tasks WHERE task_main_key = '{values$main_key}' AND task_status = 'active'")) |> - pull() - if (tasks_num > 0) { - updateActionButton(inputId = "tasks_shieesh", label = paste("активных задач:", tasks_num)) - } else { - updateActionButton(inputId = "tasks_shieesh", label = "Задачи: нет активных") - } - } - } - - # КРАТКАЯ СВОДКА ПРО ЛОГГИНГ ------------------ - # observe({ - - # output$display_log <- renderUI({ - - # con <- db$make_db_connection(scheme(),"display_log") - # on.exit(db$close_db_connection(con, "display_log"), add = TRUE) - - # query <- if (!is.null(values$main_key)) { - # sprintf("SELECT * FROM log WHERE key = '%s'", values$main_key) - # } else { - # "SELECT * FROM log" - # } - - # log_rows <- DBI::dbGetQuery(con, query) - - # if (nrow(log_rows) > 0) { - - # lines <- log_rows |> - # mutate(date = as.POSIXct(date)) |> - # mutate( - # # date = date + lubridate::hours(3), # fix datetime - # date_day = as.Date(date) - # ) |> - # mutate(cons_actions = dplyr::consecutive_id(action, user)) |> - # mutate(n_actions = row_number(), .by = c(cons_actions, user, action, date_day)) |> - # slice(which.max(n_actions), .by = c(user, action, date_day)) |> - # mutate(string_to_print = sprintf( - # "[%s %s]: %s - %s (%s)", - # format(date, "%d.%m.%y"), - # format(date, "%H:%M"), - # user, - # action, - # n_actions - # )) |> - # pull(string_to_print) |> - # paste(collapse = "
") - - # } else { - # lines <- "" - # } - - # tagList( - # paste0("ID: ", values$main_key), - # br(), - # p( - # HTML(lines), - # style = "font-size:10px;" - # ) - # ) - # }) - # }) } diff --git a/app/forms.R b/app/forms.R new file mode 100644 index 0000000..4f5bb1d --- /dev/null +++ b/app/forms.R @@ -0,0 +1,39 @@ +options(box.path = here::here()) +box::use( + modules/utils, +) + + +#' @export +load_data_to_form <- function( + df, + table_name = "main", + mhcs, + ns +) { + + input_types <- unname(mhcs()$get_id_type_list(table_name)) + input_ids <- names(mhcs()$get_id_type_list(table_name)) + if (missing(ns)) ns <- NULL + + # transform df to list + # loaded_df_for_id <- as.list(df) + # loaded_df_for_id <- df[input_ids] + + # rewrite input forms + purrr::walk2( + .x = input_types, + .y = input_ids, + .f = \(x_type, x_id) { + + # updating forms with loaded data + utils$update_forms_with_data( + form_id = x_id, + form_type = x_type, + value = df[[x_id]], + scheme = mhcs()$get_scheme(table_name), + ns = ns + ) + } + ) +} \ No newline at end of file diff --git a/app/tasks.R b/app/tasks.R new file mode 100644 index 0000000..375231f --- /dev/null +++ b/app/tasks.R @@ -0,0 +1,303 @@ + +box::use( + shiny[...], + bslib[...] +) + +options(box.path = here::here()) +box::use( + modules/db, + modules/utils, + app/forms +) + +#' @export +server <- function(id, values, scheme, mhcs) { + + ns <- NS(id) + + moduleServer(id, function(input, output, session) { + + # BOOKMARKS SETUP ======================== + # observe({ + # # print(values$current_user) + # }) + + # functions ------------------- + show_modal_for_tasks <- function() { + + if (!is.null(values$tasks_data)) { + + tasks_selector <- values$tasks_data |> + dplyr::filter(task_status != "completed") |> + dplyr::pull(task_id) + + tasks_selector <- unique(c(values$tasks_id_selector, tasks_selector)) + tasks_selector <- sort(tasks_selector) + + if (length(values$tasks_id_selector) == 0) { + values$tasks_id_selector <- if (length(tasks_selector) == 0) NULL else tasks_selector[[1]] + } + + } else { + tasks_selector <- NULL + } + + # ui -------------------- + # очень большой костыль + subroup_scheme <- mhcs()$get_scheme("tasks") |> + dplyr::filter(form_id != "dummy") + + tab <- if (length(tasks_selector) > 0) { + bslib::nav_panel( + title = "no name provided", + purrr::pmap( + .l = dplyr::distinct(subroup_scheme, form_id, form_label, form_type), + .f = utils$render_forms, + main_scheme = subroup_scheme, + ns = ns + ) + ) + } else { + bslib::nav_panel("", div("Нет доступных записей.", br(), "Необходимо создать новую запись.")) + } + + ui <- layout_sidebar( + sidebar = tagList( + selectizeInput(ns("hhhhhhh"), label = "what", choices = tasks_selector, selected = values$tasks_id_selector), + actionButton(ns("tasks_create_new_task"), "new_task"), + actionButton(ns("tasks_add_autoreview"), "add autotask"), + actionButton(ns("tasks_DT_VIEW"), "DT") + ), + tab + ) + + showModal(modalDialog( + ui, + size = "l", + footer = tagList( + actionButton(ns("tasks_saving_button"), "сохранить изменения") + ), + easyClose = TRUE + )) + } + + # button logic --------------------- + ## отображение окна ----------------- + observeEvent(input$shieesh, { + + con <- db$make_db_connection(scheme(),"nested_tables") + on.exit(db$close_db_connection(con, "nested_tables"), add = TRUE) + + values$tasks_data <- if ("tasks" %in% DBI::dbListTables(con)) { + DBI::dbGetQuery(con, glue::glue("SELECT * FROM tasks WHERE task_main_key = '{values$main_key}'")) |> + dplyr::mutate(dplyr::across(c("task_datetime_created", "task_datetime_last_updated", "task_datetime_completed"), as.POSIXct)) |> + dplyr::mutate(dplyr::across(c("task_due_date"), as.Date)) + } else { + NULL + } + + values$tasks_id_selector <- NULL + show_modal_for_tasks() + + }) + + ## изменение выбранной задачи ------- + observeEvent(input$hhhhhhh, { + req(input$hhhhhhh) + req(values$tasks_id_selector) + + # выбранный ключ в форме - перемещаем в RV + values$tasks_id_selector <- input$hhhhhhh + + }) + + ## обновление формы при измененнии id ключа ------ + observeEvent(values$tasks_id_selector, { + + df <- values$tasks_data |> + dplyr::filter(task_id == values$tasks_id_selector) + + forms$load_data_to_form( + df = df, + table_name = "tasks", + mhcs + # ns = ns + ) + }) + + ## saving button ------------------------------ + observeEvent(input$tasks_saving_button, { + + con <- db$make_db_connection(scheme(),"tasks_saving_button") + on.exit(db$close_db_connection(con, "tasks_saving_button"), add = TRUE) + + id_and_types_list <- mhcs()$get_id_type_list("tasks") + input_types <- unname(id_and_types_list) + input_ids <- names(id_and_types_list) + + exported_values <- purrr::map2( + .x = input_ids, + .y = input_types, + .f = \(x_id, x_type) { + + input_d <- input[[x_id]] + + # return empty if 0 element + if (length(input_d) == 0) { + return(utils$get_empty_data(x_type)) + } else { + input_d + } + } + ) + + exported_df <- stats::setNames(exported_values, input_ids) |> + dplyr::as_tibble() + + df <- values$tasks_data + + df[df$task_id == values$tasks_id_selector,]$task_status <- exported_df$task_status + df[df$task_id == values$tasks_id_selector,]$task_title <- exported_df$task_title + df[df$task_id == values$tasks_id_selector,]$task_description <- exported_df$task_description + df[df$task_id == values$tasks_id_selector,]$task_due_date <- exported_df$task_due_date + df[df$task_id == values$tasks_id_selector,]$task_user_last_updated <- values$current_user + df[df$task_id == values$tasks_id_selector,]$task_datetime_last_updated <- Sys.time() + + if (exported_df$task_status == "completed") { + df[df$task_id == values$tasks_id_selector,]$task_user_completed <- values$current_user + df[df$task_id == values$tasks_id_selector,]$task_datetime_completed <- Sys.time() + } + + values$tasks_data <- df + + if ("tasks" %in% DBI::dbListTables(con)) { + query <- glue::glue(" + DELETE + FROM tasks + WHERE task_main_key = '{values$main_key}' + ") + DBI::dbExecute(con, query) + } + + DBI::dbWriteTable(con, "tasks", df, append = TRUE) + + update_task_button_count(con, values) + showNotification("Задача успешно создана/обновлена", type = "message") + + tasks_selector <- values$tasks_data |> + dplyr::filter(task_status != "completed") |> + dplyr::pull(task_id) + + selector <- ifelse(!values$tasks_id_selector %in% tasks_selector, tasks_selector[1], values$tasks_id_selector) + + updateSelectInput(inputId = "hhhhhhh", choices = tasks_selector, selected = selector) + + }) + + ## show DT -------------------------- + observeEvent(input$tasks_DT_VIEW, { + + output$dt_tasks <- DT::renderDataTable( + DT::datatable( + values$tasks_data, + caption = 'Table 1: This is a simple caption for the table.', + rownames = FALSE, + # colnames = col_types |> dplyr::pull(form_id, form_label), + extensions = c('KeyTable', "FixedColumns"), + # editable = 'cell', + selection = "none", + options = list( + dom = 'tip', + scrollX = TRUE, + fixedColumns = list(leftColumns = 1), + keys = TRUE + ) + ) |> + DT::formatDate(c("task_datetime_created", "task_datetime_completed", "task_datetime_last_updated", "task_due_date"), "toLocaleDateString", params = list('ru-RU')) + ) + + showModal(modalDialog( + DT::dataTableOutput(ns("dt_tasks")), + size = "xl", + # footer = tagList( + # actionButton("nested_form_dt_save", "сохранить изменения") + # ), + easyClose = TRUE + )) + + }) + + ## functions ---------------------------------- + get_default_task <- function() { + + tibble::tibble( + task_id = paste0(format(Sys.time(), "%Y%m%d%H%M%S"), "_", values$main_key), + task_main_key = values$main_key, + task_status = "active", + task_title = "new task", + task_description = "description", + task_due_date = NA, + task_user_created = values$current_user, + task_datetime_created = Sys.time(), + task_user_last_updated = NA, + task_datetime_last_updated = NA, + task_user_completed = NA, + task_datetime_completed = NA + ) + } + + observeEvent(input$tasks_create_new_task, { + new_task <- get_default_task() + + values$tasks_data <- rbind(values$tasks_data, new_task) + values$tasks_id_selector <- new_task$task_id + + tasks_selector <- values$tasks_data |> + dplyr::filter(task_status != "completed") |> + dplyr::pull(task_id) + + updateSelectInput(inputId = "hhhhhhh", choices = tasks_selector, selected = values$tasks_id_selector) + removeModal() + show_modal_for_tasks() + }) + + observeEvent(input$tasks_add_autoreview, { + new_task <- get_default_task() + + new_task$task_title <- "autoreview" + new_task$task_description <- "напоминание об актуализации данных" + new_task$task_due_date <- Sys.Date() + 28 + + values$tasks_data <- rbind(values$tasks_data, new_task) + values$tasks_id_selector <- new_task$task_id + + tasks_selector <- values$tasks_data |> + dplyr::filter(task_status != "completed") |> + dplyr::pull(task_id) + + updateSelectInput(inputId = "hhhhhhh", choices = tasks_selector, selected = values$tasks_id_selector) + removeModal() + show_modal_for_tasks() + }) + + }) +} + +#' @export +update_task_button_count <- function(con, values, ns) { + + inputID <- "shieesh" + if (!missing(ns)) inputID <- ns(inputID) + + if ("tasks" %in% DBI::dbListTables(con)) { + tasks_num <- DBI::dbGetQuery(con, glue::glue("SELECT COUNT ('task_id') FROM tasks WHERE task_main_key = '{values$main_key}' AND task_status = 'active'")) |> + dplyr::pull() + if (tasks_num > 0) { + updateActionButton(inputId = inputID, label = paste("активных задач:", tasks_num)) + } else { + updateActionButton(inputId = inputID, label = "Задачи: нет активных") + } + } + +} \ No newline at end of file diff --git a/modules/utils.R b/modules/utils.R index 0efd8fe..1d060ed 100644 --- a/modules/utils.R +++ b/modules/utils.R @@ -263,7 +263,7 @@ update_forms_with_data = function( local_delimeter = getOption("SYMBOL_DELIM"), ns ) { - + options(box.path = here::here()) box::use(modules/data_manipulations[is_this_empty_value])