Compare commits
No commits in common. "609ccfc0c4be94c35762f35a55ca4504989506b4" and "9c9e0ebaff9603444074fc9ecdec143a4bcb4671" have entirely different histories.
609ccfc0c4
...
9c9e0ebaff
|
|
@ -5,7 +5,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type RegisterProps struct {
|
type RegisterProps struct {
|
||||||
Navbar templates.NavCollapse
|
|
||||||
GeneralError string `json:",omitempty"`
|
GeneralError string `json:",omitempty"`
|
||||||
Username templates.InputProps
|
Username templates.InputProps
|
||||||
Password templates.InputProps
|
Password templates.InputProps
|
||||||
|
|
@ -37,7 +36,6 @@ func (p *RegisterProps) Validate() (valid bool) {
|
||||||
|
|
||||||
func NewRegisterProps(usernameVal, passwordVal, confirmPassVal string) *RegisterProps {
|
func NewRegisterProps(usernameVal, passwordVal, confirmPassVal string) *RegisterProps {
|
||||||
return &RegisterProps{
|
return &RegisterProps{
|
||||||
Navbar: templates.DefaultNavCollapse(),
|
|
||||||
GeneralError: "",
|
GeneralError: "",
|
||||||
Username: templates.InputProps{
|
Username: templates.InputProps{
|
||||||
Name: "username",
|
Name: "username",
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
top_level=$(git rev-parse --show-toplevel)
|
top_level=$(git rev-parse --show-toplevel)
|
||||||
git_version=$($top_level/scripts/git-version)
|
git_version=$($top_level/scripts/git-version)
|
||||||
|
|
||||||
|
export GOOS=linux
|
||||||
|
export GOARCH=amd64
|
||||||
|
|
||||||
go build -ldflags=-X=lishwist/http/env.GitVersion=$git_version .
|
go build -ldflags=-X=lishwist/http/env.GitVersion=$git_version .
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package response
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"lishwist/http/templates"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"github.com/Teajey/rsvp"
|
"github.com/Teajey/rsvp"
|
||||||
|
|
@ -12,19 +11,10 @@ func NotFound() rsvp.Response {
|
||||||
return Error(http.StatusNotFound, "Page not found")
|
return Error(http.StatusNotFound, "Page not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
type errorProps struct {
|
|
||||||
Navbar templates.NavCollapse
|
|
||||||
Message string
|
|
||||||
}
|
|
||||||
|
|
||||||
func Error(status int, format string, a ...any) rsvp.Response {
|
func Error(status int, format string, a ...any) rsvp.Response {
|
||||||
return rsvp.Response{
|
return rsvp.Response{
|
||||||
Body: errorProps{
|
Body: fmt.Sprintf(format, a...),
|
||||||
Message: fmt.Sprintf(format, a...),
|
Status: status,
|
||||||
Navbar: templates.DefaultNavCollapse(),
|
|
||||||
},
|
|
||||||
Status: status,
|
|
||||||
TemplateName: "error_page.gotmpl",
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type AccountProps struct {
|
type AccountProps struct {
|
||||||
Navbar templates.NavCollapse
|
CurrentUsername string
|
||||||
GeneralError string `json:",omitempty"`
|
GeneralError string `json:",omitempty"`
|
||||||
PasswordFromAdmin bool `json:",omitempty"`
|
PasswordFromAdmin bool `json:",omitempty"`
|
||||||
Password templates.InputProps
|
Password templates.InputProps
|
||||||
|
|
@ -41,7 +41,7 @@ func (p *AccountProps) Validate() (valid bool) {
|
||||||
|
|
||||||
func NewAccountProps(username string, passwordFromAdmin bool, passwordVal, confirmPassVal string) *AccountProps {
|
func NewAccountProps(username string, passwordFromAdmin bool, passwordVal, confirmPassVal string) *AccountProps {
|
||||||
return &AccountProps{
|
return &AccountProps{
|
||||||
Navbar: templates.UserNavCollapse(username, false),
|
CurrentUsername: username,
|
||||||
PasswordFromAdmin: passwordFromAdmin,
|
PasswordFromAdmin: passwordFromAdmin,
|
||||||
Password: templates.InputProps{
|
Password: templates.InputProps{
|
||||||
Type: "password",
|
Type: "password",
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package routing
|
||||||
import (
|
import (
|
||||||
lishwist "lishwist/core"
|
lishwist "lishwist/core"
|
||||||
"lishwist/http/response"
|
"lishwist/http/response"
|
||||||
"lishwist/http/templates"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
|
@ -11,7 +10,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type foreignWishlistProps struct {
|
type foreignWishlistProps struct {
|
||||||
Navbar templates.NavCollapse
|
|
||||||
CurrentUserId string
|
CurrentUserId string
|
||||||
CurrentUserName string
|
CurrentUserName string
|
||||||
Username string
|
Username string
|
||||||
|
|
@ -37,12 +35,11 @@ func ForeignWishlist(app *lishwist.Session, session *response.Session, h http.He
|
||||||
log.Printf("%q couldn't get wishes of other user %q: %s\n", user.Name, otherUser.Name, err)
|
log.Printf("%q couldn't get wishes of other user %q: %s\n", user.Name, otherUser.Name, err)
|
||||||
return response.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(")
|
return response.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(")
|
||||||
}
|
}
|
||||||
p := foreignWishlistProps{Navbar: templates.UserNavCollapse(user.Name, user.PasswordFromAdmin), CurrentUserId: user.Id, Username: otherUser.Name, Gifts: wishes}
|
p := foreignWishlistProps{CurrentUserId: user.Id, CurrentUserName: user.Name, Username: otherUser.Name, Gifts: wishes}
|
||||||
return response.Data("foreign_wishlist.gotmpl", p)
|
return response.Data("foreign_wishlist.gotmpl", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
type publicWishlistProps struct {
|
type publicWishlistProps struct {
|
||||||
Navbar templates.NavCollapse
|
|
||||||
Username string
|
Username string
|
||||||
GiftCount int
|
GiftCount int
|
||||||
}
|
}
|
||||||
|
|
@ -62,6 +59,6 @@ func PublicWishlist(s *response.Session, h http.Header, r *http.Request) rsvp.Re
|
||||||
log.Printf("Couldn't get wishes of user %q on public wishlist: %s\n", otherUser.Name, err)
|
log.Printf("Couldn't get wishes of user %q on public wishlist: %s\n", otherUser.Name, err)
|
||||||
return response.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(")
|
return response.Error(http.StatusInternalServerError, "An error occurred while fetching this user :(")
|
||||||
}
|
}
|
||||||
p := publicWishlistProps{Navbar: templates.DefaultNavCollapse(), Username: otherUser.Name, GiftCount: giftCount}
|
p := publicWishlistProps{Username: otherUser.Name, GiftCount: giftCount}
|
||||||
return response.Data("public_foreign_wishlist.gotmpl", p)
|
return response.Data("public_foreign_wishlist.gotmpl", p)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,14 +7,13 @@ import (
|
||||||
|
|
||||||
lishwist "lishwist/core"
|
lishwist "lishwist/core"
|
||||||
"lishwist/http/response"
|
"lishwist/http/response"
|
||||||
"lishwist/http/templates"
|
|
||||||
|
|
||||||
"github.com/Teajey/rsvp"
|
"github.com/Teajey/rsvp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GroupProps struct {
|
type GroupProps struct {
|
||||||
Navbar templates.NavCollapse
|
Group *lishwist.Group
|
||||||
Group *lishwist.Group
|
CurrentUsername string
|
||||||
}
|
}
|
||||||
|
|
||||||
func AdminGroup(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Response {
|
func AdminGroup(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Response {
|
||||||
|
|
@ -32,8 +31,8 @@ func AdminGroup(app *lishwist.Session, h http.Header, r *http.Request) rsvp.Resp
|
||||||
group.Members = slices.Delete(group.Members, index, index+1)
|
group.Members = slices.Delete(group.Members, index, index+1)
|
||||||
}
|
}
|
||||||
p := GroupProps{
|
p := GroupProps{
|
||||||
Navbar: templates.UserNavCollapse(user.Name, user.PasswordFromAdmin),
|
Group: group,
|
||||||
Group: group,
|
CurrentUsername: user.Name,
|
||||||
}
|
}
|
||||||
return response.Data("group_page.gotmpl", p)
|
return response.Data("group_page.gotmpl", p)
|
||||||
}
|
}
|
||||||
|
|
@ -55,8 +54,8 @@ func Group(app *lishwist.Session, session *response.Session, h http.Header, r *h
|
||||||
index := group.MemberIndex(user.Id)
|
index := group.MemberIndex(user.Id)
|
||||||
group.Members = slices.Delete(group.Members, index, index+1)
|
group.Members = slices.Delete(group.Members, index, index+1)
|
||||||
p := GroupProps{
|
p := GroupProps{
|
||||||
Navbar: templates.UserNavCollapse(user.Name, user.PasswordFromAdmin),
|
Group: group,
|
||||||
Group: group,
|
CurrentUsername: user.Name,
|
||||||
}
|
}
|
||||||
return response.Data("group_page.gotmpl", p)
|
return response.Data("group_page.gotmpl", p)
|
||||||
}
|
}
|
||||||
|
|
@ -68,12 +67,8 @@ func PublicGroup(s *response.Session, h http.Header, r *http.Request) rsvp.Respo
|
||||||
log.Printf("Couldn't get group: %s\n", err)
|
log.Printf("Couldn't get group: %s\n", err)
|
||||||
return response.Error(http.StatusInternalServerError, "An error occurred while fetching this group :(")
|
return response.Error(http.StatusInternalServerError, "An error occurred while fetching this group :(")
|
||||||
}
|
}
|
||||||
if group == nil {
|
|
||||||
return response.Error(http.StatusNotFound, "Group not found")
|
|
||||||
}
|
|
||||||
p := GroupProps{
|
p := GroupProps{
|
||||||
Navbar: templates.DefaultNavCollapse(),
|
Group: group,
|
||||||
Group: group,
|
|
||||||
}
|
}
|
||||||
return response.Data("public_group_page.gotmpl", p)
|
return response.Data("public_group_page.gotmpl", p)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,16 +7,18 @@ import (
|
||||||
lishwist "lishwist/core"
|
lishwist "lishwist/core"
|
||||||
"lishwist/http/env"
|
"lishwist/http/env"
|
||||||
"lishwist/http/response"
|
"lishwist/http/response"
|
||||||
"lishwist/http/templates"
|
|
||||||
|
|
||||||
"github.com/Teajey/rsvp"
|
"github.com/Teajey/rsvp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HomeProps struct {
|
type HomeProps struct {
|
||||||
Navbar templates.NavCollapse
|
Username string
|
||||||
Gifts []lishwist.Wish
|
Gifts []lishwist.Wish
|
||||||
Todo []lishwist.Wish
|
Todo []lishwist.Wish
|
||||||
Groups []lishwist.Group
|
Reference string
|
||||||
|
HostUrl string
|
||||||
|
Groups []lishwist.Group
|
||||||
|
AccountAlert bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func Home(app *lishwist.Session, session *response.Session, h http.Header, r *http.Request) rsvp.Response {
|
func Home(app *lishwist.Session, session *response.Session, h http.Header, r *http.Request) rsvp.Response {
|
||||||
|
|
@ -36,10 +38,7 @@ func Home(app *lishwist.Session, session *response.Session, h http.Header, r *ht
|
||||||
log.Printf("Failed to get groups: %s\n", err)
|
log.Printf("Failed to get groups: %s\n", err)
|
||||||
return response.Error(http.StatusInternalServerError, "An error occurred while fetching your wishlist :(")
|
return response.Error(http.StatusInternalServerError, "An error occurred while fetching your wishlist :(")
|
||||||
}
|
}
|
||||||
p := HomeProps{Navbar: templates.NavCollapse{
|
p := HomeProps{Username: user.Name, Gifts: gifts, Todo: todo, Reference: user.Reference, HostUrl: env.Configuration.HostUrl, Groups: groups, AccountAlert: user.PasswordFromAdmin}
|
||||||
User: &templates.User{Name: user.Name, CopyList: &templates.CopyList{Domain: env.Configuration.HostUrl, Reference: user.Reference}},
|
|
||||||
AccountLink: &templates.AccountLink{Alert: user.PasswordFromAdmin},
|
|
||||||
}, Gifts: gifts, Todo: todo, Groups: groups}
|
|
||||||
return response.Data("home.gotmpl", p)
|
return response.Data("home.gotmpl", p)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,17 +33,14 @@ func Login(s *response.Session, h http.Header, r *http.Request) rsvp.Response {
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoginPost(session *response.Session, h http.Header, r *http.Request) rsvp.Response {
|
func LoginPost(session *response.Session, h http.Header, r *http.Request) rsvp.Response {
|
||||||
username, password, ok := r.BasicAuth()
|
err := r.ParseForm()
|
||||||
if !ok {
|
if err != nil {
|
||||||
err := r.ParseForm()
|
return response.Error(http.StatusBadRequest, "Failed to parse form")
|
||||||
if err != nil {
|
|
||||||
return response.Error(http.StatusBadRequest, "Failed to parse form")
|
|
||||||
}
|
|
||||||
|
|
||||||
username = r.Form.Get("username")
|
|
||||||
password = r.Form.Get("password")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
username := r.Form.Get("username")
|
||||||
|
password := r.Form.Get("password")
|
||||||
|
|
||||||
props := api.NewLoginProps(username, password)
|
props := api.NewLoginProps(username, password)
|
||||||
|
|
||||||
resp := rsvp.SeeOther(r.URL.Path, props)
|
resp := rsvp.SeeOther(r.URL.Path, props)
|
||||||
|
|
|
||||||
|
|
@ -29,21 +29,15 @@ func Register(session *response.Session, h http.Header, r *http.Request) rsvp.Re
|
||||||
}
|
}
|
||||||
|
|
||||||
func RegisterPost(s *response.Session, h http.Header, r *http.Request) rsvp.Response {
|
func RegisterPost(s *response.Session, h http.Header, r *http.Request) rsvp.Response {
|
||||||
var confirmPassword string
|
err := r.ParseForm()
|
||||||
username, newPassword, ok := r.BasicAuth()
|
if err != nil {
|
||||||
if ok {
|
return response.Error(http.StatusBadRequest, "Failed to parse form")
|
||||||
confirmPassword = newPassword
|
|
||||||
} else {
|
|
||||||
err := r.ParseForm()
|
|
||||||
if err != nil {
|
|
||||||
return response.Error(http.StatusBadRequest, "Failed to parse form")
|
|
||||||
}
|
|
||||||
|
|
||||||
username = r.Form.Get("username")
|
|
||||||
newPassword = r.Form.Get("newPassword")
|
|
||||||
confirmPassword = r.Form.Get("confirmPassword")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
username := r.Form.Get("username")
|
||||||
|
newPassword := r.Form.Get("newPassword")
|
||||||
|
confirmPassword := r.Form.Get("confirmPassword")
|
||||||
|
|
||||||
props := api.NewRegisterProps(username, newPassword, confirmPassword)
|
props := api.NewRegisterProps(username, newPassword, confirmPassword)
|
||||||
|
|
||||||
valid := props.Validate()
|
valid := props.Validate()
|
||||||
|
|
@ -55,7 +49,7 @@ func RegisterPost(s *response.Session, h http.Header, r *http.Request) rsvp.Resp
|
||||||
return rsvp.SeeOther(r.URL.Path, props)
|
return rsvp.SeeOther(r.URL.Path, props)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := lishwist.Register(username, newPassword)
|
_, err = lishwist.Register(username, newPassword)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errors.Is(err, lishwist.ErrorUsernameTaken) {
|
if errors.Is(err, lishwist.ErrorUsernameTaken) {
|
||||||
props.Username.Error = "Username is taken"
|
props.Username.Error = "Username is taken"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,41 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div style="height: 100svh;" class="d-flex flex-column">
|
<div style="height: 100svh;" class="d-flex flex-column">
|
||||||
{{template "navbar" .Navbar}}
|
<div class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="navbar-brand">Lishwist</div>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
|
||||||
|
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarToggle">
|
||||||
|
<nav>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/">Home</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<div class="flex-grow-1"></div>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="nav-link dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Logged in as '{{.CurrentUsername}}'
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li>
|
||||||
|
<form class="d-contents" method="post" action="/logout">
|
||||||
|
<button class="dropdown-item" type="submit">Logout</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="overflow-y-scroll flex-grow-1">
|
<div class="overflow-y-scroll flex-grow-1">
|
||||||
<div class="container py-5">
|
<div class="container py-5">
|
||||||
{{with .GeneralError}}
|
{{with .GeneralError}}
|
||||||
|
|
@ -46,4 +80,4 @@
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -41,62 +41,28 @@
|
||||||
</script>
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "navbar"}}
|
{{define "boilerplate"}}
|
||||||
<div class="navbar navbar-expand-lg bg-body-tertiary">
|
<head>
|
||||||
<div class="container-fluid">
|
{{template "head" .}}
|
||||||
<div class="navbar-brand">Lishwist</div>
|
</head>
|
||||||
{{with .}}
|
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
|
<body>
|
||||||
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
|
<div style="height: 100svh;" class="d-flex flex-column">
|
||||||
<span class="navbar-toggler-icon"></span>{{if and .AccountLink .AccountLink.Alert}} <span
|
<div class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
class="badge text-bg-danger">!</span>{{end}}
|
<div class="container-fluid">
|
||||||
</button>
|
<div class="navbar-brand">Lishwist</div>
|
||||||
<div class="collapse navbar-collapse" id="navbarToggle">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
|
||||||
{{with .Links}}
|
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<nav>
|
<span class="navbar-toggler-icon"></span>
|
||||||
<ul class="navbar-nav">
|
</button>
|
||||||
{{range .}}
|
<div class="collapse navbar-collapse" id="navbarToggle">
|
||||||
<li class="nav-item">
|
{{template "navbar" .}}
|
||||||
<a class="nav-link" href="{{.Href}}">{{.Name}}</a>
|
</div>
|
||||||
</li>
|
|
||||||
{{end}}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
{{end}}
|
|
||||||
<div class="flex-grow-1"></div>
|
|
||||||
{{with .User}}
|
|
||||||
<ul class="navbar-nav">
|
|
||||||
{{with .CopyList}}
|
|
||||||
<li class="nav-item"><button class="btn btn-success"
|
|
||||||
onclick="navigator.clipboard.writeText('{{.Domain}}/lists/{{.Reference}}'); alert('The share link to your wishlist has been copied to your clipboard. Anyone with the link will be able to claim gifts for you. Share it with someone!');">Copy
|
|
||||||
share link</button></li>
|
|
||||||
{{end}}
|
|
||||||
<li class="nav-item">
|
|
||||||
<div class="dropdown">
|
|
||||||
<button class="nav-link dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
|
||||||
Logged in as '{{.Name}}'
|
|
||||||
</button>
|
|
||||||
<ul class="dropdown-menu">
|
|
||||||
{{with $.AccountLink}}
|
|
||||||
<li>
|
|
||||||
<a class="dropdown-item" href="/account">Account{{if .Alert}} <span
|
|
||||||
class="badge text-bg-danger">!</span>{{end}}</a>
|
|
||||||
</li>
|
|
||||||
{{end}}
|
|
||||||
<li>
|
|
||||||
<form class="d-contents" method="post" action="/logout">
|
|
||||||
<button class="dropdown-item" type="submit">Logout</button>
|
|
||||||
</form>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
{{end}}
|
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
|
||||||
</div>
|
</div>
|
||||||
|
{{template "body" .}}
|
||||||
</div>
|
</div>
|
||||||
|
</body>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
{{define "login_prompt"}}
|
{{define "login_prompt"}}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,29 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
{{template "head"}}
|
{{template "head" .}}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div style="height: 100svh;" class="d-flex flex-column">
|
<div style="height: 100svh;" class="d-flex flex-column">
|
||||||
{{template "navbar" .Navbar}}
|
<div class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="navbar-brand">Lishwist</div>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
|
||||||
|
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarToggle">
|
||||||
|
<nav>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/">Home</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="container d-flex flex-grow-1 justify-content-center align-items-center flex-column">
|
<div class="container d-flex flex-grow-1 justify-content-center align-items-center flex-column">
|
||||||
<div class="alert alert-danger" role="alert">
|
<div class="alert alert-danger" role="alert">
|
||||||
<p class="mb-0">{{.Message}}</p>
|
<p class="mb-0">{{.Message}}</p>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,46 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
{{template "head"}}
|
{{template "head" .}}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div style="height: 100svh;" class="d-flex flex-column">
|
<div style="height: 100svh;" class="d-flex flex-column">
|
||||||
{{template "navbar" .Navbar}}
|
<div class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="navbar-brand">Lishwist</div>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
|
||||||
|
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarToggle">
|
||||||
|
<nav>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/">Home</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<div class="flex-grow-1"></div>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="nav-link dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Logged in as '{{.CurrentUserName}}'
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li>
|
||||||
|
<form class="d-contents" method="post" action="/logout">
|
||||||
|
<button class="dropdown-item" type="submit">Logout</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="overflow-y-scroll flex-grow-1">
|
<div class="overflow-y-scroll flex-grow-1">
|
||||||
<div class="container py-5">
|
<div class="container py-5">
|
||||||
<section class="card">
|
<section class="card">
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,46 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
{{template "head"}}
|
{{template "head" .}}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div style="height: 100svh;" class="d-flex flex-column">
|
<div style="height: 100svh;" class="d-flex flex-column">
|
||||||
{{template "navbar" .Navbar}}
|
<div class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="navbar-brand">Lishwist</div>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
|
||||||
|
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarToggle">
|
||||||
|
<nav>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/">Home</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<div class="flex-grow-1"></div>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="nav-link dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Logged in as '{{.CurrentUsername}}'
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li>
|
||||||
|
<form class="d-contents" method="post" action="/logout">
|
||||||
|
<button class="dropdown-item" type="submit">Logout</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="overflow-y-scroll flex-grow-1">
|
<div class="overflow-y-scroll flex-grow-1">
|
||||||
<div class="container py-5">
|
<div class="container py-5">
|
||||||
<section class="card">
|
<section class="card">
|
||||||
|
|
|
||||||
|
|
@ -2,12 +2,47 @@
|
||||||
<html>
|
<html>
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
{{template "head"}}
|
{{template "head" .}}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div style="height: 100svh;" class="d-flex flex-column">
|
<div style="height: 100svh;" class="d-flex flex-column">
|
||||||
{{template "navbar" .Navbar}}
|
<div class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="navbar-brand">Lishwist</div>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
|
||||||
|
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>{{if .AccountAlert}} <span
|
||||||
|
class="badge text-bg-danger">!</span>{{end}}
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarToggle">
|
||||||
|
<div class="flex-grow-1"></div>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item"><button class="btn btn-success"
|
||||||
|
onclick="navigator.clipboard.writeText('{{.HostUrl}}/lists/{{.Reference}}'); alert('The share link to your wishlist has been copied to your clipboard. Anyone with the link will be able to claim gifts for you. Share it with someone!');">Copy
|
||||||
|
share link</button></li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<div class="dropdown">
|
||||||
|
<button class="nav-link dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">
|
||||||
|
Logged in as '{{.Username}}'{{if .AccountAlert}} <span class="badge text-bg-danger">!</span>{{end}}
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu">
|
||||||
|
<li>
|
||||||
|
<a class="dropdown-item" href="/account">Account{{if .AccountAlert}} <span
|
||||||
|
class="badge text-bg-danger">!</span>{{end}}</a>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<form class="d-contents" method="post" action="/logout">
|
||||||
|
<button class="dropdown-item" type="submit">Logout</button>
|
||||||
|
</form>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="overflow-y-scroll flex-grow-1">
|
<div class="overflow-y-scroll flex-grow-1">
|
||||||
<div class="container py-5">
|
<div class="container py-5">
|
||||||
<section class="card mb-4">
|
<section class="card mb-4">
|
||||||
|
|
@ -100,4 +135,4 @@
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
|
|
@ -1,12 +1,22 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
{{template "head"}}
|
{{template "head" .}}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div style="height: 100svh;" class="d-flex flex-column">
|
<div style="height: 100svh;" class="d-flex flex-column">
|
||||||
{{template "navbar"}}
|
<div class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="navbar-brand">Lishwist</div>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
|
||||||
|
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarToggle">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="container d-flex flex-grow-1 justify-content-center align-items-center flex-column">
|
<div class="container d-flex flex-grow-1 justify-content-center align-items-center flex-column">
|
||||||
{{if .SuccessfulRegistration}}
|
{{if .SuccessfulRegistration}}
|
||||||
<div class="alert alert-success" role="alert">
|
<div class="alert alert-success" role="alert">
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,29 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
{{template "head"}}
|
{{template "head" .}}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div style="height: 100svh;" class="d-flex flex-column">
|
<div style="height: 100svh;" class="d-flex flex-column">
|
||||||
{{template "navbar" .Navbar}}
|
<div class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="navbar-brand">Lishwist</div>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
|
||||||
|
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarToggle">
|
||||||
|
<nav>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/">Home</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="overflow-y-scroll flex-grow-1">
|
<div class="overflow-y-scroll flex-grow-1">
|
||||||
<div class="container py-5">
|
<div class="container py-5">
|
||||||
<section class="card">
|
<section class="card">
|
||||||
|
|
|
||||||
|
|
@ -1,33 +1,50 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
{{template "head"}}
|
{{template "head" .}}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div style="height: 100svh;" class="d-flex flex-column">
|
<div style="height: 100svh;" class="d-flex flex-column">
|
||||||
{{template "navbar" .Navbar}}
|
<div class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
<div class="overflow-y-scroll flex-grow-1">
|
<div class="container-fluid">
|
||||||
<div class="container py-5">
|
<div class="navbar-brand">Lishwist</div>
|
||||||
<section class="card">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
|
||||||
<div class="card-body">
|
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
<h2><em>{{.Group.Name}}</em> group members</h2>
|
<span class="navbar-toggler-icon"></span>
|
||||||
<p>{{template "login_prompt"}} to see your groups</p>
|
</button>
|
||||||
{{with .Group.Members}}
|
<div class="collapse navbar-collapse" id="navbarToggle">
|
||||||
<ul class="list-group">
|
<nav>
|
||||||
{{range .}}
|
<ul class="navbar-nav">
|
||||||
<li class="list-group-item">
|
<li class="nav-item">
|
||||||
{{.Name}}
|
<a class="nav-link" href="/">Home</a>
|
||||||
</li>
|
</li>
|
||||||
{{end}}
|
|
||||||
</ul>
|
</ul>
|
||||||
{{else}}
|
</nav>
|
||||||
<p>There's nobody else in this group.</p>
|
</div>
|
||||||
{{end}}
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="overflow-y-scroll flex-grow-1">
|
||||||
|
<div class="container py-5">
|
||||||
|
<section class="card">
|
||||||
|
<div class="card-body">
|
||||||
|
<h2><em>{{.Group.Name}}</em> group members</h2>
|
||||||
|
<p>{{template "login_prompt"}} to see your groups</p>
|
||||||
|
{{with .Group.Members}}
|
||||||
|
<ul class="list-group">
|
||||||
|
{{range .}}
|
||||||
|
<li class="list-group-item">
|
||||||
|
{{.Name}}
|
||||||
|
</li>
|
||||||
|
{{end}}
|
||||||
|
</ul>
|
||||||
|
{{else}}
|
||||||
|
<p>There's nobody else in this group.</p>
|
||||||
|
{{end}}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,29 @@
|
||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
{{template "head"}}
|
{{template "head" .}}
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div style="height: 100svh;" class="d-flex flex-column">
|
<div style="height: 100svh;" class="d-flex flex-column">
|
||||||
{{template "navbar" .Navbar}}
|
<div class="navbar navbar-expand-lg bg-body-tertiary">
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="navbar-brand">Lishwist</div>
|
||||||
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarToggle"
|
||||||
|
aria-controls="navbarToggle" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarToggle">
|
||||||
|
<nav>
|
||||||
|
<ul class="navbar-nav">
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/">Home</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="container d-flex flex-grow-1 justify-content-center align-items-center flex-column">
|
<div class="container d-flex flex-grow-1 justify-content-center align-items-center flex-column">
|
||||||
<div class="alert alert-warning" role="alert">
|
<div class="alert alert-warning" role="alert">
|
||||||
<p>Your password will be stored in a safe, responsible manner; but don't trust my programming skills!</p>
|
<p>Your password will be stored in a safe, responsible manner; but don't trust my programming skills!</p>
|
||||||
|
|
|
||||||
|
|
@ -5,43 +5,6 @@ import (
|
||||||
"html/template"
|
"html/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AccountLink struct {
|
|
||||||
Alert bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type Link struct {
|
|
||||||
Href string
|
|
||||||
Name string
|
|
||||||
}
|
|
||||||
|
|
||||||
type CopyList struct {
|
|
||||||
Domain string
|
|
||||||
Reference string
|
|
||||||
}
|
|
||||||
|
|
||||||
type User struct {
|
|
||||||
Name string
|
|
||||||
CopyList *CopyList
|
|
||||||
}
|
|
||||||
|
|
||||||
type NavCollapse struct {
|
|
||||||
User *User
|
|
||||||
AccountLink *AccountLink
|
|
||||||
Links []Link
|
|
||||||
}
|
|
||||||
|
|
||||||
func DefaultNavCollapse() NavCollapse {
|
|
||||||
return NavCollapse{Links: []Link{{Href: "/", Name: "Home"}}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func UserNavCollapse(username string, accountAlert bool) NavCollapse {
|
|
||||||
return NavCollapse{
|
|
||||||
Links: []Link{{Href: "/", Name: "Home"}},
|
|
||||||
User: &User{Name: username},
|
|
||||||
AccountLink: &AccountLink{Alert: accountAlert},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type InputProps struct {
|
type InputProps struct {
|
||||||
Type string `json:",omitempty"`
|
Type string `json:",omitempty"`
|
||||||
Name string
|
Name string
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue