Finishing up rsvp creation and confirmation from clients

This commit is contained in:
collin 2025-06-30 12:37:33 +02:00
parent 8f1fb324a0
commit f1b10e172c
No known key found for this signature in database
6 changed files with 165 additions and 21 deletions

View file

@ -6,23 +6,29 @@
<body>
<h1 class="title fontXL textCenter backgroundLightGreen colorWhite">Collin and Lucy's Wedding</h1>
<div class="contents fontM">
<form action="/api/rsvp" method="post">
<form id="rsvps" action="/api/rsvps" method="post">
<p>
<label for="name-0">Name:</label>
<input type="text" id="name-0" name="name"/>
Attending:
<label for="attending-yes">Yes</label>
<input type="radio" id="attending-yes" name="attending" value="true" required/>
<label for="attending-no">No</label>
<input type="radio" id="attending-no" name="attending" value="false" required/>
</p>
<p>
Age:
<label for="age-child-0">Child</label>
<input type="radio" id="age-child-0" name="child" value="true"/>
<label for="age-adult-0">Adult</label>
<input type="radio" id="age-adult-0" name="child" value="false"/>
</p>
<p>
<label for="diet-0">Dietary preferences:</label>
<textarea id="diet-0" name="dietaryPreferences"></textarea>
<label for="party-size">Party Size:</label>
<select name="party-size" name="party-size" id="party-size">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</p>
<div class="members">
</div>
<button>RSVP</button>
</form>
</div>
<script src="/rsvp.js"></script>
</body>
</html>

79
client/rsvp.js Normal file
View file

@ -0,0 +1,79 @@
let partySize = 0;
function createNewMember(id) {
const memberDiv = document.createElement("div");
memberDiv.classList.add("member");
const innerDiv = document.createElement("div");
const nameP = document.createElement("p");
const nameLabel = document.createElement("label");
nameLabel.htmlFor = `name-${id}`;
nameLabel.innerHTML = "Name:";
const nameInput = document.createElement("input");
nameInput.type = "text";
nameInput.id = `name-${id}`;
nameInput.name = `name-${id}`;
nameInput.required = true;
nameP.appendChild(nameLabel);
nameP.appendChild(nameInput);
const childP = document.createElement("p");
const childLabel = document.createElement("label");
childLabel.htmlFor = `child-${id}`;
childLabel.innerHTML = "Child:";
const childInput = document.createElement("input");
childInput.type = "checkbox";
childInput.id = `child-${id}`;
childInput.name = `child-${id}`;
childP.appendChild(childLabel);
childP.appendChild(childInput);
innerDiv.appendChild(nameP);
innerDiv.appendChild(childP);
const dietP = document.createElement("p");
const dietLabel = document.createElement("label");
dietLabel.htmlFor = `diet-${id}`;
dietLabel.innerHTML = "Dietary preferences:";
const dietInput = document.createElement("textarea");
dietInput.id = `diet-${id}`;
dietInput.name = `dietaryPreferences-${id}`;
dietP.appendChild(dietLabel);
dietP.appendChild(dietInput);
memberDiv.appendChild(innerDiv);
memberDiv.appendChild(dietP);
document.querySelector("div.members").appendChild(memberDiv);
}
function updatePartySize(newPartySize) {
if (newPartySize > partySize) {
for (let i = partySize; i < newPartySize; i++) {
createNewMember(partySize);
}
} else if (newPartySize < partySize) {
for (let i = partySize; i > newPartySize; i--) {
document.querySelector("div.members").lastChild.remove();
}
}
partySize = newPartySize;
}
window.onload = function () {
const partySizeDropdown = document.querySelector("select#party-size");
partySizeDropdown.addEventListener("change", () => {
updatePartySize(Number.parseInt(partySizeDropdown.value));
});
updatePartySize(1);
};

View file

@ -0,0 +1,15 @@
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="/style.css">
</head>
<body>
<h1 class="title fontXL textCenter backgroundLightGreen colorWhite">Collin and Lucy's Wedding</h1>
<div class="contents fontM">
See you there!
<br/>
<a href="/">Back</a>
</div>
<script src="/rsvp_confirmed.js"></script>
</body>
</html>

View file

@ -146,7 +146,7 @@ body {
}
}
/* #endregion
/* #endregion */
/* #region rsvp */
.rsvpButton {
@ -161,4 +161,24 @@ body {
cursor: pointer;
}
form#rsvps {
display: flex;
flex-direction: column;
}
form#rsvps div#members {
display: flex;
flex-direction: column;
}
form#rsvps div.members {
display: flex;
flex-direction: column;
}
form#rsvps div.members div.member div {
display: flex;
flex-direction: row;
}
/* #endregion */

View file

@ -2,10 +2,10 @@ package main
import (
"database/sql"
"encoding/json"
"fmt"
"net/http"
"os"
"strconv"
"time"
)
@ -24,7 +24,6 @@ func getStaticFile(relPath string, contentType string, w http.ResponseWriter) {
w.Header().Add("Content-Type", contentType)
w.WriteHeader(http.StatusOK)
fmt.Printf("%s\n", w.Header().Get("Content-Type"))
w.Write(file)
}
@ -38,9 +37,15 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
case r.Method == "GET" && r.URL.Path == "/rsvp":
getStaticFile("../client/rsvp.html", "text/html", w)
case r.Method == "GET" && r.URL.Path == "/rsvp_confirmed":
getStaticFile("../client/rsvp_confirmed.html", "text/html", w)
case r.Method == "GET" && r.URL.Path == "/index.js":
getStaticFile("../client/index.js", "text/javascript", w)
case r.Method == "GET" && r.URL.Path == "/rsvp.js":
getStaticFile("../client/rsvp.js", "text/javascript", w)
case r.Method == "GET" && r.URL.Path == "/style.css":
getStaticFile("../client/style.css", "text/css", w)
@ -56,9 +61,7 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%#v", rsvps)
case r.Method == "POST" && r.URL.Path == "/api/rsvps":
var rsvp Rsvp
err := json.NewDecoder(r.Body).Decode(&rsvp)
err := r.ParseForm()
if err != nil {
fmt.Println(err)
@ -66,8 +69,29 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
return
}
attending := r.Form.Get("attending") == "true"
partySize, err := strconv.ParseInt(r.Form.Get("party-size"), 10, 1)
partyMembers := make([]Member, partySize)
for i := range partySize {
name := r.Form.Get(fmt.Sprintf("name-%d", i))
child := r.Form.Has(fmt.Sprintf("child-%d", i)) && r.Form.Get(fmt.Sprintf("child-%d", i)) == "true"
dietaryPreferences := r.Form.Get(fmt.Sprintf("diet-%d", i))
member := Member{
Name: name,
Child: child,
DietaryPreferences: dietaryPreferences,
}
partyMembers[i] = member
}
rsvp := Rsvp{
Attending: attending,
PartySize: partySize,
PartyMembers: partyMembers,
}
_, err = rsvp.CreateRsvp(h.db)
fmt.Printf("%#v\n", rsvp)
if err != nil {
fmt.Println(err)
@ -78,7 +102,8 @@ func (h *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if h.ntfy != nil {
SendRsvpNotification(h.ntfy, &rsvp)
}
w.WriteHeader(http.StatusAccepted)
w.Header().Add("Location", "/rsvp_confirmed")
w.WriteHeader(http.StatusSeeOther)
default:
w.WriteHeader(http.StatusNotFound)
}

View file

@ -78,7 +78,6 @@ func (rsvp *Rsvp) CreateRsvp(db *sql.DB) (int64, error) {
if err != nil {
return -1, err
}
fmt.Printf("%s\n", partyMembersBytes)
result, err := db.Exec(
"INSERT INTO rsvps (attending, partySize, partyMembers) VALUES (?, ?, ?);",