-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp_ui_initial_wireframe.R
691 lines (661 loc) · 38.8 KB
/
app_ui_initial_wireframe.R
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
#' The application User-Interface
#'
#' @param request Internal parameter for `{shiny}`.
#' DO NOT REMOVE.
#' @import shiny
#' @import shinydashboard
#'
#' @return Front end of app
#'
#' @noRd
app_ui <- function(request) {
tagList(
# enable alert messages
#shinyWidgets::useSweetAlert(),
# Leave this function for adding external resources
golem_add_external_resources(),
theme_dashboard(),
# List the first level UI elements here
dashboardPage(
dashboardHeader(title = "Survey Designer"),
dashboardSidebar(
#minified = FALSE,
#collapsed = FALSE,
#width = "10vw",
sidebarMenu(
menuItem(" Introduction", tabName = "Intro", icon = icon("circle-info")),
menuItem(" Step 1. Define Context", tabName = "Context", icon = icon("location-dot")),
menuItem(" Step 2. Configure Content", tabName = "Content", icon = icon("arrows-to-circle")),
menuItem(" Step 3. Set up Collection", tabName = "Collection", icon = icon("list-check")),
menuItem(" Step 4. Publish Surveys", tabName = "Forms", icon = icon("share-from-square")),
menuItem(" -- Save or Load Session", icon = icon("floppy-disk"),
menuSubItem("Save session Unique ID", tabName = "subitem1", icon = icon("angles-right")),
menuSubItem("Load session using unique ID", tabName = "subitem2", icon = icon("angles-left")))
)
),
dashboardBody(
tabItems(
tabItem(
tabName = "Intro",
fluidRow(
"--WARNING - you are now viewing the prototype wireframe - the application is not yet functional--",
tags$a(href="https://github.com/unhcr-americas/surveyDesigner", "Please check the github repo for more info and to provide feedback!"),
box(title = "",
width = NULL,
collapsible = FALSE,
status = "info",
tags$div(class = "jumbotron text-left",
style = "margin-bottom:5px;margin-top:5px;margin-left:5px",
tags$h1(style = 'color:#0072BC;',
style = 'margin-bottom:0px;margin-top:0px',
'As Local Survey Coordinator, integrating, configuring and tailoring multiple surveys at once
is often challenging!'),
tags$i(class = 'jumbotron-heading',
style = 'margin-left:25px',
'How can you confirm that you will not miss a specific data point to calculate your final indicators and disaggregation?...
How can you make sure to enforce all existing statistical standards and good practices while also contextualizing correctly questions labels?...
How can you maximise your resources to collect your data smarty, through multiple waves and modes?... ' )
), ## end jumbotron..
"This app aims at addressing the complex challenges associated with the design of integrated surveys. It is expected to help:" ,
tags$ol(
tags$li(" Standardizing a questionnaire design process that starts with a",tags$b(" selection of indicators ") ,"that needs
to be measured and thenpre-select automatically all the required standardized survey modules and
questions in order to measure them, aka 'collect only what you need and use everything that you collected'.
This will ensure that standards are applied,
but it will also minimize the risk of questionnaire design mistakes" ),
tags$li(" Easing the",tags$b(" contextualization process ") ,"from the global referential to the actual implementation
in each country (i.e translating and adjusting the labels as per the specific context) and
facilitate a feedback loop so that if the same ad-hoc questions are used in multiple context,
they can be considered for inclusion in the global referential "),
tags$li(" Supporting the usage of mixed-mode (e.g.CAPI/CATI/CAWI) and multiple data collection waves
to collect the final dataset within an",tags$b(" annual survey data collection cycle") ,", all of
this allowing both to promote survey integration and to maximize the financial
resources invested in those activities" )
)
#br(),
)
),
fluidRow(
# Valid statuses are: primary, success, info, warning, danger.
column(
3,
box(title = " Step 1. Define Context",
width = NULL,
collapsible = FALSE,
status = "warning",
"First, document the operational purpose of the data that you will need to collect for the incoming cycle.",
br(),
"This will be the first be the first filter of the design process",
br(),
actionButton(inputId="start",
label="Start now!",
icon("location-dot") )
)
),
column(
3,
box(title = " Step 2. Configure Content",
width = NULL,
status = "warning",
"Second, based on the consultation with partners and sectoral experts, select the topics and indicators
that needs to be covered for your annual survey cycle",
br() )
),
column(
3,
box(title = " Step 3. Set up Collection",
width = NULL,
collapsible = FALSE,
status = "warning",
"Third, provides information about your data collection capacity, what kind
of mode can you use, how many waves can you afford, what enumeration capacity can you afford...",
br() )
),
column(
3,
box(title = " Step 4. Publish Surveys",
width = NULL,
collapsible = FALSE,
status = "warning",
"Last, revise as pretty print word documents & export as xlsforms and the single or multiples
questionnaires that you will be required to implement your annual survey cycle.
You maybe need to then revise some questions labels.
Once done, you will get all the files needed to collect the data through kobotoolbox ")
)
) # End fluid row
),
# Context #####
tabItem(tabName = "Context",
fluidRow(
# Valid statuses are: primary, success, info, warning, danger.
column(
3,
box(title = " 1. Metadata",
width = NULL,
collapsible = FALSE,
status = "warning",
" ",
textInput("caption",
label="Title for your data collection project",
value="short title"),
tags$div(title=" ",
selectInput("context",
label = "Year for your data collection",
choices = list("2025" = "2025",
"2024" = "2024",
"2023" = "2023" ),
selected = "2023") ),
textAreaInput("caption",
label="Provide an abstract for the data collection activities",
value="Who will collect? What sample frame can be used? What are the main field access constraints?",
width = "100%",
height = "300px")
)
) ,
column(
3,
box(title = " 2. Methodology / Operational Purpose",
width = NULL,
collapsible = FALSE,
status = "warning",
" ",
tags$div(title="Interview",
radioButtons("library",
label = " What global referential to use?" ,
choices = list("Household Survey (Ideally representative of stable population in a protracted situation)" = "household_survey",
"Flow Monitoring (People not aiming to establish their habitual residence in the place of interview)" = "flow_monitoring",
"Key Informant (Persons with local qualitative knowledge in an emergency situation)" = "key_informant"),
selected = "household_survey" ) ),
br(),
" Referentials are centrally managed by UNHCR Global Data Service. They are based on most appropriate international statistical standards (EGRISS, UN Stat Commissions, etc.)")
),
column(
3,
box(title = " 3. Country",
width = NULL,
status = "warning",
" ",
tags$div(title="Select your country ",
selectInput("context",
label = "Country, defining then required languages & geographic Pcodes",
choices = list("Panama" = "Panama",
"Colombia" = "Colombia",
"Ecuador" = "Ecuador" ),
selected = "Panama") ),
br(),
tags$div(title="Geographic Coverage ",
selectInput("context",
label = "Are you aiming for a full national coverage?",
choices = list("yes" = "Yes",
"no" = "No" ),
selected = "yes") ),
hr(),
"This will pull up the languages translation and geographic breakdown required for the country.",
" Global referentials are contextualised for each country through the regional survey support capacity in the bureau.",
"If your country does not appear in the that list, ignite a contextualisation request",
br(),
actionButton(inputId="contextualisationRequest",
label="Contextualisation",
icon("gears")) ,
br())
),
column(
3,
box(title = " 4. Targeted Groups",
width = NULL,
collapsible = FALSE,
status = "warning",
" ",
tags$div(title="Population Group to cover",
checkboxGroupInput("population",
label = "Target Group",
choices = list("Refugees (REF) & Asylum seekers (ASY)"="RAS",
"Internally displaced persons (IDP)"="IDP",
"Other people in need of international protection (OIP)"="OIP",
"Stateless Persons (STA)"="STA",
"Others of concern to UNHCR (OOC)"="OOC",
"Returnee (RET)"="RET",
"Host community (HCT)"="HCT" ),
br() ))
)
)
) ,
fluidRow(
# Valid statuses are: primary, success, info, warning, danger.
column(
12,
box(title = " Context Summary",
width = NULL,
collapsible = FALSE,
status = "success",
" ",
hr(),
actionButton(inputId="start",
label="Go to Configure Content",
icon("arrows-to-circle"))
)
)
)# End fluid row
), ## end context tiem
# Content #####
tabItem(tabName = "Content",
fluidRow(
# Valid statuses are: primary, success, info, warning, danger.
column(
6,
box(title = " 1. Apply filters ",
width = NULL,
collapsible = FALSE,
status = "warning",
" One indicator might be linked to mulitple topics and framewoks.
You can use the different filters below to narrow down the list indicators
to search in",
br(),
fluidRow(
# Valid statuses are: primary, success, info, warning, danger.
column(
6,
box(title = " Filters on Topics",
width = NULL,
collapsible = FALSE,
status = "warning",
checkboxGroupInput("topic",
label = " Tick to filter",
choiceNames = list(
list(
icon("gavel") ,
"Rights"
),
list(
icon("home"),
"Trajectory"
),
list(
icon("thermometer-full"),
"Intention"
),
list(
icon("check-circle"),
"Basic Needs"
),
list(
icon("certificate"),
"Coping Capacity"
),
list(
icon("users"),
"Well-Being"
),
list(
icon("blind"),
"Victimisation"
),
list(
icon("usd"),
"Livelihood"
),
list(
icon("book"),
"Education"
),
list(
icon("medkit"),
"Health-Nutrition"
),
list(
icon("eye"),
"Accountability"
),
list(
icon("info-circle"),
"Information"
)),
choiceValues = list("Rights" ,
"Trajectory" ,
"Intention" ,
"Basic Needs" ,
"Coping Capacity" ,
"Well-Being" ,
"Victimisation" ,
"Livelihood" ,
"Education" ,
"Health-Nutrition" ,
"Accountability",
"Information" ))
)
),
column(
6,
box(title = " Filters on Framework",
width = NULL,
status = "warning",
tags$div(title="Standard Indicators",
checkboxGroupInput("Disaggregation",
label = " Tick to filter ",
choices = list("UNHCR Compass / RBM"="RBM",
"UN Stat Commission / EGRISS"="EGRISS",
"Sustainable Development Goal / SDG"="SDG",
"Global Compact on Refugee / GCR"="GCR",
"Humanitarian Sphere Standards"="Sphere",
"Post Distribution & Assistance Monitoring"="PDM",
"Assistance Targeting / Excellence Hub"="targeting",
"Framework on Durable Solutions /IASC "="Durable",
"Protection Mainstreaming / ECHO "="Durable",
"Composite Indicator / Index"="Index"
) ) )
)
)
)
)
),
column(
3,
box(title = " 2. Select Indicators",
width = NULL,
status = "warning",
"Note that one indicator might requires multiples questions
within the final survey to be calculated.
Do not worry the app will take care of that complex step",
tags$div(title="Standard Indicators",
checkboxGroupInput("indicator",
label = "Select indicators - to be turned to autocomplete-multiple selection..",
choices = list("Access to drinking water"="outcome12_1",
"Residing in physically safe and secure settlements with access to basic facilities"="impact2_2",
"Could access health facilities"="impact2_3",
"Feeling safe walking alone"="impact3_3",
"Children under 5 birth registered with civil authorities"="outcome1_2",
"Has legally recognized documents"="outcome1_3",
"Proportion of PoC who know where to access available GBV services"="outcome4_1",
"Do not accept violence against women"="outcome4_2",
"With primary reliance on clean (cooking) fuels and technology"="outcome8_2",
"Living in Habitable and affordable shelter"="outcome9_1",
"Access to energy to ensure lighting"="outcome9_2",
"With access to a safe household toilet"="outcome12_2",
"With a bank account or mobile-money service provider"="outcome13_1",
"Working age individuals who are unemployed"="outcome13_3",
"With secure tenure and/or property rights"="outcome16_1",
"Covered by social protection floors/systems"="outcome16_2",
"and so on..."="oo"
) ) )
)
),
column(
3,
box(title = " 3. Context-specific Questions",
width = NULL,
status = "warning",
" You may here add context specific questions, that is to say questions for which
the equivalent statistical construct is not (yet) available within the global referential.
Thoses questions will be shared back with the global referential manager
for potential inclusion in the next release of the referential",
br() ,
hr(),
actionButton(inputId="start",
label="Select / Define Ad-hoc Questions",
icon("gears")))
)
) ,
fluidRow(
# Valid statuses are: primary, success, info, warning, danger.
column(
12,
box(title = " Indicator Plan -- Analysis",
width = NULL,
collapsible = FALSE,
status = "success",
" A small summary review will be automatically generated below.
it's not for data entry but rather to output a summary
of the decision points that have been set up above..
For instance - indicating how many unique questions
would be required in order to compile all the indicators
that got selected..",
br(),
hr(),
actionButton(inputId="start",
label="Next Set up Collection",
icon("list-check"))
)
)
)# End fluid row
), # end content item...
# Collection ####
tabItem(tabName = "Collection" ,
fluidRow(
# Valid statuses are: primary, success, info, warning, danger.
column(
4,
box(title = " 1. Modes",
width = NULL,
collapsible = FALSE,
status = "warning",
"Using multiple data collection modes can help minimizing
non-reponse and increase coverage. ",
tags$a(href="https://www.youtube.com/watch?v=qRmy2OAnyWc", "Learn more on mixed-mode here"),
br(),
checkboxGroupInput("mode",
label = "What Data Collection Modes do you have capacity to implement for this annual cycle?" ,
choiceNames = list(
list(
icon("clipboard-question"),
"CAPI"="CAPI, i.e. Face-to-face "
),
list(
icon("phone-square") ,
"CATI"="CATI, i.e. Phone-Interview "
),
list(
icon("wifi"),
"CAWI"= "CAWI, i.e. Self-administered "
)),
choiceValues = list("CAPI" ,
"CATI" ,
"CAWI" )),
br() )
),
column(
4,
box(title = " 2. Waves",
width = NULL,
status = "warning",
" Define how many data collection waves you can manage within the year.",
"Using multiple data collection waves can provide an opportunity for
more indicators to be collected. ",
tags$a(href="https://openknowledge.worldbank.org/server/api/core/bitstreams/e80ce277-f8d0-58aa-87b7-288c2895e87a/content#page=4", "Learn more on multi-waves here"),
br(),
selectInput("wave",
label = "How many data collection waves for this annual cycle?",
choices = list("One Wave" = "One-wave",
"Two Waves" = "Two-waves",
"Three Waves" = "Three-waves",
"Four Waves" = "Four-waves" ),
selected = "One-waves"),
"As a result of this stage, below is an estimation of Interview lenght per questionnaire",
br() )
),
column(
4,
box(title = " 3. Enumeration Capacity",
width = NULL,
collapsible = FALSE,
status = "warning",
# tags$div(title="Budget",
# numericInput("budget in $",
# label = "Available Annual Budget for Data Collection (in US$)",
# 30000,
# min = 5000,
# max = 500000) ),
"The Maximum monthly data collection depends on the capacity of the
organisation that partners or on the company that got contracted for that activity",
hr(),
tags$div(title="Face1",
numericInput("face",
label = "Total Cost per Interview (in US$) for Face to face",
50, min = 20, max = 300) ),
tags$div(title="Face2",
numericInput("face",
label = "Max Capacity of monthly working day for Face to face enumerators",
50, min = 1, max = 100) ),
hr(),
tags$div(title="Face1",
numericInput("face",
label = "Total Cost per Interview (in US$) for Telephone",
20, min = 5, max = 100) ),
tags$div(title="Call Center enumerators",
numericInput("face",
label = "Max Capacity of monthly working day for Call Center enumerators",
20, min = 1, max = 100) ),
br() )
)
) ,
fluidRow(
# Valid statuses are: primary, success, info, warning, danger.
column(
12,
box(title = " Data Collection Plan -- Analysis",
width = NULL,
collapsible = FALSE,
status = "success",
"Feedback on your data collection analysis plan based on
estimated interview duration, budget and enumeration capacity
will be automatically generated below.
indication of the suggested length of the interview given the mode:
CAPI, 60-90 min; CATI; 30-45 min…and that dictates the number
of questions that are able to be included.
function for that https://edouard-legoupil.github.io/XlsFormPrettyPrint/reference/interview_duration.html",
hr(),
actionButton(inputId="start",
label="Now Publish Survey",
icon("share-from-square")
)
))
)# End fluid row
), # end collection item
# Forms ####
tabItem(tabName = "Forms",
fluidRow(
# Valid statuses are: primary, success, info, warning, danger.
# column(
# 3,
# box(title = " 1. Survey Introduction Scripts",
# width = NULL,
# collapsible = FALSE,
# status = "warning",
# " This is the text that enumerator shold use to introduce the interview.
# It is important for it to be context sensitive... ",
# textAreaInput("caption",
# label="Add the script",
# value=" Hello, my name is .. and we would like to interview you for....
# Data will be used by.. You will be able to see aggregated results here...",
# width = "100%"),
# br() )
# ),
column(
4,
box(title = " 1. Survey Modules Sequence",
width = NULL,
status = "warning",
" You can adjust the default sequence between modules and
then between questions within modules.",
br(),
"Note that the design already comes with suggested sequence of modules.
For instance depending on the mode, sensitive or longer/more complicated
questions comes towards the end for CAPI while they are more at the begining for CATI.
Also not all sequences can be changed as the questionnaire logic comes with hard constraints
to be respected.",
br(),
actionButton(inputId="start",
label="Change Sequence",
icon("gears")) )
),
column(
4,
box(title = " 2. Preview Questionnaires",
width = NULL,
collapsible = FALSE,
status = "warning",
" Get a word version of the different questionnaires for a review with field colleagues",
br(),
actionButton(inputId="start",
label="Pretty Print",
icon("file-word"))
)
) ,
column(
4,
box(title = " 3. Labels Review",
width = NULL,
collapsible = FALSE,
status = "warning",
" Building on expert review, use this function to apply changes in the contextualisable content.... ",
br(),
actionButton(inputId="start",
label="Labels",
icon("gears"))
)
)
),
fluidRow(
column(
6,
box(title = " Documentation",
width = NULL,
collapsible = FALSE,
status = "success",
"Download the documentation for your annual survey data collection cycle",
actionButton(inputId="start",
label="Annual Survey Cycle",
icon("boxes-packing"))
)
),
column(
6,
box(title = " Publication",
width = NULL,
collapsible = FALSE,
status = "success",
"Publish all Surveys directly on the data collection server: ",
br(),
actionButton(inputId="start",
label="Publish on Kobo",
icon("paper-plane")),
br(),
" or download the corresponding xlsform files to upload them yourself on kobotoolbox: ",
br(),
actionButton(inputId="start",
label="XlsForm",
icon("file-excel"))
)
)
)# End fluid row
) # end publish item
) #end all items
) # end body
) # end page
) # end tag
}
#' Add external Resources to the Application
#'
#' This function is internally used to add external
#' resources inside the Shiny application.
#'
#' @import shiny
#' @importFrom golem add_resource_path activate_js favicon bundle_resources
#' @noRd
golem_add_external_resources <- function() {
add_resource_path(
"www",
app_sys("app/www")
)
tags$head(
golem::activate_js(),
golem::favicon(),
bundle_resources(
path = app_sys("app/www"),
app_title = "surveyDesigner"
)
# Add here other external resources
# If you have a custom.css in the inst/app/www
# Or for example, you can add shinyalert::useShinyalert() here
#tags$link(rel="stylesheet", type="text/css", href="www/custom.css")
)
}