feat: todo list

This commit is contained in:
Teajey 2024-05-13 17:33:49 +12:00
parent 583e5d6beb
commit 8dc6d3363b
Signed by: Teajey
GPG Key ID: 970E790FE834A713
6 changed files with 125 additions and 35 deletions

View File

@ -42,9 +42,15 @@ func (ctx *Context) WishlistDelete(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)
} }
func (ctx *Context) updateClaims(w http.ResponseWriter, r *http.Request) { func (ctx *Context) UpdateForeignWishlist(w http.ResponseWriter, r *http.Request) {
user := ctx.Auth.ExpectUser(r) user := ctx.Auth.ExpectUser(r)
if err := r.ParseForm(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
userReference := r.PathValue("userReference") userReference := r.PathValue("userReference")
switch r.Form.Get("mode") {
case "claim":
claims := r.Form["unclaimed"] claims := r.Form["unclaimed"]
unclaims := r.Form["claimed"] unclaims := r.Form["claimed"]
err := user.ClaimGifts(claims, unclaims) err := user.ClaimGifts(claims, unclaims)
@ -52,32 +58,16 @@ func (ctx *Context) updateClaims(w http.ResponseWriter, r *http.Request) {
http.Error(w, "Failed to update claim...", http.StatusInternalServerError) http.Error(w, "Failed to update claim...", http.StatusInternalServerError)
return return
} }
http.Redirect(w, r, "/"+userReference, http.StatusSeeOther) case "complete":
}
func (ctx *Context) completeGifts(w http.ResponseWriter, r *http.Request) {
user := ctx.Auth.ExpectUser(r)
userReference := r.PathValue("userReference")
claims := r.Form["claimed"] claims := r.Form["claimed"]
err := user.CompleteGifts(claims) err := user.CompleteGifts(claims)
if err != nil { if err != nil {
log.Printf("Failed to complete gifts: %s\n", err)
http.Error(w, "Failed to complete gifts...", http.StatusInternalServerError) http.Error(w, "Failed to complete gifts...", http.StatusInternalServerError)
return return
} }
http.Redirect(w, r, "/"+userReference, http.StatusSeeOther)
}
func (ctx *Context) UpdateForeignWishlist(w http.ResponseWriter, r *http.Request) {
if err := r.ParseForm(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
switch r.Form.Get("mode") {
case "claim":
ctx.updateClaims(w, r)
case "complete":
ctx.completeGifts(w, r)
default: default:
http.Error(w, "Invalid mode", http.StatusBadRequest) http.Error(w, "Invalid mode", http.StatusBadRequest)
} }
http.Redirect(w, r, "/"+userReference, http.StatusSeeOther)
} }

View File

@ -10,6 +10,7 @@ import (
type HomeProps struct { type HomeProps struct {
Username string Username string
Gifts []db.Gift Gifts []db.Gift
Todo []db.Gift
Reference string Reference string
} }
@ -20,6 +21,11 @@ func (ctx *Context) Home(w http.ResponseWriter, r *http.Request) {
http.Error(w, "An error occurred while fetching your wishlist :(", http.StatusInternalServerError) http.Error(w, "An error occurred while fetching your wishlist :(", http.StatusInternalServerError)
return return
} }
p := HomeProps{Username: user.Name, Gifts: gifts, Reference: user.Reference} todo, err := user.GetTodo()
if err != nil {
http.Error(w, "An error occurred while fetching your wishlist :(", http.StatusInternalServerError)
return
}
p := HomeProps{Username: user.Name, Gifts: gifts, Todo: todo, Reference: user.Reference}
templates.Execute(w, "home.gotmpl", p) templates.Execute(w, "home.gotmpl", p)
} }

34
context/todo_update.go Normal file
View File

@ -0,0 +1,34 @@
package context
import (
"log"
"net/http"
)
func (ctx *Context) TodoUpdate(w http.ResponseWriter, r *http.Request) {
user := ctx.Auth.ExpectUser(r)
if err := r.ParseForm(); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
switch r.Form.Get("mode") {
case "unclaim":
unclaims := r.Form["gift"]
err := user.ClaimGifts([]string{}, unclaims)
if err != nil {
http.Error(w, "Failed to update claim...", http.StatusInternalServerError)
return
}
case "complete":
claims := r.Form["gift"]
err := user.CompleteGifts(claims)
if err != nil {
log.Printf("Failed to complete gifts: %s\n", err)
http.Error(w, "Failed to complete gifts...", http.StatusInternalServerError)
return
}
default:
http.Error(w, "Invalid mode", http.StatusBadRequest)
}
http.Redirect(w, r, "/", http.StatusSeeOther)
}

View File

@ -19,6 +19,8 @@ type Gift struct {
ClaimantId string ClaimantId string
ClaimantName string ClaimantName string
Sent bool Sent bool
RecipientName string
RecipientRef string
} }
func queryForUser(query string, args ...any) (*User, error) { func queryForUser(query string, args ...any) (*User, error) {
@ -111,6 +113,37 @@ func (u *User) GetGifts() ([]Gift, error) {
return gifts, nil return gifts, nil
} }
func (u *User) GetTodo() ([]Gift, error) {
stmt := "SELECT gift.id, gift.name, gift.sent, recipient.name, recipient.reference FROM gift JOIN user ON gift.claimant_id = user.id JOIN user AS recipient ON gift.recipient_id = recipient.id WHERE user.id = ? ORDER BY gift.sent, gift.name DESC"
rows, err := database.Query(stmt, u.Id)
if err != nil {
return nil, err
}
defer rows.Close()
gifts := []Gift{}
for rows.Next() {
var id string
var name string
var sent bool
var recipientName string
var recipientRef string
rows.Scan(&id, &name, &sent, &recipientName, &recipientRef)
gift := Gift{
Id: id,
Name: name,
Sent: sent,
RecipientName: recipientName,
RecipientRef: recipientRef,
}
gifts = append(gifts, gift)
}
err = rows.Err()
if err != nil {
return nil, err
}
return gifts, nil
}
func (u *User) AddGift(name string) error { func (u *User) AddGift(name string) error {
stmt := "INSERT INTO gift (name, recipient_id, creator_id) VALUES (?, ?, ?)" stmt := "INSERT INTO gift (name, recipient_id, creator_id) VALUES (?, ?, ?)"
_, err := database.Exec(stmt, name, u.Id, u.Id) _, err := database.Exec(stmt, name, u.Id, u.Id)

View File

@ -39,6 +39,7 @@ func main() {
protectedMux.HandleFunc("POST /{userReference}/update", ctx.UpdateForeignWishlist) protectedMux.HandleFunc("POST /{userReference}/update", ctx.UpdateForeignWishlist)
protectedMux.HandleFunc("POST /wishlist/add", ctx.WishlistAdd) protectedMux.HandleFunc("POST /wishlist/add", ctx.WishlistAdd)
protectedMux.HandleFunc("POST /wishlist/delete", ctx.WishlistDelete) protectedMux.HandleFunc("POST /wishlist/delete", ctx.WishlistDelete)
protectedMux.HandleFunc("POST /todo/update", ctx.TodoUpdate)
protectedMux.HandleFunc("POST /logout", authMiddleware.LogoutPost) protectedMux.HandleFunc("POST /logout", authMiddleware.LogoutPost)
http.Handle("/", authMiddleware) http.Handle("/", authMiddleware)

View File

@ -10,7 +10,7 @@
<pre>{{.Reference}}</pre> <pre>{{.Reference}}</pre>
</dd> </dd>
</dl> </dl>
<h2>Your list</h2> <h2>Your wishlist</h2>
<form method="post" action="/wishlist/delete" onchange="acceptNames(this, 'deleteSubmit', 'gift')" autocomplete="off"> <form method="post" action="/wishlist/delete" onchange="acceptNames(this, 'deleteSubmit', 'gift')" autocomplete="off">
<button id="deleteSubmit" type="submit" name="mode" value="delete" disabled>Delete</button> <button id="deleteSubmit" type="submit" name="mode" value="delete" disabled>Delete</button>
<ul> <ul>
@ -26,6 +26,32 @@
</form> </form>
<form method="post" action="/wishlist/add"> <form method="post" action="/wishlist/add">
<input name="gift_name" required> <input name="gift_name" required>
<input type="submit"> <button type="submit">Add gift idea</button>
</form>
<h2>Your todo list</h2>
<form method="post" action="/todo/update"
onchange="acceptNames(this, 'unclaimSubmit', 'gift'); acceptNames(this, 'completeSubmit', 'gift')" autocomplete="off">
<button id="unclaimSubmit" type="submit" name="mode" value="unclaim" disabled>Unclaim</button>
<button id="completeSubmit" type="submit" name="mode" value="complete" disabled>Complete</button>
<ul>
{{range .Todo}}
<li>
<label>
{{if .Sent}}
<input type="checkbox" disabled>
{{else}}
<input type="checkbox" name="gift" value="{{.Id}}">
{{end}}
<em>
{{if .Sent}}
<s>{{.Name}}</s>
{{else}}
{{.Name}}
{{end}}
</em> for <a href="/{{.RecipientRef}}">{{.RecipientName}}</a>
</label>
</li>
{{end}}
</ul>
</form> </form>
{{end}} {{end}}