From ec33dc3f8ac574de6090d32483536fd56b23b520 Mon Sep 17 00:00:00 2001 From: Brittany Ellich Date: Sun, 17 May 2026 22:36:49 -0700 Subject: [PATCH] Add tests, badges, favicon, etc. --- features/about/pages/about.templ | 2 +- features/about/pages/about_templ.go | 2 +- features/admin/handlers.go | 419 +++++++++- features/admin/pages/badges.templ | 138 ++++ features/admin/pages/badges_templ.go | 500 ++++++++++++ features/admin/pages/dashboard.templ | 3 +- features/admin/pages/dashboard_templ.go | 14 +- features/admin/pages/events.templ | 84 +- features/admin/pages/events_templ.go | 546 ++++++++----- features/admin/pages/users.templ | 2 +- features/admin/pages/users_templ.go | 2 +- features/admin/routes.go | 7 + features/auth/drain_middleware.go | 26 +- features/auth/handlers.go | 2 +- features/auth/pages/signin.templ | 2 +- features/auth/pages/signin_atproto.templ | 2 +- features/auth/pages/signin_atproto_templ.go | 2 +- features/auth/pages/signin_templ.go | 2 +- features/common/layouts/base.templ | 5 + features/common/layouts/base_templ.go | 89 ++- features/connect/handlers.go | 27 +- features/connect/pages/connect.templ | 2 +- features/connect/pages/connect_templ.go | 2 +- features/connections/pages/connections.templ | 2 +- .../connections/pages/connections_templ.go | 2 +- features/connections/pages/profile.templ | 4 +- features/connections/pages/profile_templ.go | 4 +- features/event/pages/event.templ | 2 +- features/event/pages/event_templ.go | 2 +- features/events/handlers.go | 118 ++- features/events/pages/detail.templ | 67 +- features/events/pages/detail_templ.go | 748 +++++++++++------- features/events/pages/events.templ | 2 +- features/events/pages/events_templ.go | 2 +- features/index/handlers.go | 6 +- features/index/pages/index.templ | 54 +- features/index/pages/index_templ.go | 271 ++++--- features/profile/handlers.go | 9 + features/profile/pages/profile.templ | 32 +- features/profile/pages/profile_edit.templ | 2 +- features/profile/pages/profile_edit_templ.go | 2 +- features/profile/pages/profile_templ.go | 96 ++- features/settings/handlers.go | 263 +++++- features/settings/pages/notes_list.templ | 94 +++ features/settings/pages/notes_list_templ.go | 217 +++++ features/settings/pages/pds_data.templ | 93 +++ features/settings/pages/pds_data_templ.go | 216 +++++ features/settings/pages/settings.templ | 146 ++-- features/settings/pages/settings_templ.go | 264 +++++-- features/settings/routes.go | 7 +- internal/admincrypto/signer_test.go | 235 ++++++ internal/badge/award.go | 138 ++++ internal/badge/badge.go | 98 ++- internal/badge/badge_test.go | 303 +++++++ internal/badge/definitions.go | 19 +- internal/badge/definitions_test.go | 291 +++++++ internal/checkin/list_test.go | 216 +++++ internal/connection/drain.go | 23 +- .../db/migrations/009_privacy_toggles.sql | 6 + .../migrations/010_new_badge_definitions.sql | 15 + .../migrations/011_event_badges_per_type.sql | 31 + .../migrations/012_badge_message_and_host.sql | 11 + internal/event/put.go | 48 ++ internal/event/stats_test.go | 324 ++++++++ internal/interest/match_test.go | 94 +++ internal/notes/notes_test.go | 231 ++++++ internal/qrcode/qrcode.go | 2 +- internal/users/users.go | 64 ++ internal/users/users_test.go | 349 ++++++++ router/router.go | 36 + .../static/assets/android-chrome-192x192.png | Bin 0 -> 36029 bytes .../static/assets/android-chrome-512x512.png | Bin 0 -> 167096 bytes .../static/assets/apple-touch-icon.png | Bin 0 -> 32265 bytes web/resources/static/assets/favicon-16x16.png | Bin 0 -> 801 bytes web/resources/static/assets/favicon-32x32.png | Bin 0 -> 2270 bytes web/resources/static/assets/favicon.ico | Bin 110360 -> 15406 bytes web/resources/static/assets/site.webmanifest | 19 + web/resources/static/css/terminal.css | 220 +++++- web/resources/static/js/badge-designer.js | 55 ++ web/resources/static/js/badge-toast.js | 42 + web/resources/static/js/profile.js | 16 + 81 files changed, 6572 insertions(+), 919 deletions(-) create mode 100644 features/admin/pages/badges.templ create mode 100644 features/admin/pages/badges_templ.go create mode 100644 features/settings/pages/notes_list.templ create mode 100644 features/settings/pages/notes_list_templ.go create mode 100644 features/settings/pages/pds_data.templ create mode 100644 features/settings/pages/pds_data_templ.go create mode 100644 internal/admincrypto/signer_test.go create mode 100644 internal/badge/badge_test.go create mode 100644 internal/badge/definitions_test.go create mode 100644 internal/checkin/list_test.go create mode 100644 internal/db/migrations/009_privacy_toggles.sql create mode 100644 internal/db/migrations/010_new_badge_definitions.sql create mode 100644 internal/db/migrations/011_event_badges_per_type.sql create mode 100644 internal/db/migrations/012_badge_message_and_host.sql create mode 100644 internal/event/stats_test.go create mode 100644 internal/interest/match_test.go create mode 100644 internal/notes/notes_test.go create mode 100644 internal/users/users_test.go create mode 100644 web/resources/static/assets/android-chrome-192x192.png create mode 100644 web/resources/static/assets/android-chrome-512x512.png create mode 100644 web/resources/static/assets/apple-touch-icon.png create mode 100644 web/resources/static/assets/favicon-16x16.png create mode 100644 web/resources/static/assets/favicon-32x32.png create mode 100644 web/resources/static/assets/site.webmanifest create mode 100644 web/resources/static/js/badge-designer.js create mode 100644 web/resources/static/js/badge-toast.js diff --git a/features/about/pages/about.templ b/features/about/pages/about.templ index 64dffdb..8d2f536 100644 --- a/features/about/pages/about.templ +++ b/features/about/pages/about.templ @@ -7,7 +7,7 @@ import "atmoquest/features/common/layouts" // to understand what "your data goes to your repo, not ours" means before // they sign in. templ AboutATProto() { - @layouts.Base("What's ATProto? — atmo.quest", "ATProto is an open social protocol — your records live in your repo, not ours.") { + @layouts.Base("What's ATProto?", "ATProto is an open social protocol — your records live in your repo, not ours.") {
diff --git a/features/about/pages/about_templ.go b/features/about/pages/about_templ.go index 1d95bcf..08cd774 100644 --- a/features/about/pages/about_templ.go +++ b/features/about/pages/about_templ.go @@ -66,7 +66,7 @@ func AboutATProto() templ.Component { } return nil }) - templ_7745c5c3_Err = layouts.Base("What's ATProto? — atmo.quest", "ATProto is an open social protocol — your records live in your repo, not ours.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = layouts.Base("What's ATProto?", "ATProto is an open social protocol — your records live in your repo, not ours.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/features/admin/handlers.go b/features/admin/handlers.go index d3dc44e..ec22fc1 100644 --- a/features/admin/handlers.go +++ b/features/admin/handlers.go @@ -5,6 +5,7 @@ package admin import ( + "context" "database/sql" "encoding/json" "errors" @@ -23,6 +24,8 @@ import ( "atmoquest/features/auth" "atmoquest/internal/admincrypto" "atmoquest/internal/badge" + "atmoquest/internal/checkin" + "atmoquest/internal/connection" "atmoquest/internal/event" "atmoquest/internal/users" ) @@ -266,20 +269,28 @@ func (h *Handlers) AdminEventBadge(w http.ResponseWriter, r *http.Request) { QRPublicURL: strings.TrimRight(config.Global.PublicURL, "/") + "/e/" + token, } + // Which badge type are we editing? + badgeType := r.URL.Query().Get("type") + if badgeType == "" { + badgeType = "event-attendee" + } + view.SelectedBadgeType = badgeType + if h.Signer != nil { - if d, err := badge.Get(r.Context(), h.DB, h.Signer, ev.URI); err == nil { + if d, err := badge.Get(r.Context(), h.DB, h.Signer, ev.URI, badgeType); err == nil { view.Design = pages.AdminBadgeDesign{ - Shape: d.Shape, - PrimaryColor: d.PrimaryColor, - AccentColor: d.AccentColor, - RibbonColor: d.RibbonColor, - Label: d.Label, - Signature: d.Signature, - SigningKeyID: d.SigningKeyID, + Shape: d.Shape, + PrimaryColor: d.PrimaryColor, + AccentColor: d.AccentColor, + RibbonColor: d.RibbonColor, + Label: d.Label, + MessageTemplate: d.MessageTemplate, + Signature: d.Signature, + SigningKeyID: d.SigningKeyID, } view.SVGPreview = badge.RenderSVG(d, 200) } else if !errors.Is(err, badge.ErrNotFound) { - slog.Warn("admin badge: get", "event_uri", ev.URI, "err", err) + slog.Warn("admin badge: get", "event_uri", ev.URI, "type", badgeType, "err", err) } } @@ -295,12 +306,67 @@ func (h *Handlers) AdminEventBadge(w http.ResponseWriter, r *http.Request) { }, 200) } + // Build event badge overview — show which badge types have designs. + eventBadgeTypes := []struct { + slug, name, desc string + }{ + {"event-attendee", "Event check-in", "Awarded when someone checks into this event"}, + {"event-leaderboard-1st", "Leaderboard 1st place", "Awarded to the top connector at event end"}, + {"event-leaderboard-2nd", "Leaderboard 2nd place", "Awarded to the runner-up at event end"}, + {"event-leaderboard-3rd", "Leaderboard 3rd place", "Awarded to third place at event end"}, + {"event-exceeded", "Beyond expectations", "Awarded when check-ins exceed expected attendees"}, + } + designMap, _ := badge.ListForEvent(r.Context(), h.DB, ev.URI) + for _, bt := range eventBadgeTypes { + eb := pages.EventBadgeOverview{ + Slug: bt.slug, + Name: bt.name, + Description: bt.desc, + } + if d, ok := designMap[bt.slug]; ok { + eb.Designed = true + eb.Label = d.Label + } + view.EventBadges = append(view.EventBadges, eb) + } + w.Header().Set("Content-Type", "text/html; charset=utf-8") if err := pages.AdminEventBadge(view).Render(r.Context(), w); err != nil { slog.Error("render admin badge", "err", err) } } +// BadgePreviewSVG renders a badge SVG from query parameters for the live +// designer preview. No signing or persistence — purely visual. +func (h *Handlers) BadgePreviewSVG(w http.ResponseWriter, r *http.Request) { + d := badge.Design{ + Shape: r.URL.Query().Get("shape"), + PrimaryColor: r.URL.Query().Get("primary_color"), + AccentColor: r.URL.Query().Get("accent_color"), + RibbonColor: r.URL.Query().Get("ribbon_color"), + Label: r.URL.Query().Get("label"), + } + if d.Shape == "" { + d.Shape = "shield" + } + if d.PrimaryColor == "" { + d.PrimaryColor = "#fab387" + } + if d.AccentColor == "" { + d.AccentColor = "#1e1e2e" + } + if d.RibbonColor == "" { + d.RibbonColor = "#f38ba8" + } + if d.Label == "" { + d.Label = "preview" + } + svg := badge.RenderSVG(d, 200) + w.Header().Set("Content-Type", "image/svg+xml") + w.Header().Set("Cache-Control", "no-cache") + _, _ = w.Write([]byte(svg)) +} + // AdminEventBadgeSave handles POST /admin/events/{token}/badge. func (h *Handlers) AdminEventBadgeSave(w http.ResponseWriter, r *http.Request) { if h.Signer == nil { @@ -314,20 +380,27 @@ func (h *Handlers) AdminEventBadgeSave(w http.ResponseWriter, r *http.Request) { return } + badgeType := r.FormValue("badge_type") + if badgeType == "" { + badgeType = "event-attendee" + } + d := badge.Design{ - EventURI: ev.URI, - Shape: r.FormValue("shape"), - PrimaryColor: r.FormValue("primary_color"), - AccentColor: r.FormValue("accent_color"), - RibbonColor: r.FormValue("ribbon_color"), - Label: strings.TrimSpace(r.FormValue("label")), + EventURI: ev.URI, + BadgeType: badgeType, + Shape: r.FormValue("shape"), + PrimaryColor: r.FormValue("primary_color"), + AccentColor: r.FormValue("accent_color"), + RibbonColor: r.FormValue("ribbon_color"), + Label: strings.TrimSpace(r.FormValue("label")), + MessageTemplate: strings.TrimSpace(r.FormValue("message_template")), } if _, err := badge.Save(r.Context(), h.DB, h.Signer, d); err != nil { slog.Warn("admin badge save", "err", err) http.Error(w, err.Error(), http.StatusBadRequest) return } - http.Redirect(w, r, "/admin/events/"+token+"/badge?saved=1", http.StatusSeeOther) + http.Redirect(w, r, "/admin/events/"+token+"/badge?type="+badgeType+"&saved=1", http.StatusSeeOther) } // parseEventForm pulls form values into a CreateInput, returning a @@ -371,6 +444,320 @@ func parseEventForm(r *http.Request) (event.CreateInput, pages.AdminEventFormVie return in, view, "" } +// AdminEventDelete handles POST /admin/events/{token}/delete — removes the +// event, its PDS record, and all associated local data (checkins, badges, etc.). +func (h *Handlers) AdminEventDelete(w http.ResponseWriter, r *http.Request) { + _, sess, ok := h.Auth.RequireSession(w, r) + if !ok { + return + } + + token := chi.URLParam(r, "token") + ev, err := event.LookupByQRToken(r.Context(), h.DB, token) + if err != nil { + http.NotFound(w, r) + return + } + + if err := event.Delete(r.Context(), sess, h.DB, ev.URI); err != nil { + slog.Error("admin event delete", "uri", ev.URI, "err", err) + http.Error(w, "failed to delete event", http.StatusInternalServerError) + return + } + + slog.Info("admin event deleted", "uri", ev.URI, "name", ev.Name) + http.Redirect(w, r, "/admin/events", http.StatusSeeOther) +} + +// AdminEventFinalize awards end-of-event badges: leaderboard top 3 and +// "exceeded expectations" if check-ins surpassed expected attendees. +// This is a manual admin action triggered after the event ends. +func (h *Handlers) AdminEventFinalize(w http.ResponseWriter, r *http.Request) { + _, _, ok := h.Auth.RequireSession(w, r) + if !ok { + return + } + + token := chi.URLParam(r, "token") + ev, err := event.LookupByQRToken(r.Context(), h.DB, token) + if err != nil { + http.NotFound(w, r) + return + } + + ctx := r.Context() + + // Get all checked-in attendees. + attendeeDIDs, _ := checkin.ListAttendeesForEvent(ctx, h.DB, ev.URI) + + // Build leaderboard (connection counts per attendee at this event). + type connCount struct { + did string + count int + } + var leaderboard []connCount + for _, didStr := range attendeeDIDs { + did, err := syntax.ParseDID(didStr) + if err != nil { + continue + } + // Skip users who opted out of leaderboard. + if users.IsHiddenFromLeaderboard(ctx, h.DB, didStr) { + continue + } + pds := lookupPDSForDIDAdmin(ctx, h.DB, did) + conns, _ := connection.List(ctx, pds, did) + eventConns := 0 + for _, c := range conns { + if c.EventURI == ev.URI { + eventConns++ + } + } + if eventConns > 0 { + leaderboard = append(leaderboard, connCount{did: didStr, count: eventConns}) + } + } + + // Sort by count desc. + for i := 0; i < len(leaderboard); i++ { + for j := i + 1; j < len(leaderboard); j++ { + if leaderboard[j].count > leaderboard[i].count { + leaderboard[i], leaderboard[j] = leaderboard[j], leaderboard[i] + } + } + } + + // Load custom message templates for this event's badges. + msgTemplates := make(map[string]string) + if designs, err := badge.ListForEvent(ctx, h.DB, ev.URI); err == nil { + for badgeType, d := range designs { + msgTemplates[badgeType] = d.MessageTemplate + } + } + + // Award leaderboard badges (1st, 2nd, 3rd) using competition ranking. + placeSlugs := []string{"event-leaderboard-1st", "event-leaderboard-2nd", "event-leaderboard-3rd"} + placeIdx := 0 + prevCount := -1 + for i, entry := range leaderboard { + if placeIdx >= len(placeSlugs) { + break + } + if entry.count != prevCount { + placeIdx = i + prevCount = entry.count + } + if placeIdx < len(placeSlugs) { + def, err := badge.GetDefinitionBySlug(ctx, h.DB, placeSlugs[placeIdx]) + if err == nil && !badge.HasEarned(ctx, h.DB, entry.did, def.ID) { + msg := badgeMessage(msgTemplates, placeSlugs[placeIdx], ev.Name) + _ = badge.RecordEarned(ctx, h.DB, entry.did, def.ID, "", msg) + slog.Info("finalize: awarded leaderboard badge", + "did", entry.did, "place", placeIdx+1, "connections", entry.count) + } + } + } + + // Award "exceeded expectations" badge if check-ins > expected attendees. + if ev.ExpectedAttendees > 0 && len(attendeeDIDs) > ev.ExpectedAttendees { + def, err := badge.GetDefinitionBySlug(ctx, h.DB, "event-exceeded") + if err == nil { + msg := badgeMessage(msgTemplates, "event-exceeded", ev.Name) + for _, didStr := range attendeeDIDs { + if !badge.HasEarned(ctx, h.DB, didStr, def.ID) { + _ = badge.RecordEarned(ctx, h.DB, didStr, def.ID, "", msg) + } + } + slog.Info("finalize: awarded exceeded badge", + "event", ev.Name, "checkins", len(attendeeDIDs), "expected", ev.ExpectedAttendees) + } + } + + // Award "event-host" badge if the event was successful (>1 check-in + >=1 connection). + totalConns := 0 + for _, cc := range leaderboard { + totalConns += cc.count + } + if len(attendeeDIDs) > 1 && totalConns > 0 && ev.OrganizerDID.String() != "" { + def, err := badge.GetDefinitionBySlug(ctx, h.DB, "event-host") + if err == nil && !badge.HasEarned(ctx, h.DB, ev.OrganizerDID.String(), def.ID) { + msg := badgeMessage(msgTemplates, "event-host", ev.Name) + if msg == "" { + msg = "Successfully hosted " + ev.Name + } + _ = badge.RecordEarned(ctx, h.DB, ev.OrganizerDID.String(), def.ID, "", msg) + slog.Info("finalize: awarded event-host badge", + "did", ev.OrganizerDID.String(), "event", ev.Name) + } + } + + slog.Info("admin event finalized", "uri", ev.URI, "name", ev.Name, + "attendees", len(attendeeDIDs), "leaderboard_entries", len(leaderboard)) + http.Redirect(w, r, "/admin/events", http.StatusSeeOther) +} + +// badgeMessage looks up a custom message template for a badge type, +// falling back to a default message with the event name. +func badgeMessage(templates map[string]string, badgeType, eventName string) string { + if msg, ok := templates[badgeType]; ok && msg != "" { + return msg + } + return "Earned at " + eventName +} + +// AdminBadges lists all app-wide badge definitions. +func (h *Handlers) AdminBadges(w http.ResponseWriter, r *http.Request) { + defs, err := badge.ListDefinitions(r.Context(), h.DB) + if err != nil { + slog.Error("admin badges: list", "err", err) + http.Error(w, "internal error", http.StatusInternalServerError) + return + } + + var view pages.AdminBadgesView + for _, d := range defs { + svg := badge.RenderSVG(badge.Design{ + Shape: d.Shape, + PrimaryColor: d.PrimaryColor, + AccentColor: d.AccentColor, + RibbonColor: d.RibbonColor, + Label: d.Label, + }, 80) + view.Badges = append(view.Badges, pages.AdminBadgeRow{ + ID: d.ID, + Slug: d.Slug, + Name: d.Name, + Description: d.Description, + Shape: d.Shape, + PrimaryColor: d.PrimaryColor, + AccentColor: d.AccentColor, + RibbonColor: d.RibbonColor, + Label: d.Label, + Trigger: d.Trigger, + SVGPreview: svg, + }) + } + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + if err := pages.AdminBadges(view).Render(r.Context(), w); err != nil { + slog.Error("render admin badges", "err", err) + } +} + +// AdminBadgeEditPage renders the badge edit form for a specific definition. +func (h *Handlers) AdminBadgeEditPage(w http.ResponseWriter, r *http.Request) { + idStr := chi.URLParam(r, "id") + id, err := strconv.Atoi(idStr) + if err != nil { + http.NotFound(w, r) + return + } + + defs, _ := badge.ListDefinitions(r.Context(), h.DB) + var def *badge.Definition + for i := range defs { + if defs[i].ID == id { + def = &defs[i] + break + } + } + if def == nil { + http.NotFound(w, r) + return + } + + svg := badge.RenderSVG(badge.Design{ + Shape: def.Shape, + PrimaryColor: def.PrimaryColor, + AccentColor: def.AccentColor, + RibbonColor: def.RibbonColor, + Label: def.Label, + }, 200) + + view := pages.AdminBadgeEditView{ + Badge: pages.AdminBadgeRow{ + ID: def.ID, + Slug: def.Slug, + Name: def.Name, + Description: def.Description, + Shape: def.Shape, + PrimaryColor: def.PrimaryColor, + AccentColor: def.AccentColor, + RibbonColor: def.RibbonColor, + Label: def.Label, + Trigger: def.Trigger, + SVGPreview: svg, + }, + Shapes: badge.DefaultShapes, + Primary: badge.DefaultPrimaryColors, + Accent: badge.DefaultAccentColors, + Ribbon: badge.DefaultRibbonColors, + Saved: r.URL.Query().Get("saved") == "1", + } + + w.Header().Set("Content-Type", "text/html; charset=utf-8") + if err := pages.AdminBadgeEdit(view).Render(r.Context(), w); err != nil { + slog.Error("render admin badge edit", "err", err) + } +} + +// AdminBadgeEditSave handles POST /admin/badges/{id}/edit — updates the +// badge definition's visual design. +func (h *Handlers) AdminBadgeEditSave(w http.ResponseWriter, r *http.Request) { + idStr := chi.URLParam(r, "id") + id, err := strconv.Atoi(idStr) + if err != nil { + http.NotFound(w, r) + return + } + + if err := r.ParseForm(); err != nil { + http.Error(w, "bad request", http.StatusBadRequest) + return + } + + _, err = h.DB.ExecContext(r.Context(), ` + UPDATE badge_definitions SET + name = ?, + description = ?, + shape = ?, + primary_color = ?, + accent_color = ?, + ribbon_color = ?, + label = ? + WHERE id = ? + `, + strings.TrimSpace(r.FormValue("name")), + strings.TrimSpace(r.FormValue("description")), + r.FormValue("shape"), + r.FormValue("primary_color"), + r.FormValue("accent_color"), + r.FormValue("ribbon_color"), + strings.TrimSpace(r.FormValue("label")), + id, + ) + if err != nil { + slog.Warn("admin badge edit save", "id", id, "err", err) + http.Error(w, "failed to save", http.StatusInternalServerError) + return + } + + http.Redirect(w, r, fmt.Sprintf("/admin/badges/%d/edit?saved=1", id), http.StatusSeeOther) +} + +// lookupPDSForDIDAdmin is a helper that looks up PDS host from oauth_sessions. +func lookupPDSForDIDAdmin(ctx context.Context, db *sql.DB, did syntax.DID) string { + var host string + err := db.QueryRowContext(ctx, ` + SELECT data ->> 'host_url' FROM oauth_sessions + WHERE did = ? ORDER BY updated_at DESC LIMIT 1 + `, did.String()).Scan(&host) + if err == nil && host != "" { + return host + } + return "https://bsky.social" +} + // toAdminEventRows projects []event.AdminEvent into the view type. We // pre-format times here so the template stays dumb. func toAdminEventRows(list []event.AdminEvent, publicURL string) []pages.AdminEventRow { diff --git a/features/admin/pages/badges.templ b/features/admin/pages/badges.templ new file mode 100644 index 0000000..2b86770 --- /dev/null +++ b/features/admin/pages/badges.templ @@ -0,0 +1,138 @@ +package pages + +import ( + "fmt" + + "atmoquest/features/common/layouts" +) + +// AdminBadgesView is the data for /admin/badges. +type AdminBadgesView struct { + Badges []AdminBadgeRow +} + +// AdminBadgeRow is one badge definition in the admin list. +type AdminBadgeRow struct { + ID int + Slug string + Name string + Description string + Shape string + PrimaryColor string + AccentColor string + RibbonColor string + Label string + Trigger string + SVGPreview string +} + +// AdminBadgeEditView is the data for /admin/badges/{id}/edit. +type AdminBadgeEditView struct { + Badge AdminBadgeRow + Shapes []string + Primary []string + Accent []string + Ribbon []string + Saved bool + Error string +} + +templ AdminBadges(v AdminBadgesView) { + @layouts.Base("admin · badges", "Manage app-wide badge definitions.") { + @adminShell("badges", "") { +

App badges

+

+ These are the app-wide badge definitions. Click a badge to edit its design. +

+ + if len(v.Badges) == 0 { +

no badge definitions found.

+ } else { + + } + } + } +} + +templ AdminBadgeEdit(v AdminBadgeEditView) { + @layouts.Base("admin · edit badge", "Edit a badge definition.") { + @adminShell("badges", "") { +

edit badge — { v.Badge.Name }

+ + if v.Saved { +
+ ✓ badge design saved. +
+ } + if v.Error != "" { + + } + +
+
+ @templ.Raw(v.Badge.SVGPreview) +

{ v.Badge.Slug }

+ if v.Badge.Trigger != "" { +

trigger: { v.Badge.Trigger }

+ } +
+ +
+
+ + +
+
+ + +
+
+ + +
+ @colorSwatchField("primary_color", "primary color", v.Primary, v.Badge.PrimaryColor) + @colorSwatchField("accent_color", "accent color", v.Accent, v.Badge.AccentColor) + @colorSwatchField("ribbon_color", "ribbon color", v.Ribbon, v.Badge.RibbonColor) +
+ + +
+
+ + ← all badges +
+
+
+ + } + } +} + +func badgeIDStr(id int) string { + return fmt.Sprintf("%d", id) +} diff --git a/features/admin/pages/badges_templ.go b/features/admin/pages/badges_templ.go new file mode 100644 index 0000000..25adfe3 --- /dev/null +++ b/features/admin/pages/badges_templ.go @@ -0,0 +1,500 @@ +// Code generated by templ - DO NOT EDIT. + +// templ: version: v0.3.1020 +package pages + +//lint:file-ignore SA4006 This context is only used if a nested component is present. + +import "github.com/a-h/templ" +import templruntime "github.com/a-h/templ/runtime" + +import ( + "fmt" + + "atmoquest/features/common/layouts" +) + +// AdminBadgesView is the data for /admin/badges. +type AdminBadgesView struct { + Badges []AdminBadgeRow +} + +// AdminBadgeRow is one badge definition in the admin list. +type AdminBadgeRow struct { + ID int + Slug string + Name string + Description string + Shape string + PrimaryColor string + AccentColor string + RibbonColor string + Label string + Trigger string + SVGPreview string +} + +// AdminBadgeEditView is the data for /admin/badges/{id}/edit. +type AdminBadgeEditView struct { + Badge AdminBadgeRow + Shapes []string + Primary []string + Accent []string + Ribbon []string + Saved bool + Error string +} + +func AdminBadges(v AdminBadgesView) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var1 := templ.GetChildren(ctx) + if templ_7745c5c3_Var1 == nil { + templ_7745c5c3_Var1 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var2 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var3 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "

App badges

These are the app-wide badge definitions. Click a badge to edit its design.

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if len(v.Badges) == 0 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 2, "

no badge definitions found.

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 3, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + for _, b := range v.Badges { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 4, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templ.Raw(b.SVGPreview).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 6, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var5 string + templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(b.Name) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/badges.templ`, Line: 58, Col: 51} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 7, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var6 string + templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(b.Description) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/badges.templ`, Line: 59, Col: 64} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 8, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if b.Trigger != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 9, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var7 string + templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(b.Trigger) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/badges.templ`, Line: 61, Col: 64} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 10, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 11, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 12, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + return nil + }) + templ_7745c5c3_Err = adminShell("badges", "").Render(templ.WithChildren(ctx, templ_7745c5c3_Var3), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) + templ_7745c5c3_Err = layouts.Base("admin · badges", "Manage app-wide badge definitions.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func AdminBadgeEdit(v AdminBadgeEditView) templ.Component { + return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + if templ_7745c5c3_CtxErr := ctx.Err(); templ_7745c5c3_CtxErr != nil { + return templ_7745c5c3_CtxErr + } + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var8 := templ.GetChildren(ctx) + if templ_7745c5c3_Var8 == nil { + templ_7745c5c3_Var8 = templ.NopComponent + } + ctx = templ.ClearChildren(ctx) + templ_7745c5c3_Var9 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Var10 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context + templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) + if !templ_7745c5c3_IsBuffer { + defer func() { + templ_7745c5c3_BufErr := templruntime.ReleaseBuffer(templ_7745c5c3_Buffer) + if templ_7745c5c3_Err == nil { + templ_7745c5c3_Err = templ_7745c5c3_BufErr + } + }() + } + ctx = templ.InitializeContext(ctx) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "

edit badge — ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var11 string + templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(v.Badge.Name) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/badges.templ`, Line: 75, Col: 64} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if v.Saved { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "
✓ badge design saved.
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, " ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if v.Error != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "
error: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var12 string + templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(v.Error) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/badges.templ`, Line: 84, Col: 60} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templ.Raw(v.Badge.SVGPreview).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var13 string + templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(v.Badge.Slug) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/badges.templ`, Line: 91, Col: 65} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 21, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if v.Badge.Trigger != "" { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 22, "

trigger: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var14 string + templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(v.Badge.Trigger) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/badges.templ`, Line: 93, Col: 62} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 23, "

") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = colorSwatchField("primary_color", "primary color", v.Primary, v.Badge.PrimaryColor).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = colorSwatchField("accent_color", "accent color", v.Accent, v.Badge.AccentColor).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = colorSwatchField("ribbon_color", "ribbon color", v.Ribbon, v.Badge.RibbonColor).Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "
← all badges
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) + templ_7745c5c3_Err = adminShell("badges", "").Render(templ.WithChildren(ctx, templ_7745c5c3_Var10), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) + templ_7745c5c3_Err = layouts.Base("admin · edit badge", "Edit a badge definition.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var9), templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + return nil + }) +} + +func badgeIDStr(id int) string { + return fmt.Sprintf("%d", id) +} + +var _ = templruntime.GeneratedTemplate diff --git a/features/admin/pages/dashboard.templ b/features/admin/pages/dashboard.templ index 7d8faf6..4c2d4de 100644 --- a/features/admin/pages/dashboard.templ +++ b/features/admin/pages/dashboard.templ @@ -19,7 +19,7 @@ type AdminDashboardView struct { } templ AdminDashboard(v AdminDashboardView) { - @layouts.Base("admin — atmo.quest", "atmo.quest admin console.") { + @layouts.Base("admin", "atmo.quest admin console.") { @adminShell("dashboard", v.CurrentHandle) {
@statTile("users", intStr(v.TotalUsers), "total signed in") @@ -73,6 +73,7 @@ templ adminShell(active, currentHandle string) { @adminNavLink("/admin", "dashboard", active == "dashboard") @adminNavLink("/admin/users", "users", active == "users") @adminNavLink("/admin/events", "events", active == "events") + @adminNavLink("/admin/badges", "badges", active == "badges") ← back to app diff --git a/features/admin/pages/dashboard_templ.go b/features/admin/pages/dashboard_templ.go index 58e3d85..ff096b9 100644 --- a/features/admin/pages/dashboard_templ.go +++ b/features/admin/pages/dashboard_templ.go @@ -143,7 +143,7 @@ func AdminDashboard(v AdminDashboardView) templ.Component { } return nil }) - templ_7745c5c3_Err = layouts.Base("admin — atmo.quest", "atmo.quest admin console.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = layouts.Base("admin", "atmo.quest admin console.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -285,6 +285,10 @@ func adminShell(active, currentHandle string) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } + templ_7745c5c3_Err = adminNavLink("/admin/badges", "badges", active == "badges").Render(ctx, templ_7745c5c3_Buffer) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "← back to app") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err @@ -330,7 +334,7 @@ func adminNavLink(href, label string, active bool) templ.Component { var templ_7745c5c3_Var14 templ.SafeURL templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(href)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/dashboard.templ`, Line: 92, Col: 64} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/dashboard.templ`, Line: 93, Col: 64} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { @@ -343,7 +347,7 @@ func adminNavLink(href, label string, active bool) templ.Component { var templ_7745c5c3_Var15 string templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(label) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/dashboard.templ`, Line: 92, Col: 78} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/dashboard.templ`, Line: 93, Col: 78} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) if templ_7745c5c3_Err != nil { @@ -361,7 +365,7 @@ func adminNavLink(href, label string, active bool) templ.Component { var templ_7745c5c3_Var16 templ.SafeURL templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL(href)) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/dashboard.templ`, Line: 94, Col: 54} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/dashboard.templ`, Line: 95, Col: 54} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) if templ_7745c5c3_Err != nil { @@ -374,7 +378,7 @@ func adminNavLink(href, label string, active bool) templ.Component { var templ_7745c5c3_Var17 string templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(label) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/dashboard.templ`, Line: 94, Col: 64} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/dashboard.templ`, Line: 95, Col: 64} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) if templ_7745c5c3_Err != nil { diff --git a/features/admin/pages/events.templ b/features/admin/pages/events.templ index 263392b..1d2dd1e 100644 --- a/features/admin/pages/events.templ +++ b/features/admin/pages/events.templ @@ -21,7 +21,7 @@ type AdminEventRow struct { } templ AdminEvents(v AdminEventsView) { - @layouts.Base("admin · events — atmo.quest", "Manage atmo.quest events.") { + @layouts.Base("admin · events", "Manage atmo.quest events.") { @adminShell("events", "") {
▸ new event @@ -57,7 +57,14 @@ templ AdminEvents(v AdminEventsView) { { intStr(ev.ExpectedAttendees) } { ev.QRPublicURL } + QR ↓ badge → +
+ +
+
+ +
} @@ -81,7 +88,7 @@ type AdminEventFormView struct { } templ AdminEventNew(v AdminEventFormView) { - @layouts.Base("admin · new event — atmo.quest", "Create a new atmo.quest event.") { + @layouts.Base("admin · new event", "Create a new atmo.quest event.") { @adminShell("events", "") {

create event

@@ -136,29 +143,69 @@ type AdminEventBadgeView struct { Primary []string Accent []string Ribbon []string - Design AdminBadgeDesign - SVGPreview string - QRPublicURL string + Design AdminBadgeDesign + SVGPreview string + QRPublicURL string + EventBadges []EventBadgeOverview // all badge types for this event + SelectedBadgeType string // which badge type is being edited +} + +// EventBadgeOverview shows the status of a badge type for an event. +type EventBadgeOverview struct { + Slug string + Name string + Description string + Designed bool // true if a design has been saved + Label string // the label on the badge (if designed) } // AdminBadgeDesign is the current state of the badge being designed. type AdminBadgeDesign struct { - Shape string - PrimaryColor string - AccentColor string - RibbonColor string - Label string - Signature string - SigningKeyID string + Shape string + PrimaryColor string + AccentColor string + RibbonColor string + Label string + MessageTemplate string + Signature string + SigningKeyID string } templ AdminEventBadge(v AdminEventBadgeView) { - @layouts.Base("admin · badge designer — atmo.quest", "Design an event badge.") { + @layouts.Base("admin · badge designer", "Design an event badge.") { @adminShell("events", "") {

badge designer — { v.EventName }

+ if len(v.EventBadges) > 0 { + + } +
-
+
@templ.Raw(v.SVGPreview)

{ v.EventURI } @@ -171,6 +218,7 @@ templ AdminEventBadge(v AdminEventBadgeView) {

+
+
+ + + shown when someone views their badge +
- download QR + ← all events

@@ -203,6 +256,7 @@ templ AdminEventBadge(v AdminEventBadgeView) {

+ } } } diff --git a/features/admin/pages/events_templ.go b/features/admin/pages/events_templ.go index e531846..5992073 100644 --- a/features/admin/pages/events_templ.go +++ b/features/admin/pages/events_templ.go @@ -191,20 +191,59 @@ func AdminEvents(v AdminEventsView) templ.Component { return templ_7745c5c3_Err } var templ_7745c5c3_Var11 templ.SafeURL - templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/admin/events/" + ev.QRToken + "/badge")) + templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinURLErrs(templ.SafeURL("/e/" + ev.QRToken + "/qr.svg")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 60, Col: 104} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 60, Col: 94} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\">badge →") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 13, "\">QR ↓ badge →
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 14, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -217,7 +256,7 @@ func AdminEvents(v AdminEventsView) templ.Component { } return nil }) - templ_7745c5c3_Err = layouts.Base("admin · events — atmo.quest", "Manage atmo.quest events.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = layouts.Base("admin · events", "Manage atmo.quest events.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -252,12 +291,12 @@ func AdminEventNew(v AdminEventFormView) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var12 := templ.GetChildren(ctx) - if templ_7745c5c3_Var12 == nil { - templ_7745c5c3_Var12 = templ.NopComponent + templ_7745c5c3_Var15 := templ.GetChildren(ctx) + if templ_7745c5c3_Var15 == nil { + templ_7745c5c3_Var15 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var13 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var16 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -269,7 +308,7 @@ func AdminEventNew(v AdminEventFormView) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var14 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var17 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -281,107 +320,107 @@ func AdminEventNew(v AdminEventFormView) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 15, "

create event

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "

create event

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if v.Error != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 16, "
heads up: ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 19, "
heads up: ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var15 string - templ_7745c5c3_Var15, templ_7745c5c3_Err = templ.JoinStringErrs(v.Error) + var templ_7745c5c3_Var18 string + templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(v.Error) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 90, Col: 63} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 97, Col: 63} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var15)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 17, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 20, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 18, "
cancel

creating an event writes a quest.atmo.event record to your PDS. you'll be taken to the badge designer next.

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "\">
cancel

creating an event writes a quest.atmo.event record to your PDS. you'll be taken to the badge designer next.

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = adminShell("events", "").Render(templ.WithChildren(ctx, templ_7745c5c3_Var14), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = adminShell("events", "").Render(templ.WithChildren(ctx, templ_7745c5c3_Var17), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = layouts.Base("admin · new event — atmo.quest", "Create a new atmo.quest event.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var13), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = layouts.Base("admin · new event", "Create a new atmo.quest event.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var16), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -391,27 +430,39 @@ func AdminEventNew(v AdminEventFormView) templ.Component { // AdminEventBadgeView feeds the badge designer page. type AdminEventBadgeView struct { - EventURI string - EventName string - Token string - Shapes []string - Primary []string - Accent []string - Ribbon []string - Design AdminBadgeDesign - SVGPreview string - QRPublicURL string + EventURI string + EventName string + Token string + Shapes []string + Primary []string + Accent []string + Ribbon []string + Design AdminBadgeDesign + SVGPreview string + QRPublicURL string + EventBadges []EventBadgeOverview // all badge types for this event + SelectedBadgeType string // which badge type is being edited +} + +// EventBadgeOverview shows the status of a badge type for an event. +type EventBadgeOverview struct { + Slug string + Name string + Description string + Designed bool // true if a design has been saved + Label string // the label on the badge (if designed) } // AdminBadgeDesign is the current state of the badge being designed. type AdminBadgeDesign struct { - Shape string - PrimaryColor string - AccentColor string - RibbonColor string - Label string - Signature string - SigningKeyID string + Shape string + PrimaryColor string + AccentColor string + RibbonColor string + Label string + MessageTemplate string + Signature string + SigningKeyID string } func AdminEventBadge(v AdminEventBadgeView) templ.Component { @@ -430,12 +481,12 @@ func AdminEventBadge(v AdminEventBadgeView) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var21 := templ.GetChildren(ctx) - if templ_7745c5c3_Var21 == nil { - templ_7745c5c3_Var21 = templ.NopComponent + templ_7745c5c3_Var24 := templ.GetChildren(ctx) + if templ_7745c5c3_Var24 == nil { + templ_7745c5c3_Var24 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Var22 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var25 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -447,7 +498,7 @@ func AdminEventBadge(v AdminEventBadgeView) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var23 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { + templ_7745c5c3_Var26 := templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { templ_7745c5c3_W, ctx := templ_7745c5c3_Input.Writer, templ_7745c5c3_Input.Context templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templruntime.GetBuffer(templ_7745c5c3_W) if !templ_7745c5c3_IsBuffer { @@ -459,20 +510,140 @@ func AdminEventBadge(v AdminEventBadgeView) templ.Component { }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 24, "

badge designer — ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "

badge designer — ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var24 string - templ_7745c5c3_Var24, templ_7745c5c3_Err = templ.JoinStringErrs(v.EventName) + var templ_7745c5c3_Var27 string + templ_7745c5c3_Var27, templ_7745c5c3_Err = templ.JoinStringErrs(v.EventName) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 158, Col: 67} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 177, Col: 67} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var27)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var24)) + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 25, "
") + if len(v.EventBadges) > 0 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, "
Event badges — click to design
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + for _, eb := range v.EventBadges { + var templ_7745c5c3_Var28 = []any{"settings-row", templ.KV("active-badge-type", eb.Slug == v.SelectedBadgeType)} + templ_7745c5c3_Err = templ.RenderCSSItems(ctx, templ_7745c5c3_Buffer, templ_7745c5c3_Var28...) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if eb.Designed { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 33, "◆") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 34, "◇") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 35, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var31 string + templ_7745c5c3_Var31, templ_7745c5c3_Err = templ.JoinStringErrs(eb.Name) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 193, Col: 50} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var31)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 36, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var32 string + templ_7745c5c3_Var32, templ_7745c5c3_Err = templ.JoinStringErrs(eb.Description) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 194, Col: 56} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var32)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 37, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if eb.Designed { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 38, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var33 string + templ_7745c5c3_Var33, templ_7745c5c3_Err = templ.JoinStringErrs(eb.Label) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 197, Col: 57} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var33)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } else { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 40, "
not designed
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 41, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 42, "
") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -480,125 +651,138 @@ func AdminEventBadge(v AdminEventBadgeView) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 26, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var25 string - templ_7745c5c3_Var25, templ_7745c5c3_Err = templ.JoinStringErrs(v.EventURI) + var templ_7745c5c3_Var34 string + templ_7745c5c3_Var34, templ_7745c5c3_Err = templ.JoinStringErrs(v.EventURI) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 164, Col: 25} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 211, Col: 25} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var25)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var34)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 27, "

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 45, "

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } if v.Design.Signature != "" { - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 28, "

● signed (key ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 46, "

● signed (key ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var26 string - templ_7745c5c3_Var26, templ_7745c5c3_Err = templ.JoinStringErrs(v.Design.SigningKeyID) + var templ_7745c5c3_Var35 string + templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(v.Design.SigningKeyID) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 168, Col: 70} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 215, Col: 70} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var26)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 29, ")

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 47, ")

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 30, "
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 57, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -614,71 +798,71 @@ func AdminEventBadge(v AdminEventBadgeView) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 39, "

public scan URL: shown when someone views their badge

public scan URL: ") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 61, "\">") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var35 string - templ_7745c5c3_Var35, templ_7745c5c3_Err = templ.JoinStringErrs(v.QRPublicURL) + var templ_7745c5c3_Var45 string + templ_7745c5c3_Var45, templ_7745c5c3_Err = templ.JoinStringErrs(v.QRPublicURL) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 199, Col: 93} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `features/admin/pages/events.templ`, Line: 252, Col: 93} } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var35)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var45)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 43, "
saving signs this design with the server's admin key (ed25519). tampering with the row in the database will fail signature verification on next read.

") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 62, "
saving signs this design with the server's admin key (ed25519). tampering with the row in the database will fail signature verification on next read.

") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = adminShell("events", "").Render(templ.WithChildren(ctx, templ_7745c5c3_Var23), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = adminShell("events", "").Render(templ.WithChildren(ctx, templ_7745c5c3_Var26), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } return nil }) - templ_7745c5c3_Err = layouts.Base("admin · badge designer — atmo.quest", "Design an event badge.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var22), templ_7745c5c3_Buffer) + templ_7745c5c3_Err = layouts.Base("admin · badge designer", "Design an event badge.").Render(templ.WithChildren(ctx, templ_7745c5c3_Var25), templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -703,128 +887,128 @@ func colorSwatchField(name, label string, options []string, selected string) tem }() } ctx = templ.InitializeContext(ctx) - templ_7745c5c3_Var36 := templ.GetChildren(ctx) - if templ_7745c5c3_Var36 == nil { - templ_7745c5c3_Var36 = templ.NopComponent + templ_7745c5c3_Var46 := templ.GetChildren(ctx) + if templ_7745c5c3_Var46 == nil { + templ_7745c5c3_Var46 = templ.NopComponent } ctx = templ.ClearChildren(ctx) - templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 44, "
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/features/admin/pages/users.templ b/features/admin/pages/users.templ index d95bc24..adb356d 100644 --- a/features/admin/pages/users.templ +++ b/features/admin/pages/users.templ @@ -25,7 +25,7 @@ type AdminUserRow struct { } templ AdminUsers(v AdminUsersView) { - @layouts.Base("admin · users — atmo.quest", "Manage atmo.quest users.") { + @layouts.Base("admin · users", "Manage atmo.quest users.") { @adminShell("users", "") {