-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp.R
3060 lines (2788 loc) · 214 KB
/
app.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
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#---------------------------------------------------------------------------------#
#Fines and Fees Calculator Shiny App----
#created: 2021/10/19 by Adam S. Cohen (Hawaii State Judiciary)
#Last modified: 2024/09/09 by Adam S. Cohen (Hawaii State Judiciary)
#---------------------------------------------------------------------------------#
#---------------------------------------------------------------------------------#
# load packages ----
#---------------------------------------------------------------------------------#
library(readxl)
library(dplyr)
library(tidyr)
library(stringr)
library(shiny)
library(shinydashboard)
library(shinyWidgets)
library(scales)
library(shinyBS)
library(stringi)
library(shinyjs)
# remotes::install_github("dreamRs/capture")
library(capture)
library(bsplus)
library(digest)
# devtools::install_github("karthik/rdrop2")
# remotes::install_version("rdrop2", "0.8.2.1")
library(rdrop2)
library(shinylogs)
# library(googledrive)
# library(googlesheets4)
# library(shinyCleave)
# library(rebus)
library(shinyvalidate)
library(gfonts)
#---------------------------------------------------------------------------------#
# ---START DROPBOX PERSISTENT STORAGE----
#---------------------------------------------------------------------------------#
#h/t: https://deanattali.com/blog/shiny-persistent-data-storage/#dropbox
##updated method to address issue with short term authentication
##https://github.com/karthik/rdrop2/issues/201
##https://stackoverflow.com/questions/71393752/get-a-refresh-token-for-dropbox-api-using-rdrop2-and-drop-auth
##https://github.com/karthik/rdrop2/issues/206
#---------------------------------------------------------------------------------#
# token<-drop_auth(new_user = TRUE)
# saveRDS(token,'H:\\p11_LFO_calculator\\lfo_calculator\\droptoken.rds')
token<-readRDS("droptoken.rds")
# token<-readRDS("H:\\p11_LFO_calculator\\lfo_calculator\\droptoken.rds")
token$refresh()
TABLE_NAME <- "responsesLFO" #Fine and fee info
TABLE_NAME_2 <- "shinylogs" #user and widget info
# get a formatted string of the timestamp
get_time_human <- function() {
format(Sys.time(), "%Y%m%d-%H%M%OS")
}
fields_all <- c("role", "chargeDate", "charges")
file_linker <- capture.output(cat(runif(6,0,9)%/%1, sep = "")) #unique identifer so we can link the two output files
save_data_dropbox <- function(data) {
# Create a temporary file to hold the data
# data <- t(data) #comment out if using data in reactives, comment in if using inputs
file_name <- paste0(
paste(
get_time_human(),
file_linker,
digest(data, algo = "md5"),
sep = "_"
),
".csv"
)
file_path <- file.path(tempdir(), file_name)
write.csv(data ,file_path, row.names = FALSE, quote = TRUE)
# Upload the file to dropbox
drop_upload(file_path, path = TABLE_NAME)
}
#attempt to use shinylogs with dropbox
#use data argument (form_data()) to create filename so that it matches LFO data filename
#use shinylog argument to pass data to save
save_shinylogs_dropbox <- function(data) {
# Create a temporary file to hold the data
# data <- t(data) #comment out if using data in reactives, comment in if using inputs
file_name <- paste0(
paste(
get_time_human(),
file_linker,
digest(data, algo = "md5"),
sep = "_"
),
".csv"
)
file_path <- file.path(tempdir(), file_name)
write.csv(data ,file_path, row.names = FALSE, quote = TRUE)
# Upload the file to dropbox
drop_upload(file_path, path = TABLE_NAME_2)
# drop_upload(file_path, path = TABLE_NAME, dtoken = token)
}
#odd behavior: file not saved to dropbox unless the load data function is also run
#testing shows that the drop_read_csv in the lapply is critical to also triggering the saving of data (2022/12/09)
load_data_dropbox <- function() {
files_info <- drop_dir(TABLE_NAME)
file_paths <- files_info$path_display
# Only take the last 20 because each file takes ~1 second to download
file_paths <- tail(file_paths, 5)
data <-
lapply(file_paths, drop_read_csv, stringsAsFactors = FALSE) %>%
do.call(rbind, .)
data
}
#---------------------------------------------------------------------------------#
# ---END DROPBOX PERSISTENT STORAGE----
#---------------------------------------------------------------------------------#
#-----------------------------------#
#read in files----
#-----------------------------------#
#calc_inputs contains two lists
#charge list (for step 1 of calc) that’s been filtered to include 1st Circuit, traffic, non-felonies and appended with seat belt offenses and cell phone offenses not in top 100 and pedestrian cross walk offenses
#fines and fees list (for step 3 of calc)
calc_inputs <- readRDS("calc_inputs_2023-06-21_1405.rds")
lfoTableJIMS_chargeList_non_felony <- calc_inputs[[2]] %>%
filter(!(grepl("HRS 431:10C-104",CHARGE_CODE) & CHARGE_LEVEL == "PM")) %>% #remove to avoid confusion with VL and MD, wait until we decide on how we want to include it with VL and MD
filter(!(grepl("^HRS 291C-102\\(a\\)\\(1\\)$",CHARGE_CODE) & is.na(CHARGE_QUALIFIER))) %>% #remove so people don't select it by accident
filter(!(grepl("^HRS 286-102",CHARGE_CODE) & !is.na(CHARGE_QUALIFIER))) #remove JUV version, added checkbox at step 3 so user can select Juvenile
lfoTableJIMS_non_felony <- calc_inputs[[1]]
#-----------------------------------#
#set up HTML/CSS----
#-----------------------------------#
HTML('<head>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
</head>')
#-----------------------------------#
#set up JS print function for summary page
#h/t: https://stackoverflow.com/questions/41459361/rshiny-print-current-page
#custom date in filename, and b/c it changes the title of the webpage/tab, change back after printing
#https://stackoverflow.com/questions/10189225/how-to-create-a-custom-file-name-for-a-file-while-saving-the-print-page
#custom date in filename, add 1 to month since getMonth() goes from 0-11
#h/t: https://stackoverflow.com/questions/2013255/how-to-get-year-month-day-from-a-date-object/60609524
#add leading zeros to custom date and add +1 one to month without concatenating month and 1
#h/t: https://stackoverflow.com/questions/3605214/javascript-add-leading-zeroes-to-date
#-----------------------------------#
#custom code to display HST i.e. Pacific/Honolulu time
#h/t (custom time zone function): https://stackoverflow.com/questions/15141762/how-to-initialize-a-javascript-date-to-a-particular-time-zone
#test code from jsfiddle, shows how to print date to screen/console and some other neat tricks:
#h/t (how to test print to console: https://stackoverflow.com/a/18537115
jsCode <- "shinyjs.winprint = function(){
var shinyappsTime = new Date();
var invdate = new Date(shinyappsTime.toLocaleString('en-US', {
timeZone: 'Pacific/Honolulu'
}));
var diff = shinyappsTime.getTime() - invdate.getTime();
var hnl = new Date(shinyappsTime.getTime() - diff); // needs to substract
document.title = 'LFO_calculator_summary_'+hnl.getFullYear()+'-'+('0'+(hnl.getMonth()+1)).slice(-2)+'-'+('0'+hnl.getDate()).slice(-2);window.print();
document.title = 'Hawaii Judiciary Fines and Fees Calculator'
}"
#-----------------------------------#
# enhancement to bs_accordion----
# allow multiple panels to be open and to control which if any panels are initially open
#h/t: https://github.com/ijlyttle/bsplus/issues/112
#h/t: https://github.com/ijlyttle/bsplus/pull/113
#-----------------------------------#
bs_multi_open <- function(X, multi=TRUE, open=1) {
for(i in 1:length(X$children))
{
if(multi)
# Remove 'data-parent' attribute so multiple panels can be open at once
X$children[[i]]$children[[1]]$attribs$`data-parent` <- NULL
# Remove 'in' class to prevent *any* panel from starting as open
classAttribs <- which(names(X$children[[i]]$children[[2]]$attribs) == "class")
for(j in classAttribs)
{
if(X$children[[i]]$children[[2]]$attribs[j]=="in")
{
X$children[[i]]$children[[2]]$attribs[j] <- NULL
}
}
# add 'in' class (back) to panels selected to start as open
if(i %in% open)
X$children[[i]]$children[[2]]$attribs <- append(X$children[[i]]$children[[2]]$attribs, list(class="in"))
}
X
}
#-----------------------------------#
# Define UI for application----
#-----------------------------------#
ui <- dashboardPage(
dashboardHeader(disable = TRUE),
dashboardSidebar(disable = TRUE),
dashboardBody(
# Application title
fluidRow(style = "background-image: url(windward-glory-kaneohe-bay-rainbowD.jpg); background-size: cover; margin:-25px;",
column(width = 12, align = 'right',
#h/t: https://www.w3schools.com/css/css_font.asp
#h/t: https://stackoverflow.com/questions/48028990/what-is-the-shiny-default-font
titlePanel(title = div(HTML("Fines and Fees Calculator "),
style = 'color:white',#font-family: Metropolis, "Times New Roman", Helvetica, Arial, sans-serif;
# img(src='hawaii seal _judiciary.png', href = "https://www.courts.state.hi.us/", height = '6%', width = '6%')
a(href = "https://www.courts.state.hi.us/", img(src='hawaii seal _judiciary.png', height = '9%', width = '9%'), target = "_blank")),
windowTitle = 'Hawaii Judiciary Fines and Fees Calculator')
)#column
),#fluidRow header - title
br(),
br(),
use_font("montserrat", "www/css/montserrat.css", selector = c("div","h3","h4")),#default selector is "body" but wasn't selecting everything
useSweetAlert(),#for modal at step 2, contest ticket
useShinyjs(),
extendShinyjs(text = paste(jsCode#,
# 'document.title = LFO_calculator_summary.pdf;'
), functions = c("winprint")),
#set default orientation to landscape for printing
#h/t: https://stackoverflow.com/questions/138422/landscape-printing-from-html/
#h/t: https://stackoverflow.com/questions/4249532/is-page-sizelandscape-obsolete
# tags$style(type ='text/css', '@media print{@page {size: landscape}}'),
tags$style(type ='text/css', '@media print{@page {size: portrait;
margin-left: 0.25in;
margin-right: 0.25in;
}}'),
#validation error formatting
tags$style(HTML("
.shiny-output-error-validation {
color: red;
font-weight:bold;
}
")),
#to customize color of shinyWidgets actionBttn
#h/t: https://stackoverflow.com/questions/59150267/how-to-override-actionbttn-color-in-shinywidget-package
tags$style(HTML(".buttonStartCalc .bttn-primary{background-color: #3c8dbc;}")),
# tags$style(HTML('.box {font-family: "Times New Roman";}')),
#for floating ATP and CSW boxes at step 3
#h/t: https://stackoverflow.com/questions/75902762/getting-css-positionsticky-to-work-with-shinydashboard
tags$head(
tags$style(HTML(
"#hhh {position: sticky; top: 0;}
.wrapper {overflow: visible;}"
))
),
tabsetPanel(
id = "wizard",
type = "hidden",
#-----------------------------------#
#tabPanel page_0----
#-----------------------------------#
tabPanel("page_0",
##Blurb----
fluidRow(column(width = 6, offset = 3,
# h3("Welcome to the Hawaii Judiciary's"),
h3(HTML("Welcome to the Hawaiʻi Judiciary's<br><span style='font-size:22px;color:#2758a5;font-style:italic;'> FINES AND FEES CALCULATOR </span>"), style = "display:inline-block;border-left:6px solid orange;border-bottom:6px solid orange;"),
br(),br(),
HTML("<span style='font-size:16px;'>This calculator is an online tool designed to help people calculate the fines and fees owed on a specific ticket, and if eligible, request either a reduction in their fines or a conversion to community service. This calculator can be used by defendants, attorneys, the court, and the general public.<br>
<br>
The calculator provides only an <u>estimate</u> of the fines and fees a person may owe. Please note that certain fines and fees are mandatory and may not be eligible for reduction or conversion to community service.
All requests will be decided by the judge.<br>
<br>
Some offenses may carry additional requirements/penalties, including but not limited to, driver license suspension, driver improvement class, child passenger restraint class, and/or jail.</span>")
)),
br(),
##Calc button----
fluidRow(
column(width = 6, offset = 3, align = 'center',
div(class = "buttonStartCalc",
actionBttn(inputId = 'startCalc',
# label = 'Go to the calculator (no data storage and no API)',
label = 'Go to the calculator',
# width = '33%',
style = 'jelly',
color = 'primary')
)#div
)#column
),#fluidRow #
br(),
##FAQs----
fluidRow(
column(width = 6, offset = 3,
box(status = "primary", solidHeader = TRUE, width = 12,
title = HTML('<span style="font-size:20px;">Frequently Asked Questions</span>'),
bs_accordion(id = 'faqLFOcalc') %>%
bs_append(title = "Do I have to pay to use this calculator?", content = HTML("<span style='font-size:16px'>No, there is no fee to use this tool.</span>")) %>%
bs_append(title = "How do I look up the fines and fees on my ticket?", content = HTML('<span style="font-size:16px">Look for the charges/violations written on your ticket. For example, ROH 15-22.11(b) EXPIRED PRKG METER. Input the violation exactly how it is written on your ticket. If you don\'t have the ticket, you may look it up on <a href="http://jimspss1.courts.state.hi.us:8080/eCourt/", target = "_blank">eCourt Kokua</a> (more info <a href = "https://www.courts.state.hi.us/legal_references/records/jims_system_availability", target = "_blank">here</a>) by your name (Party Search), license plate/VIN for parking tickets (Vehicle Search), or citation number (Case Search).</span>')) %>%
bs_append(title = "What if I have more than one ticket?", content = HTML("<span style='font-size:16px'>You will need to look up each ticket. If your ticket has more than one charge/violation, you can input up to 11 charges from a single ticket into the calculator.</span>")) %>%
bs_append(title = "What if I do not have enough money to pay for my ticket?", content = HTML('<span style="font-size:16px">If you don\'t have enough money to pay the monetary assessment due to financial hardship, you have options. <br><br> <ul> <li>You may ask the court to consider your financial circumstances in determining any fine and/or imposing community service in lieu of a fine. The court does not have access to your financial information, so it is your responsibility to provide such information.* </li><br><li>If your financial circumstances change (for example, if you lose your job after the judgment is entered), you may request to reduce your fine or convert it to community service.</li></ul> You can request a reduction in your fines or conversion to community service either in person or by written statement. Please see the back of your ticket for instructions on how to request a hearing or submit a written statement.<br><br>*The calculator is designed to help you collect this information and have it ready whether you make the request in person or by written statement. You will be required to provide information about your household income, your expenses, and how much you can pay per month. You will also need to present copies of your paystubs or other income information. This does not guarantee the court will lower the amount on your fines or give you the chance to do community service.</span>')) %>%
bs_append(title = "Can I pay for my ticket here?", content = HTML('<span style="font-size:16px">No, this tool is not for payment. if you want to pay for the ticket and it has not been longer than 21 days since receiving it, you can pay online at <a href="https://etraffic.ehawaii.gov/etraffic/home", target = "_blank">https://etraffic.ehawaii.gov/etraffic/home</a>. If more than 21 days has passed, then information on how to pay is on the back of the ticket.</span>')) %>%
bs_append(title = "Can I contest my ticket here?", content = HTML("<span style='font-size:16px'>No, this site is not to challenge or dispute your ticket. To do so, please read the instructions on the back of your ticket.</span>")) %>%
# bs_append(title = "What if I do not have the ability to pay for my ticket?", content = HTML('If you cannot afford to pay for the fine and fees, you are welcome to submit a request to reduce the fees and/or set up a payment plan associated with the ticket. You will be required to submit a <a href="Declaration of Financial Status.docx", target = "_blank">Declaration of Financial Status</a> and provide relevant information for the consideration of the court, including but not limited to employment information, household income, current debt, and how much you can pay per month. Please have copies of paystubs or other relevant income information ready to upload. This does not guarantee a reduction in your ticket, but only your request. Additionally, if eligible, you may request to convert the fines and fees to community service.')) %>%
bs_append(title = "Does the calculator track my personal information?", content = HTML("<span style='font-size:16px'>The data you enter is saved for aggregate statistics only. We do not collect case or ticket numbers, so the information you enter is not linked to you or your case unless you bring copies to your court appearance and submit them to the court.</span>")) %>%
bs_append(title = "Who can I contact with questions, feedback, or suggestions relating to the calculator?", content = HTML("<span style='font-size:16px'>Please contact <a href = 'mailto:finescalculator@courts.hawaii.gov'>finescalculator@courts.hawaii.gov</a>. We will aim to respond to questions within 48 hours. Please note that <em>we cannot provide any legal advice</em>. For legal advice, please contact an attorney, or if you cannot afford one, please consider these <a href=' https://www.courts.state.hi.us/self-represented-litigants-srl', target = '_blank'>resources</a>.</span>")) %>%
bs_append(title = "Acknowledgements", content = HTML("<span style='font-size:16px'>This calculator was inspired by the <a href ='https://lfocalculator.org/', target = '_blank'>Washington State LFO Calculator</a>. We gratefully acknowledge their pioneering efforts and thank them for their time helping us get started.</span>")
) %>%
bs_multi_open(multi = FALSE,
open = c()
)
)#box
)#column
)#fluidRow
),#tabPanel page_0
#-----------------------------------#
#tabPanel page_1----
#-----------------------------------#
tabPanel("page_1",
# progress meter/line
fluidRow(
column(width = 6, offset = 3, align = 'center',
box(width = 12,
title = HTML("<div><img src='number1.png' height = 20px width = 20px'/><span style='color:#0073b7;'><strong> Charges </strong></span><hr style='display:inline-block; width:30px;border-color:black;margin-bottom:1%;' /> <img src='number2.png' height = 20px width = 20px> Ability to Pay <hr style='display:inline-block; width:30px;border-color:black;margin-bottom:1%;' /> <img src='number3.png' height = 20px width = 20px> Fines and Fees <hr style='display:inline-block; width:30px;border-color:black;margin-bottom:1%;' /> <img src='number4.png' height = 20px width = 20px> Summary</div>")
)#box
)#column
),#fluidRow progress meter/line
# box to select role, date of charges, and charges
fluidRow(
column(width = 6, offset = 3,
box(title = 'Charges', solidHeader = TRUE, status = 'primary', width = 12, align = 'center',
##select role----
selectInput(input = 'role',
label = 'Select your role',
choices = c('Defendant','Public Defender/Defense Attorney','Prosecutor','Judge','Other'),
selected = 'Defendant',
width = '50%'),
##select date----
#replaced dateInput with airDatepickerInput b/c unable to center align date value using dateInput
airDatepickerInput(inputId = 'chargeDate',
label = list('Select date of citation ',br(),
#h/t: https://stackoverflow.com/questions/40513153/shiny-extra-white-space-in-selectinput-choice-display-label
# stri_dup(intToUtf8(160), 3), # Replace 6 with the desired number,
actionLink(inputId = 'infoChargeDate',
label = '(Why do I need to provide this information?)'
#icon = icon('info-circle'),
# class = 'btn-xs'
),
tags$style(".popover{max-width:30%;}"),
bsPopover(id = 'infoChargeDate',
title = 'Why do I need to provide this information?',
content = paste('Fines and fees change over time (for example, when laws change). The calculator needs the date to select the fines and fees that applied at the time of your citation. Failing to enter the correct date may yield inaccurate fines and fees.',
em('What if I have citations with different dates?'),
'If you have tickets with different dates, please run the calculator separately for each citation.', sep = '<br>'),
placement = 'right',
# trigger = c('hover','focus'),
trigger = c('focus'),
options = list(container = 'body')
)#bsPopover
),#label list
value = Sys.Date()+1, #appears to be a bug in airDatepickerInput, need to increment by 1 to get Sys.Date()
minDate = '1960-01-01',
maxDate = Sys.Date(),
dateFormat = 'MMMM dd, yyyy',
addon = 'none',
width = '50%'),
#center align date; dateInput, which is left aligned by default, doesn't center align using css, couldn't troubleshoot, so using airDatepickerInput
tags$style(type="text/css", "#chargeDate {text-align: center;}"),
##select charges----
#charge list is set up on server-side to speed up loading b/c list is large
#OPTION 1: use css to position dropdown downward and use optionsCount to control how many options are visible in dropdown
#dropdown open in downward direction, h/t: https://stackoverflow.com/questions/52270897/shiny-selectize-dropdown-menu-open-in-upward-direction
# modify virtual select css https://github.com/sa-si-dev/virtual-select/blob/master/dist/virtual-select.min.css
# tags$head(tags$style(type = "text/css", paste0(".vscomp-dropbox {
# position: absolute !important;
# bottom: auto !important;
# top: 100% !important;
# }}"))),
#OPTION 2: use showDropboxAsPopup and popupDropboxBreakpoint to display dropdown as popup
#see properties here: https://sa-si-dev.github.io/virtual-select/#/properties
virtualSelectInput(inputId = 'charges',
label = list('Select charges',br(),
a(href="https://adamcohen3.github.io/courts/commonChargesReferenceSheet.html", target = "_blank", '(Help! I don\'t see my charge!)')),
choices = NULL,
multiple = TRUE,
search = TRUE,
placeholder = "Type one or more charges",
# hideClearButton = TRUE,
showSelectedOptionsFirst = TRUE,
showValueAsTags = TRUE,
showDropboxAsPopup = TRUE, #https://sa-si-dev.github.io/virtual-select/#/examples?id=show-dropbox-as-popup
popupDropboxBreakpoint = '3000px', #Maximum screen width that allowed to show dropbox as popup
dropboxWidth = "800px",
# popupPosition = "right",
optionsCount = 9,
# noOfDisplayValues = 3
disableSelectAll = TRUE,
html = TRUE,
hasOptionDescription = TRUE,
markSearchResults = TRUE
),
##select default judgment----
radioGroupButtons(inputId = "default_judgment",
# label = HTML(paste0("Has a Default Judgment has been entered against you?", "<span style = 'color:red;'> *Required field</span>")),
label = list('Has a ',
actionLink(inputId = "default_judgment_info", label = "default judgment"),
# HTML('<span id="csw291D" style="color:blue;text-decoration:underline blue dotted;">community service</span>'),
'been entered against you?',
tags$style(".popover{max-width:30%;}"),
bsPopover(#session=session,
id="default_judgment_info",
title = "What is a default judgment?",
content = 'According to <a href= "https://www.courts.state.hi.us/docs/court_rules/rules/hctr.htm#Rule_3" target = "_blank">HCTR Rule 3(b)</a>, "<strong>Default Judgment.</strong> A judgment entered in favor of the State of Hawai‘i when a defendant fails to answer or respond, either in person or in writing, to the notice of infraction within twenty-one (21) calendar days from the date the notice of infraction was issued."<br><br> Please also refer to <a href= "https://www.courts.state.hi.us/docs/court_rules/rules/hctr.htm#Rule_15" target = "_blank">HCTR Rule 15</a> "DEFAULT JUDGMENT AND STOPPER," <a href= "https://www.courts.state.hi.us/docs/court_rules/rules/hctr.htm#Rule_18" target = "_blank">HCTR Rule 18</a> "POST-JUDGMENT RELIEF," and <a href= "https://www.capitol.hawaii.gov/hrscurrent/Vol05_Ch0261-0319/HRS0291D/HRS_0291D-0007.htm" target = "_blank">HRS 291D-7</a> subsections (d) and (e).',
placement = 'right',
trigger = c('focus'),
options = list(container = 'body'))),
choices = c("Yes", "No"),
selected = character(0),
width = "81%",
justified = TRUE,
individual = TRUE),
fluidRow(column(width = 12, align = 'center',
actionButton("page_10", "back"),
actionButton("page_12", "next"))
)#fluidRow buttons
)#box
)#column
)#fluidRow
), #tabPanel page_1
#-----------------------------------#
#tabPanel page_2----
#-----------------------------------#
tabPanel("page_2",
# progress meter/line
fluidRow(
column(width = 6, offset = 3, align = 'center',
box(width = 12,
title = HTML("<div><img src='number1.png' height = 20px width = 20px'/> Charges <hr style='display:inline-block; width:30px;border-color:black;margin-bottom:1%;' /> <img src='number2.png' height = 20px width = 20px><span style='color:#0073b7;'><strong> Ability to Pay </strong></span><hr style='display:inline-block; width:30px;border-color:black;margin-bottom:1%;' /> <img src='number3.png' height = 20px width = 20px> Fines and Fees <hr style='display:inline-block; width:30px;border-color:black;margin-bottom:1%;' /> <img src='number4.png' height = 20px width = 20px> Summary</div>")
)#box
)#column
),#fluidRow header - title
fluidRow(id = "print_page2_box",
column(width = 6, offset = 3,
box(title = 'Ability to Pay', solidHeader = TRUE, status = 'primary', width = 12,
##box to select requests----
checkboxGroupInput(inputId = 'atp_requests',
label = HTML('If you are unable to pay your ticket, would you like the Court to consider any of the following requests? <br><em>Note: There may be additional administrative fees associated with setting up a community service work plan.</em>'),
choices = c('Reduce fines',
# 'Set up a payment plan', #2023/10/05 - disable payment plan option per discussion with Judge May and Angela
'Convert fines to community service',
"Contest ticket",
# "Skip this step to preview fines and fees first*",
"None of the above*"
# "None of the above (I just want to view the fines and fees)" = "None of the above"
),
selected = NULL),
em("*Select \"None of the above\" to skip to the next step if you would like to first preview
your fines and fees. After previewing, if you decide you would like to make a request to the
court, please return to this step to indicate which request(s) you would like to make and complete
the ability to pay form."),
br(),
br(),
#conditionally show ATP input fields
conditionalPanel("input.atp_requests[0] == 'Reduce fines' || input.atp_requests[0] == 'Convert fines to community service' || input.atp_requests[0] == 'Set up a payment plan' && input.atp_requests!='None of the above'",
strong('We will walk you through a few questions to help the court determine whether you are eligible for this request.
Please be prepared to share information about your monthly income, monthly expenses, and any public benefits you currently receive.'),
br(),br(),
strong(style = "color:red;", "Please be advised that your financial information will not be submitted through this app."),
HTML("<ul style = 'color:red'><li>You will need to print a hard copy to bring to court or to submit with a written statement (see the back of your ticket for instructions). You can print a copy of this form at the bottom of the page. <strong>WARNING:</strong> The public kiosk computers at the courthouses do not allow printing.</li>
<li>You will need to bring to court or submit with a written statement any supporting documents (original pay stubs, bills, other documentation of your income, monthly expenses, and public benefits you currently receive). </li></ul>"),
br(),
##income----
strong(em('a. What is your monthly gross income (before taxes)?')),
br(),
fluidRow(column(width = 6, align = 'right', br(), 'Employment'),
column(width = 6,
autonumericInput(inputId = 'income1',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'right', br(), 'Social Security'),
column(width = 6,
autonumericInput(inputId = 'income2',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'right', br(), 'Unemployment Income'),
column(width = 6,
autonumericInput(inputId = 'income3',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'right', br(), 'Child and/or spousal support as income'),
column(width = 6,
autonumericInput(inputId = 'income4',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'right', br(), 'Other Income (rental, disability/workers compensation, etc.)'),
column(width = 6,
autonumericInput(inputId = 'income5',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'right', strong('Monthly Gross Income')),
column(width = 6, align = 'center',
strong(textOutput('incomeSum')))),
br(),
fluidRow(column(width = 6, align = 'left', br(), p(span('b. What is your monthly',style = 'font-weight:bold;font-style:italic;'),span('household',style = 'font-weight:bold;'),span(' gross income (before taxes)? This is any amount NOT included above but earned by your spouse/live-in partner and other adults in household.',style = 'font-weight:bold;font-style:italic;'))),
column(width = 6,
autonumericInput(inputId = 'householdIncome',
label = '',
value = 0,
currencySymbol = '$'))),
br(),
##employed?----
wellPanel(#style = "background-color:light-gray;",
radioButtons(inputId = 'workingYN',
label = 'Are you currently employed?',
choices = c('Yes','No'),
selected = character(0),
# selected = 'No',
width = '100%'),
#conditionally show past employment info
conditionalPanel("input.workingYN=='No'",
fluidRow(column(width = 12, em('If not working, when was the last time you were employed and what was your income?'))),
br(),
fluidRow(column(width = 6, align = 'right', br(), 'Last day of employment'),
column(width = 6,
dateInput(inputId = 'prevEmployedDate',
label = ''))),
# fluidRow(column(width = 6, align = 'right', br(), 'Income when last employed'),
# column(width = 6,
# autonumericInput('prevEmployIncome',
# label = '',
# value = 0,
# currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'right', br(), 'What was your monthly gross income (before taxes)?'),
column(width = 6,
autonumericInput('prevEmployMonthlyGross',
label = '',
value = 0,
currencySymbol = '$')))
),#conditionalPanel
),
##household size----
#disabled, instead using adults, seniors, infants, preschoolers, and school-age numericInputs that can also be used for ALICE guideline
# fluidRow(column(width = 6, align = 'left', br(), strong(em('c. How many people are in your household? List adults and children, including yourself.'))),
# column(width = 6,
# numericInput(inputId = 'householdSize',
# label = '',
# value = 0))),
# br(),
fluidRow(column(width = 12, align = 'left', br(), strong(em('c. How many people are in your household, including yourself?'))),
),
br(),
fluidRow(column(width = 9, align = 'right', br(), 'Number of adults (18-64 years old):'),
column(width = 3,
numericInput(inputId = 'adultNum',
label = '',
value = 0))),
br(),
fluidRow(column(width = 9, align = 'right', br(), 'Number of seniors (65+):'),
column(width = 3,
numericInput(inputId = 'seniorNum',
label = '',
value = 0))),
br(),
fluidRow(column(width = 9, align = 'right', br(), 'Number of infants (0-2)'),
column(width = 3,
numericInput(inputId = 'infantNum',
label = '',
value = 0))),
br(),
fluidRow(column(width = 9, align = 'right', br(), 'Number of preschoolers (3-5)'),
column(width = 3,
numericInput(inputId = 'preschoolerNum',
label = '',
value = 0))),
br(),
fluidRow(column(width = 9, align = 'right', br(), 'Number of school-age children (5-17)'),
column(width = 3,
numericInput(inputId = 'childNum',
label = '',
value = 0))),
br(),
##poverty guideline----
#poverty guideline output generated on server-side, involves formatted text
fluidRow(column(width = 6, align = 'left', br(),
actionButton('povertyGuidelineCheck',
label = 'Check poverty guideline')),
column(width = 6,
uiOutput('povertyGuidelineEstimate'))),
br(),
##ALICE guideline----
#ALICE guideline output generated on server-side, involves formatted text
fluidRow(column(width = 6, align = 'left', br(),
div(actionButton('aliceGuidelineCheck',
label = 'Check ALICE guideline'),
br(),
div(actionLink(inputId = 'whatIsAlice',
label = '(What is ALICE?)'
#icon = icon('info-circle'),
# class = 'btn-xs'
),#actionLink
style = 'position:relative;left:30px;')#inner div on actionLink
),#outer div on actionButton
tags$style(".popover{max-width:30%;}"),
bsPopover(id = 'whatIsAlice',
title = 'What is ALICE?',
#use paste0 with HTML syntax to add formatting to popover, and add link to ALICE Report
content = paste0('<p>ALICE stands for <strong>A</strong>sset <strong>L</strong>imited, <strong>I</strong>ncome <strong>C</strong>onstrained, <strong>E</strong>mployed, and refers to households not earning enough to afford basic household necessities. The difference between the Federal poverty guideline and the ALICE guideline is that, "Traditional economic measures systematically underestimate the actual cost of basic needs and their rate of increase over time, concealing important aspects of the local and national economy. To better capture the reality of how much income households need to live and work," the ALICE Household Survival Budget for Hawaii provides a "more accurate estimate of the cost of living and a clearer way to track changes over time" (<a href="https://www.unitedforalice.org/Attachments/AllReports/2020ALICEReport_HI_FINAL.pdf" target="_blank">ALICE Report, 2020</a>).</p>',
"<br>",
'<em>What is the ALICE Household Survival Budget?</em><br>',
'The ALICE Household Survival Budget is an estimate of the minimal total cost of household essentials - housing, child care, food, transportation, health care, and a smartphone plan, plus taxes and a miscellaneous contingency fund equal to 10% of the budget. It does not include savings, auto repairs, cable service, travel, laundry costs, or amenities such as holiday gifts or dinner at a restaurant that many families take for granted.',
'<br><br>',
'<em>Why do the Household Survival Budget estimates differ slightly from those in the ALICE Report tables?</em><br>',
'<p>The estimates here are similar but not identical to those in the ALICE Report because the LFO calculator configures a budget starting with a base of 1 adult and then uses a marginal cost formula to determine the budget for each additional adult or child whereas the <a href="https://www.unitedforalice.org/household-budgets/hawaii" _target="blank">ALICE Report table</a> determines the budgets for the standard six households and computes the budget for other configurations using the marginal cost formula. Despite this difference, the estimates should differ by no more than 5%.</p>'),
placement = 'right',
# trigger = c('hover','focus'),
trigger = c('focus'),
options = list(container = 'body')
)),#bsPopover),
column(width = 6,
uiOutput('aliceGuidelineEstimate'))),
br(),
##benefits----
strong(em('d. Are you currently receiving any of the following benefits?')),
br(),
fluidRow(column(width = 6, align = 'left',
checkboxInput(inputId = 'benefitsButtonSNAP',
# label = '',
# choices = c("Supplement Nutrition Assistance Program (SNAP)/Food Stamps" = "SNAP")
label = 'Supplement Nutrition Assistance Program (SNAP)/Food Stamps'
)),
column(width = 6,
autonumericInput(inputId = 'benefitsAmountSNAP',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'left',
checkboxInput(inputId = 'benefitsButtonTANF',
# label = '',
# choices = c("Temporary Assistance for Needy Families (TANF)" = "TANF")
label = 'Temporary Assistance for Needy Families (TANF)'
)),
column(width = 6,
autonumericInput(inputId = 'benefitsAmountTANF',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'left', br(),
checkboxInput(inputId = 'benefitsButtonSSI',
# label = '',
# choices = c('Supplemental Security Income (SSI)' = "SSI")
label = 'Supplemental Security Income (SSI)'
)),
column(width = 6,
autonumericInput(inputId = 'benefitsAmountSSI',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'left',
checkboxInput(inputId = 'benefitsButtonGA',
# label = '',
# choices = c("General Assistance (GA)" = "GA")
label = 'General Assistance (GA)'
)),
column(width = 6,
autonumericInput(inputId = 'benefitsAmountGA',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'left',
checkboxInput(inputId = 'benefitsButtonOther',
# label = '',
# choices = c("Other" = "OTHER")
label = 'Other'
)),
column(width = 6,
autonumericInput(inputId = 'benefitsAmountOther',
label = '',
value = 0,
currencySymbol = '$'))),
br(),
##expenses and debt----
strong(em('e. What are your monthly expenses?')),
br(),
fluidRow(column(width = 6, align = 'right', br(), 'Rent/Mortgage'),
column(width = 6,
autonumericInput(inputId = 'expenses1',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'right', br(), 'Utilities'),
column(width = 6,
autonumericInput(inputId = 'expenses2',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'right', br(), 'Food'),
column(width = 6,
autonumericInput(inputId = 'expenses3',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'right', br(), 'Insurance'),
column(width = 6,
autonumericInput(inputId = 'expenses4',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'right', br(), 'Child/Spousal Support'),
column(width = 6,
autonumericInput(inputId = 'expenses5',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'right', br(), 'Transportation'),
column(width = 6,
autonumericInput(inputId = 'expenses6',
label = '',
value = 0,
currencySymbol = '$'))),
fluidRow(column(width = 6, align = 'right', strong('Total Expenses')),
column(width = 6, align = 'center',
strong(textOutput('expenseSum')))),
br(),
fluidRow(column(width = 6, align = 'left', br(), strong(em('f. What is your current debt?'))),
column(width = 6,
autonumericInput(inputId = 'debt',
label = '',
value = 0,
currencySymbol = '$'))),
br(),
##additional info----
fluidRow(column(width = 12,
textAreaInput(inputId = 'additionalInfo',
label = strong(em('g. Is there anything else that the court should know about that would impact your ability to pay your legal financial obligations?')),
placeholder = 'additional information...'))),
#2023/03/30: discussed with AM, for now disable this section
# fluidRow(column(width = 12,
# strong(em('i. Declaration of financial status')),
# br(),
# div('Step 1.',downloadLink('downloadDoFS','Download Declaration of Financial Status')),
# fileInput(inputId = 'myFile',
# label = div('Step 2. Upload completed Declaration of Financial Status:',style='font-weight:normal;'),
# multiple = TRUE,
# accept = 'application/pdf',
# buttonLabel = 'upload')))
p('(Optional) Please include a valid e-mail and phone number in case the court needs to communicate with you about your request.'),
textInput(inputId = 'email', label = 'email', placeholder = 'jdoe@mail.com'),
textInput(inputId = 'phone', label = 'phone', placeholder = '(XXX) XXX-XXXX'),
# phoneInput(inputId = 'phone', label = 'phone', placeholder = '(###)###-####', country = "us")
uiOutput("phone_check"),
##print----
wellPanel(fluidRow(column(width = 12, align = 'center',
"Print your ability to pay information for your court appearance or written statement: "),
),
fluidRow(
column(width = 12, align = 'center',
#disable capture_pdf, inconsistent behavior, use JS print for now
# capture_pdf(selector = '#print_page2_box',#hack for now b/c not working when using "body" to print whole page
# # filename = paste0('LFO_calculator_summary_',Sys.Date(),'.pdf'),
# filename = paste0('ATP_summary_',format(Sys.time()-10*60*60,'%Y-%m-%d'),'.pdf'),
# icon("fas fa-download"),strong("Download"),
# loading = loading(text = 'Generating PDF, please wait...')
# ),
actionButton('print_page_2',strong('Print'))
)
), #fluidRow buttons
br(),
fluidRow(column(width = 12, align ="left",
HTML("<p style='color:red;font-weight:bold;'>Please be advised that your financial information will not be submitted through this app. </p>")
),
HTML("<ul style = 'color:red'><li>You will need to print a hard copy to bring to court or to submit with a written statement (see the back of your ticket for instructions). <strong>WARNING:</strong> The public kiosk computers at the courthouses do not allow printing.</li>
<li>In addition to the information provided on this page, when you come to court or submit a written statement, please be prepared to show original pay stubs, bills, and other documentation of your income, monthly expenses, and any public benefits you currently receive. </li></ul>"),
)
),#wellPanel
),#conditionalPanel
fluidRow(column(width = 12, align = 'center',
actionButton("page_21", "back"),
actionButton("page_23", "next"))
) #fluidRow buttons
)#box
)#column
)#fluidRow
),#tabPanel page_2
#-----------------------------------#
#tabPanel page_3----
#-----------------------------------#
tabPanel("page_3",
# progress meter/line
fluidRow(
column(width = 6, offset = 3, align = 'center',
box(width = 12,
title = HTML("<div><img src='number1.png' height = 20px width = 20px'/> Charges <hr style='display:inline-block; width:30px;border-color:black;margin-bottom:1%;' /> <img src='number2.png' height = 20px width = 20px> Ability to Pay <hr style='display:inline-block; width:30px;border-color:black;margin-bottom:1%;' /> <img src='number3.png' height = 20px width = 20px><span style='color:#0073b7;'><strong> Fines and Fees </strong></span><hr style='display:inline-block; width:30px;border-color:black;margin-bottom:1%;' /> <img src='number4.png' height = 20px width = 20px> Summary</div>")
)#box
)#column
),#fluidRow progress meter/line
##Disclaimer (if role selected is defendant or other; logic on server-side)----
uiOutput("srl_warning1"),
#fines and fees box
fluidRow(
column(width = 6, offset = 3,
##Fines and fees----
box(title = 'Fines and Fees', solidHeader = TRUE, status = 'primary', width = 12,
#if no charges selected, send message, otherwise sends a blank message (logic on server-side)
fluidRow(column(width = 12, align = 'center', uiOutput('noChargesSelected_p3'))),
#if N > 1 charges selected, conditionally show N boxes, generated on the server-side
conditionalPanel(condition = "output.chargeCounter",
uiOutput('chargeBox')),
fluidRow(column(width = 12, align = 'center',
actionButton("page_32", "back"),
actionButton("page_34", "next"))
) #fluidRow buttons
),#box fines and fees
),#column
##Ability to pay----
column(id = "hhh", width = 3,
box(title = 'Ability to Pay Determination', solidHeader = TRUE, status = 'primary', width = 12,
#if N > 1 charges selected, conditionally show N lines, generated on the server-side
conditionalPanel(condition = "output.chargeCounter",
uiOutput('summaryATP1'),
uiOutput('csPopover1')
),#conditionalPanel for summaryATP1
),#box ATP
##Payment plan (disabled)---- #2023/10/05 - disable payment plan option per discussion with Judge May and Angela
# conditionalPanel(condition = "input.addRequests[0] == 'Payment Plan' || input.addRequests[1] == 'Payment Plan'",
# box(title = "Payment Plan Planner",
# solidHeader = TRUE,
# status = 'warning',
# width = 12,
# conditionalPanel(condition = "output.chargeCounter",
# fluidRow(column(width = 6, align = "right",
# HTML("<strong>Total Fines and Fees: </strong>"),
# ),#column
# column(width = 6, align = "left",
# uiOutput('summaryPPTotalFinesFees')
# )#column
# ),#fluidRow Total Fines
# br(),
# #move label to side of widget
# #h/t: https://stackoverflow.com/questions/39230356/how-to-make-label-and-box-align-next-to-each-other-in-shinynumericinput
# fluidRow(column(width = 6, align ="right",
# HTML("<strong>How much can you pay per month?</strong>")
# ),
# column(width = 6, align ="left",
# autonumericInput(inputId = "payplan",
# label = NULL,
# value = 0,
# width = "100%",
# align = "left",
# currencySymbol = '$'
# )
# )#column
# ),#fluidRow conversation rate
# br(),
# fluidRow(column(width = 6, align ="right",
# HTML("<strong>Months needed to pay in full: </strong>")
# ),
# column(width = 6, align ="left",
# uiOutput('pay_plan_monthly')
# )#column
# )#fluidRow
# )#conditionalPanel
# )#box for Payment Plan
# ),#conditionalPanel for Payment Plan
##Community Service----
conditionalPanel(condition = "input.atp_requests[0] == 'Convert fines to community service' || input.atp_requests[1] == 'Convert fines to community service' || input.atp_requests[2] == 'Convert fines to community service'",
box(title = "Convert Fines to Community Service (CS)?",
solidHeader = TRUE,
status = 'warning',
width = 12,
fluidRow(column(width = 10, align ="left",
checkboxInput(inputId = "convertCSW",
label = "Convert fines to CS hours",
value = FALSE)
),#column
),#fluidRow checkbox
fluidRow(style="font-style:italic;",
column(width = 12, align = 'left',
"Please note that certain fines and fees are mandatory and may not be eligible for conversion to community service.",
br(),br())),
conditionalPanel(condition = "output.chargeCounter && input.convertCSW==1",
fluidRow(column(width = 6, align = "right",
# HTML("<strong>Total Fines: </strong>"),
actionLink(inputId = 'cswFinesInfo',
label = HTML("<strong>Total Fines: </strong>")
#icon = icon('info-circle'),
# class = 'btn-xs'
),#actionLink
tags$style(".popover{max-width:30%;}"),
bsPopover(id = 'cswFinesInfo',
title = 'Exclusion of fees and court costs',
content = paste0('<p>According to <a href = "https://www.courts.state.hi.us/docs/court_rules/rules/hctr.htm#Rule_17" target = "_blank">HCTR Rule 17</a>, community service may not be ordered in lieu of payment of costs and fees.</p>'),
placement = 'left',
# trigger = c('hover','focus'),
trigger = c('focus'),
options = list(container = 'body')
)#bsPopover
),#column
column(width = 6, align = "left",
uiOutput('summaryCSWTotalFines')
)#column
),#fluidRow Total Fines
br(),
#move label to side of widget
#h/t: https://stackoverflow.com/questions/39230356/how-to-make-label-and-box-align-next-to-each-other-in-shinynumericinput
fluidRow(column(width = 6, align ="right",
HTML("<strong>Conversion Rate: </strong>")
),#column
column(width = 6, align ="left",
autonumericInput(inputId = "cswRate",
label = NULL,
value = 15,
width = "100%",
align = "left",
currencySymbol = '$',
currencySymbolPlacement = 'p',
suffixText = '/hour')
)#column
),#fluidRow conversation rate
fluidRow(column(width = 6, align = "right",
# HTML("<strong>Total Hours: </strong>")
actionLink(inputId = 'cswHoursInfo',
label = HTML("<strong>Total Hours: </strong>")
#icon = icon('info-circle'),
# class = 'btn-xs'
),#actionLink
tags$style(".popover{max-width:30%;}"),
bsPopover(id = 'cswHoursInfo',
title = 'Calculation',