feat: multi-delete

This commit is contained in:
Teajey 2024-05-08 00:43:55 +12:00
parent 73e64f096d
commit 51c33543e8
Signed by: Teajey
GPG Key ID: 970E790FE834A713
6 changed files with 126 additions and 54 deletions

View File

@ -32,15 +32,11 @@ func (ctx *Context) WishlistDelete(w http.ResponseWriter, r *http.Request) {
http.Error(w, err.Error(), http.StatusBadRequest) http.Error(w, err.Error(), http.StatusBadRequest)
return return
} }
target := r.Form.Get("gift_id") targets := r.Form["gift"]
if target == "" { err := user.RemoveGifts(targets...)
http.Error(w, "Gift ID not provided"+target, http.StatusBadRequest)
return
}
err := user.RemoveGift(target)
if err != nil { if err != nil {
log.Printf("Failed to remove gift: %s\n", err) log.Printf("Failed to remove gifts: %s\n", err)
http.Error(w, "Failed to remove gift.", http.StatusInternalServerError) http.Error(w, "Failed to remove gifts.", http.StatusInternalServerError)
return return
} }
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)

View File

@ -8,6 +8,7 @@ import (
) )
type HomeProps struct { type HomeProps struct {
Username string
Gifts []db.Gift Gifts []db.Gift
Reference string Reference string
} }
@ -19,6 +20,6 @@ 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{Gifts: gifts, Reference: user.Reference} p := HomeProps{Username: user.Name, Gifts: gifts, Reference: user.Reference}
templates.Execute(w, "home.gotmpl", p) templates.Execute(w, "home.gotmpl", p)
} }

View File

@ -120,38 +120,84 @@ func (u *User) AddGift(name string) error {
return nil return nil
} }
func (u *User) RemoveGift(id string) error { func (u *User) deleteGifts(tx *sql.Tx, ids []string) error {
stmt := "DELETE FROM gift WHERE gift.creator_id = ? AND gift.id = ?" stmt := "DELETE FROM gift WHERE gift.creator_id = ? AND gift.id = ?"
result, err := database.Exec(stmt, u.Id, id) for _, id := range ids {
r, err := tx.Exec(stmt, u.Id, id)
if err != nil {
return err
}
rE, err := r.RowsAffected()
if err != nil {
return err
}
if rE < 1 {
return fmt.Errorf("Gift deletion failed for '%s'", id)
}
}
return nil
}
func (u *User) RemoveGifts(ids ...string) error {
if len(ids) < 1 {
return fmt.Errorf("Attempt to remove zero gifts")
}
tx, err := database.Begin()
if err != nil { if err != nil {
return err return err
} }
affected, _ := result.RowsAffected()
if affected == 0 { err = u.deleteGifts(tx, ids)
return fmt.Errorf("No gift match.") if err != nil {
rollBackErr := tx.Rollback()
if rollBackErr != nil {
return err
}
return err
} }
return nil
err = tx.Commit()
return err
} }
func (u *User) executeClaims(tx *sql.Tx, claims, unclaims []string) error { func (u *User) executeClaims(tx *sql.Tx, claims, unclaims []string) error {
claimStmt := "UPDATE gift SET claimant_id = ? WHERE id = ?" claimStmt := "UPDATE gift SET claimant_id = ? WHERE id = ?"
unclaimStmt := "UPDATE gift SET claimant_id = NULL WHERE id = ?" unclaimStmt := "UPDATE gift SET claimant_id = NULL WHERE id = ?"
for _, id := range claims { for _, id := range claims {
_, err := tx.Exec(claimStmt, u.Id, id) r, err := tx.Exec(claimStmt, u.Id, id)
if err != nil { if err != nil {
return err return err
} }
} rE, err := r.RowsAffected()
for _, id := range unclaims {
_, err := tx.Exec(unclaimStmt, id)
if err != nil { if err != nil {
return err return err
} }
if rE < 1 {
return fmt.Errorf("Gift claim failed for '%s'", id)
}
}
for _, id := range unclaims {
r, err := tx.Exec(unclaimStmt, id)
if err != nil {
return err
}
rE, err := r.RowsAffected()
if err != nil {
return err
}
if rE < 1 {
return fmt.Errorf("Gift unclaim failed for '%s'", id)
}
} }
return nil return nil
} }
func (u *User) ClaimGifts(claims, unclaims []string) error { func (u *User) ClaimGifts(claims, unclaims []string) error {
if len(claims) < 1 && len(unclaims) < 1 {
return fmt.Errorf("Attempt to claim/unclaim zero gifts")
}
tx, err := database.Begin() tx, err := database.Begin()
if err != nil { if err != nil {
return err return err
@ -159,7 +205,10 @@ func (u *User) ClaimGifts(claims, unclaims []string) error {
err = u.executeClaims(tx, claims, unclaims) err = u.executeClaims(tx, claims, unclaims)
if err != nil { if err != nil {
err = tx.Rollback() rollBackErr := tx.Rollback()
if rollBackErr != nil {
return err
}
return err return err
} }
@ -170,15 +219,26 @@ func (u *User) ClaimGifts(claims, unclaims []string) error {
func (u *User) executeCompletions(tx *sql.Tx, claims []string) error { func (u *User) executeCompletions(tx *sql.Tx, claims []string) error {
claimStmt := "UPDATE gift SET sent = 1 WHERE id = ?" claimStmt := "UPDATE gift SET sent = 1 WHERE id = ?"
for _, id := range claims { for _, id := range claims {
_, err := tx.Exec(claimStmt, id) r, err := tx.Exec(claimStmt, id)
if err != nil { if err != nil {
return err return err
} }
rE, err := r.RowsAffected()
if err != nil {
return err
}
if rE < 1 {
return fmt.Errorf("Gift completion failed for '%s'", id)
}
} }
return nil return nil
} }
func (u *User) CompleteGifts(claims []string) error { func (u *User) CompleteGifts(claims []string) error {
if len(claims) < 1 {
return fmt.Errorf("Attempt to complete zero gifts")
}
tx, err := database.Begin() tx, err := database.Begin()
if err != nil { if err != nil {
return err return err
@ -186,7 +246,10 @@ func (u *User) CompleteGifts(claims []string) error {
err = u.executeCompletions(tx, claims) err = u.executeCompletions(tx, claims)
if err != nil { if err != nil {
err = tx.Rollback() rollBackErr := tx.Rollback()
if rollBackErr != nil {
return err
}
return err return err
} }

View File

@ -4,10 +4,18 @@
<head> <head>
<title>Lishwist</title> <title>Lishwist</title>
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<script>
function someChecked(checkList) {
if (!checkList) {
return
}
return Array.from(checkList).some(({checked}) => checked);
}
</script>
</head> </head>
<body> <body>
{{template "body" .}} {{template "body" .}}
</body> </body>
</html> </html>

View File

@ -8,34 +8,35 @@
</ul> </ul>
</nav> </nav>
<h2>{{.Username}}'s list</h2> <h2>{{.Username}}'s list</h2>
<form method="post" action="/{{.UserReference}}/update" autocomplete="off"> <form method="post" action="/{{.UserReference}}/update" autocomplete="off"
<button type="submit" name="mode" value="claim">Claim/Unclaim</button> onchange="const checked = someChecked(this.unclaimed) || someChecked(this.claimed); this.mode[0].disabled = !checked; this.mode[1].disabled = !checked;">
<button type="submit" name="mode" value="complete">Complete</button> <button type="submit" name="mode" value="claim" disabled>Claim/Unclaim</button>
<button type="submit" name="mode" value="complete" disabled>Complete</button>
<ul> <ul>
{{range .Gifts}} {{range .Gifts}}
<li> <li>
<label> <label>
{{if .ClaimantId}} {{if .ClaimantId}}
{{if eq .ClaimantId $.CurrentUserId}} {{if eq .ClaimantId $.CurrentUserId}}
<input type="checkbox" name="claimed" value="{{.Id}}" {{if .Sent}}disabled{{end}}> <input type="checkbox" name="claimed" value="{{.Id}}" {{if .Sent}}disabled{{end}}>
{{else}} {{else}}
<input type="checkbox" disabled> <input type="checkbox" disabled>
{{end}} {{end}}
{{else}} {{else}}
<input type="checkbox" name="unclaimed" value="{{.Id}}" {{if .Sent}}disabled{{end}}> <input type="checkbox" name="unclaimed" value="{{.Id}}" {{if .Sent}}disabled{{end}}>
{{end}}
{{if .Sent}}
<s>{{.Name}}</s>
{{else}}
{{.Name}}
{{end}}
{{if .ClaimantId}}
{{if eq .ClaimantId $.CurrentUserId}}
<span style="color: blue;">{{if .Sent}}Completed{{else}}Claimed{{end}} by you</span>
{{else}}
<span style="color: red;">{{if .Sent}}Completed{{else}}Claimed{{end}} by {{.ClaimantName}}</span>
{{end}} {{end}}
{{end}} {{if .Sent}}
<s>{{.Name}}</s>
{{else}}
{{.Name}}
{{end}}
{{if .ClaimantId}}
{{if eq .ClaimantId $.CurrentUserId}}
<span style="color: blue;">{{if .Sent}}Completed{{else}}Claimed{{end}} by you</span>
{{else}}
<span style="color: red;">{{if .Sent}}Completed{{else}}Claimed{{end}} by {{.ClaimantName}}</span>
{{end}}
{{end}}
</label> </label>
</li> </li>
{{end}} {{end}}

View File

@ -1,4 +1,5 @@
{{define "body"}} {{define "body"}}
<p>Logged in as '{{.Username}}'</p>
<form method="post" action="/logout"> <form method="post" action="/logout">
<input type="submit" value="Logout"> <input type="submit" value="Logout">
</form> </form>
@ -10,16 +11,18 @@
</dd> </dd>
</dl> </dl>
<h2>Your list</h2> <h2>Your list</h2>
<ul> <form method="post" action="/wishlist/delete" onchange="this.mode.disabled = !someChecked(this.gift)"
{{range .Gifts}} autocomplete="off">
<li>{{.Name}} <button type="submit" name="mode" value="delete" disabled>Delete</button>
<form method="post" action="wishlist/delete"> <ul>
<input type="hidden" name="gift_id" value="{{.Id}}"> {{range .Gifts}}
<input type="submit" value="Delete"> <li>
</form> <input type="checkbox" name="gift" value="{{.Id}}">
</li> {{.Name}}
{{end}} </li>
</ul> {{end}}
</ul>
</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"> <input type="submit">