Skip to content

Commit 3c2dacf

Browse files
explodedclaude
andcommitted
Add admin-only Users page for james67@gmail.com
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 74f6ad8 commit 3c2dacf

7 files changed

Lines changed: 134 additions & 1 deletion

File tree

cmd/wtw/main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,7 @@ func main() {
187187
mux.HandleFunc("GET /recs/refresh", middleware.RequireAuth(store, h.RecsRefresh))
188188
mux.HandleFunc("POST /recs/new-verdict", middleware.RequireAuth(store, h.RecsNewVerdict))
189189
mux.HandleFunc("GET /profile", middleware.RequireAuth(store, h.Profile))
190+
mux.HandleFunc("GET /users", middleware.RequireAuth(store, h.Users))
190191
mux.HandleFunc("POST /ratings/{show_id}", middleware.RequireAuth(store, h.SetRating))
191192
mux.HandleFunc("DELETE /ratings/{show_id}", middleware.RequireAuth(store, h.DeleteRating))
192193

internal/db/queries.sql

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,3 +160,11 @@ SELECT * FROM shows WHERE LOWER(title) = LOWER(?) LIMIT 1;
160160

161161
-- name: CountShows :one
162162
SELECT COUNT(*) FROM shows;
163+
164+
-- name: ListUsers :many
165+
SELECT u.id, u.email, u.name, u.avatar_url, u.created_at,
166+
COUNT(r.show_id) AS rating_count
167+
FROM users u
168+
LEFT JOIN ratings r ON r.user_id = u.id
169+
GROUP BY u.id
170+
ORDER BY u.created_at DESC;

internal/db/queries.sql.go

Lines changed: 48 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

internal/handlers/render.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"log/slog"
66
"net/http"
77
"path/filepath"
8+
"strings"
89
"sync"
910

1011
"wtw/internal/db"
@@ -45,7 +46,7 @@ func loadTemplates() {
4546

4647
// Each page template = layout + partials + page-specific content
4748
pageTemplates = make(map[string]*template.Template)
48-
pages := []string{"landing.html", "onboarding.html", "recs.html", "catalog.html", "profile.html", "together.html"}
49+
pages := []string{"landing.html", "onboarding.html", "recs.html", "catalog.html", "profile.html", "together.html", "users.html"}
4950
for _, page := range pages {
5051
// Clone partials so each page gets its own template set
5152
t := template.Must(partialTmpl.Clone())
@@ -146,8 +147,11 @@ type PageData struct {
146147
UserEmail string
147148
UserInitials string
148149
Partners []PartnerInfo
150+
IsAdmin bool
149151
}
150152

153+
const adminEmail = "james67@gmail.com"
154+
151155
func basePageData(r *http.Request, activeTab string) PageData {
152156
name := middleware.GetUserName(r.Context())
153157
email := middleware.GetUserEmail(r.Context())
@@ -157,6 +161,7 @@ func basePageData(r *http.Request, activeTab string) PageData {
157161
UserName: name,
158162
UserEmail: email,
159163
UserInitials: ini,
164+
IsAdmin: strings.EqualFold(email, adminEmail),
160165
}
161166
}
162167

internal/handlers/users.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package handlers
2+
3+
import (
4+
"net/http"
5+
"strings"
6+
7+
"wtw/internal/db"
8+
"wtw/internal/middleware"
9+
)
10+
11+
type UsersData struct {
12+
PageData
13+
Users []db.ListUsersRow
14+
}
15+
16+
func (h *Handler) Users(w http.ResponseWriter, r *http.Request) {
17+
email := middleware.GetUserEmail(r.Context())
18+
if !strings.EqualFold(email, adminEmail) {
19+
http.Redirect(w, r, "/recs", http.StatusSeeOther)
20+
return
21+
}
22+
23+
users, _ := h.queries.ListUsers(r.Context())
24+
25+
data := UsersData{
26+
PageData: h.basePageDataWithPartners(r, "users"),
27+
Users: users,
28+
}
29+
30+
if isHTMX(r) {
31+
renderPartial(w, "users-content", data)
32+
return
33+
}
34+
renderPage(w, "users.html", data)
35+
}

templates/partials/appbar.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
{{if .Partners}}<a class="tab{{if eq .ActiveTab "together"}} tab--active{{end}}" href="/together/{{(index .Partners 0).PartnershipID}}" hx-get="/together/{{(index .Partners 0).PartnershipID}}" hx-target="#app" hx-push-url="true">Together</a>{{end}}
1010
<a class="tab{{if eq .ActiveTab "catalog"}} tab--active{{end}}" href="/catalog" hx-get="/catalog" hx-target="#app" hx-push-url="true">Catalog</a>
1111
<a class="tab{{if eq .ActiveTab "profile"}} tab--active{{end}}" href="/profile" hx-get="/profile" hx-target="#app" hx-push-url="true">Taste</a>
12+
{{if .IsAdmin}}<a class="tab{{if eq .ActiveTab "users"}} tab--active{{end}}" href="/users" hx-get="/users" hx-target="#app" hx-push-url="true">Users</a>{{end}}
1213
</nav>
1314
<button class="avatar" hx-get="/profile" hx-target="#app" hx-push-url="/profile" title="Profile">{{.UserInitials}}</button>
1415
</header>

templates/users.html

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
{{define "title"}}Users - What to Watch{{end}}
2+
{{define "users-content"}}
3+
{{template "appbar" .}}
4+
5+
<main class="profile">
6+
7+
<section class="profile__head">
8+
<h2 class="h-display h-display--sm">Users</h2>
9+
<p class="lede lede--tight">{{len .Users}} registered</p>
10+
</section>
11+
12+
<section class="profile__settings">
13+
{{range .Users}}
14+
<div class="settings-row">
15+
<div class="partner-row">
16+
<div class="partner-avatar">{{initials .Name}}</div>
17+
<div>
18+
<strong>{{.Name}}</strong>
19+
<p>{{.Email}}</p>
20+
</div>
21+
</div>
22+
<div class="partner-actions">
23+
<span class="lede lede--tight">{{.RatingCount}} ratings</span>
24+
<span class="lede lede--tight">Joined {{.CreatedAt.Format "Jan 2006"}}</span>
25+
</div>
26+
</div>
27+
{{end}}
28+
</section>
29+
30+
</main>
31+
{{end}}
32+
33+
{{define "content"}}
34+
{{template "users-content" .}}
35+
{{end}}

0 commit comments

Comments
 (0)