summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--frontend/student.js36
-rw-r--r--frontend/style.css11
-rw-r--r--templates/student.html138
-rw-r--r--ws_connection.go13
-rw-r--r--wsmsg_confirm.go2
-rw-r--r--wsmsg_hello.go16
-rw-r--r--wsmsg_unconfirm.go61
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",
+ )
+}