From 872f371f502f22e2b6f5600139187c92b2ca2f58 Mon Sep 17 00:00:00 2001 From: marisa07 Date: Thu, 5 Mar 2026 11:18:57 -0800 Subject: [PATCH 01/12] qual scouting app - initial test --- 2026/qual_config.js | 59 +++ pit.html | 126 ++--- qual.html | 429 ++++++++++++++++ resources/css/scoutingPASS.css | 867 +++++++++++++++++++-------------- 4 files changed, 1059 insertions(+), 422 deletions(-) create mode 100644 2026/qual_config.js create mode 100644 qual.html diff --git a/2026/qual_config.js b/2026/qual_config.js new file mode 100644 index 000000000..65188ff66 --- /dev/null +++ b/2026/qual_config.js @@ -0,0 +1,59 @@ +var qualitativeConfig = ` +{ + "title": "Qualitative Scouting", + "page_title": "Robot Behavior", + "dataFormat": "tsv", + "checkboxAs": "10", + "prematch": { + "title": "Pre-Match", + "fields": [ + { "name": "Scouter", "code": "s", "type": "text", "size": 10, "maxSize": 20, "subtype": "scouter" }, + { "name": "Event", "code": "e", "type": "text", "size": 10, "maxSize": 20, "subtype": "event", "defaultValue": "2026TXHOU", "disabled": "true" }, + { "name": "Match", "code": "m", "type": "number", "size": 3, "maxSize": 3 }, + { "name": "Team #", "code": "t", "type": "number", "size": 5, "maxSize": 5 } + ] + }, + "sections": [ + { + "title": "Robot Behavior", + "fields": [ + { + "name": "Driving Style", + "code": "ds", + "type": "radio", + "choices": { "ag": "Aggressive", "def": "Defensive", "cau": "Cautious", "oth": "Other" } + }, + { + "name": "Defense Quality", + "code": "dq", + "type": "radio", + "choices": { "ex": "Excellent", "gd": "Good", "fa": "Fair", "po": "Poor", "na": "N/A" } + }, + { + "name": "Recovery from Faults", + "code": "rf", + "type": "radio", + "choices": { "qu": "Quick", "sl": "Slow", "no": "No Recovery" } + }, + { + "name": "Plays Well with Alliance", + "code": "pa", + "type": "checkbox" + }, + { + "name": "Frequently Disabled/Tipped", + "code": "fd", + "type": "checkbox" + }, + { + "name": "Notes", + "code": "n", + "type": "text", + "size": 40, + "maxSize": 200 + } + ] + } + ] +} +`; \ No newline at end of file diff --git a/pit.html b/pit.html index 11afb3a97..de662c1c2 100644 --- a/pit.html +++ b/pit.html @@ -1,63 +1,63 @@ - - - - - Scouting PASS - - - - - - - - - - - -
-
-
-

PWNAGE
Scouting PASS

-

Pit Scouting

-

- - -
-

-
- -
-

PWnAGE
Scouting PASS

-

Generate QR Code

-

-

- - -
  -
- -
-
 
  -
-

-
-
 
-

-

-
- -
-
- -
-
-
-
- -
- - - + + + + + Scouting PASS + + + + + + + + + + + +
+
+
+

PWNAGE
Scouting PASS

+

Pit Scouting

+

+ + +
+

+
+ +
+

PWnAGE
Scouting PASS

+

Generate QR Code

+

+

+ + +
  +
+ +
+
 
  +
+

+
+
 
+

+

+
+ +
+
+ +
+
+
+
+ +
+ + + diff --git a/qual.html b/qual.html new file mode 100644 index 000000000..f9209bc53 --- /dev/null +++ b/qual.html @@ -0,0 +1,429 @@ + + + + + Scouting PASS + + + + + + + +
+
+ + +
+

PWNAGE
Scouting PASS

+

Qualitative Scouting

+

+ + + + + + + + + + + + + + + + + + + + +
Scouter
Event Code
Match #
+ +
+ + + +

+
+ + +
+

PWnAGE
Scouting PASS

+

Tier & Ranking

+

+ + +

+ + +
+

Assign each team a tier. If 2+ teams share a tier, drag to rank them against each other (optional).

+
+
+

+ + +

+
+ + +
+

PWnAGE
Scouting PASS

+

Generate QR Code

+

+

+ + + + + + + + + + + +
 
 
 
 
+

+

+ + +

+
+ +
+
+
+ +
+
+ + + + diff --git a/resources/css/scoutingPASS.css b/resources/css/scoutingPASS.css index a1cb449c0..861e8ef7a 100644 --- a/resources/css/scoutingPASS.css +++ b/resources/css/scoutingPASS.css @@ -1,359 +1,508 @@ -/* @font-face { - font-family: 'Roboto'; - src: url('./fonts/Roboto-Regular.ttf') format('truetype'); - font-weight: 400; - font-style: normal; - font-display: swap; -} -*/ - -html, body{ - height: 100%; - width: 100%; - margin: 0; - padding: 0; - color: white; - background-color: black; - font-family: Roboto, sans-serif; -} - -#prematch_table, #postmatch_table { - - font-family : Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif !important; - text-align: center; - color: white; - font-size: 13px; - font-style: bold; - font-weight: 700; - letter-spacing: 1px; - border-width: 4px; - border-color: white; -} - -#auton_table, #endgame_table, #teleop_table { - font-family : Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif !important; - color: black; - font-size: 13px; - font-style: bold; - font-weight: 700; - letter-spacing: 1px; - border-width: 4px; - border-color: black; -} - - -.button { - font-family: Roboto; - font-size: 16px; - font-weight: 500; -} - -.counter-button { - width: 40px; - height: 40px; - font-size: 14px; - padding: 5px; - border-radius: 4px; - background-color: #f6faff; - color: black; - border: black; - border-width: 1px; - border-style: solid; - cursor: pointer; - - display: flex; - justify-content: center; - align-items: center; -} - -/* hide the actual radio input */ -.radio-btn input[type="radio"] { - display: none; -} - -.radio-btn span { - display: inline-block; - padding: 6px 12px; /* size of the box */ - margin: 2px; /* spacing between buttons */ - border: 1px solid #444; /* box border */ - border-radius: 4px; /* rounded corners */ - cursor: pointer; - user-select: none; - text-align: center; -} - -/* highlight when selected */ -.radio-btn input[type="radio"]:checked + span { - background-color: rgb(239, 239, 236); - color: black; -} - -.radio-btn input[type="radio"][value^="r"] + span { - border-color: #d9534f; - border-style: solid; - border-width: 1px; - color: white; -} - -.radio-btn input[type="radio"][value^="b"] + span { - border-color: #0275d8; - border-style: solid; - border-width: 1px; - color: white; -} - -.radio-btn input[type="radio"][value^="r"]:checked + span { - background-color: #d9534f; - border-color: #d9534f; - border-style: solid; - border-width: 5px; - color: white; -} - -.radio-btn input[type="radio"][value^="b"]:checked + span { - background-color: #0275d8; - border-color: #0275d8; - border-style: solid; - border-width: 5px; - color: white; -} - -/* Style the label to look like a button */ -.check-btn { - display: inline-block; - padding: 15px 15px; - margin: 5px; - border-radius: 4px; - border-color: black; - border-style: solid; - border-width: 1px; - cursor: pointer; - color: white; - text-align: center; - user-select: none; /* Disable text selection */ -} - -@media (max-width: 768px) { - .check-btn { - padding: 4vw 6vw; /* Adjust padding for mobile */ - } -} - -/* Ensure no default checkbox styles */ -input[type="checkbox"] { - display: none; /* Hide the checkbox, it will be visually represented by the label */ -} - -#autonHeader, #endgameHeader, #teleopHeader1, #teleopHeader2{ - color: black; - border-color: black; - border-width: 4px; -} - -#postmatchHeader{ - border-width: 4px; - border-color: white; -} - -#prematchHeader1, #prematchHeader2{ - color: white; - border-color: white; - border-width: 4px; -} - -#qrHeader1, #qrHeader2 { - color: black; -} - -.field-image { - margin: 2vw; -} - -.field-image-src { - width: 225px; - max-width: 500px; - height: auto; - background-position: center; - border: 1px solid; -} - -.main-panel{ - display: none; - height: 100%; - min-height: 100%; - width: 100vw; - position: absolute; - left: 0; - top: 0; - color: white; - background-color: black; - font-family : Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif !important; - text-align: justify; -} - -#prematch{ - display: table; -} - -#auton{ -} - -#teleop{ -} - -#endgame{ -} - -#postmatch{ -} - -#qr-code{ - text-align: center; - color: black; -} - -h1{ - font-family: Roboto; - text-align: center; - border-style: groove; - border-color: white; - border-width: 2px; - font-size: 40px; - padding: 10px; - font-weight: 500; -} - -#page2, #page4 { - color: black; -} - -h2{ - font-family: Roboto; - text-align: center; - font-size: 30px; - font-weight: 400; - font-style: italic; -} - -.match-label{ - text-align: center; -} - -.half-button-l{ - width: 48%; - margin: 1vw; - height: 100%; - font: roboto; -} - -.half-button-r{ - flex-grow: 1; - height: 100%; - width: 48%; - margin: 1vw; - font: roboto; -} - -.half-button-container{ - display: flex; -} - -#nextButton1, #nextButton2, #nextButton9, #nextButton10, .clearForm, .submitForm, #displayButton, #copyButton { - font-family: Roboto; - font-weight: 500; - padding: 10px; - background-color: rgb(151, 199, 240); - text-decoration: none; - display: inline-block; -} - -#nextButton3, #nextButton4, #nextButton5, #nextButton6, #nextButton7, #nextButton8 { - font-family: Roboto; - font-weight: 500; - background-color: black; - padding: 10px; - color: white; -} - -.undoButton, .flipButton { - font-family: Roboto; - font-weight: 500; - background-color: black; - padding: 10px; - color: white; - margin-left: 20px; -} - -#prevButton7, #prevButton8, #prevButton9{ - font-family: Roboto; - font-weight: 500; - background-color: rgb(151, 199, 240); - padding: 10px; -} - -#prevButton1, #prevButton2, #prevButton3, #prevButton4, #prevButton5, #prevButton6{ - font-family: Roboto; - font-weight: 500; - background-color: black; - padding: 10px; - color: white; - -} - -#prematch_table{ - font: Roboto; - font-weight: 500; - text-align: center; -} - - -h1 .odd { - font-family: Roboto; - font-weight: 500; - text-align: center; - color: #0AB4EC; -} - -h1 .even { - font-family: Roboto; - font-weight: 500; - text-align: center; - color: #42B333; -} - -table { - border: 1px solid black; - width: 100%; -} - -td { - padding: 10px 4px; -} - -input { - font-size: 16px; - font: roboto; - color: black; - background-color: white; -} - -.title { - width: "50%"; - text-align: right; -} - -.field { - width: "50%"; - text-align: left; -} - -#qr-table { - text-align: center; - color: black; - background-color: white; -} - -#data { - font-family:'Lucida Console', 'monospace'; - text-align: justify; -} +/* @font-face { + font-family: 'Roboto'; + src: url('./fonts/Roboto-Regular.ttf') format('truetype'); + font-weight: 400; + font-style: normal; + font-display: swap; +} +*/ + +html, body{ + height: 100%; + width: 100%; + margin: 0; + padding: 0; + color: white; + background-color: black; + font-family: Roboto, sans-serif; +} + +#prematch_table, #postmatch_table { + + font-family : Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif !important; + text-align: center; + color: white; + font-size: 13px; + font-style: bold; + font-weight: 700; + letter-spacing: 1px; + border-width: 4px; + border-color: white; +} + +#auton_table, #endgame_table, #teleop_table { + font-family : Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif !important; + color: black; + font-size: 13px; + font-style: bold; + font-weight: 700; + letter-spacing: 1px; + border-width: 4px; + border-color: black; +} + + +.button { + font-family: Roboto; + font-size: 16px; + font-weight: 500; +} + +.counter-button { + width: 40px; + height: 40px; + font-size: 14px; + padding: 5px; + border-radius: 4px; + background-color: #f6faff; + color: black; + border: black; + border-width: 1px; + border-style: solid; + cursor: pointer; + + display: flex; + justify-content: center; + align-items: center; +} + +/* hide the actual radio input */ +.radio-btn input[type="radio"] { + display: none; +} + +.radio-btn span { + display: inline-block; + padding: 6px 12px; /* size of the box */ + margin: 2px; /* spacing between buttons */ + border: 1px solid #444; /* box border */ + border-radius: 4px; /* rounded corners */ + cursor: pointer; + user-select: none; + text-align: center; +} + +/* highlight when selected */ +.radio-btn input[type="radio"]:checked + span { + background-color: rgb(239, 239, 236); + color: black; +} + +.radio-btn input[type="radio"][value^="r"] + span { + border-color: #d9534f; + border-style: solid; + border-width: 1px; + color: white; +} + +.radio-btn input[type="radio"][value^="b"] + span { + border-color: #0275d8; + border-style: solid; + border-width: 1px; + color: white; +} + +.radio-btn input[type="radio"][value^="r"]:checked + span { + background-color: #d9534f; + border-color: #d9534f; + border-style: solid; + border-width: 5px; + color: white; +} + +.radio-btn input[type="radio"][value^="b"]:checked + span { + background-color: #0275d8; + border-color: #0275d8; + border-style: solid; + border-width: 5px; + color: white; +} + +/* Style the label to look like a button */ +.check-btn { + display: inline-block; + padding: 15px 15px; + margin: 5px; + border-radius: 4px; + border-color: black; + border-style: solid; + border-width: 1px; + cursor: pointer; + color: white; + text-align: center; + user-select: none; /* Disable text selection */ +} + +@media (max-width: 768px) { + .check-btn { + padding: 4vw 6vw; /* Adjust padding for mobile */ + } +} + +/* Ensure no default checkbox styles */ +input[type="checkbox"] { + display: none; /* Hide the checkbox, it will be visually represented by the label */ +} + +#autonHeader, #endgameHeader, #teleopHeader1, #teleopHeader2{ + color: black; + border-color: black; + border-width: 4px; +} + +#postmatchHeader{ + border-width: 4px; + border-color: white; +} + +#prematchHeader1, #prematchHeader2{ + color: white; + border-color: white; + border-width: 4px; +} + +#qrHeader1, #qrHeader2 { + color: black; +} + +.field-image { + margin: 2vw; +} + +.field-image-src { + width: 225px; + max-width: 500px; + height: auto; + background-position: center; + border: 1px solid; +} + +.main-panel{ + display: none; + height: 100%; + min-height: 100%; + width: 100vw; + position: absolute; + left: 0; + top: 0; + color: white; + background-color: black; + font-family : Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif !important; + text-align: justify; +} + +#prematch{ + display: table; +} + +#auton{ +} + +#teleop{ +} + +#endgame{ +} + +#postmatch{ +} + +#qr-code{ + text-align: center; + color: black; +} + +h1{ + font-family: Roboto; + text-align: center; + border-style: groove; + border-color: white; + border-width: 2px; + font-size: 40px; + padding: 10px; + font-weight: 500; +} + +#page2, #page4 { + color: black; +} + +h2{ + font-family: Roboto; + text-align: center; + font-size: 30px; + font-weight: 400; + font-style: italic; +} + +.match-label{ + text-align: center; +} + +.half-button-l{ + width: 48%; + margin: 1vw; + height: 100%; + font: roboto; +} + +.half-button-r{ + flex-grow: 1; + height: 100%; + width: 48%; + margin: 1vw; + font: roboto; +} + +.half-button-container{ + display: flex; +} + +#nextButton1, #nextButton2, #nextButton9, #nextButton10, .clearForm, .submitForm, #displayButton, #copyButton { + font-family: Roboto; + font-weight: 500; + padding: 10px; + background-color: rgb(151, 199, 240); + text-decoration: none; + display: inline-block; +} + +#nextButton3, #nextButton4, #nextButton5, #nextButton6, #nextButton7, #nextButton8 { + font-family: Roboto; + font-weight: 500; + background-color: black; + padding: 10px; + color: white; +} + +.undoButton, .flipButton { + font-family: Roboto; + font-weight: 500; + background-color: black; + padding: 10px; + color: white; + margin-left: 20px; +} + +#prevButton7, #prevButton8, #prevButton9{ + font-family: Roboto; + font-weight: 500; + background-color: rgb(151, 199, 240); + padding: 10px; +} + +#prevButton1, #prevButton2, #prevButton3, #prevButton4, #prevButton5, #prevButton6{ + font-family: Roboto; + font-weight: 500; + background-color: black; + padding: 10px; + color: white; + +} + +#prematch_table{ + font: Roboto; + font-weight: 500; + text-align: center; +} + + +h1 .odd { + font-family: Roboto; + font-weight: 500; + text-align: center; + color: #0AB4EC; +} + +h1 .even { + font-family: Roboto; + font-weight: 500; + text-align: center; + color: #42B333; +} + +table { + border: 1px solid black; + width: 100%; +} + +td { + padding: 10px 4px; +} + +input { + font-size: 16px; + font: roboto; + color: black; + background-color: white; +} + +.title { + width: "50%"; + text-align: right; +} + +.field { + width: "50%"; + text-align: left; +} + +#qr-table { + text-align: center; + color: black; + background-color: white; +} + +#data { + font-family:'Lucida Console', 'monospace'; + text-align: justify; +} + +/* ── Qualitative scouting additions ─────────────────────────────────────── */ + +.qual-status-msg { + font-style: italic; + font-size: 0.9em; + text-align: center; + padding: 4px 0 8px; + min-height: 1.4em; +} + +.qual-hint { + font-size: 0.85em; + font-style: italic; + color: #ccc; + margin: 0 0 10px; +} + +.qual-divider { + border: none; + border-top: 1px solid #666; + margin: 6px 0; +} + +.qual-team-header { + font-weight: bold; + font-size: 1.0em; + padding: 8px 4px 4px; +} + +.qual-red { color: #ff6666; } +.qual-blue { color: #6699ff; } + +.qual-label { + font-weight: bold; + font-size: 0.9em; +} + +.qual-radio { + font-size: 0.88em; + margin-right: 6px; + white-space: nowrap; +} + +.qual-radio input[type="radio"] { + display: inline; /* override the hidden rule for checkboxes */ + width: auto; +} + +.qual-textarea { + width: 98%; + font-size: 0.9em; + padding: 4px; + box-sizing: border-box; + background-color: white; + color: black; +} + +/* Tier row */ +.qual-tier-row { + display: flex; + align-items: center; + gap: 8px; + margin-bottom: 8px; + flex-wrap: wrap; +} + +.qual-tier-team { + font-weight: bold; + font-size: 0.9em; + min-width: 130px; +} + +.qual-tier-pills { + display: flex; + gap: 4px; + flex-wrap: wrap; +} + +.qual-tier-pill { + padding: 4px 9px; + border: 1px solid #888; + border-radius: 4px; + font-size: 0.8em; + cursor: pointer; + user-select: none; + color: white; +} + +.qual-tier-pill input[type="radio"] { + display: none; +} + +.qual-tier-pill:has(input:checked) { + background-color: rgb(151, 199, 240); + color: black; + border-color: white; +} + +/* Within-tier drag ranking */ +.qual-rank-group { + margin: 14px 0 6px; +} + +.qual-rank-group-label { + font-size: 0.82em; + font-style: italic; + color: #bbb; + margin-bottom: 4px; +} + +.qual-rank-list { + list-style: none; + padding: 0; + margin: 0; +} + +.qual-rank-item { + display: flex; + align-items: center; + gap: 8px; + border: 1px solid #888; + border-radius: 4px; + padding: 6px 10px; + margin-bottom: 5px; + cursor: grab; + font-size: 0.92em; + background-color: rgba(255,255,255,0.08); + touch-action: none; +} + +.qual-rank-item:active { cursor: grabbing; } + +.qual-rank-item.qual-dragging { opacity: 0.4; } + +.qual-rank-item.qual-drag-over { + border: 1px dashed #fff; + background-color: rgba(255,255,255,0.22); +} + +.qual-drag-handle { + opacity: 0.5; + font-size: 1.1em; +} + +.qual-rank-name { + font-size: 0.85em; + opacity: 0.75; +} \ No newline at end of file From 9189e033939ddfd090532cf80c4163a872b6b185 Mon Sep 17 00:00:00 2001 From: marisa07 Date: Thu, 5 Mar 2026 11:57:39 -0800 Subject: [PATCH 02/12] remove defense and shuffling rating and center --- qual.html | 50 +++++++++++++++------------------- resources/css/scoutingPASS.css | 4 +-- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/qual.html b/qual.html index f9209bc53..9d1d8764b 100644 --- a/qual.html +++ b/qual.html @@ -14,19 +14,19 @@
-
-

PWNAGE
Scouting PASS

+
+

Golden Gate Robotics

Qualitative Scouting

-

+

- +
Scouter Event Code - + Match # @@ -46,16 +46,20 @@

Qualitative Scouting

-

+ +

-
-

PWnAGE
Scouting PASS

+
+

Golden Gate Robotics

Tier & Ranking

- - + +

+

+

@@ -64,16 +68,19 @@

Tier & Ranking

- - + +

+

-
-

PWnAGE
Scouting PASS

+
+

Golden Gate Robotics

Generate QR Code

-

+ +

@@ -182,19 +189,6 @@

Generate QR Code

+ " — "+team.number+"" + (team.name ? " · "+team.name : ""); - const defRow = tbl.insertRow(); - defRow.insertCell().innerHTML = "Defense"; - const defTd = defRow.insertCell(); - ["None","Poor","Fair","Good","Excellent"].forEach(v => { - defTd.innerHTML += ` `; - }); - - const shtRow = tbl.insertRow(); - shtRow.insertCell().innerHTML = "Pass/Shoot"; - const shtTd = shtRow.insertCell(); - ["None","Weak","Average","Strong","Elite"].forEach(v => { - shtTd.innerHTML += ` `; - }); const cRow = tbl.insertRow(); cRow.insertCell().innerHTML = "Comments"; diff --git a/resources/css/scoutingPASS.css b/resources/css/scoutingPASS.css index 861e8ef7a..02cbbb6f5 100644 --- a/resources/css/scoutingPASS.css +++ b/resources/css/scoutingPASS.css @@ -311,14 +311,14 @@ h1 .odd { font-family: Roboto; font-weight: 500; text-align: center; - color: #0AB4EC; + color: #C0362C; } h1 .even { font-family: Roboto; font-weight: 500; text-align: center; - color: #42B333; + color: #C0362C; } table { From ce7f3a6b29e51de4493dfa5a2e60a3de3fe59e35 Mon Sep 17 00:00:00 2001 From: marisa07 Date: Thu, 5 Mar 2026 12:01:41 -0800 Subject: [PATCH 03/12] remove extra divs --- qual.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qual.html b/qual.html index 9d1d8764b..8b0331ba2 100644 --- a/qual.html +++ b/qual.html @@ -14,12 +14,12 @@
-
+

Golden Gate Robotics

Qualitative Scouting

-
+ @@ -51,7 +51,7 @@

Qualitative Scouting

-
+

Golden Gate Robotics

Tier & Ranking

From f0447efa78f0fccc87481e400c64c954b4b3be82 Mon Sep 17 00:00:00 2001 From: marisa07 Date: Thu, 5 Mar 2026 12:03:32 -0800 Subject: [PATCH 04/12] correct buttons and divs --- qual.html | 40 +++++++++++++--------------------------- 1 file changed, 13 insertions(+), 27 deletions(-) diff --git a/qual.html b/qual.html index 8b0331ba2..24f2f024e 100644 --- a/qual.html +++ b/qual.html @@ -17,7 +17,7 @@

Golden Gate Robotics

Qualitative Scouting

-

+

Scouter
@@ -43,44 +43,37 @@

Qualitative Scouting

- -

+

-
+

Golden Gate Robotics

Tier & Ranking

- -

-

- + +

- -
+ +

Assign each team a tier. If 2+ teams share a tier, drag to rank them against each other (optional).

- -

-

+ +

-
+

Golden Gate Robotics

Generate QR Code

- -

+

@@ -189,7 +182,6 @@

Generate QR Code

+ " — "+team.number+"" + (team.name ? " · "+team.name : ""); - const cRow = tbl.insertRow(); cRow.insertCell().innerHTML = "Comments"; cRow.insertCell().innerHTML = ``; @@ -244,7 +236,6 @@

Generate QR Code

function qualRenderRankGroups() { const div = document.getElementById("qual-rank-groups"); - // Snapshot existing order before re-render const prevOrder = {}; div.querySelectorAll(".qual-rank-list").forEach(list => { prevOrder[list.dataset.tier] = Array.from(list.querySelectorAll(".qual-rank-item")).map(el=>el.dataset.num); @@ -274,7 +265,6 @@

Generate QR Code

list.className = "qual-rank-list"; list.dataset.tier = tier; - // Preserve prior drag order if available let ordered = groups[tier]; if (prevOrder[tier]) { const reordered = prevOrder[tier].map(n=>groups[tier].find(t=>t.number===n)).filter(Boolean); @@ -301,7 +291,6 @@

Generate QR Code

let qualDragSrc = null; function qualAddDragHandlers(li, list) { - // Mouse li.addEventListener("dragstart", e => { qualDragSrc = li; li.classList.add("qual-dragging"); @@ -328,7 +317,6 @@

Generate QR Code

else list.insertBefore(qualDragSrc, li); } }); - // Touch li.addEventListener("touchstart", e => { qualDragSrc = li; li.classList.add("qual-dragging"); @@ -363,11 +351,9 @@

Generate QR Code

let out = ["qs", scouter, event, match].join("\t"); qualTeams.forEach(t => { - const def = (document.querySelector(`input[name="qdef-${t.number}"]:checked`)||{}).value||""; - const sht = (document.querySelector(`input[name="qsht-${t.number}"]:checked`)||{}).value||""; const tier = qualGetTier(t.number); const notes = (document.getElementById("qnotes-"+t.number)?.value||"").replace(/[\t\n\r]/g," "); - out += "\n" + [t.number, t.alliance[0].toUpperCase(), tier, def, sht, notes].join("\t"); + out += "\n" + [t.number, t.alliance[0].toUpperCase(), tier, notes].join("\t"); }); document.querySelectorAll(".qual-rank-list").forEach(list => { From 3dae0946b915f1700e6aab89a0fba46b539fb52d Mon Sep 17 00:00:00 2001 From: marisa07 Date: Thu, 5 Mar 2026 12:12:43 -0800 Subject: [PATCH 05/12] line up tier labels --- resources/css/scoutingPASS.css | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/css/scoutingPASS.css b/resources/css/scoutingPASS.css index 02cbbb6f5..544786d11 100644 --- a/resources/css/scoutingPASS.css +++ b/resources/css/scoutingPASS.css @@ -417,11 +417,11 @@ input { /* Tier row */ .qual-tier-row { - display: flex; + display: grid; + grid-template-columns: 140px repeat(5, 1fr); align-items: center; - gap: 8px; - margin-bottom: 8px; - flex-wrap: wrap; + gap: 4px; + margin-bottom: 6px; } .qual-tier-team { @@ -431,12 +431,12 @@ input { } .qual-tier-pills { - display: flex; - gap: 4px; - flex-wrap: wrap; + display: contents; /* lets each pill sit directly in the grid */ } .qual-tier-pill { + display: block; + text-align: center; padding: 4px 9px; border: 1px solid #888; border-radius: 4px; From ea9b08e9104e373f6da70b78a258af7f6e4c29a9 Mon Sep 17 00:00:00 2001 From: marisa07 Date: Thu, 5 Mar 2026 12:21:18 -0800 Subject: [PATCH 06/12] add google sheets send --- qual.html | 19 ++++++++++++++++++- resources/js/googleSheets.js | 4 +++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/qual.html b/qual.html index 24f2f024e..9a8ddc0ca 100644 --- a/qual.html +++ b/qual.html @@ -7,6 +7,7 @@ + @@ -92,6 +93,9 @@

Generate QR Code

+
+ +
@@ -393,7 +397,18 @@

Generate QR Code

function qualClearForm() { if (!confirm("Clear all qualitative scouting data?")) return; - ["q-scouter","q-match"].forEach(id => document.getElementById(id).value=""); + + // Save values we want to keep + const scouter = document.getElementById("q-scouter").value; + const event = document.getElementById("q-event").value; + const match = parseInt(document.getElementById("q-match").value) || 0; + + // Restore them after clear + document.getElementById("q-scouter").value = scouter; + document.getElementById("q-event").value = event; + document.getElementById("q-match").value = match + 1; + + // Rest of your existing clear code... qualTeams = []; document.getElementById("qual-teams-section").style.display = "none"; document.getElementById("qual-teams-table").innerHTML = ""; @@ -404,6 +419,8 @@

Generate QR Code

qualSetStatus("", false); qualShowPage(0); } + + setUpGoogleSheets(); diff --git a/resources/js/googleSheets.js b/resources/js/googleSheets.js index 99cdb646c..550b5772a 100644 --- a/resources/js/googleSheets.js +++ b/resources/js/googleSheets.js @@ -11,7 +11,9 @@ function setUpGoogleSheets() { let fd = getData("tsv"); if (pitScouting) { - fd = "PIT\t" + fd; + fd = "PIT\t" + fd; + } else if (typeof qualCollectData === 'function') { + fd = "QUAL\t" + qualCollectData(); } fetch(scriptURL, { method: "POST", From c7d7f1ca99af4ce9e6973daa3a45dc0d3fd1561a Mon Sep 17 00:00:00 2001 From: marisa07 Date: Thu, 5 Mar 2026 12:29:24 -0800 Subject: [PATCH 07/12] fix send to google sheets --- qual.html | 4 ++-- resources/js/googleSheets.js | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/qual.html b/qual.html index 9a8ddc0ca..05fe9e1cf 100644 --- a/qual.html +++ b/qual.html @@ -7,11 +7,10 @@ - -
+
@@ -422,5 +421,6 @@

Generate QR Code

setUpGoogleSheets(); + diff --git a/resources/js/googleSheets.js b/resources/js/googleSheets.js index 550b5772a..6175d66b2 100644 --- a/resources/js/googleSheets.js +++ b/resources/js/googleSheets.js @@ -2,11 +2,12 @@ function setUpGoogleSheets() { const scriptURL = 'https://script.google.com/macros/s/AKfycbxYpArzIdDBCy3ZZmjH2KZTErAfJWl0OsqWO_46VfBQaPsyqC0CuW3rL8fK_Qp80zE/exec' const form = document.querySelector('#scoutingForm') const btn = document.querySelector('#submit') - - + + if (!form || !btn) return; + form.addEventListener('submit', e => { e.preventDefault() - btn.disabled = false + btn.disabled = true btn.innerHTML = "Sending..." let fd = getData("tsv"); From 1c0c686537b7c6b995c21338a08c7330816e5edc Mon Sep 17 00:00:00 2001 From: marisa07 Date: Thu, 5 Mar 2026 13:52:53 -0800 Subject: [PATCH 08/12] correct send to google sheets --- qual.html | 9 ++++++- resources/js/googleSheets.js | 50 +++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/qual.html b/qual.html index 05fe9e1cf..006f86e05 100644 --- a/qual.html +++ b/qual.html @@ -95,6 +95,9 @@

Generate QR Code

+
+ +
@@ -418,7 +421,11 @@

Generate QR Code

qualSetStatus("", false); qualShowPage(0); } - + // Shim so googleSheets.js works on this page + function getData() { + return qualCollectData(); + } + var pitScouting = false; setUpGoogleSheets(); diff --git a/resources/js/googleSheets.js b/resources/js/googleSheets.js index 6175d66b2..1fce6c546 100644 --- a/resources/js/googleSheets.js +++ b/resources/js/googleSheets.js @@ -6,27 +6,37 @@ function setUpGoogleSheets() { if (!form || !btn) return; form.addEventListener('submit', e => { - e.preventDefault() - btn.disabled = true - btn.innerHTML = "Sending..." + e.preventDefault() + btn.disabled = true + btn.innerHTML = "Sending..." - let fd = getData("tsv"); - if (pitScouting) { - fd = "PIT\t" + fd; - } else if (typeof qualCollectData === 'function') { - fd = "QUAL\t" + qualCollectData(); - } - fetch(scriptURL, { - method: "POST", - mode: 'no-cors', - body: fd - }) - .then(response => { - alert('Yippee!', response) }) - .catch(error => { - alert('Error!', error.message)}) + let fd; + if (typeof qualCollectData === 'function') { + fd = "QUAL\t" + qualCollectData(); + } else if (typeof getData === 'function') { + fd = getData("tsv"); + if (typeof pitScouting !== 'undefined' && pitScouting) { + fd = "PIT\t" + fd; + } + } else { + alert('No data function found.'); + btn.disabled = false; + btn.innerHTML = "Send to Google Sheets"; + return; + } + fetch(scriptURL, { + method: "POST", + mode: 'no-cors', + body: fd + }) + .then(response => { + alert('Yippee!', response) }) + .catch(error => { + alert('Error!', error.message)}) - btn.disabled = false - btn.innerHTML = "Send to Google Sheets" + btn.disabled = false + btn.innerHTML = "Send to Google Sheets" }) } + + From 50f62d8b3021c3d3c08b5650aa38a01f9dba533b Mon Sep 17 00:00:00 2001 From: marisa07 Date: Thu, 5 Mar 2026 14:10:10 -0800 Subject: [PATCH 09/12] reuse code for google sheets send --- qual.html | 31 ++++++++++++----------- resources/js/googleSheets.js | 48 +++++++++++++++--------------------- 2 files changed, 37 insertions(+), 42 deletions(-) diff --git a/qual.html b/qual.html index 006f86e05..feb523d41 100644 --- a/qual.html +++ b/qual.html @@ -7,6 +7,9 @@ + + + @@ -89,15 +92,12 @@

Generate QR Code

- - + +

-
- -
@@ -386,16 +386,16 @@

Generate QR Code

} } - function qualDisplayData() { - const el = document.getElementById("data"); - el.style.display = el.style.display==="block" ? "none" : "block"; - el.textContent = qualCollectData(); - } + // function qualDisplayData() { + // const el = document.getElementById("data"); + // el.style.display = el.style.display==="block" ? "none" : "block"; + // el.textContent = qualCollectData(); + // } - function qualCopyData() { - navigator.clipboard.writeText(qualCollectData()); - document.getElementById("copyButton").value = "Copied"; - } + // function qualCopyData() { + // navigator.clipboard.writeText(qualCollectData()); + // document.getElementById("copyButton").value = "Copied"; + // } function qualClearForm() { if (!confirm("Clear all qualitative scouting data?")) return; @@ -426,6 +426,9 @@

Generate QR Code

return qualCollectData(); } var pitScouting = false; + var enableGoogleSheets = true; + var dataFormat = "tsv"; + setUpGoogleSheets(); diff --git a/resources/js/googleSheets.js b/resources/js/googleSheets.js index 1fce6c546..74d1ba66a 100644 --- a/resources/js/googleSheets.js +++ b/resources/js/googleSheets.js @@ -6,36 +6,28 @@ function setUpGoogleSheets() { if (!form || !btn) return; form.addEventListener('submit', e => { - e.preventDefault() - btn.disabled = true - btn.innerHTML = "Sending..." + e.preventDefault() + btn.disabled = true + btn.innerHTML = "Sending..." - let fd; - if (typeof qualCollectData === 'function') { - fd = "QUAL\t" + qualCollectData(); - } else if (typeof getData === 'function') { - fd = getData("tsv"); - if (typeof pitScouting !== 'undefined' && pitScouting) { - fd = "PIT\t" + fd; - } - } else { - alert('No data function found.'); - btn.disabled = false; - btn.innerHTML = "Send to Google Sheets"; - return; - } - fetch(scriptURL, { - method: "POST", - mode: 'no-cors', - body: fd - }) - .then(response => { - alert('Yippee!', response) }) - .catch(error => { - alert('Error!', error.message)}) + let fd = getData("tsv"); + if (pitScouting) { + fd = "PIT\t" + fd; + } else if (typeof qualCollectData === 'function') { + fd = "QUAL\t" + qualCollectData(); + } + fetch(scriptURL, { + method: "POST", + mode: 'no-cors', + body: fd + }) + .then(response => { + alert('Yippee!', response) }) + .catch(error => { + alert('Error!', error.message)}) - btn.disabled = false - btn.innerHTML = "Send to Google Sheets" + btn.disabled = false + btn.innerHTML = "Send to Google Sheets" }) } From 5ced6e688418081ad6b04155750eb53cf9cfe185 Mon Sep 17 00:00:00 2001 From: marisa07 Date: Thu, 5 Mar 2026 14:21:26 -0800 Subject: [PATCH 10/12] separate teams ranked --- qual.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qual.html b/qual.html index feb523d41..47b40e945 100644 --- a/qual.html +++ b/qual.html @@ -364,7 +364,7 @@

Generate QR Code

document.querySelectorAll(".qual-rank-list").forEach(list => { const tier = list.dataset.tier; - const order = Array.from(list.querySelectorAll(".qual-rank-item")).map(el=>el.dataset.num).join(","); + const order = Array.from(list.querySelectorAll(".qual-rank-item")).map(el=>el.dataset.num).join("\t"); out += "\nTIER_RANK\t" + tier + "\t" + order; }); From 62297f22735b509009adc2b2848ba6b4fd8ca758 Mon Sep 17 00:00:00 2001 From: marisa07 Date: Thu, 5 Mar 2026 15:08:43 -0800 Subject: [PATCH 11/12] label each line with match number --- qual.html | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/qual.html b/qual.html index 47b40e945..54fbfb4fe 100644 --- a/qual.html +++ b/qual.html @@ -351,21 +351,21 @@

Generate QR Code

// ── Data collection + QR ───────────────────────────── function qualCollectData() { - const scouter = document.getElementById("q-scouter").value.trim(); - const event = document.getElementById("q-event").value.trim(); - const match = document.getElementById("q-match").value.trim(); - let out = ["qs", scouter, event, match].join("\t"); + // const scouter = document.getElementById("q-scouter").value.trim(); + // const event = document.getElementById("q-event").value.trim(); + const match = document.getElementById("q-match").value.trim(); + let out = ""; qualTeams.forEach(t => { - const tier = qualGetTier(t.number); + const tier = qualGetTier(t.number); const notes = (document.getElementById("qnotes-"+t.number)?.value||"").replace(/[\t\n\r]/g," "); - out += "\n" + [t.number, t.alliance[0].toUpperCase(), tier, notes].join("\t"); + out += "\n" + [match, t.number, t.alliance[0].toUpperCase(), tier, notes].join("\t"); }); document.querySelectorAll(".qual-rank-list").forEach(list => { - const tier = list.dataset.tier; + const tier = list.dataset.tier; const order = Array.from(list.querySelectorAll(".qual-rank-item")).map(el=>el.dataset.num).join("\t"); - out += "\nTIER_RANK\t" + tier + "\t" + order; + out += "\n" + match + "\tTIER_RANK\t" + tier + "\t" + order; }); return out; From 92d6957ccf128cb332dec36b23cd0b65a116f4f7 Mon Sep 17 00:00:00 2001 From: marisa07 Date: Thu, 5 Mar 2026 15:26:08 -0800 Subject: [PATCH 12/12] update rank --- qual.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/qual.html b/qual.html index 54fbfb4fe..7d1bcdf83 100644 --- a/qual.html +++ b/qual.html @@ -363,9 +363,9 @@

Generate QR Code

}); document.querySelectorAll(".qual-rank-list").forEach(list => { - const tier = list.dataset.tier; - const order = Array.from(list.querySelectorAll(".qual-rank-item")).map(el=>el.dataset.num).join("\t"); - out += "\n" + match + "\tTIER_RANK\t" + tier + "\t" + order; + const tier = list.dataset.tier; + const order = Array.from(list.querySelectorAll(".qual-rank-item")).map(el=>el.dataset.num).join(" > "); + out += "\n" + [match, "TIER_RANK", tier, order].join("\t"); }); return out;