From 014ff5794487459c8ff8abda47c57a7407c332ef Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Mon, 2 Mar 2026 09:25:03 +0000 Subject: [PATCH 01/17] Fix warning from devtools::check() --- R/app.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/app.R b/R/app.R index 0f25055..6f5cc2b 100644 --- a/R/app.R +++ b/R/app.R @@ -178,7 +178,7 @@ campagneApp <- function() { style = "display:flex; align-items:center; gap:10px; flex-wrap:nowrap;", imageOutput("drc_cdc_logo", height = "60px"), h5( - "Portail de création des masques de saisie des campagnes de vaccination en RDC", + "Portail de cr\u00e9ation des masques de saisie des campagnes de vaccination en RDC", style = " margin:0; font-weight:500; From 09e8c6053ff2976daf9ee22d73769808eab08b93 Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Mon, 2 Mar 2026 10:03:29 +0000 Subject: [PATCH 02/17] Convert non-ASCII characters to ASCII-compatible Unicode escapes --- R/compile_masques.R | 2 +- R/compile_masques_antenne.R | 4 +- R/compile_masques_national.R | 2 +- R/compile_masques_province.R | 4 +- R/create_campaign_progress_heatmap.R | 2 +- R/create_daily_campaign_progress_heatmap.R | 2 +- R/create_recovery_heatmap.R | 14 +-- R/create_urban_rural_heatmap.R | 12 +- R/get_campaign_progress.R | 14 +-- R/get_sheet_info.R | 140 ++++++++++----------- R/login_page_ui.R | 4 +- R/server.R | 20 +-- R/ui_campaign_creation.R | 14 +-- R/ui_geodatabase.R | 24 ++-- R/ui_monitoring.R | 16 +-- R/ui_perm_table.R | 36 +++--- 16 files changed, 155 insertions(+), 155 deletions(-) diff --git a/R/compile_masques.R b/R/compile_masques.R index 5e49eed..fe3bb12 100644 --- a/R/compile_masques.R +++ b/R/compile_masques.R @@ -38,7 +38,7 @@ compile_masques <- function(campaign_name) { }) # Compile all templates for one sheet at a time - withProgress(message = "Compilation des données dans: ", + withProgress(message = "Compilation des donn\u00e9es dans: ", { n <- length(tab_names) for (i in 1:n) { diff --git a/R/compile_masques_antenne.R b/R/compile_masques_antenne.R index c9ca0c1..cb7fbbe 100644 --- a/R/compile_masques_antenne.R +++ b/R/compile_masques_antenne.R @@ -55,7 +55,7 @@ compile_masques_antenne <- function(campaign_name) { # Compile masques # Compile all templates for one sheet at a time - withProgress(message = "Compilation des données dans: ", + withProgress(message = "Compilation des donn\u00e9es dans: ", { n <- length(tab_names) for (j in 1:n) { @@ -67,7 +67,7 @@ compile_masques_antenne <- function(campaign_name) { } }) } - showNotification("Masques au niveau des antennes compilés", type = "message") + showNotification("Masques au niveau des antennes compil\u00e9es", type = "message") return(invisible()) } diff --git a/R/compile_masques_national.R b/R/compile_masques_national.R index ec3b694..68fdf1c 100644 --- a/R/compile_masques_national.R +++ b/R/compile_masques_national.R @@ -53,7 +53,7 @@ compile_masques_national <- function(campaign_name) { showNotification("Autorisations requises obtenues.", type = "message") # Compile all templates for one sheet at a time - withProgress(message = "Compilation des données dans: ", + withProgress(message = "Compilation des donn\u00e9es dans: ", { n <- length(tab_names) for (i in 1:n) { diff --git a/R/compile_masques_province.R b/R/compile_masques_province.R index e7c2d9b..101e256 100644 --- a/R/compile_masques_province.R +++ b/R/compile_masques_province.R @@ -46,7 +46,7 @@ compile_masques_province <- function(campaign_name) { # Compile masques # Compile all templates for one sheet at a time - withProgress(message = "Compilation des données dans: ", + withProgress(message = "Compilation des donn\u00e9es dans: ", { n <- length(tab_names) for (j in 1:n) { @@ -58,7 +58,7 @@ compile_masques_province <- function(campaign_name) { } }) } - showNotification("Masques au niveau des provinces compilés", type = "message") + showNotification("Masques au niveau des provinces compil\u00e9es", type = "message") return(invisible()) } diff --git a/R/create_campaign_progress_heatmap.R b/R/create_campaign_progress_heatmap.R index d22a94a..ec69a87 100644 --- a/R/create_campaign_progress_heatmap.R +++ b/R/create_campaign_progress_heatmap.R @@ -36,7 +36,7 @@ create_campaign_progress_heatmap <- function(summary) { ggplot2::ylab("Aire De Sante") + ggplot2::xlab("") + ggplot2::labs(subtitle = paste0("Objectif: Jour 1: 40%, Jour 2: 70%, Jour 3: 100%, Jour 4: 100%\n", - "A risque si à moins de 5 % et en échec si à plus de 5 % de l'objectif")) + + "A risque si à moins de 5 % et en \u00e9chec si à plus de 5 % de l'objectif")) + ggplot2::theme(axis.text.x = ggplot2::element_text(size = 14), axis.text.y = ggplot2::element_text(size = 14), axis.title.x = ggplot2::element_text(size = 14, face = "bold"), diff --git a/R/create_daily_campaign_progress_heatmap.R b/R/create_daily_campaign_progress_heatmap.R index 9f1f8b7..6519537 100644 --- a/R/create_daily_campaign_progress_heatmap.R +++ b/R/create_daily_campaign_progress_heatmap.R @@ -34,7 +34,7 @@ create_daily_campaign_progress_heatmap <- function(summary) { ggplot2::ylab("Aire De Sante") + ggplot2::xlab("") + ggplot2::labs(subtitle = paste0("Objectif: Jour 1: 40%, Jour 2-3: 30%, Jour 4: 100%\n", - "A risque si à moins de 5 % et en échec si à plus de 5 % de l'objectif")) + + "A risque si à moins de 5 % et en \u00e9echec si à plus de 5 % de l'objectif")) + ggplot2::theme(axis.text.x = ggplot2::element_text(size = 14), axis.text.y = ggplot2::element_text(size = 14), axis.title.x = ggplot2::element_text(size = 14, face = "bold"), diff --git a/R/create_recovery_heatmap.R b/R/create_recovery_heatmap.R index cbcd869..d9ab4f1 100644 --- a/R/create_recovery_heatmap.R +++ b/R/create_recovery_heatmap.R @@ -5,26 +5,26 @@ create_recovery_heatmap <- function(summary) { cat_0_11 = case_when( recovery_0_11 == 0 & recovery_0_11_cumulative > 0 & jour %in% c("Jour 2", "Jour 3", "Jour 4") ~ "Aucun changement", recovery_0_11_cumulative > 0 ~ "Bien", - .default = "Zéro" + .default = "Z\u00e9ro" ), cat_12_23 = case_when( recovery_12_23 == 0 & recovery_12_23_cumulative > 0 & jour %in% c("Jour 2", "Jour 3", "Jour 4") ~ "Aucun changement", recovery_12_23_cumulative > 0 ~ "Bien", - .default = "Zéro" + .default = "Z\u00e9ro" ), cat_24_59 = case_when( recovery_24_59 == 0 & recovery_24_59_cumulative > 0 & jour %in% c("Jour 2", "Jour 3", "Jour 4") ~ "Aucun changement", recovery_24_59_cumulative > 0 ~ "Bien", - .default = "Zéro" + .default = "Z\u00e9ro" ) ) |> dplyr::mutate(across(all_of(c("cat_0_11", "cat_12_23", "cat_24_59")), - \(x) factor(x, levels = c("Bien", "Aucun changement", "Zéro")))) + \(x) factor(x, levels = c("Bien", "Aucun changement", "Z\u00e9ro")))) plot_0_11 <- ggplot2::ggplot(plot_data) + ggplot2::geom_tile(ggplot2::aes(y =aire_de_sante, x = jour, fill = cat_0_11), show.legend = TRUE) + - ggplot2::scale_fill_manual(values = c("Zéro" = "lightgrey", "Bien" = "#006B3E", "Aucun changement" = "#338F65"), + ggplot2::scale_fill_manual(values = c("Z\u00e9ro" = "lightgrey", "Bien" = "#006B3E", "Aucun changement" = "#338F65"), name = "Statut") + ggplot2::geom_text(ggplot2::aes(y = aire_de_sante, x = jour, label = recovery_0_11_cumulative), size = 8) + ggplot2::theme_void() + @@ -42,7 +42,7 @@ create_recovery_heatmap <- function(summary) { plot_12_23 <- ggplot2::ggplot(plot_data) + ggplot2::geom_tile(ggplot2::aes(y =aire_de_sante, x = jour, fill = cat_12_23), show.legend = TRUE) + - ggplot2::scale_fill_manual(values = c("Zéro" = "lightgrey", "Bien" = "#006B3E", "Aucun changement" = "#338F65"), + ggplot2::scale_fill_manual(values = c("Z\u00e9ro" = "lightgrey", "Bien" = "#006B3E", "Aucun changement" = "#338F65"), name = "Statut") + ggplot2::geom_text(ggplot2::aes(y = aire_de_sante, x = jour, label = recovery_12_23_cumulative), size = 8) + ggplot2::theme_void() + @@ -60,7 +60,7 @@ create_recovery_heatmap <- function(summary) { plot_24_59 <- ggplot2::ggplot(plot_data) + ggplot2::geom_tile(ggplot2::aes(y =aire_de_sante, x = jour, fill = cat_24_59), show.legend = TRUE) + - ggplot2::scale_fill_manual(values = c("Zéro" = "lightgrey", "Bien" = "#006B3E", "Aucun changement" = "#338F65"), + ggplot2::scale_fill_manual(values = c("Z\u00e9ro" = "lightgrey", "Bien" = "#006B3E", "Aucun changement" = "#338F65"), name = "Statut") + ggplot2::geom_text(ggplot2::aes(y = aire_de_sante, x = jour, label = recovery_24_59_cumulative), size = 8) + ggplot2::theme_void() + diff --git a/R/create_urban_rural_heatmap.R b/R/create_urban_rural_heatmap.R index 081d91b..938ba43 100644 --- a/R/create_urban_rural_heatmap.R +++ b/R/create_urban_rural_heatmap.R @@ -6,15 +6,15 @@ create_urban_rural_heatmap <- function(summary) { cat_rural = case_when( between(avg_vax_rural, 1, 99) ~ "Objectif Non Atteint", avg_vax_rural >= 100 ~ "Bien", - .default = "Zéro" + .default = "Z\u00e9ro" ), - cat_rural = factor(cat_rural, levels = c("Bien", "Objectif Non Atteint", "Zéro")), + cat_rural = factor(cat_rural, levels = c("Bien", "Objectif Non Atteint", "Z\u00e9ro")), cat_urban = case_when( between(avg_vax_urban, 1, 199) ~ "Objectif Non Atteint", avg_vax_urban >= 200 ~ "Bien", - .default = "Zéro" + .default = "Z\u00e9ro" ), - cat_urban = factor(cat_urban, levels = c("Bien", "Objectif Non Atteint", "Zéro")), + cat_urban = factor(cat_urban, levels = c("Bien", "Objectif Non Atteint", "Z\u00e9ro")), avg_vax_rural = round(avg_vax_rural), avg_vax_urban = round(avg_vax_urban) ) |> @@ -22,7 +22,7 @@ create_urban_rural_heatmap <- function(summary) { rural_plot <- ggplot2::ggplot(data = plot_data) + ggplot2::geom_tile(ggplot2::aes(y =aire_de_sante, x = jour, fill = cat_rural), show.legend = TRUE) + - ggplot2::scale_fill_manual(values = c("Zéro" = "lightgrey", "Bien" = "#006B3E", "Objectif Non Atteint" = "#ED2938"), + ggplot2::scale_fill_manual(values = c("Z\u00e9ro" = "lightgrey", "Bien" = "#006B3E", "Objectif Non Atteint" = "#ED2938"), name = "Statut") + ggplot2::geom_text(ggplot2::aes(y = aire_de_sante, x = jour, label = avg_vax_rural), size = 8) + ggplot2::theme_void() + @@ -40,7 +40,7 @@ create_urban_rural_heatmap <- function(summary) { urban_plot <- ggplot2::ggplot(data = plot_data) + ggplot2::geom_tile(ggplot2::aes(y =aire_de_sante, x = jour, fill = cat_urban), show.legend = TRUE) + - ggplot2::scale_fill_manual(values = c("Zéro" = "lightgrey", "Bien" = "#006B3E", "Objectif Non Atteint" = "#ED2938"), + ggplot2::scale_fill_manual(values = c("Z\u00e9ro" = "lightgrey", "Bien" = "#006B3E", "Objectif Non Atteint" = "#ED2938"), name = "Statut") + ggplot2::geom_text(ggplot2::aes(y = aire_de_sante, x = jour, label = avg_vax_urban), size = 8) + ggplot2::theme_void() + diff --git a/R/get_campaign_progress.R b/R/get_campaign_progress.R index 663527d..610d8e1 100644 --- a/R/get_campaign_progress.R +++ b/R/get_campaign_progress.R @@ -92,17 +92,17 @@ get_campaign_progress <- function(dribble, sheets = 5:8) { dplyr::rename( "province" = "Province", "antenne" = "Antenne", - "zone_de_sante" = "Zone de Santé", - "aire_de_sante" = "Aire de Santé", + "zone_de_sante" = "Zone de Sant\u00e9", + "aire_de_sante" = "Aire de Sant\u00e9", "rapports_de_vaccination_attendus" = "Attendus", "rapports_de_vaccination_recus" = "Recus", "cible_0_59_mois" = "Cible 0-59 mois", "vaccination_0_59_mois" = "Total", - "avg_vax_rural" = "Nb moyen d'enfants vaccinés/équipe (ref 100 enfants)", - "avg_vax_urban" = "Nb moyen d'enfants vaccinés/équipe (ref 200 enfants)", - "recovery_0_11" = "# Récupérations 0-11 mois", - "recovery_12_23" = "# Récupérations 12-23 mois", - "recovery_24_59" = "# Récupérations 24-59 mois" + "avg_vax_rural" = "Nb moyen d'enfants vaccin\u00e9s/\u00e9quipe (ref 100 enfants)", + "avg_vax_urban" = "Nb moyen d'enfants vaccin\u00e9s/\u00e9quipe (ref 200 enfants)", + "recovery_0_11" = "# R\u00e9cup\u00e9rations 0-11 mois", + "recovery_12_23" = "# R\u00e9cup\u00e9rations 12-23 mois", + "recovery_24_59" = "# R\u00e9cup\u00e9rations 24-59 mois" ) |> dplyr::mutate(cible_0_59_mois = round(cible_0_59_mois, 0), rapport_completude_pct = round(rapports_de_vaccination_recus / rapports_de_vaccination_attendus * 100, 0), diff --git a/R/get_sheet_info.R b/R/get_sheet_info.R index 24a93f4..f89d1f2 100644 --- a/R/get_sheet_info.R +++ b/R/get_sheet_info.R @@ -118,63 +118,63 @@ get_sheet_info <- function(dribble, sheets = 1:8) { get_range_from_name <- function(sheet, name) { if (sheet == 1) { switch(name, - "Situation des équipes de vaccination par aires de santé" = "E:G", - "Populations cibles vaccinées à la campagne précédente" = "N:O", - "Disponilité du vaccin, antigène utilisé" = "V:V", - "Nombre d'équipes de mobilisation sociale de proxi" = "Y:Y", + "Situation des \u00e9quipes de vaccination par aires de sant\u00e9" = "E:G", + "Populations cibles vaccin\u00e9es à la campagne pr\u00e9c\u00e9dente" = "N:O", + "Disponilit\u00e9 du vaccin, antigène utilis\u00e9" = "V:V", + "Nombre d'\u00e9quipes de mobilisation sociale de proxi" = "Y:Y", "RESEAU/ INTERNET" = "Z:AB", - "Enfants 0 - 11 mois identifiés pour etre récupérés en PEV de routine" = "AC:AQ", - "Enfants 12 - 23 mois identifiés pour etre récupérés en PEV de routine" = "AS:BG", - "Enfants 24 - 59 mois identifiés pour etre récupérés en PEV de routine" = "BI:BW", - "Femmes cibles identifiées pour etre récupérées en PEV de routine" = "BY:CC", + "Enfants 0 - 11 mois identifi\u00e9s pour etre r\u00e9cup\u00e9r\u00e9s en PEV de routine" = "AC:AQ", + "Enfants 12 - 23 mois identifi\u00e9s pour etre r\u00e9cup\u00e9r\u00e9s en PEV de routine" = "AS:BG", + "Enfants 24 - 59 mois identifi\u00e9s pour etre r\u00e9cup\u00e9r\u00e9s en PEV de routine" = "BI:BW", + "Femmes cibles identifi\u00e9es pour etre r\u00e9cup\u00e9r\u00e9es en PEV de routine" = "BY:CC", "LOCALISATION DES POPULATIONS SPECIALES" = "CE:CG", - "Nombre de ménages dénombrés" = "CH:CH", - "Populations spéciales (Nombre de personnes selon le Statut)" = "CI:CK", - "Effectifs des Populations spéciales recensées" = "CL:CU") + "Nombre de m\u00e9nages d\u00e9nombr\u00e9s" = "CH:CH", + "Populations sp\u00e9ciales (Nombre de personnes selon le Statut)" = "CI:CK", + "Effectifs des Populations sp\u00e9ciales recens\u00e9es" = "CL:CU") } else if (sheet %in% 2:4) { switch(name, - "Nombre de ménage" = "G:G", - "Complétude MoSo" = "I:I", - "Nombre de personnes de plus de 15 ans sensibilisées" = "K:L", - "0-11 mois dénombrés" = "N:O", - "12-59 mois dénombrés" = "Q:R", - "5-14 ans dénombrés" = "W:X", + "Nombre de m\u00e9nage" = "G:G", + "Compl\u00e9tude MoSo" = "I:I", + "Nombre de personnes de plus de 15 ans sensibilis\u00e9es" = "K:L", + "0-11 mois d\u00e9nombr\u00e9s" = "N:O", + "12-59 mois d\u00e9nombr\u00e9s" = "Q:R", + "5-14 ans d\u00e9nombr\u00e9s" = "W:X", "Cas de maladies sous surveillance" = "Y:AC", "REFUS" = "AD:AE", "Mobilisation communautaire des FEMMES" = "AF:AN", "Mobilisation communautaire des HOMMES" = "AO:AW", - "Mobilisation communautaire générale" = "AX:BI", - "Rattrapage/récupération en PEV" = "BJ:BK" + "Mobilisation communautaire g\u00e9n\u00e9rale" = "AX:BI", + "Rattrapage/r\u00e9cup\u00e9ration en PEV" = "BJ:BK" ) } else if (sheet %in% 5:8) { switch(name, - "Nombre de ménage" = "G:G", - "Complétude MoSo" = "I:I", - "Nombre de personnes de plus de 15 ans sensibilisées" = "K:L", - "0-11 mois dénombrés" = "N:O", - "12-59 mois dénombrés" = "Q:R", - "5-14 ans dénombrés" = "W:X", + "Nombre de m\u00e9nage" = "G:G", + "Compl\u00e9tude MoSo" = "I:I", + "Nombre de personnes de plus de 15 ans sensibilis\u00e9es" = "K:L", + "0-11 mois d\u00e9nombr\u00e9s" = "N:O", + "12-59 mois d\u00e9nombr\u00e9s" = "Q:R", + "5-14 ans d\u00e9nombr\u00e9s" = "W:X", "Cas de maladies sous surveillance" = "Y:AC", "REFUS" = "AD:AE", "Mobilisation communautaire des FEMMES" = "AF:AN", "Mobilisation communautaire des HOMMES" = "AO:AW", - "Mobilisation communautaire générale" = "AX:BI", - "Rattrapage/récupération en PEV" = "BJ:BK", - "Complétude des rapports de vaccination" = "CL:CN", - "Vaccines 0-11 mois Zéro Dose" = "CP:CQ", + "Mobilisation communautaire g\u00e9n\u00e9rale" = "AX:BI", + "Rattrapage/r\u00e9cup\u00e9ration en PEV" = "BJ:BK", + "Compl\u00e9tude des rapports de vaccination" = "CL:CN", + "Vaccines 0-11 mois Z\u00e9ro Dose" = "CP:CQ", "Vaccines 0-11 mois 1 dose et plus" = "CR:CS", - "Vaccines 12-59 mois Zéro Dose" = "CW:CX", + "Vaccines 12-59 mois Z\u00e9ro Dose" = "CW:CX", "Vaccines 12-59 mois 1 dose et plus" = "CY:CZ", - "Vaccines 0-11 mois Zéro Dose" = "DD:DE", + "Vaccines 0-11 mois Z\u00e9ro Dose" = "DD:DE", "Vaccines 0-11 mois 1 dose et plus" = "DF:DG", - "Vaccines 12-59 mois Zéro Dose" = "DK:DL", + "Vaccines 12-59 mois Z\u00e9ro Dose" = "DK:DL", "Vaccines 12-59 mois 1 dose et plus" = "DM:DN", - "Vaccinés par les Equipes en milieu rural" = "EA:EB", - "Vaccinés par les Equipes en milieu urbain" = "EC:ED", + "Vaccin\u00e9s par les Equipes en milieu rural" = "EA:EB", + "Vaccin\u00e9s par les Equipes en milieu urbain" = "EC:ED", "GESTION DES FLACONS" = "EQ:EW", "Recherche active des cas de MEV" = "EX:FA", "MAPI" = "FB:FC", - "Ver de Guinée" = "FD:FD", + "Ver de Guin\u00e9e" = "FD:FD", "BCG (0-11)" = "FE:FF", "VPI1 (0-11)" = "FG:FH", "DTC-HepB-Hib 1 (0-11)" = "FI:FJ", @@ -233,66 +233,66 @@ get_sheet_info <- function(dribble, sheets = 1:8) { get_names_from_sheet <- function(sheet = NULL) { if (sheet == 1) { c( - "Situation des équipes de vaccination par aires de santé", - "Populations cibles vaccinées à la campagne précédente", - "Disponilité du vaccin, antigène utilisé" , - "Nombre d'équipes de mobilisation sociale de proxi", + "Situation des \u00e9quipes de vaccination par aires de sant\u00e9", + "Populations cibles vaccin\u00e9es à la campagne pr\u00e9c\u00e9dente", + "Disponilit\u00e9 du vaccin, antigène utilis\u00e9" , + "Nombre d'\u00e9quipes de mobilisation sociale de proxi", "RESEAU/ INTERNET" , - "Enfants 0 - 11 mois identifiés pour etre récupérés en PEV de routine", - "Enfants 12 - 23 mois identifiés pour etre récupérés en PEV de routine", - "Enfants 24 - 59 mois identifiés pour etre récupérés en PEV de routine", - "Femmes cibles identifiées pour etre récupérées en PEV de routine", + "Enfants 0 - 11 mois identifi\u00e9s pour etre r\u00e9cup\u00e9r\u00e9s en PEV de routine", + "Enfants 12 - 23 mois identifi\u00e9s pour etre r\u00e9cup\u00e9r\u00e9s en PEV de routine", + "Enfants 24 - 59 mois identifi\u00e9s pour etre r\u00e9cup\u00e9r\u00e9s en PEV de routine", + "Femmes cibles identifi\u00e9es pour etre r\u00e9cup\u00e9r\u00e9es en PEV de routine", "LOCALISATION DES POPULATIONS SPECIALES", - "Nombre de ménages dénombrés", - "Populations spéciales (Nombre de personnes selon le Statut)", - "Effectifs des Populations spéciales recensées" + "Nombre de m\u00e9nages d\u00e9nombr\u00e9s", + "Populations sp\u00e9ciales (Nombre de personnes selon le Statut)", + "Effectifs des Populations sp\u00e9ciales recens\u00e9es" ) } else if (sheet %in% 2:4) { - c("Nombre de ménage", - "Complétude MoSo", - "Nombre de personnes de plus de 15 ans sensibilisées", - "0-11 mois dénombrés", - "12-59 mois dénombrés", - "5-14 ans dénombrés", + c("Nombre de m\u00e9nage", + "Compl\u00e9tude MoSo", + "Nombre de personnes de plus de 15 ans sensibilis\u00e9es", + "0-11 mois d\u00e9nombr\u00e9s", + "12-59 mois d\u00e9nombr\u00e9s", + "5-14 ans d\u00e9nombr\u00e9s", "Cas de maladies sous surveillance", "REFUS", "Mobilisation communautaire des FEMMES", "Mobilisation communautaire des HOMMES", - "Mobilisation communautaire générale", - "Rattrapage/récupération en PEV") + "Mobilisation communautaire g\u00e9n\u00e9rale", + "Rattrapage/r\u00e9cup\u00e9ration en PEV") } else if (sheet %in% 5:8) { c( - "Nombre de ménage", - "Complétude MoSo", - "Nombre de personnes de plus de 15 ans sensibilisées", - "0-11 mois dénombrés", - "12-59 mois dénombrés", - "5-14 ans dénombrés", + "Nombre de m\u00e9nage", + "Compl\u00e9tude MoSo", + "Nombre de personnes de plus de 15 ans sensibilis\u00e9es", + "0-11 mois d\u00e9nombr\u00e9s", + "12-59 mois d\u00e9nombr\u00e9s", + "5-14 ans d\u00e9nombr\u00e9s", "Cas de maladies sous surveillance", "REFUS", "Mobilisation communautaire des FEMMES", "Mobilisation communautaire des HOMMES", - "Mobilisation communautaire générale", - "Rattrapage/récupération en PEV", - "Complétude des rapports de vaccination", - "Vaccines 0-11 mois Zéro Dose", + "Mobilisation communautaire g\u00e9n\u00e9rale", + "Rattrapage/r\u00e9cup\u00e9ration en PEV", + "Compl\u00e9tude des rapports de vaccination", + "Vaccines 0-11 mois Z\u00e9ro Dose", "Vaccines 0-11 mois 1 dose et plus", - "Vaccines 12-59 mois Zéro Dose", + "Vaccines 12-59 mois Z\u00e9ro Dose", "Vaccines 12-59 mois 1 dose et plus", - "Vaccines 0-11 mois Zéro Dose", + "Vaccines 0-11 mois Z\u00e9ro Dose", "Vaccines 0-11 mois 1 dose et plus", - "Vaccines 12-59 mois Zéro Dose", + "Vaccines 12-59 mois Z\u00e9ro Dose", "Vaccines 12-59 mois 1 dose et plus", - "Vaccinés par les Equipes en milieu rural", - "Vaccinés par les Equipes en milieu urbain", + "Vaccin\u00e9s par les Equipes en milieu rural", + "Vaccin\u00e9s par les Equipes en milieu urbain", "GESTION DES FLACONS", "Recherche active des cas de MEV", "MAPI", - "Ver de Guinée" + "Ver de Guin\u00e9e" ) } diff --git a/R/login_page_ui.R b/R/login_page_ui.R index ddb53e5..a5815d6 100644 --- a/R/login_page_ui.R +++ b/R/login_page_ui.R @@ -32,7 +32,7 @@ ui_authenticate <- function() { tokens = list(childrenGap = 2), Text( variant = "xLarge", - "Portail de création des masques de saisie des campagnes de vaccination en RDC" + "Portail de cr\u00e9ation des masques de saisie des campagnes de vaccination en RDC" ), Text( variant = "small", @@ -49,7 +49,7 @@ ui_authenticate <- function() { Stack(tokens = list(childrenGap = 6), Text("Veuillez saisir votre adresse mail, ensuite cliquez sur “S’authentifier”."), Text("Cette application ne collecte ni n’enregistre votre mot de passe."), - Text("Une fenêtre va s’ouvrir : suivez les instructions pour compléter l’authentification.") + Text("Une fenêtre va s’ouvrir : suivez les instructions pour compl\u00e9ter l’authentification.") ) ), Separator(), diff --git a/R/server.R b/R/server.R index e1118be..a48ce0f 100644 --- a/R/server.R +++ b/R/server.R @@ -876,7 +876,7 @@ observeEvent(input$download_geo,{ user_email(user$emailAddress) showNotification( - paste("Authentifié avec succès en tant que", user_email()), + paste("Authentifi\u00e9 avec succès en tant que", user_email()), type = "message" ) showNotification("Donn\u00e9es Google Drive collect\u00e9es.", @@ -1487,7 +1487,7 @@ observeEvent(input$download_geo,{ output$campaign_template_url <- renderUI({tagList(a("Lien vers le masque de campagne", href = national_dribble_url))}) # Obtain completeness information - incProgress(1/5, message = "Analyse de la qualité des données") + incProgress(1/5, message = "Analyse de la qualit\u00e9 des donn\u00e9es") national_template_dribble <- googledrive::drive_get(national_dribble_url) surveillance_summary(get_sheet_info(national_template_dribble)) data_quality_info <- surveillance_summary() @@ -1508,7 +1508,7 @@ observeEvent(input$download_geo,{ strftime(Sys.time(), format = "%d/%m/%Y %R", usetz = TRUE), "; Compiled in ", round(compile_end, 2), "mins")) show("refresh_date") - showNotification("Traitement terminé", type = "message") + showNotification("Traitement termin\u00e9", type = "message") } ) }) @@ -1642,9 +1642,9 @@ observeEvent(input$click_download_campaign_quality_monitoring,{ "Section", "Cellules Remplies", "Cellules Totales", - "Exhaustivité", + "Exhaustivit\u00e9", "Jours Depuis la Dernière Modification", - "Date d'Exécution" + "Date d'Ex\u00e9cution" ) ) ) @@ -1670,11 +1670,11 @@ observeEvent(input$click_download_campaign_quality_monitoring,{ "Rapport Completude (%)", "Couverture (%)", "Couverture (Cumulative)", - "Nb moyen d'enfants vaccinés/équipe (Rural)", - "Nb moyen d'enfants vaccinés/équipe (Urban)", - "Récupérations (0-11 mois, cumulative)", - "Récupérations (12-23 mois, cumulative)", - "Récupérations (24-59 mois, cumulative)" + "Nb moyen d'enfants vaccin\u00e9s/\u00e9quipe (Rural)", + "Nb moyen d'enfants vaccin\u00e9s/\u00e9quipe (Urban)", + "R\u00e9cup\u00e9rations (0-11 mois, cumulative)", + "R\u00e9cup\u00e9rations (12-23 mois, cumulative)", + "R\u00e9cup\u00e9rations (24-59 mois, cumulative)" ) ) ) diff --git a/R/ui_campaign_creation.R b/R/ui_campaign_creation.R index b6487d9..1f7f702 100644 --- a/R/ui_campaign_creation.R +++ b/R/ui_campaign_creation.R @@ -1,6 +1,6 @@ ui_campaign_creation <- function() { nav_panel( - "Créer Une Campagne", + "Cr\u00e9er Une Campagne", Stack( tokens = list(childrenGap = 14), br(), @@ -24,7 +24,7 @@ ui_campaign_creation <- function() { style = "flex:1; min-width:220px;", DatePicker.shinyInput( "start_date", - label = "Début", + label = "D\u00e9but", isRequired = TRUE ) ), @@ -52,7 +52,7 @@ ui_campaign_creation <- function() { Text(variant = "mediumPlus", "Provinces"), DefaultButton.shinyInput( "select_all_prov", - text = "Tout sélectionner / désélectionner", + text = "Tout s\u00e9lectionner / d\u00e9s\u00e9lectionner", iconProps = list(iconName = "CheckList") ), div( @@ -72,7 +72,7 @@ ui_campaign_creation <- function() { Text(variant = "mediumPlus", "Antennes"), DefaultButton.shinyInput( "select_all_ant", - text = "Tout sélectionner / désélectionner", + text = "Tout s\u00e9lectionner / d\u00e9s\u00e9lectionner", iconProps = list(iconName = "CheckList") ), div( @@ -89,10 +89,10 @@ ui_campaign_creation <- function() { div( style = "flex:1; min-width:260px;", Stack(tokens = list(childrenGap = 6), - Text(variant = "mediumPlus", "Zones de santé"), + Text(variant = "mediumPlus", "Zones de sant\u00e9"), DefaultButton.shinyInput( "select_all_zs", - text = "Tout sélectionner / désélectionner", + text = "Tout s\u00e9lectionner / d\u00e9s\u00e9lectionner", iconProps = list(iconName = "CheckList") ), div( @@ -127,7 +127,7 @@ ui_campaign_creation <- function() { style = "flex:1; min-width:240px;", PrimaryButton.shinyInput( "create_campaign", - text = "Créer une campagne", + text = "Cr\u00e9er une campagne", iconProps = list(iconName = "Rocket") ) ) diff --git a/R/ui_geodatabase.R b/R/ui_geodatabase.R index fbe8838..3a288b8 100644 --- a/R/ui_geodatabase.R +++ b/R/ui_geodatabase.R @@ -1,7 +1,7 @@ ui_geodatabase <- function() { nav_panel( - "Informations Géographiques",br(), + "Informations G\u00e9ographiques",br(), Stack( tokens = list(childrenGap = 14), MessageBar( @@ -22,7 +22,7 @@ ui_geodatabase <- function() { horizontal = TRUE, verticalAlign = "center", tokens = list(childrenGap = 6), - Text("Vous pouvez télécharger au besoin un template"), + Text("Vous pouvez t\u00e9l\u00e9charger au besoin un template"), actionLink( "download_template_link", "ici", @@ -36,7 +36,7 @@ ui_geodatabase <- function() { tokens = list(childrenGap = 10), Text(variant = "large", "Charger un fichier CSV"), - Text("Veuillez charger un fichier CSV avec les informations géographiques."), + Text("Veuillez charger un fichier CSV avec les informations g\u00e9ographiques."), fileInput( "upload_geo", label = NULL, @@ -55,12 +55,12 @@ ui_geodatabase <- function() { verticalAlign = "center", tokens = list(childrenGap = 8), - icon_btn("delete_row", "Delete", "Supprimer la sélection", type = "danger"), + icon_btn("delete_row", "Delete", "Supprimer la s\u00e9lection", type = "danger"), icon_btn("undo_geo", "Undo", "Annuler", type = "primary"), - icon_btn("redo_geo", "Redo", "Rétablir", type = "primary"), + icon_btn("redo_geo", "Redo", "R\u00e9tablir", type = "primary"), icon_btn("clear_geo", "DeleteRows", "Tout effacer", type = "danger"), - icon_btn("download_geo", "Download", "Télécharger", type = "success"), - icon_btn("add_row_question", "Add", "Ajouter une entrée", type = "success") + icon_btn("download_geo", "Download", "T\u00e9l\u00e9charger", type = "success"), + icon_btn("add_row_question", "Add", "Ajouter une entr\u00e9e", type = "success") ), div( id = "add_new_row", @@ -76,7 +76,7 @@ ui_geodatabase <- function() { Stack( tokens = list(childrenGap = 12), - Text(variant = "mediumPlus", "Nouvelle entrée"), + Text(variant = "mediumPlus", "Nouvelle entr\u00e9e"), Stack( horizontal = TRUE, @@ -103,16 +103,16 @@ ui_geodatabase <- function() { style = "flex:1; min-width:240px;", TextField.shinyInput( "new_zs", - label = "Zones de santé", - placeholder = "Veuillez entrer le nom de la Zones de santé ici" + label = "Zones de sant\u00e9", + placeholder = "Veuillez entrer le nom de la Zones de sant\u00e9 ici" ) ), div( style = "flex:1; min-width:240px;", TextField.shinyInput( "new_as", - label = "Aires de santé", - placeholder = "Veuillez entrer le nom de l'aire de santé ici" + label = "Aires de sant\u00e9", + placeholder = "Veuillez entrer le nom de l'aire de sant\u00e9 ici" ) ), div( diff --git a/R/ui_monitoring.R b/R/ui_monitoring.R index afc372b..fd19373 100644 --- a/R/ui_monitoring.R +++ b/R/ui_monitoring.R @@ -164,7 +164,7 @@ ui_monitoring <- function() { ), tags$h3( class = "fluent-accordion-title", - "Informations sur la complétude" + "Informations sur la compl\u00e9tude" ) ), tags$span( @@ -176,7 +176,7 @@ ui_monitoring <- function() { class = "fluent-accordion-content", tags$div( class = "fluent-download-btn", - icon_btn("click_download_data_quality_monitoring", "Download", "Télécharger", type = "success") + icon_btn("click_download_data_quality_monitoring", "Download", "T\u00e9l\u00e9charger", type = "success") ), DT::DTOutput("campaign_info_table") ) @@ -211,7 +211,7 @@ ui_monitoring <- function() { class = "fluent-accordion-content", tags$div( class = "fluent-download-btn", - icon_btn("click_download_campaign_quality_monitoring", "Download", "Télécharger", type = "success") + icon_btn("click_download_campaign_quality_monitoring", "Download", "T\u00e9l\u00e9charger", type = "success") ), DT::DTOutput("campaign_progress_table") ) @@ -248,17 +248,17 @@ ui_monitoring <- function() { class = "selector-group", shiny.fluent::Dropdown.shinyInput( inputId = "prov_selector_campaign_completeness", - label = "Sélectionner une province:", + label = "S\u00e9lectionner une province:", options = list() ), shiny.fluent::Dropdown.shinyInput( inputId = "ant_selector_campaign_completeness", - label = "Sélectionner une antenne:", + label = "S\u00e9lectionner une antenne:", options = list() ), shiny.fluent::Dropdown.shinyInput( inputId = "zs_selector_campaign_completeness", - label = "Sélectionner une zone de sante:", + label = "S\u00e9lectionner une zone de sante:", options = list() ) ), @@ -274,11 +274,11 @@ ui_monitoring <- function() { plotOutput("campaign_completeness_plot_daily", height = "700px") ), shiny.fluent::PivotItem( - headerText = "Nb moyen d'enfants vaccinés/équipe", + headerText = "Nb moyen d'enfants vaccin\u00e9s/\u00e9quipe", plotOutput("campaign_urban_rural_plot", height = "700px") ), shiny.fluent::PivotItem( - headerText = "Récupérations", + headerText = "R\u00e9cup\u00e9rations", plotOutput("campaign_recovery_plot", height = "700px") ) ) diff --git a/R/ui_perm_table.R b/R/ui_perm_table.R index 5b894c4..77652cc 100644 --- a/R/ui_perm_table.R +++ b/R/ui_perm_table.R @@ -1,12 +1,12 @@ ui_perm_table <- function() { nav_panel( - "Gérer les Autorisations",br(), + "G\u00e9rer les Autorisations",br(), Stack( tokens = list(childrenGap = 14), - Text(variant = "xLarge", "Gérer les Niveaux d'Autorisation"), + Text(variant = "xLarge", "G\u00e9rer les Niveaux d'Autorisation"), fluent_card( Stack( @@ -19,7 +19,7 @@ ui_perm_table <- function() { label = NULL, accept = ".csv", buttonLabel = "Parcourir…", - placeholder = "Aucun fichier sélectionné" + placeholder = "Aucun fichier s\u00e9lectionn\u00e9" ), MessageBar( @@ -27,15 +27,15 @@ ui_perm_table <- function() { isMultiline = TRUE, styles = list(text = list(fontSize = 14, lineHeight = "1.55")), Stack(tokens = list(childrenGap = 6), - Text('Si le niveau est "national", les zones géographiques sont facultatives.'), - Text("Une zone géographique valide doit être incluse dans les autres niveaux."), - Text('Exemple : si le niveau est "zone de santé", incluez une valeur pour "province", "antenne" et "zone de santé".') + Text('Si le niveau est "national", les zones g\u00e9ographiques sont facultatives.'), + Text("Une zone g\u00e9ographique valide doit être incluse dans les autres niveaux."), + Text('Exemple : si le niveau est "zone de sant\u00e9", incluez une valeur pour "province", "antenne" et "zone de sant\u00e9".') ),br(), Stack( horizontal = TRUE, verticalAlign = "center", tokens = list(childrenGap = 6), - Text("Vous pouvez télécharger au besoin un template"), + Text("Vous pouvez t\u00e9l\u00e9charger au besoin un template"), actionLink( "download_template_link_permissions", "ici", @@ -58,12 +58,12 @@ ui_perm_table <- function() { verticalAlign = "center", tokens = list(childrenGap = 8), - icon_btn("delete_permission", "Delete", "Supprimer la sélection", type = "danger"), + icon_btn("delete_permission", "Delete", "Supprimer la s\u00e9lection", type = "danger"), icon_btn("undo_perm", "Undo", "Annuler", type = "primary"), - icon_btn("redo_perm", "Redo", "Rétablir", type = "primary"), + icon_btn("redo_perm", "Redo", "R\u00e9tablir", type = "primary"), icon_btn("clear_perm", "DeleteRows", "Tout effacer", type = "danger"), - icon_btn("press_download_permissions", "Download", "Télécharger", type = "success"), - icon_btn("add_row_question_perm", "Add", "Ajouter une entrée", type = "success") + icon_btn("press_download_permissions", "Download", "T\u00e9l\u00e9charger", type = "success"), + icon_btn("add_row_question_perm", "Add", "Ajouter une entr\u00e9e", type = "success") ), br(), div( @@ -144,7 +144,7 @@ ui_perm_table <- function() { Dropdown.shinyInput( "perm_province", label = "Province", - placeholder = "Sélectionnez une province", + placeholder = "S\u00e9lectionnez une province", options = list(), required = TRUE ) @@ -157,7 +157,7 @@ ui_perm_table <- function() { Dropdown.shinyInput( "perm_antenne", label = "Antenne", - placeholder = "Sélectionnez une antenne", + placeholder = "S\u00e9lectionnez une antenne", required = TRUE, options = list() ) @@ -170,8 +170,8 @@ ui_perm_table <- function() { style = "flex:1; min-width:240px;", Dropdown.shinyInput( "perm_zs", - label = "Zones de Santé", - placeholder = "Sélectionnez une zone de santé", + label = "Zones de Sant\u00e9", + placeholder = "S\u00e9lectionnez une zone de sant\u00e9", options = list(), required = TRUE ) @@ -185,7 +185,7 @@ ui_perm_table <- function() { PrimaryButton.shinyInput( "add_permission", - text = "Ajouter une entrée", + text = "Ajouter une entr\u00e9e", iconProps = list(iconName = "Add") ) ) @@ -203,7 +203,7 @@ ui_perm_table <- function() { Stack( tokens = list(childrenGap = 12), - Text(variant = "xLarge", "Sélection de Campagne"), + Text(variant = "xLarge", "S\u00e9lection de Campagne"), uiOutput("campaign_drive_picker"), @@ -214,7 +214,7 @@ ui_perm_table <- function() { PrimaryButton.shinyInput( "set_permissions_btn", - text = "Définir l'autorisation", + text = "D\u00e9finir l'autorisation", iconProps = list(iconName = "Permissions") ), From 9c96652e59853c2b8d50626a75b00a726ba79ed2 Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Mon, 2 Mar 2026 10:30:28 +0000 Subject: [PATCH 03/17] declare missing import --- DESCRIPTION | 3 +++ NAMESPACE | 5 +++++ R/rdcAVS-package.R | 3 +++ 3 files changed, 11 insertions(+) diff --git a/DESCRIPTION b/DESCRIPTION index 916e5bd..bb4c710 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -22,16 +22,19 @@ Imports: foreach, fs, future, + ggplot2, ggpubr, googledrive, googlesheets4, httr, lubridate, + openxlsx, pkgload, progressr, purrr, rappdirs, readr, + scales, shiny, shiny.fluent, shinyFiles, diff --git a/NAMESPACE b/NAMESPACE index f221fbb..e05b66f 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -13,6 +13,7 @@ export(icon_btn) import(bsicons) import(bslib) import(dplyr) +import(ggplot2) import(googledrive) import(readr) import(shiny) @@ -40,6 +41,9 @@ importFrom(googlesheets4,range_write) importFrom(httr,POST) importFrom(httr,add_headers) importFrom(lubridate,as_date) +importFrom(openxlsx,loadWorkbook) +importFrom(openxlsx,saveWorkbook) +importFrom(openxlsx,writeData) importFrom(progressr,handlers) importFrom(progressr,progressor) importFrom(progressr,with_progress) @@ -47,6 +51,7 @@ importFrom(purrr,pwalk) importFrom(purrr,walk) importFrom(rappdirs,user_data_dir) importFrom(readr,type_convert) +importFrom(scales,percent) importFrom(shiny.fluent,DatePicker.shinyInput) importFrom(shiny.fluent,DefaultButton.shinyInput) importFrom(shiny.fluent,Dropdown.shinyInput) diff --git a/R/rdcAVS-package.R b/R/rdcAVS-package.R index 5b3f6d5..b59f77b 100644 --- a/R/rdcAVS-package.R +++ b/R/rdcAVS-package.R @@ -9,6 +9,9 @@ #' @import dplyr stringr readr #' @import googledrive #' @import shiny shinyFiles +#' @import ggplot2 +#' @importFrom openxlsx loadWorkbook writeData saveWorkbook +#' @importFrom scales percent #' @importFrom cli cli_abort #' @importFrom cli cli_alert #' @importFrom cli cli_alert_info From f52ff5743365222e0f0e5d1852749619b30d598e Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Mon, 2 Mar 2026 10:54:08 +0000 Subject: [PATCH 04/17] add documentation for data_perm and template_data_geographics dataset --- R/data.R | 40 ++++++++++++++++++++++++++++++++ man/data_perm.Rd | 35 ++++++++++++++++++++++++++++ man/template_data_geographics.Rd | 31 +++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 R/data.R create mode 100644 man/data_perm.Rd create mode 100644 man/template_data_geographics.Rd diff --git a/R/data.R b/R/data.R new file mode 100644 index 0000000..c6ec28e --- /dev/null +++ b/R/data.R @@ -0,0 +1,40 @@ +#' Template for user permissions data +#' +#' A template dataset showing the expected structure and columns for the user +#' permissions file. Users can download this template to prepare their own +#' permissions file before importing it into the application. +#' +#' @format A tibble with 9 columns +#' \describe{ +#' \item{name}{Full name of the user (character)} +#' \item{phone}{Phone number of the user (numeric)} +#' \item{notes}{Optional notes about the user (character)} +#' \item{email}{Email address of the user (character)} +#' \item{level}{Access level of the user, e.g. \code{"national"} (character)} +#' \item{role}{Role of the user, e.g. \code{"writer"} (character)} +#' \item{province}{Province assigned to the user, \code{NA} if not applicable (logical)} +#' \item{antenne}{Antenne assigned to the user, \code{NA} if not applicable (logical)} +#' \item{zone_de_sante}{Health zone assigned to the user, \code{NA} if not applicable (logical)} +#' } +#' @note The expected file format is \code{.csv}. +#' @source Internal rdcAVS package template +"data_perm" + + +#' Template for geographic data +#' +#' A template dataset showing the expected structure and columns for the +#' geographic data file. Users can download this template to prepare their +#' own geographic file before importing it into the application. +#' +#' @format A tibble with 5 columns: +#' \describe{ +#' \item{provinces}{Name of the province (character)} +#' \item{antennes}{Name of the antenne (character)} +#' \item{zones_de_sante}{Name of the health zone (character)} +#' \item{aires_de_sante}{Name of the health area (character)} +#' \item{population_totale}{Total population of the health area (numeric)} +#' } +#' @note The expected file format is \code{.csv}. +#' @source Internal rdcAVS package template +"template_data_geographics" \ No newline at end of file diff --git a/man/data_perm.Rd b/man/data_perm.Rd new file mode 100644 index 0000000..50e7ba3 --- /dev/null +++ b/man/data_perm.Rd @@ -0,0 +1,35 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{data_perm} +\alias{data_perm} +\title{Template for user permissions data} +\format{ +A tibble +\describe{ +\item{name}{Full name of the user (character)} +\item{phone}{Phone number of the user (numeric)} +\item{notes}{Optional notes about the user (character)} +\item{email}{Email address of the user (character)} +\item{level}{Access level of the user, e.g. \code{"national"} (character)} +\item{role}{Role of the user, e.g. \code{"writer"} (character)} +\item{province}{Province assigned to the user, \code{NA} if not applicable (logical)} +\item{antenne}{Antenne assigned to the user, \code{NA} if not applicable (logical)} +\item{zone_de_sante}{Health zone assigned to the user, \code{NA} if not applicable (logical)} +} +} +\source{ +Internal rdcAVS package template +} +\usage{ +data_perm +} +\description{ +A template dataset showing the expected structure and columns for the user +permissions file. Users can download this template to prepare their own +permissions file before importing it into the application. +} +\note{ +The expected file format is \code{.csv}. +} +\keyword{datasets} diff --git a/man/template_data_geographics.Rd b/man/template_data_geographics.Rd new file mode 100644 index 0000000..a403883 --- /dev/null +++ b/man/template_data_geographics.Rd @@ -0,0 +1,31 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/data.R +\docType{data} +\name{template_data_geographics} +\alias{template_data_geographics} +\title{Template for geographic data} +\format{ +A tibble with rows and 5 columns: +\describe{ +\item{provinces}{Name of the province (character)} +\item{antennes}{Name of the antenne (character)} +\item{zones_de_sante}{Name of the health zone (character)} +\item{aires_de_sante}{Name of the health area (character)} +\item{population_totale}{Total population of the health area (numeric)} +} +} +\source{ +Internal rdcAVS package template +} +\usage{ +template_data_geographics +} +\description{ +A template dataset showing the expected structure and columns for the +geographic data file. Users can download this template to prepare their +own geographic file before importing it into the application. +} +\note{ +The expected file format is \code{.csv}. +} +\keyword{datasets} From cdba807c8eec725e14185252485d75cc31d0b9d2 Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Mon, 2 Mar 2026 11:19:28 +0000 Subject: [PATCH 05/17] Declare write.csv() in the import --- DESCRIPTION | 3 ++- NAMESPACE | 10 ++++++++++ R/rdcAVS-package.R | 2 ++ man/data_perm.Rd | 2 +- man/template_data_geographics.Rd | 2 +- 5 files changed, 16 insertions(+), 3 deletions(-) diff --git a/DESCRIPTION b/DESCRIPTION index bb4c710..f490b7c 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -40,7 +40,8 @@ Imports: shinyFiles, shinyjs, stringr, - tidyr + tidyr, + utils Suggests: stringi URL: https://mcuadera.github.io/rdcAVS/ diff --git a/NAMESPACE b/NAMESPACE index e05b66f..2ec9ca2 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -70,3 +70,13 @@ importFrom(shinyjs,show) importFrom(shinyjs,toggle) importFrom(shinyjs,useShinyjs) importFrom(tidyr,pivot_longer) +importFrom(utils,Rprof) +importFrom(utils,available.packages) +importFrom(utils,contrib.url) +importFrom(utils,download.file) +importFrom(utils,globalVariables) +importFrom(utils,read.table) +importFrom(utils,summaryRprof) +importFrom(utils,txtProgressBar) +importFrom(utils,winProgressBar) +importFrom(utils,write.csv) diff --git a/R/rdcAVS-package.R b/R/rdcAVS-package.R index b59f77b..3d25805 100644 --- a/R/rdcAVS-package.R +++ b/R/rdcAVS-package.R @@ -45,6 +45,7 @@ #' @importFrom shinyjs show toggle click #' @importFrom shinyjs useShinyjs #' @importFrom tidyr pivot_longer +#' @importFrom utils txtProgressBar winProgressBar globalVariables Rprof summaryRprof download.file contrib.url available.packages read.table write.csv #' @importFrom shiny.fluent Stack MessageBar updateDropdown.shinyInput Dropdown.shinyInput DefaultButton.shinyInput IconButton.shinyInput TooltipHost DatePicker.shinyInput Text TextField.shinyInput Separator PrimaryButton.shinyInput ## usethis namespace: end @@ -53,6 +54,7 @@ utils::globalVariables(c( "fin", "level", "local_path", "masque_names", "mime_type", "path", "population_totale", "province", "provinces", "role", "x", "zone_de_sante", "zones_de_sante" + )) NULL diff --git a/man/data_perm.Rd b/man/data_perm.Rd index 50e7ba3..8b50948 100644 --- a/man/data_perm.Rd +++ b/man/data_perm.Rd @@ -5,7 +5,7 @@ \alias{data_perm} \title{Template for user permissions data} \format{ -A tibble +A tibble with 9 columns \describe{ \item{name}{Full name of the user (character)} \item{phone}{Phone number of the user (numeric)} diff --git a/man/template_data_geographics.Rd b/man/template_data_geographics.Rd index a403883..110c18f 100644 --- a/man/template_data_geographics.Rd +++ b/man/template_data_geographics.Rd @@ -5,7 +5,7 @@ \alias{template_data_geographics} \title{Template for geographic data} \format{ -A tibble with rows and 5 columns: +A tibble with 5 columns: \describe{ \item{provinces}{Name of the province (character)} \item{antennes}{Name of the antenne (character)} From 6ceb065d4c87bbdbdd71556b7bdb18301109b288 Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Mon, 2 Mar 2026 11:30:45 +0000 Subject: [PATCH 06/17] Fix import statement in rdcAVS-package.R --- R/rdcAVS-package.R | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/R/rdcAVS-package.R b/R/rdcAVS-package.R index 3d25805..46599bf 100644 --- a/R/rdcAVS-package.R +++ b/R/rdcAVS-package.R @@ -45,7 +45,7 @@ #' @importFrom shinyjs show toggle click #' @importFrom shinyjs useShinyjs #' @importFrom tidyr pivot_longer -#' @importFrom utils txtProgressBar winProgressBar globalVariables Rprof summaryRprof download.file contrib.url available.packages read.table write.csv +#' @importFrom utils txtProgressBar globalVariables Rprof summaryRprof download.file contrib.url available.packages read.table write.csv #' @importFrom shiny.fluent Stack MessageBar updateDropdown.shinyInput Dropdown.shinyInput DefaultButton.shinyInput IconButton.shinyInput TooltipHost DatePicker.shinyInput Text TextField.shinyInput Separator PrimaryButton.shinyInput ## usethis namespace: end From 0305458cc19dd4e0eb1753c592960fe35dbd2e00 Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Mon, 2 Mar 2026 11:51:49 +0000 Subject: [PATCH 07/17] Declare write.csv() in the import --- NAMESPACE | 9 --------- R/rdcAVS-package.R | 17 ++++++++++++++--- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/NAMESPACE b/NAMESPACE index 2ec9ca2..a64ab50 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -70,13 +70,4 @@ importFrom(shinyjs,show) importFrom(shinyjs,toggle) importFrom(shinyjs,useShinyjs) importFrom(tidyr,pivot_longer) -importFrom(utils,Rprof) -importFrom(utils,available.packages) -importFrom(utils,contrib.url) -importFrom(utils,download.file) -importFrom(utils,globalVariables) -importFrom(utils,read.table) -importFrom(utils,summaryRprof) -importFrom(utils,txtProgressBar) -importFrom(utils,winProgressBar) importFrom(utils,write.csv) diff --git a/R/rdcAVS-package.R b/R/rdcAVS-package.R index 3d25805..0089b79 100644 --- a/R/rdcAVS-package.R +++ b/R/rdcAVS-package.R @@ -45,7 +45,7 @@ #' @importFrom shinyjs show toggle click #' @importFrom shinyjs useShinyjs #' @importFrom tidyr pivot_longer -#' @importFrom utils txtProgressBar winProgressBar globalVariables Rprof summaryRprof download.file contrib.url available.packages read.table write.csv +#' @importFrom utils write.csv #' @importFrom shiny.fluent Stack MessageBar updateDropdown.shinyInput Dropdown.shinyInput DefaultButton.shinyInput IconButton.shinyInput TooltipHost DatePicker.shinyInput Text TextField.shinyInput Separator PrimaryButton.shinyInput ## usethis namespace: end @@ -53,8 +53,19 @@ utils::globalVariables(c( "aires_de_sante", "antenne", "antennes", "debut", "email", "fin", "level", "local_path", "masque_names", "mime_type", "path", "population_totale", "province", "provinces", "role", - "x", "zone_de_sante", "zones_de_sante" - + "x", "zone_de_sante", "zones_de_sante", + "name", "web_view_link", "grid_columns", "max_rows", + "aire_de_sante", "jour", "couverture_campaign_cumulative", + "couverture_campagne_pct", "cat_0_11", "recovery_0_11_cumulative", + "cat_12_23", "recovery_12_23_cumulative", "cat_24_59", + "recovery_24_59_cumulative", "cat_rural", "cat_urban", + "avg_vax_rural", "avg_vax_urban", "period", "zs_path", + "...1", "section", "empty_cells", "filled_cells", "total_cells", + "prov", "completeness", "cible_0_59_mois", "vaccination_0_59_mois", + "rapports_de_vaccination_recus", "rapports_de_vaccination_attendus", + "recovery_0_11", "recovery_12_23", "recovery_24_59", + "template_data_geographics", "data_perm", "dir_path", + "write.csv" )) NULL From 052d10e212a27abd07d51322c112b23f37e267c0 Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Mon, 2 Mar 2026 14:42:10 +0000 Subject: [PATCH 08/17] Replace non-ASCII characters with ASCII equivalents (2) --- R/app.R | 2 +- R/create_campaign_progress_heatmap.R | 2 +- R/create_daily_campaign_progress_heatmap.R | 2 +- R/get_sheet_info.R | 8 ++++---- R/login_page_ui.R | 6 +++--- R/server.R | 4 ++-- R/ui_campaign_creation.R | 4 ++-- R/ui_geodatabase.R | 2 +- R/ui_monitoring.R | 6 +++--- R/ui_perm_table.R | 8 ++++---- man/campagneApp.Rd | 2 +- 11 files changed, 23 insertions(+), 23 deletions(-) diff --git a/R/app.R b/R/app.R index 6f5cc2b..3d26c34 100644 --- a/R/app.R +++ b/R/app.R @@ -19,7 +19,7 @@ #' tabs that creates a campaign, add geographic information, and set Google Drive #' permissions. Creating a campaign outputs a folder prefixed with "CAMPAGNE_", #' which contains a hierarchical structure, going from the largest geographic unit (Province) -#' to the lowest geographic unit (Zone de Sant\u00e9). Within the Zone de Sant\u00e9 folder, contains +#' to the lowest geographic unit (Zone de Santé). Within the Zone de Santé folder, contains #' a template file. #' #' @details diff --git a/R/create_campaign_progress_heatmap.R b/R/create_campaign_progress_heatmap.R index ec69a87..05cef8a 100644 --- a/R/create_campaign_progress_heatmap.R +++ b/R/create_campaign_progress_heatmap.R @@ -36,7 +36,7 @@ create_campaign_progress_heatmap <- function(summary) { ggplot2::ylab("Aire De Sante") + ggplot2::xlab("") + ggplot2::labs(subtitle = paste0("Objectif: Jour 1: 40%, Jour 2: 70%, Jour 3: 100%, Jour 4: 100%\n", - "A risque si à moins de 5 % et en \u00e9chec si à plus de 5 % de l'objectif")) + + "A risque si \u00e0 moins de 5 % et en \u00e9chec si \u00e0 plus de 5 % de l'objectif")) + ggplot2::theme(axis.text.x = ggplot2::element_text(size = 14), axis.text.y = ggplot2::element_text(size = 14), axis.title.x = ggplot2::element_text(size = 14, face = "bold"), diff --git a/R/create_daily_campaign_progress_heatmap.R b/R/create_daily_campaign_progress_heatmap.R index 6519537..d53b686 100644 --- a/R/create_daily_campaign_progress_heatmap.R +++ b/R/create_daily_campaign_progress_heatmap.R @@ -34,7 +34,7 @@ create_daily_campaign_progress_heatmap <- function(summary) { ggplot2::ylab("Aire De Sante") + ggplot2::xlab("") + ggplot2::labs(subtitle = paste0("Objectif: Jour 1: 40%, Jour 2-3: 30%, Jour 4: 100%\n", - "A risque si à moins de 5 % et en \u00e9echec si à plus de 5 % de l'objectif")) + + "A risque si \u00e0 moins de 5 % et en \u00e9echec si \u00e0 plus de 5 % de l'objectif")) + ggplot2::theme(axis.text.x = ggplot2::element_text(size = 14), axis.text.y = ggplot2::element_text(size = 14), axis.title.x = ggplot2::element_text(size = 14, face = "bold"), diff --git a/R/get_sheet_info.R b/R/get_sheet_info.R index f89d1f2..45f157e 100644 --- a/R/get_sheet_info.R +++ b/R/get_sheet_info.R @@ -119,8 +119,8 @@ get_sheet_info <- function(dribble, sheets = 1:8) { if (sheet == 1) { switch(name, "Situation des \u00e9quipes de vaccination par aires de sant\u00e9" = "E:G", - "Populations cibles vaccin\u00e9es à la campagne pr\u00e9c\u00e9dente" = "N:O", - "Disponilit\u00e9 du vaccin, antigène utilis\u00e9" = "V:V", + "Populations cibles vaccin\u00e9es \u00e0 la campagne pr\u00e9c\u00e9dente" = "N:O", + "Disponilit\u00e9 du vaccin, antig\u00e8ne utilis\u00e9" = "V:V", "Nombre d'\u00e9quipes de mobilisation sociale de proxi" = "Y:Y", "RESEAU/ INTERNET" = "Z:AB", "Enfants 0 - 11 mois identifi\u00e9s pour etre r\u00e9cup\u00e9r\u00e9s en PEV de routine" = "AC:AQ", @@ -234,8 +234,8 @@ get_sheet_info <- function(dribble, sheets = 1:8) { if (sheet == 1) { c( "Situation des \u00e9quipes de vaccination par aires de sant\u00e9", - "Populations cibles vaccin\u00e9es à la campagne pr\u00e9c\u00e9dente", - "Disponilit\u00e9 du vaccin, antigène utilis\u00e9" , + "Populations cibles vaccin\u00e9es \u00e0 la campagne pr\u00e9c\u00e9dente", + "Disponilit\u00e9 du vaccin, antig\u00e8ne utilis\u00e9" , "Nombre d'\u00e9quipes de mobilisation sociale de proxi", "RESEAU/ INTERNET" , "Enfants 0 - 11 mois identifi\u00e9s pour etre r\u00e9cup\u00e9r\u00e9s en PEV de routine", diff --git a/R/login_page_ui.R b/R/login_page_ui.R index a5815d6..e0402fd 100644 --- a/R/login_page_ui.R +++ b/R/login_page_ui.R @@ -47,9 +47,9 @@ ui_authenticate <- function() { isMultiline = TRUE, styles = list(text = list(fontSize = 14, lineHeight = "1.55")), Stack(tokens = list(childrenGap = 6), - Text("Veuillez saisir votre adresse mail, ensuite cliquez sur “S’authentifier”."), - Text("Cette application ne collecte ni n’enregistre votre mot de passe."), - Text("Une fenêtre va s’ouvrir : suivez les instructions pour compl\u00e9ter l’authentification.") + Text("Veuillez saisir votre adresse mail, ensuite cliquez sur 'S\u0027authentifier'."), + Text("Cette application ne collecte ni n\u0027enregistre votre mot de passe."), + Text("Une fen\u00eatre va s\u0027ouvrir : suivez les instructions pour compl\u00e9ter l\u0027authentification.") ) ), Separator(), diff --git a/R/server.R b/R/server.R index a48ce0f..54f9189 100644 --- a/R/server.R +++ b/R/server.R @@ -876,7 +876,7 @@ observeEvent(input$download_geo,{ user_email(user$emailAddress) showNotification( - paste("Authentifi\u00e9 avec succès en tant que", user_email()), + paste("Authentifi\u00e9 avec succ\u00e8s en tant que", user_email()), type = "message" ) showNotification("Donn\u00e9es Google Drive collect\u00e9es.", @@ -1643,7 +1643,7 @@ observeEvent(input$click_download_campaign_quality_monitoring,{ "Cellules Remplies", "Cellules Totales", "Exhaustivit\u00e9", - "Jours Depuis la Dernière Modification", + "Jours Depuis la Derni\u00e8re Modification", "Date d'Ex\u00e9cution" ) ) diff --git a/R/ui_campaign_creation.R b/R/ui_campaign_creation.R index 1f7f702..82ddecd 100644 --- a/R/ui_campaign_creation.R +++ b/R/ui_campaign_creation.R @@ -117,8 +117,8 @@ ui_campaign_creation <- function() { style = "flex:2; min-width:300px;", TextField.shinyInput( "zs_template_url", - label = "Adresse URL du modèle de masque", - placeholder = "https://…", + label = "Adresse URL du mod\u00e8le de masque", + placeholder = "https://...", iconProps = list(iconName = "Link") ) ), diff --git a/R/ui_geodatabase.R b/R/ui_geodatabase.R index 3a288b8..0ef2a23 100644 --- a/R/ui_geodatabase.R +++ b/R/ui_geodatabase.R @@ -41,7 +41,7 @@ ui_geodatabase <- function() { "upload_geo", label = NULL, accept = ".csv", - buttonLabel = "Parcourir…", + buttonLabel = "Parcourir...", placeholder = "Aucun fichier choisi" ) ) diff --git a/R/ui_monitoring.R b/R/ui_monitoring.R index fd19373..2a52fef 100644 --- a/R/ui_monitoring.R +++ b/R/ui_monitoring.R @@ -169,7 +169,7 @@ ui_monitoring <- function() { ), tags$span( class = "fluent-accordion-chevron", - "▼" + "\u25BC" ) ), tags$div( @@ -203,7 +203,7 @@ ui_monitoring <- function() { tags$span( class = "fluent-accordion-chevron", - "▼" + "\u25BC" ) ), @@ -238,7 +238,7 @@ ui_monitoring <- function() { tags$span( class = "fluent-accordion-chevron", - "▼" + "\u25BC" ) ), diff --git a/R/ui_perm_table.R b/R/ui_perm_table.R index 77652cc..89c4ebc 100644 --- a/R/ui_perm_table.R +++ b/R/ui_perm_table.R @@ -18,7 +18,7 @@ ui_perm_table <- function() { "upload_permissions", label = NULL, accept = ".csv", - buttonLabel = "Parcourir…", + buttonLabel = "Parcourir...", placeholder = "Aucun fichier s\u00e9lectionn\u00e9" ), @@ -28,7 +28,7 @@ ui_perm_table <- function() { styles = list(text = list(fontSize = 14, lineHeight = "1.55")), Stack(tokens = list(childrenGap = 6), Text('Si le niveau est "national", les zones g\u00e9ographiques sont facultatives.'), - Text("Une zone g\u00e9ographique valide doit être incluse dans les autres niveaux."), + Text("Une zone g\u00e9ographique valide doit \u00eatre incluse dans les autres niveaux."), Text('Exemple : si le niveau est "zone de sant\u00e9", incluez une valeur pour "province", "antenne" et "zone de sant\u00e9".') ),br(), Stack( @@ -91,11 +91,11 @@ ui_perm_table <- function() { ), div( style = "flex:1; min-width:240px;", - TextField.shinyInput("perm_phone", label = "Phone", placeholder = "+243…") + TextField.shinyInput("perm_phone", label = "Phone", placeholder = "+243...") ), div( style = "flex:2; min-width:280px;", - TextField.shinyInput("perm_notes", label = "Notes", placeholder = "Affiliation, Poste, etc…") + TextField.shinyInput("perm_notes", label = "Notes", placeholder = "Affiliation, Poste, etc...") ) ), diff --git a/man/campagneApp.Rd b/man/campagneApp.Rd index 4348e8c..962e169 100644 --- a/man/campagneApp.Rd +++ b/man/campagneApp.Rd @@ -14,7 +14,7 @@ The rdcAVS application deploys to the web browser locally. It consists of three tabs that creates a campaign, add geographic information, and set Google Drive permissions. Creating a campaign outputs a folder prefixed with "CAMPAGNE_", which contains a hierarchical structure, going from the largest geographic unit (Province) -to the lowest geographic unit (Zone de Sant\u00e9). Within the Zone de Sant\u00e9 folder, contains +to the lowest geographic unit (Zone de Santé). Within the Zone de Santé folder, contains a template file. } \details{ From ee803fe5a7c614f9ee3fe538be7f9ad91a9ce199 Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Wed, 4 Mar 2026 09:49:54 +0000 Subject: [PATCH 09/17] resolve date parsing failure for timestamp format --- R/drive_init_campaign.R | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/R/drive_init_campaign.R b/R/drive_init_campaign.R index c9954f0..d066e12 100644 --- a/R/drive_init_campaign.R +++ b/R/drive_init_campaign.R @@ -41,8 +41,9 @@ drive_init_campaign <- function(start_date, campaign_name <- Sys.Date() } - start_date <- lubridate::as_date(start_date, format = c("%d/%m/%Y", "%Y-%m-%d")) - end_date <- lubridate::as_date(end_date, format = c("%d/%m/%Y", "%Y-%m-%d")) + start_date <- lubridate::as_date(start_date) + end_date <- lubridate::as_date(end_date) + campaign_name <- paste0("CAMPAGNE_", campaign_name) if (!is.null(prov_target)) { From 074f0fc5c55e60ededa0a60025a25a97e016b59b Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:00:19 +0000 Subject: [PATCH 10/17] shiny.fluent task button functions with busy/ready state management --- R/shiny_fluent_task_button.R | 223 +++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 R/shiny_fluent_task_button.R diff --git a/R/shiny_fluent_task_button.R b/R/shiny_fluent_task_button.R new file mode 100644 index 0000000..ffdcbde --- /dev/null +++ b/R/shiny_fluent_task_button.R @@ -0,0 +1,223 @@ +#' @export + +task_button_js <- " +(function() { + var style = document.createElement('style'); + style.textContent = '@keyframes task-spin { to { transform: rotate(360deg); } }'; + document.head.appendChild(style); + + Shiny.addCustomMessageHandler('taskBtnBusy', function(msg) { + var wrapper = document.getElementById(msg.id); + if (!wrapper) return; + wrapper.setAttribute('data-state', 'busy'); + var fluentBtn = wrapper.querySelector('button'); + if (fluentBtn) fluentBtn.disabled = true; + var fluentLabel = wrapper.querySelector('.ms-Button-label'); + if (fluentLabel) fluentLabel.textContent = msg.label_busy; + var icon = wrapper.querySelector('.ms-Button-icon'); + if (icon) { + icon.dataset.originalContent = icon.innerHTML; + icon.innerHTML = ''; + } + }); + + Shiny.addCustomMessageHandler('taskBtnReady', function(msg) { + var wrapper = document.getElementById(msg.id); + if (!wrapper) return; + wrapper.setAttribute('data-state', 'ready'); + var fluentBtn = wrapper.querySelector('button'); + if (fluentBtn) fluentBtn.disabled = false; + var fluentLabel = wrapper.querySelector('.ms-Button-label'); + var labelReady = wrapper.querySelector('.task-btn-label-ready'); + if (fluentLabel && labelReady) fluentLabel.textContent = labelReady.textContent; + var icon = wrapper.querySelector('.ms-Button-icon'); + if (icon && icon.dataset.originalContent) { + icon.innerHTML = icon.dataset.originalContent; + } + }); + +})(); +" +#' @export + +task_button_css <- " +@keyframes task-spin { + to { transform: rotate(360deg); } +} + +.task-btn-spinner { + display: none; + width: 14px; + height: 14px; + border: 2px solid rgba(255,255,255,0.3); + border-top-color: #fff; + border-radius: 50%; + animation: task-spin 0.8s linear infinite; + margin-right: 6px; + vertical-align: middle; +} + +[data-state='busy'] .task-btn-spinner { display: inline-block; } +[data-state='ready'] .task-btn-spinner { display: none; } + + .notification-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + z-index: 99998; + display: none; + opacity: 0; + transition: opacity 0.3s ease-in-out; + } + + .notification-overlay.show { + display: block; + opacity: 1; + } + + #shiny-notification-panel { + position: fixed; + top: 50% !important; + left: 50% !important; + right: auto !important; + bottom: auto !important; + transform: translate(-50%, -50%) !important; + width: auto; + max-width: 500px; + z-index: 99999; + } + + @keyframes slideDown { + from { + opacity: 0; + transform: translate(-50%, -60%); + } + to { + opacity: 1; + transform: translate(-50%, -50%); + } + } + + @keyframes pulse { + 0%, 100% { + transform: scale(1); + } + 50% { + transform: scale(1.05); + } + } + + .shiny-notification { + background-color: white; + border: none; + border-radius: 12px; + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); + padding: 40px 30px 30px 30px; + text-align: center; + font-size: 16px; + color: #333; + opacity: 1 !important; + min-width: 350px; + position: relative; + animation: slideDown 0.4s ease-out; + } + + .shiny-notification::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 4px; + border-radius: 12px 12px 0 0; + background: linear-gradient(90deg, #3498db, #2980b9); + } + + .shiny-notification-message::before { + background: linear-gradient(90deg, #3498db, #2980b9); + } + + .shiny-notification-warning::before { + background: linear-gradient(90deg, #f39c12, #e67e22); + } + + .shiny-notification-error::before { + background: linear-gradient(90deg, #e74c3c, #c0392b); + } + + .shiny-notification.success-notification::before { + background: linear-gradient(90deg, #27ae60, #229954); + } + + .shiny-notification-message, + .shiny-notification-warning, + .shiny-notification-error { + background-color: white; + border-left: none; + } + + .shiny-notification .message-text { + color: #555; + line-height: 1.6; + margin-top: 8px; + font-size: 15px; + }" + +#' Task button wrapper for shiny.fluent +#' +#' Wraps a \code{PrimaryButton.shinyInput} with busy/ready state management. +#' +#' @param inputId `chr` The input that will be used to access the value. +#' @param label `chr` The button label. +#' @param ... Additional arguments passed to \code{PrimaryButton.shinyInput}. +#' +#' @return A \code{tagList} containing the button with CSS and JS handlers. +#' @noRd +#' @export + +taskButton <- function(inputId, label, label_busy = "Processing...", ...) { + div( + id = paste0(inputId, "_wrapper"), + `data-state` = "ready", + tags$span(class = "task-btn-label-ready", style = "display:none;", label), + tags$span(class = "task-btn-label-busy", style = "display:none;", label_busy), + PrimaryButton.shinyInput( + inputId = inputId, + text = label, + ... + ) + ) +} + +#' Server-side handler for task button busy/ready states +#' +#' Use inside an \code{observeEvent} to toggle a \code{taskButton} +#' between busy and ready states around a long-running expression. +#' +#' @param session Shiny session object. +#' @param inputId `chr` The input ID of the target \code{taskButton}. +#' @param expr Expression to evaluate while the button is in busy state. +#' @param label_busy `chr` Label shown during processing. Defaults to \code{"Processing..."}. +#' +#' @return Invisibly returns the result of \code{expr}. +#' @noRd +#' @export + +withTaskButton <- function(session, inputId, expr, + label_busy = "Processing...") { + session$sendCustomMessage("taskBtnBusy", list( + id = paste0(inputId, "_wrapper"), + label_busy = label_busy + )) + + result <- force(expr) + + session$sendCustomMessage("taskBtnReady", list( + id = paste0(inputId, "_wrapper") + )) + + invisible(result) +} \ No newline at end of file From da863e5ce6413e3e5c572734a184f0dddfec5cb6 Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:07:51 +0000 Subject: [PATCH 11/17] document shiny.fluent task button functions --- man/taskButton.Rd | 23 +++++++++++++++++++++++ man/task_button_css.Rd | 16 ++++++++++++++++ man/task_button_js.Rd | 16 ++++++++++++++++ man/withTaskButton.Rd | 24 ++++++++++++++++++++++++ 4 files changed, 79 insertions(+) create mode 100644 man/taskButton.Rd create mode 100644 man/task_button_css.Rd create mode 100644 man/task_button_js.Rd create mode 100644 man/withTaskButton.Rd diff --git a/man/taskButton.Rd b/man/taskButton.Rd new file mode 100644 index 0000000..a5ccf78 --- /dev/null +++ b/man/taskButton.Rd @@ -0,0 +1,23 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/shiny_fluent_task_button.R +\name{taskButton} +\alias{taskButton} +\title{Task button wrapper for shiny.fluent} +\usage{ +taskButton(inputId, label, label_busy = "Processing...", ...) +} +\arguments{ +\item{inputId}{\code{chr} The input that will be used to access the value.} + +\item{label}{\code{chr} The button label.} + +\item{label_busy}{\code{chr} Label shown during busy state. Defaults to \code{"Processing..."}} + +\item{...}{Additional arguments passed to \code{PrimaryButton.shinyInput}.} +} +\value{ +A \code{tagList} containing the button with CSS and JS handlers. +} +\description{ +Wraps a \code{PrimaryButton.shinyInput} with busy/ready state management. +} diff --git a/man/task_button_css.Rd b/man/task_button_css.Rd new file mode 100644 index 0000000..49a11fd --- /dev/null +++ b/man/task_button_css.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/shiny_fluent_task_button.R +\docType{data} +\name{task_button_css} +\alias{task_button_css} +\title{CSS for taskButton spinner animation} +\format{ +An object of class \code{character} of length 1. +} +\usage{ +task_button_css +} +\description{ +CSS for taskButton spinner animation +} +\keyword{internal} diff --git a/man/task_button_js.Rd b/man/task_button_js.Rd new file mode 100644 index 0000000..7d68077 --- /dev/null +++ b/man/task_button_js.Rd @@ -0,0 +1,16 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/shiny_fluent_task_button.R +\docType{data} +\name{task_button_js} +\alias{task_button_js} +\title{JS handlers for taskButton busy/ready states} +\format{ +An object of class \code{character} of length 1. +} +\usage{ +task_button_js +} +\description{ +JS handlers for taskButton busy/ready states +} +\keyword{internal} diff --git a/man/withTaskButton.Rd b/man/withTaskButton.Rd new file mode 100644 index 0000000..a2ec17c --- /dev/null +++ b/man/withTaskButton.Rd @@ -0,0 +1,24 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/shiny_fluent_task_button.R +\name{withTaskButton} +\alias{withTaskButton} +\title{Server-side handler for task button busy/ready states} +\usage{ +withTaskButton(session, inputId, expr, label_busy = "Processing...") +} +\arguments{ +\item{session}{Shiny session object.} + +\item{inputId}{\code{chr} The input ID of the target \code{taskButton}.} + +\item{expr}{Expression to evaluate while the button is in busy state.} + +\item{label_busy}{\code{chr} Label shown during processing. Defaults to \code{"Processing..."}.} +} +\value{ +Invisibly returns the result of \code{expr}. +} +\description{ +Use inside an \code{observeEvent} to toggle a \code{taskButton} +between busy and ready states around a long-running expression. +} From cd11b29114e831cca229b498b4fecc3a48e8d106 Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:24:58 +0000 Subject: [PATCH 12/17] load task button JS and CSS globally in UI head --- R/app.R | 112 ++------------------------------------------------------ 1 file changed, 3 insertions(+), 109 deletions(-) diff --git a/R/app.R b/R/app.R index 3d26c34..c034169 100644 --- a/R/app.R +++ b/R/app.R @@ -45,115 +45,9 @@ campagneApp <- function() { bootswatch = "yeti" ), tags$head( - tags$style(HTML(" - .notification-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(0, 0, 0, 0.5); - z-index: 99998; - display: none; - opacity: 0; - transition: opacity 0.3s ease-in-out; - } - - .notification-overlay.show { - display: block; - opacity: 1; - } - - #shiny-notification-panel { - position: fixed; - top: 50% !important; - left: 50% !important; - right: auto !important; - bottom: auto !important; - transform: translate(-50%, -50%) !important; - width: auto; - max-width: 500px; - z-index: 99999; - } - - @keyframes slideDown { - from { - opacity: 0; - transform: translate(-50%, -60%); - } - to { - opacity: 1; - transform: translate(-50%, -50%); - } - } - - @keyframes pulse { - 0%, 100% { - transform: scale(1); - } - 50% { - transform: scale(1.05); - } - } - - .shiny-notification { - background-color: white; - border: none; - border-radius: 12px; - box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2); - padding: 40px 30px 30px 30px; - text-align: center; - font-size: 16px; - color: #333; - opacity: 1 !important; - min-width: 350px; - position: relative; - animation: slideDown 0.4s ease-out; - } - - .shiny-notification::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 4px; - border-radius: 12px 12px 0 0; - background: linear-gradient(90deg, #3498db, #2980b9); - } - - .shiny-notification-message::before { - background: linear-gradient(90deg, #3498db, #2980b9); - } - - .shiny-notification-warning::before { - background: linear-gradient(90deg, #f39c12, #e67e22); - } - - .shiny-notification-error::before { - background: linear-gradient(90deg, #e74c3c, #c0392b); - } - - .shiny-notification.success-notification::before { - background: linear-gradient(90deg, #27ae60, #229954); - } - - .shiny-notification-message, - .shiny-notification-warning, - .shiny-notification-error { - background-color: white; - border-left: none; - } - - .shiny-notification .message-text { - color: #555; - line-height: 1.6; - margin-top: 8px; - font-size: 15px; - }" - ) - ) - ), + tags$style(HTML(task_button_css)), + tags$script(HTML(task_button_js)) + ), useShinyjs(), bslib::input_dark_mode(id = "mode"), downloadButton("download_template", label = "", style = "visibility:hidden;"), From 13099f869771c16472ec244a0e1f8c78c45405cf Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:33:11 +0000 Subject: [PATCH 13/17] Export shiny.fluent task button functions in the NAMESPACE --- NAMESPACE | 5 +++++ R/rdcAVS-package.R | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/NAMESPACE b/NAMESPACE index a64ab50..2806a8d 100644 --- a/NAMESPACE +++ b/NAMESPACE @@ -10,6 +10,10 @@ export(fluent_card) export(get_campaign_progress) export(get_sheet_info) export(icon_btn) +export(taskButton) +export(task_button_css) +export(task_button_js) +export(withTaskButton) import(bsicons) import(bslib) import(dplyr) @@ -47,6 +51,7 @@ importFrom(openxlsx,writeData) importFrom(progressr,handlers) importFrom(progressr,progressor) importFrom(progressr,with_progress) +importFrom(purrr,map) importFrom(purrr,pwalk) importFrom(purrr,walk) importFrom(rappdirs,user_data_dir) diff --git a/R/rdcAVS-package.R b/R/rdcAVS-package.R index 0089b79..0128058 100644 --- a/R/rdcAVS-package.R +++ b/R/rdcAVS-package.R @@ -38,7 +38,7 @@ #' @importFrom progressr progressor #' @importFrom progressr with_progress #' @importFrom purrr pwalk -#' @importFrom purrr walk +#' @importFrom purrr walk map #' @importFrom rappdirs user_data_dir #' @importFrom readr type_convert #' @importFrom shinyjs disable From 421c06d3ae89ac012ed319bf1e90ccb0735c2a4b Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:36:47 +0000 Subject: [PATCH 14/17] replace PrimaryButton with taskButton --- R/ui_campaign_creation.R | 4 ++-- R/ui_monitoring.R | 6 +++--- R/ui_perm_table.R | 8 ++------ 3 files changed, 7 insertions(+), 11 deletions(-) diff --git a/R/ui_campaign_creation.R b/R/ui_campaign_creation.R index 82ddecd..bd78598 100644 --- a/R/ui_campaign_creation.R +++ b/R/ui_campaign_creation.R @@ -125,9 +125,9 @@ ui_campaign_creation <- function() { div( style = "flex:1; min-width:240px;", - PrimaryButton.shinyInput( + taskButton( "create_campaign", - text = "Cr\u00e9er une campagne", + label = "Cr\u00e9er une campagne", iconProps = list(iconName = "Rocket") ) ) diff --git a/R/ui_monitoring.R b/R/ui_monitoring.R index 2a52fef..cb89900 100644 --- a/R/ui_monitoring.R +++ b/R/ui_monitoring.R @@ -134,9 +134,9 @@ ui_monitoring <- function() { style = list(fontWeight = "600", color = "#201f1e") ), uiOutput("campaign_surveillance"), - shiny.fluent::PrimaryButton.shinyInput( - inputId = "compile_campaign_btn", - text = "Compiler des masques", + taskButton( + "compile_campaign_btn", + label = "Compiler des masques", iconProps = list(iconName = "CompactDisc") ), shiny.fluent::Stack( diff --git a/R/ui_perm_table.R b/R/ui_perm_table.R index 89c4ebc..c8eb2a3 100644 --- a/R/ui_perm_table.R +++ b/R/ui_perm_table.R @@ -204,20 +204,16 @@ ui_perm_table <- function() { tokens = list(childrenGap = 12), Text(variant = "xLarge", "S\u00e9lection de Campagne"), - uiOutput("campaign_drive_picker"), - Stack( horizontal = TRUE, wrap = TRUE, tokens = list(childrenGap = 10), - - PrimaryButton.shinyInput( + taskButton( "set_permissions_btn", - text = "D\u00e9finir l'autorisation", + label = "D\u00e9finir l'autorisation", iconProps = list(iconName = "Permissions") ), - DefaultButton.shinyInput( "refresh_drive", text = "Actualiser", From 2833418e7af439cbd5bfe7c5fcc0be6c7393245e Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:44:53 +0000 Subject: [PATCH 15/17] integrate withTaskButton for busy/ready state management --- R/server.R | 91 ++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 61 insertions(+), 30 deletions(-) diff --git a/R/server.R b/R/server.R index 54f9189..fa1e377 100644 --- a/R/server.R +++ b/R/server.R @@ -486,23 +486,29 @@ server <- function(input, output, session) { }) ##### Campaign creation button ---- - observeEvent(input$create_campaign, { + + observeEvent(input$create_campaign, { req( input$campaign_name, input$start_date, input$end_date, - input$zs_template_url - ) - req( + input$zs_template_url, input$selected_provinces, input$selected_ant, input$selected_zs ) - showNotification("Veuillez patienter pendant la cr\u00e9ation de la campagne...", - type = "default") - - tryCatch( + campaign_name <- input$campaign_name + start_date <- input$start_date + end_date <- input$end_date + template_url <- input$zs_template_url + prov_target <- input$selected_provinces + ant_target <- input$selected_ant + zs_target <- input$selected_zs + gdb <- geo_data_reactive() + + withTaskButton(session, "create_campaign", label_busy = "En cours...", { + tryCatch( { zs_masque_dribble <- googledrive::drive_get(stringr::str_trim(input$zs_template_url)) campagne_folder_url <- drive_init_campaign( @@ -516,19 +522,21 @@ server <- function(input, output, session) { zs_masque = zs_masque_dribble ) - showNotification("Campagne initialis\u00e9e avec succ\u00e8s", - type = "message") - output$campagne_folder_url <- renderUI({tagList(a("Lien vers le dossier Campagne", href = campagne_folder_url))}) + session$sendCustomMessage("taskBtnReady", list(id = "create_campaign_wrapper")) + showNotification("Campagne initialis\u00e9e avec succ\u00e8s", type = "message") + output$campagne_folder_url <- renderUI({ + tagList(a("Lien vers le dossier Campagne", href = campagne_folder_url)) + }) }, error = function(e) { - showNotification(paste("Erreur - " , "Quelque chose s'est mal pass\u00e9:", e$message), - type = "error", - duration = NULL) - + session$sendCustomMessage("taskBtnReady", list(id = "create_campaign_wrapper")) + showNotification(paste("Erreur:", e$message), type = "error", duration = NULL) } ) + }) }) + ##### Geographic table editing ---- ###### Upload option for geo table ---- @@ -957,23 +965,27 @@ observeEvent(input$download_geo,{ ###### Display available campaigns ---- output$campaign_drive_picker <- renderUI({ folders <- campaign_drive_folders() - selectInput( - "selected_campaign_drive_folder", - NULL, - choices = folders$name + column(3, + shiny.fluent::Dropdown.shinyInput( + "selected_campaign_drive_folder", + options = map(folders$name, ~ list(key = .x, text = .x)), + value = folders$name[1] + ) ) - }) +}) ###### Display available campaigns in the surveillance tab ---- output$campaign_surveillance <- renderUI({ - shiny.fluent::Dropdown.shinyInput( + column(3, + shiny.fluent::Dropdown.shinyInput( inputId = "selected_surveillance_drive_folder", label = shiny::h6("Campagne"), options = lapply( campaign_drive_folders()$name, function(x) list(key = x, text = x) - ) -) + ) + ) + ) }) output$refresh_date <- renderText({ refresh_status() @@ -1381,8 +1393,12 @@ observeEvent(input$download_geo,{ showNotification("Veuillez patienter pendant que les autorisations sont d\u00e9finies...", type = "default") - - + + withTaskButton( + session, + inputId = "set_permissions_btn", + label_busy = "En cours...", + { folders <- campaign_drive_folders() files <- drive_files() @@ -1402,8 +1418,9 @@ observeEvent(input$download_geo,{ files <- googledrive::drive_reveal(files, what = "path") drive_files(files) } - + tryCatch( + { set_permissions( input$selected_campaign_drive_folder, @@ -1420,7 +1437,10 @@ observeEvent(input$download_geo,{ paste("Erreur - Quelque chose s'est mal pass\u00e9:", e$message), type = "error") } - ) + ) + } + ) + }) ###### Undo/redo/clear all perm table ---- @@ -1468,9 +1488,16 @@ observeEvent(input$download_geo,{ observeEvent(input$compile_campaign_btn, { req(input$selected_surveillance_drive_folder) + withTaskButton( + session, + "compile_campaign_btn", + label_busy = "En cours...", + { + surveillance_folder <- campaign_drive_folders() |> - dplyr::filter(name == input$selected_surveillance_drive_folder) - + dplyr::filter(name == input$selected_surveillance_drive_folder) + + withProgress( { compile_start <- Sys.time() @@ -1511,7 +1538,11 @@ observeEvent(input$download_geo,{ showNotification("Traitement termin\u00e9", type = "message") } ) - }) + + } + + ) +}) ##### Download current data quality monitoring table ---- From 5d500f255f4f4c4df5af31ce4076b790848f68e2 Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:50:55 +0000 Subject: [PATCH 16/17] Add a brief description of task_buttons( JS and CSS) function in the documentation --- R/shiny_fluent_task_button.R | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/R/shiny_fluent_task_button.R b/R/shiny_fluent_task_button.R index ffdcbde..eda8ccf 100644 --- a/R/shiny_fluent_task_button.R +++ b/R/shiny_fluent_task_button.R @@ -1,3 +1,5 @@ +#' JS handlers for taskButton busy/ready states +#' @keywords internal #' @export task_button_js <- " @@ -38,8 +40,11 @@ task_button_js <- " })(); " +#' CSS for taskButton spinner animation +#' @keywords internal #' @export + task_button_css <- " @keyframes task-spin { to { transform: rotate(360deg); } @@ -172,10 +177,10 @@ task_button_css <- " #' #' @param inputId `chr` The input that will be used to access the value. #' @param label `chr` The button label. +#' @param label_busy `chr` Label shown during busy state. Defaults to \code{"Processing..."} #' @param ... Additional arguments passed to \code{PrimaryButton.shinyInput}. -#' #' @return A \code{tagList} containing the button with CSS and JS handlers. -#' @noRd +#' #' @export taskButton <- function(inputId, label, label_busy = "Processing...", ...) { @@ -203,7 +208,7 @@ taskButton <- function(inputId, label, label_busy = "Processing...", ...) { #' @param label_busy `chr` Label shown during processing. Defaults to \code{"Processing..."}. #' #' @return Invisibly returns the result of \code{expr}. -#' @noRd +#' #' @export withTaskButton <- function(session, inputId, expr, From 4a7cedbef41ef262b50522f82c3adad03947409b Mon Sep 17 00:00:00 2001 From: aureol-le-rocher <95870033+aureol-le-rocher@users.noreply.github.com> Date: Wed, 4 Mar 2026 15:55:11 +0000 Subject: [PATCH 17/17] bump app version --- DESCRIPTION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION b/DESCRIPTION index f490b7c..da49139 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -1,6 +1,6 @@ Package: rdcAVS Title: SIA Campaign Builder for DRC -Version: 0.0.0.9008 +Version: 0.0.0.9009 Authors@R: person("Mervin Keith", "Cuadera", , "xrg9@cdc.com", role = c("aut", "cre")) Description: This app assists in creating campaigns and setting permissions for