DaveM2430 commited on
Commit
85c55f5
Β·
verified Β·
1 Parent(s): 5039001

Update app.R

Browse files
Files changed (1) hide show
  1. app.R +162 -4
app.R CHANGED
@@ -4,6 +4,22 @@ library(arrow)
4
  library(DT)
5
  library(ggplot2)
6
  library(shinyjs)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  # ── Load functions ─────────────────────────────────────────────────────────────
9
  load_data <- function(level) {
@@ -106,6 +122,14 @@ build_pitcher_table <- function(lb, min_pitches, portal_only) {
106
  `Twitter` = paste0('<a href="https://www.google.com/search?q=',
107
  gsub(" ", "+", Name),
108
  '+baseball+twitter" target="_blank">&#x1F426; Search</a>'),
 
 
 
 
 
 
 
 
109
  Name = ifelse(HasSinker,
110
  paste0('<span style="background:#cce5ff; padding:2px 6px;',
111
  'border-radius:3px; font-weight:700;">', Name, '</span>'),
@@ -113,7 +137,7 @@ build_pitcher_table <- function(lb, min_pitches, portal_only) {
113
  )
114
  if (portal_only) df <- df %>% filter(Portal == "Yes")
115
  df %>%
116
- select(Name, Level, Team, Portal, `Plot`, `Twitter`, HomeState, Throws, Height, Weight,
117
  Pitches, ERA, FIP, WHIP, `FB Velo`, `Zone%`, `Strike%`,
118
  `K's`, `BB's`, `K/BB`, `Whiff%`, `Chase%`, `Hard Hit%`,
119
  `GB%`, `FB%`, `LD%`, `Stuff+`, `Location+`) %>%
@@ -185,6 +209,11 @@ ui <- fluidPage(
185
  .plot-btn { background:#002147; color:white; border:none; border-radius:4px;
186
  padding:4px 10px; font-size:12px; cursor:pointer; font-weight:600; }
187
  .plot-btn:hover { background:#003366; }
 
 
 
 
 
188
  table.dataTable tbody td a {
189
  color:#002147; font-weight:600; font-size:12px; text-decoration:none;
190
  background:#e8f0fe; border-radius:4px; padding:3px 8px; display:inline-block;
@@ -279,6 +308,38 @@ ui <- fluidPage(
279
  Shiny.setInputValue('hitter_modal_closed', Math.random(), {priority: 'event'});
280
  }
281
  });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
282
 
283
  // update min label
284
  Shiny.addCustomMessageHandler('update_label', function(msg) {
@@ -420,6 +481,65 @@ ui <- fluidPage(
420
  tags$div("vs. Pitch Type", class = "section-label"),
421
  DTOutput("hitter_pitch_table")
422
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
423
  )
424
  )
425
 
@@ -580,13 +700,13 @@ make_color_ramp <- function(values, low, high, flip = FALSE,
580
  scrollY = "65vh",
581
  scrollCollapse= TRUE,
582
  fixedHeader = TRUE,
583
- order = list(list(10, "desc")),
584
  orderMulti = FALSE,
585
  dom = "lfrtip",
586
  columnDefs = list(
587
  list(className = "dt-left", targets = 0:1),
588
- list(className = "dt-center", targets = 2:26),
589
- list(orderable = FALSE, targets = c(4, 5))
590
  ),
591
  initComplete = JS("function(settings, json) {
592
  $('#loading_msg').hide();
@@ -834,6 +954,44 @@ make_color_ramp <- function(values, low, high, flip = FALSE,
834
  observeEvent(input$hitter_modal_closed, {
835
  shinyjs::hide("hitter_modal_wrapper")
836
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
837
 
838
  batter_data <- reactive({
839
  req(current_batter(), raw_data())
 
4
  library(DT)
5
  library(ggplot2)
6
  library(shinyjs)
7
+ library(googlesheets4)
8
+
9
+ # ── Google Sheets auth ─────────────────────────────────────────────────────────
10
+ gs_key <- Sys.getenv("GS_KEY")
11
+ sheet_id <- Sys.getenv("GS_SHEET_ID")
12
+
13
+ if (nzchar(gs_key)) {
14
+ tryCatch({
15
+ key_file <- tempfile(fileext = ".json")
16
+ writeLines(gs_key, key_file)
17
+ gs4_auth(path = key_file)
18
+ message("Google Sheets auth successful")
19
+ }, error = function(e) {
20
+ message("Google Sheets auth failed: ", e$message)
21
+ })
22
+ }
23
 
24
  # ── Load functions ─────────────────────────────────────────────────────────────
25
  load_data <- function(level) {
 
122
  `Twitter` = paste0('<a href="https://www.google.com/search?q=',
123
  gsub(" ", "+", Name),
124
  '+baseball+twitter" target="_blank">&#x1F426; Search</a>'),
125
+ `Board` = paste0('<button class="board-btn" data-pitcher="',
126
+ gsub('"', '&quot;', Pitcher),
127
+ '" data-name="', gsub('"', '&quot;', Name),
128
+ '" data-team="', gsub('"', '&quot;', ifelse(is.na(Team), "", Team)),
129
+ '" data-throws="', gsub('"', '&quot;', ifelse(is.na(Throws), "", Throws)),
130
+ '" data-height="', gsub('"', '&quot;', ifelse(is.na(Height), "", Height)),
131
+ '" data-weight="', gsub('"', '&quot;', ifelse(is.na(Weight), "", Weight)),
132
+ '">⭐ Add</button>'),
133
  Name = ifelse(HasSinker,
134
  paste0('<span style="background:#cce5ff; padding:2px 6px;',
135
  'border-radius:3px; font-weight:700;">', Name, '</span>'),
 
137
  )
138
  if (portal_only) df <- df %>% filter(Portal == "Yes")
139
  df %>%
140
+ select(Name, Level, Team, Portal, `Plot`, `Twitter`, `Board`, HomeState, Throws, Height, Weight,
141
  Pitches, ERA, FIP, WHIP, `FB Velo`, `Zone%`, `Strike%`,
142
  `K's`, `BB's`, `K/BB`, `Whiff%`, `Chase%`, `Hard Hit%`,
143
  `GB%`, `FB%`, `LD%`, `Stuff+`, `Location+`) %>%
 
209
  .plot-btn { background:#002147; color:white; border:none; border-radius:4px;
210
  padding:4px 10px; font-size:12px; cursor:pointer; font-weight:600; }
211
  .plot-btn:hover { background:#003366; }
212
+ .plot-btn:hover { background:#003366; }
213
+
214
+ .board-btn { background:#f5c842; color:#002147; border:none; border-radius:4px;
215
+ padding:4px 10px; font-size:12px; cursor:pointer; font-weight:600; }
216
+ .board-btn:hover { background:#e0b030; }
217
  table.dataTable tbody td a {
218
  color:#002147; font-weight:600; font-size:12px; text-decoration:none;
219
  background:#e8f0fe; border-radius:4px; padding:3px 8px; display:inline-block;
 
308
  Shiny.setInputValue('hitter_modal_closed', Math.random(), {priority: 'event'});
309
  }
310
  });
311
+ // board modal
312
+ $(document).on('click', '.board-btn', function() {
313
+ var d = $(this).data();
314
+ // parse name from HTML span
315
+ var nameText = $(this).closest('tr').find('td:first button').text().trim();
316
+ var parts = nameText.split(' ');
317
+ $('#board_first').val(parts[0] || '');
318
+ $('#board_last').val(parts.slice(1).join(' ') || '');
319
+ $('#board_school').val(d.team || '');
320
+ $('#board_throws').val(d.throws || '');
321
+ $('#board_height').val(d.height || '');
322
+ $('#board_weight').val(d.weight || '');
323
+ $('#board_position').val('P');
324
+ $('#board_notes').val('');
325
+ $('#board_modal_wrapper').show();
326
+ });
327
+ $(document).on('click', '#board_modal_close, #board_cancel_btn', function() {
328
+ $('#board_modal_wrapper').hide();
329
+ });
330
+ $(document).on('click', '#board_submit_btn', function() {
331
+ Shiny.setInputValue('board_submit', {
332
+ first: $('#board_first').val(),
333
+ last: $('#board_last').val(),
334
+ school: $('#board_school').val(),
335
+ position: $('#board_position').val(),
336
+ height: $('#board_height').val(),
337
+ weight: $('#board_weight').val(),
338
+ throws: $('#board_throws').val(),
339
+ notes: $('#board_notes').val(),
340
+ added_by: $('#board_added_by').val()
341
+ }, {priority: 'event'});
342
+ });
343
 
344
  // update min label
345
  Shiny.addCustomMessageHandler('update_label', function(msg) {
 
481
  tags$div("vs. Pitch Type", class = "section-label"),
482
  DTOutput("hitter_pitch_table")
483
  )
484
+ ),
485
+ tags$div(
486
+ id = "board_modal_wrapper", class = "modal-overlay", style = "display:none;",
487
+ tags$div(
488
+ class = "modal-box", style = "width:480px;",
489
+ tags$button("βœ•", id = "board_modal_close", class = "modal-close"),
490
+ tags$p("Add to Scout Board", class = "modal-title"),
491
+ tags$div(style = "display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-bottom:12px;",
492
+ tags$div(
493
+ tags$div("First Name", class = "filter-label"),
494
+ textInput("board_first", label = NULL, width = "100%")
495
+ ),
496
+ tags$div(
497
+ tags$div("Last Name", class = "filter-label"),
498
+ textInput("board_last", label = NULL, width = "100%")
499
+ ),
500
+ tags$div(
501
+ tags$div("School", class = "filter-label"),
502
+ textInput("board_school", label = NULL, width = "100%")
503
+ ),
504
+ tags$div(
505
+ tags$div("Position", class = "filter-label"),
506
+ textInput("board_position", label = NULL, value = "P", width = "100%")
507
+ ),
508
+ tags$div(
509
+ tags$div("Height", class = "filter-label"),
510
+ textInput("board_height", label = NULL, width = "100%")
511
+ ),
512
+ tags$div(
513
+ tags$div("Weight", class = "filter-label"),
514
+ textInput("board_weight", label = NULL, width = "100%")
515
+ ),
516
+ tags$div(
517
+ tags$div("Throws", class = "filter-label"),
518
+ textInput("board_throws", label = NULL, width = "100%")
519
+ ),
520
+ tags$div(
521
+ tags$div("Added By", class = "filter-label"),
522
+ textInput("board_added_by", label = NULL, width = "100%")
523
+ )
524
+ ),
525
+ tags$div(
526
+ tags$div("Notes", class = "filter-label"),
527
+ tags$textarea(id = "board_notes", style = "width:100%; height:80px;
528
+ border:1px solid #d0d0d0; border-radius:4px; padding:8px;
529
+ font-family:'Segoe UI',Arial,sans-serif; font-size:13px;",
530
+ placeholder = "Scouting notes...")
531
+ ),
532
+ tags$br(),
533
+ tags$div(style = "display:flex; gap:10px; justify-content:flex-end;",
534
+ tags$button("Cancel", id = "board_cancel_btn",
535
+ style = "background:#f0f0f0; border:none; border-radius:4px;
536
+ padding:8px 16px; cursor:pointer; font-size:13px;"),
537
+ tags$button("Submit", id = "board_submit_btn",
538
+ style = "background:#002147; color:white; border:none; border-radius:4px;
539
+ padding:8px 16px; cursor:pointer; font-size:13px; font-weight:600;")
540
+ ),
541
+ uiOutput("board_confirm_msg")
542
+ )
543
  )
544
  )
545
 
 
700
  scrollY = "65vh",
701
  scrollCollapse= TRUE,
702
  fixedHeader = TRUE,
703
+ order = list(list(11, "desc")),
704
  orderMulti = FALSE,
705
  dom = "lfrtip",
706
  columnDefs = list(
707
  list(className = "dt-left", targets = 0:1),
708
+ list(className = "dt-center", targets = 2:27),
709
+ list(orderable = FALSE, targets = c(4, 5, 6))
710
  ),
711
  initComplete = JS("function(settings, json) {
712
  $('#loading_msg').hide();
 
954
  observeEvent(input$hitter_modal_closed, {
955
  shinyjs::hide("hitter_modal_wrapper")
956
  })
957
+ # ── Scout board submission ────────────────────────────────────────────────────
958
+ observeEvent(input$board_submit, {
959
+ req(input$board_submit)
960
+ d <- input$board_submit
961
+
962
+ tryCatch({
963
+ sheet_data <- data.frame(
964
+ First = d$first,
965
+ Last = d$last,
966
+ School = d$school,
967
+ Position = d$position,
968
+ Height = d$height,
969
+ Weight = d$weight,
970
+ Throws = d$throws,
971
+ Notes = d$notes,
972
+ `Added By` = d$added_by,
973
+ `Date Added` = format(Sys.time(), "%Y-%m-%d %H:%M"),
974
+ check.names = FALSE
975
+ )
976
+
977
+ sheet_append(sheet_id, sheet_data)
978
+
979
+ output$board_confirm_msg <- renderUI({
980
+ tags$p("βœ… Added to Scout Board!",
981
+ style = "color:#00840D; font-weight:700; margin-top:10px; text-align:center;")
982
+ })
983
+
984
+ shinyjs::delay(2000, {
985
+ shinyjs::runjs("$('#board_modal_wrapper').hide();")
986
+ })
987
+
988
+ }, error = function(e) {
989
+ output$board_confirm_msg <- renderUI({
990
+ tags$p(paste("❌ Error:", e$message),
991
+ style = "color:#E1463E; font-weight:700; margin-top:10px;")
992
+ })
993
+ })
994
+ })
995
 
996
  batter_data <- reactive({
997
  req(current_batter(), raw_data())