HyannisHarborHawks commited on
Commit
1903e94
·
verified ·
1 Parent(s): 2a8b1f2

Update app.R

Browse files
Files changed (1) hide show
  1. app.R +150 -75
app.R CHANGED
@@ -494,98 +494,173 @@ clean_college_data <- function(data, teams = NA) {
494
 
495
 
496
  # ============================================================
497
- # CSS
498
  # ============================================================
499
- app_css <- "
500
- body { background:#f5f5f5; font-family:'Segoe UI',Arial,sans-serif; }
501
- .header { background:linear-gradient(135deg,#002855 0%,#164EA7 100%); color:#fff;
502
- padding:24px 30px; box-shadow:0 4px 6px rgba(0,0,0,.1); margin-bottom:24px;
503
- border-bottom:3px solid #F25B00; }
504
- .header h1 { margin:0; font-size:2em; font-weight:800; }
505
- .header p { margin:8px 0 0; opacity:.9; }
506
- .main-panel { background:#fff; border-radius:12px; padding:24px; box-shadow:0 2px 8px rgba(0,0,0,.1); }
507
- .btn-primary { background:#164EA7 !important; border:none !important; font-weight:700; }
508
- .btn-primary:hover { background:#002855 !important; }
509
- .btn-hf { background:#F25B00 !important; border:none !important; color:#fff !important; font-weight:700; }
510
- .status-box { background:#eef3fb; border-left:4px solid #164EA7; padding:14px; border-radius:4px;
511
- white-space:pre-wrap; font-family:monospace; font-size:13px; }
512
- "
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
513
 
514
  # ============================================================
515
- # UI
516
  # ============================================================
517
- login_ui <- fluidPage(
518
- tags$style(HTML("
519
- body { background:#f0f4f8; font-family:'Segoe UI',sans-serif; }
520
- .login-container { max-width:360px; margin:120px auto; background:#002855; padding:30px 25px;
521
- border-radius:8px; box-shadow:0 4px 15px #A1A1A4; text-align:center; color:#fff; }
522
- .btn-primary { background:#F25B00 !important; border-color:#F25B00 !important; color:#fff !important;
523
- font-weight:bold; width:100%; margin-top:10px; }
524
- .form-control { border-radius:4px; border:1.5px solid #164EA7 !important; }
525
- ")),
526
- div(class = "login-container",
527
- h3("Hyannis TrackMan Scraper"),
528
- passwordInput("login_password", "Password:"),
529
- actionButton("login", "Login", class = "btn-primary"),
530
- textOutput("wrong_pass")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
531
  )
532
  )
533
 
534
- app_ui <- fluidPage(
535
- tags$head(tags$style(HTML(app_css))),
536
- div(class = "header",
537
- h1("TrackMan Scraper \u2014 2026 Cape"),
538
- p("Scrape TrackMan PBP or Positional data and push it to the PastSeasonData dataset")
539
- ),
540
- fluidRow(
541
- column(4,
542
- div(class = "main-panel",
543
- h4("Controls", style = "color:#002855; margin-top:0;"),
544
- radioButtons("scrape_source", "Data Source",
545
- choices = c("TrackMan PBP" = "pbp", "TrackMan Positional" = "pos"),
546
- selected = "pbp"),
547
- dateInput("start_date", "Start Date:", value = Sys.Date() - 1),
548
- dateInput("end_date", "End Date:", value = Sys.Date() - 1),
549
- br(),
550
- actionButton("scrape_btn", "Scrape Data", class = "btn-primary", width = "100%"),
551
- br(), br(),
552
- downloadButton("download_scrape", "Download CSV", style = "width:100%;"),
553
- br(), br(),
554
- actionButton("upload_hf_btn", "Upload to HF Dataset", class = "btn-hf", width = "100%")
555
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
556
  ),
557
- column(8,
558
- div(class = "main-panel",
559
- h4("Progress", style = "color:#002855; margin-top:0;"),
560
- div(class = "status-box", textOutput("scrape_status")),
561
- hr(),
562
- h4("Data Preview", style = "color:#002855;"),
563
- DT::dataTableOutput("scrape_preview")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
564
  )
565
  )
566
- )
 
 
567
  )
568
 
569
- ui <- fluidPage(uiOutput("page"))
570
-
571
  # ============================================================
572
  # Server
573
  # ============================================================
574
  server <- function(input, output, session) {
575
 
576
- logged_in <- reactiveVal(FALSE)
577
-
578
- output$page <- renderUI({ if (logged_in()) app_ui else login_ui })
579
-
580
- observeEvent(input$login, {
581
- if (nzchar(PASSWORD) && identical(input$login_password, PASSWORD)) {
582
- logged_in(TRUE)
583
- output$wrong_pass <- renderText("")
584
- } else {
585
- output$wrong_pass <- renderText("Incorrect password, please try again.")
586
- }
587
- })
588
-
589
  scraped_data <- reactiveVal(NULL)
590
  scrape_polling <- reactiveVal(FALSE)
591
  scrape_status_msg <- reactiveVal("Ready.")
 
494
 
495
 
496
  # ============================================================
497
+ # Theme + CSS (Hawks look, inlined — no theme.R needed)
498
  # ============================================================
499
+ HAWKS_ORANGE <- "#F25B00"; HAWKS_BLUE <- "#164EA7"; HAWKS_DARK <- "#0D1B2A"
500
+ HAWKS_LIGHT <- "#F5F7FA"; HAWKS_GRAY <- "#6B7280"; HAWKS_BORDER <- "#E2E8F0"
501
+
502
+ hawks_theme <- bs_theme(
503
+ version = 5, bg = "#FFFFFF", fg = HAWKS_DARK,
504
+ primary = HAWKS_BLUE, secondary = HAWKS_ORANGE,
505
+ base_font = font_google("Inter"), heading_font = font_google("Inter"),
506
+ `border-radius` = "0.5rem", `font-size-base` = "0.925rem"
507
+ )
508
+
509
+ hawks_css <- tags$style(HTML(glue("
510
+ * {{ box-sizing:border-box; }}
511
+ body {{ background:{HAWKS_LIGHT}; font-family:'Inter',system-ui,sans-serif; margin:0; }}
512
+ .hawks-shell {{ display:flex; flex-direction:column; min-height:100vh; }}
513
+ .hawks-topbar {{ background:{HAWKS_DARK}; padding:0 24px; display:flex; align-items:center;
514
+ justify-content:space-between; border-bottom:3px solid {HAWKS_ORANGE}; }}
515
+ .topbar-brand {{ display:flex; align-items:center; gap:10px; padding:14px 0; }}
516
+ .topbar-brand .app-title {{ color:#fff; font-size:16px; font-weight:800; }}
517
+ .topbar-title {{ color:#fff; font-size:26px; font-weight:800; flex:1; text-align:center; white-space:nowrap; }}
518
+ .topbar-spacer {{ width:110px; }}
519
+ .hawks-content {{ flex:1; padding:24px; }}
520
+ .data-card {{ background:#fff; border:1px solid {HAWKS_BORDER}; border-radius:8px; overflow:hidden; }}
521
+ .data-card-hdr {{ padding:10px 14px; border-bottom:1px solid {HAWKS_BORDER};
522
+ display:flex; align-items:center; justify-content:space-between; }}
523
+ .data-card-title {{ font-size:11px; font-weight:700; color:{HAWKS_DARK};
524
+ text-transform:uppercase; letter-spacing:.05em; }}
525
+ .data-card-sub {{ font-size:10px; color:#9CA3AF; }}
526
+ .data-card-body {{ padding:16px; }}
527
+ .status-box {{ background:#eef3fb; border-left:4px solid {HAWKS_BLUE}; padding:14px;
528
+ border-radius:4px; white-space:pre-wrap; font-family:monospace; font-size:13px; color:{HAWKS_DARK}; }}
529
+ .btn-scrape {{ background:{HAWKS_BLUE} !important; border:none !important; color:#fff !important;
530
+ font-weight:700; width:100%; }}
531
+ .btn-scrape:hover {{ background:#0f3d82 !important; }}
532
+ .btn-hf {{ background:{HAWKS_ORANGE} !important; border:none !important; color:#fff !important;
533
+ font-weight:700; width:100%; }}
534
+ .btn-hf:hover {{ background:#d44f00 !important; }}
535
+ ")))
536
 
537
  # ============================================================
538
+ # Login overlay (JS, matches the other apps)
539
  # ============================================================
540
+ login_css <- tags$style(HTML("
541
+ #login_screen { position:fixed; inset:0; z-index:9999; display:flex; flex-direction:column;
542
+ align-items:center; justify-content:center;
543
+ background:linear-gradient(135deg,#001a3a 0%,#003366 40%,#0a4a8a 100%);
544
+ transition:opacity .8s ease, transform .8s ease; }
545
+ #login_screen.fade-out { opacity:0; transform:scale(1.02); pointer-events:none; }
546
+ #login_screen .login-logo { width:120px; height:120px; margin-bottom:20px; border-radius:50%;
547
+ object-fit:cover; border:3px solid rgba(255,165,0,.6); box-shadow:0 0 30px rgba(255,165,0,.2); }
548
+ #login_screen .login-title { font-size:42px; font-weight:800; color:#fff; letter-spacing:1.5px;
549
+ margin-bottom:6px; text-align:center; }
550
+ #login_screen .login-subtitle { font-size:16px; color:rgba(255,165,0,.7); letter-spacing:3px;
551
+ text-transform:uppercase; margin-bottom:40px; }
552
+ #login_screen .login-accent { width:60px; height:3px; background:#FF8C00; margin:0 auto 30px; border-radius:2px; }
553
+ #login_screen .login-box { background:rgba(255,255,255,.06); border:1px solid rgba(255,165,0,.2);
554
+ border-radius:12px; padding:32px 40px; backdrop-filter:blur(10px); width:340px; text-align:center; }
555
+ #login_screen .login-box input[type='password'] { width:100%; padding:12px 16px;
556
+ border:1px solid rgba(255,165,0,.3); border-radius:6px; background:rgba(255,255,255,.08);
557
+ color:#fff; font-size:15px; margin-bottom:16px; outline:none; text-align:center; letter-spacing:2px; }
558
+ #login_screen .login-box input[type='password']:focus { border-color:#FF8C00; }
559
+ #login_screen .login-btn { width:100%; padding:12px; border:none; border-radius:6px;
560
+ background:#FF8C00; color:#fff; font-size:14px; font-weight:700; letter-spacing:1px;
561
+ text-transform:uppercase; cursor:pointer; }
562
+ #login_screen .login-btn:hover { background:#e67e00; }
563
+ #login_screen .login-error { color:#ff6b6b; font-size:13px; margin-top:12px; min-height:20px; }
564
+ "))
565
+
566
+ login_overlay <- tags$div(id = "login_screen",
567
+ tags$img(class = "login-logo", src = logo_uri),
568
+ tags$div(class = "login-title", "TrackMan Scraper"),
569
+ tags$div(class = "login-accent"),
570
+ tags$div(class = "login-subtitle", "Hyannis Harbor Hawks"),
571
+ tags$div(class = "login-box",
572
+ tags$input(id = "login_pw", type = "password", placeholder = "Enter password"),
573
+ tags$button(id = "login_btn", class = "login-btn", "Enter"),
574
+ tags$div(id = "login_error", class = "login-error")
575
  )
576
  )
577
 
578
+ login_js <- tags$script(HTML(paste0("
579
+ var APP_PASSWORD = '", PASSWORD, "';
580
+ function tryLogin() {
581
+ var pw = document.getElementById('login_pw').value;
582
+ if (pw === APP_PASSWORD) {
583
+ document.getElementById('login_screen').classList.add('fade-out');
584
+ setTimeout(function(){ document.getElementById('login_screen').style.display='none'; }, 800);
585
+ } else {
586
+ document.getElementById('login_error').textContent = 'Incorrect password';
587
+ document.getElementById('login_pw').value = '';
588
+ }
589
+ }
590
+ document.addEventListener('DOMContentLoaded', function(){
591
+ document.getElementById('login_btn').addEventListener('click', tryLogin);
592
+ document.getElementById('login_pw').addEventListener('keydown', function(e){
593
+ if (e.key === 'Enter') tryLogin();
594
+ });
595
+ });
596
+ ")))
597
+
598
+ # ============================================================
599
+ # UI
600
+ # ============================================================
601
+ ui <- fluidPage(
602
+ theme = hawks_theme,
603
+ hawks_css,
604
+ login_css,
605
+ tags$div(class = "hawks-shell",
606
+ tags$div(class = "hawks-topbar",
607
+ tags$div(class = "topbar-brand",
608
+ tags$svg(width = "24", height = "24", viewBox = "0 0 30 30", fill = "none",
609
+ tags$polygon(points = "15,2 28,26 15,21 2,26", fill = HAWKS_ORANGE, opacity = "0.9"),
610
+ tags$polygon(points = "15,2 15,21 2,26", fill = "#fff", opacity = "0.18")),
611
+ tags$span(class = "app-title", "Scraper")
612
+ ),
613
+ tags$div(class = "topbar-title", "TrackMan Scraper \u2014 2026 Cape"),
614
+ tags$div(class = "topbar-spacer")
615
  ),
616
+ tags$div(class = "hawks-content",
617
+ fluidRow(
618
+ column(4,
619
+ tags$div(class = "data-card",
620
+ tags$div(class = "data-card-hdr",
621
+ tags$span(class = "data-card-title", "Controls"),
622
+ tags$span(class = "data-card-sub", "Source & dates")),
623
+ tags$div(class = "data-card-body",
624
+ radioButtons("scrape_source", "Data Source",
625
+ choices = c("TrackMan PBP" = "pbp", "TrackMan Positional" = "pos"), selected = "pbp"),
626
+ dateInput("start_date", "Start Date:", value = Sys.Date() - 1),
627
+ dateInput("end_date", "End Date:", value = Sys.Date() - 1),
628
+ br(),
629
+ actionButton("scrape_btn", "Scrape Data", class = "btn-scrape"),
630
+ br(), br(),
631
+ downloadButton("download_scrape", "Download CSV", style = "width:100%;"),
632
+ br(), br(),
633
+ actionButton("upload_hf_btn", "Upload to HF Dataset", class = "btn-hf")
634
+ )
635
+ )
636
+ ),
637
+ column(8,
638
+ tags$div(class = "data-card", style = "margin-bottom:16px;",
639
+ tags$div(class = "data-card-hdr",
640
+ tags$span(class = "data-card-title", "Progress"),
641
+ tags$span(class = "data-card-sub", "Live status")),
642
+ tags$div(class = "data-card-body",
643
+ tags$div(class = "status-box", textOutput("scrape_status")))
644
+ ),
645
+ tags$div(class = "data-card",
646
+ tags$div(class = "data-card-hdr",
647
+ tags$span(class = "data-card-title", "Data Preview"),
648
+ tags$span(class = "data-card-sub", "Scraped rows")),
649
+ tags$div(class = "data-card-body", DT::dataTableOutput("scrape_preview"))
650
+ )
651
+ )
652
  )
653
  )
654
+ ),
655
+ login_overlay,
656
+ login_js
657
  )
658
 
 
 
659
  # ============================================================
660
  # Server
661
  # ============================================================
662
  server <- function(input, output, session) {
663
 
 
 
 
 
 
 
 
 
 
 
 
 
 
664
  scraped_data <- reactiveVal(NULL)
665
  scrape_polling <- reactiveVal(FALSE)
666
  scrape_status_msg <- reactiveVal("Ready.")