diff options
-rw-r--r-- | frontend/student.js | 36 | ||||
-rw-r--r-- | frontend/style.css | 11 | ||||
-rw-r--r-- | templates/student.html | 138 | ||||
-rw-r--r-- | ws_connection.go | 13 | ||||
-rw-r--r-- | wsmsg_confirm.go | 2 | ||||
-rw-r--r-- | wsmsg_hello.go | 16 | ||||
-rw-r--r-- | wsmsg_unconfirm.go | 61 |
7 files changed, 213 insertions, 64 deletions
diff --git a/frontend/student.js b/frontend/student.js index c85a6a2..178611c 100644 --- a/frontend/student.js +++ b/frontend/student.js @@ -73,8 +73,8 @@ document.addEventListener("DOMContentLoaded", () => { forEach(c => { c.style.display = "none" }) - if (mar[2] !== "") { - let courseIDs = mar[2].split(",") + if (mar[1] !== "") { + let courseIDs = mar[1].split(",") for (let i = 0; i < courseIDs.length; i++) { document.getElementById( `tick${ courseIDs[i] }` @@ -181,12 +181,14 @@ document.addEventListener("DOMContentLoaded", () => { gstate = 0 document.getElementById("stateindicator").textContent = "disabled" document.getElementById("confirmbutton").disabled = true + document.getElementById("unconfirmbutton").disabled = true document.querySelectorAll(".coursecheckbox").forEach(c => { c.disabled = true }) break case "START": gstate = 1 + document.getElementById("unconfirmbutton").disabled = false document.querySelectorAll(".courseitem").forEach(c => { if (c.querySelector(".selected-number").textContent !== c.querySelector(".max-number").textContent || @@ -202,6 +204,31 @@ document.addEventListener("DOMContentLoaded", () => { } document.getElementById("stateindicator").textContent = "enabled" break + case "YC": + document.querySelectorAll(".unconfirmed").forEach(c => { + c.style.display = "none" + }) + document.querySelectorAll(".confirmed").forEach(c => { + c.style.display = "block" + }) + document.querySelectorAll(".neither-confirmed").forEach(c => { + c.style.display = "none" + }) + break + case "NC": + document.querySelectorAll(".unconfirmed").forEach(c => { + c.style.display = "block" + }) + document.querySelectorAll(".confirmed").forEach(c => { + c.style.display = "none" + }) + document.querySelectorAll(".neither-confirmed").forEach(c => { + c.style.display = "none" + }) + break + case "RC": + alert(mar[1]) + break default: alert(`Invalid command ${ mar[0] } received from socket. Something is wrong.`) } @@ -251,7 +278,10 @@ document.addEventListener("DOMContentLoaded", () => { }) document.getElementById("confirmbutton").addEventListener("click", () => { - socket.send("C") + socket.send("YC") + }) + document.getElementById("unconfirmbutton").addEventListener("click", () => { + socket.send("NC") }) document.querySelectorAll(".script-required").forEach(c => { diff --git a/frontend/style.css b/frontend/style.css index 78be469..2aa191e 100644 --- a/frontend/style.css +++ b/frontend/style.css @@ -373,6 +373,16 @@ table.table-of-courses { display: none; } +.unconfirmed { + display: none; +} +.confirmed { + display: none; +} +.neither-confirmed { + display: block; +} + /* * This site heavily uses CSS styling to display and hide messages, so by * default we put a big warning about CSS being broken, which disappears @@ -382,3 +392,4 @@ table.table-of-courses { .broken-styling-warning { display: none; } + diff --git a/templates/student.html b/templates/student.html index d06de4b..d3c88e6 100644 --- a/templates/student.html +++ b/templates/student.html @@ -120,62 +120,92 @@ <p> Course selections are <span style="font-weight: bold;" id="stateindicator">disabled</span>. </p> - <table class="table-of-courses"> - <thead> - <tr> - <th scope="col">Tick</th> - <th scope="col">Selected</th> - <th scope="col">Max</th> - <th scope="col">Name</th> - <th scope="col">Type</th> - <th scope="col">Teacher</th> - <th scope="col">Location</th> - </tr> - <tr> - <th colspan="7" class="tdinput"> - <input type="text" id="search" placeholder="Search..." /> - </th> - </tr> - </thead> - <tbody> - {{- range .Groups }} - <tr><th colspan="7">{{ .Name }}</th></tr> - {{- range .Courses }} - <tr class="courseitem" id="course{{.ID}}" data-group="{{.Group}}"> - <th style="font-weight: normal;" scope="row"> - <input aria-label="Enroll in course" class="coursecheckbox" type="checkbox" id="tick{{.ID}}" name="tick{{.ID}}" value="tick{{.ID}}" data-group="{{.Group}}" disabled ></input> - <span id="coursestatus{{.ID}}"></span> - </th> - <td> - <span class="selected-number" id="selected{{.ID}}">{{.Selected}}</span> - </td> - <td> - <span class="max-number" id="max{{.ID}}">{{.Max}}</span> - </td> - <td>{{.Title}}</td> - <td id="type{{.ID}}">{{.Type}}</td> - <td>{{.Teacher}}</td> - <td>{{.Location}}</td> - </tr> - {{- end }} - {{- end }} - </tbody> - <tfoot> - <tr> - <td class="th-like" colspan="7"> - <div class="flex-justify"> - <div class="left"> - Sport: <span id="Sport-chosen">0</span>/<span id="Sport-required">{{ .Required.Sport }}</span>, - Non-sport: <span id="Non-sport-chosen">0</span>/<span id="Non-sport-required">{{ .Required.NonSport }}</span> + <div class="confirmed"> + <table class="table-of-choices"> + <thead> + <tr> + <th scope="col">Period</th> + <th scope="col">Name</th> + <th scope="col">Type</th> + <th scope="col">Teacher</th> + <th scope="col">Location</th> + </tr> + </thead> + <tbody> + </tbody> + <tfoot> + <tr> + <td class="th-like" colspan="7"> + <div class="flex-justify"> + <div class="left"> + </div> + <div class="right"> + <button id="unconfirmbutton" class="btn-danger btn" disabled>Unconfirm</button> + </div> </div> - <div class="right"> - <button id="confirmbutton" class="btn-primary btn" disabled>Confirm</button> + </td> + </tr> + </tfoot> + </table> + </div> + <div class="unconfirmed"> + <table class="table-of-courses"> + <thead> + <tr> + <th scope="col">Tick</th> + <th scope="col">Selected</th> + <th scope="col">Max</th> + <th scope="col">Name</th> + <th scope="col">Type</th> + <th scope="col">Teacher</th> + <th scope="col">Location</th> + </tr> + <tr> + <th colspan="7" class="tdinput"> + <input type="text" id="search" placeholder="Search..." /> + </th> + </tr> + </thead> + <tbody> + {{- range .Groups }} + <tr><th colspan="7">{{ .Name }}</th></tr> + {{- range .Courses }} + <tr class="courseitem" id="course{{.ID}}" data-group="{{.Group}}"> + <th style="font-weight: normal;" scope="row"> + <input aria-label="Enroll in course" class="coursecheckbox" type="checkbox" id="tick{{.ID}}" name="tick{{.ID}}" value="tick{{.ID}}" data-group="{{.Group}}" disabled ></input> + <span id="coursestatus{{.ID}}"></span> + </th> + <td> + <span class="selected-number" id="selected{{.ID}}">{{.Selected}}</span> + </td> + <td> + <span class="max-number" id="max{{.ID}}">{{.Max}}</span> + </td> + <td>{{.Title}}</td> + <td id="type{{.ID}}">{{.Type}}</td> + <td>{{.Teacher}}</td> + <td>{{.Location}}</td> + </tr> + {{- end }} + {{- end }} + </tbody> + <tfoot> + <tr> + <td class="th-like" colspan="7"> + <div class="flex-justify"> + <div class="left"> + Sport: <span id="Sport-chosen">0</span>/<span id="Sport-required">{{ .Required.Sport }}</span>, + Non-sport: <span id="Non-sport-chosen">0</span>/<span id="Non-sport-required">{{ .Required.NonSport }}</span> + </div> + <div class="right"> + <button id="confirmbutton" class="btn-primary btn" disabled>Confirm</button> + </div> </div> - </div> - </td> - </tr> - </tfoot> - </table> + </td> + </tr> + </tfoot> + </table> + </div> </div> </div> </div> diff --git a/ws_connection.go b/ws_connection.go index 068b6db..62cc05a 100644 --- a/ws_connection.go +++ b/ws_connection.go @@ -327,7 +327,7 @@ func handleConn( if err != nil { return err } - case "C": + case "YC": err := messageConfirm( newCtx, c, @@ -340,6 +340,17 @@ func handleConn( if err != nil { return err } + case "NC": + err := messageUnconfirm( + newCtx, + c, + reportError, + mar, + userID, + ) + if err != nil { + return err + } default: return reportError("Unknown command " + mar[0]) } diff --git a/wsmsg_confirm.go b/wsmsg_confirm.go index 8ae51a6..c03320f 100644 --- a/wsmsg_confirm.go +++ b/wsmsg_confirm.go @@ -57,7 +57,7 @@ func messageConfirm( ctx, c, fmt.Sprintf( - "NC :You chose %d out of required %d of type %s", + "RC :Cannot confirm choices: You chose %d out of required %d of type %s", (*userCourseTypes)[courseType], minimum, courseType, diff --git a/wsmsg_hello.go b/wsmsg_hello.go index 02e8ac1..08e8e8c 100644 --- a/wsmsg_hello.go +++ b/wsmsg_hello.go @@ -71,13 +71,19 @@ func messageHello( if err != nil { return err } - var confirmedString string - if confirmed { - confirmedString = "1" + if !confirmed { + err = writeText(ctx, c, "NC") + if err != nil { + return wrapError(errCannotSend, err) + } } else { - confirmedString = "0" + err = writeText(ctx, c, "YC") + if err != nil { + return wrapError(errCannotSend, err) + } } - err = writeText(ctx, c, "HI "+confirmedString+" :"+strings.Join(courseIDs, ",")) + + err = writeText(ctx, c, "HI :"+strings.Join(courseIDs, ",")) if err != nil { return wrapError(errCannotSend, err) } diff --git a/wsmsg_unconfirm.go b/wsmsg_unconfirm.go new file mode 100644 index 0000000..9e12230 --- /dev/null +++ b/wsmsg_unconfirm.go @@ -0,0 +1,61 @@ +/* + * Handle the "C" message + * + * Copyright (C) 2024 Runxi Yu <https://runxiyu.org> + * SPDX-License-Identifier: AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +package main + +import ( + "context" + + "github.com/coder/websocket" +) + +func messageUnconfirm( + ctx context.Context, + c *websocket.Conn, + reportError reportErrorT, + mar []string, + userID string, +) error { + _ = mar + + select { + case <-ctx.Done(): + return wrapError( + errContextCanceled, + ctx.Err(), + ) + default: + } + + _, err := db.Exec( + ctx, + "UPDATE users SET confirmed = false WHERE id = $1", + userID, + ) + if err != nil { + return reportError("error updating database setting confirmation") + } + + return writeText( + ctx, + c, + "NC", + ) +} |