console: account nav gains Team and Settings; the address opens your settings
/settings itself lands with the personal-settings page later in this branch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
4 files changed,
+36
−2
internal/console/ownership.go+1 −0internal/console/server.go+2 −0internal/console/server_account_test.go+27 −0internal/console/templates/layout.html+6 −2
diff --git a/internal/console/ownership.go b/internal/console/ownership.go| index 15fb14578..e7ab12b33 100644 |
| --- a/internal/console/ownership.go |
| +++ b/internal/console/ownership.go |
| @@ -180,6 +180,7 @@ func (s *Server) handleTrash(w http.ResponseWriter, r *http.Request) { |
| } |
| p := s.accountPage(w, r, addr, raw, acct, role) |
| p.Title = "Trash" |
| + p.ActiveNav = "apps" |
| p.Trashed = TrashedApps(r.Context(), s.cfg.Releases, acct.Sqid, rows) |
| s.render(w, http.StatusOK, "trash", p) |
| } |
diff --git a/internal/console/server.go b/internal/console/server.go| index 70a81f386..518bb2e65 100644 |
| --- a/internal/console/server.go |
| +++ b/internal/console/server.go |
| @@ -1134,6 +1134,7 @@ func (s *Server) handleAccountHome(w http.ResponseWriter, r *http.Request) { |
| } |
| p := s.accountPage(w, r, addr, raw, acct, role) |
| p.Title = acct.Name |
| + p.ActiveNav = "apps" |
| if c, err := r.Cookie(welcomeCookie); err == nil && c.Value == "1" { |
| p.Welcome = true |
| s.clearCookie(w, welcomeCookie) |
| @@ -1279,6 +1280,7 @@ func (s *Server) handleApp(w http.ResponseWriter, r *http.Request) { |
| } |
| p := s.accountPage(w, r, addr, raw, acct, role) |
| p.Title = app |
| + p.ActiveNav = "apps" |
| p.Detail = &views[0] |
| versions, err := Versions(s.cfg.Releases, acct.Sqid, app) |
| if err != nil { |
diff --git a/internal/console/server_account_test.go b/internal/console/server_account_test.go| index e67ca3e0c..d1ea9e302 100644 |
| --- a/internal/console/server_account_test.go |
| +++ b/internal/console/server_account_test.go |
| @@ -764,3 +764,30 @@ func TestSameAppNameInTwoAccounts(t *testing.T) { |
| } |
| } |
| } |
| + |
| +// The account nav grows Apps · Team · Settings, account-scoped and marking |
| +// whichever section the page sits in, and the signed-in address becomes the |
| +// door to personal settings (registered later this branch). |
| +func TestAccountNavHasSections(t *testing.T) { |
| + fx := newConsoleFixture(t) |
| + c := signInPaul(t, fx) |
| + _, home, _ := get(t, c, fx.ts.URL+"/") |
| + nav := strings.TrimSuffix(home, "/") |
| + _, _, body := get(t, c, fx.ts.URL+home) |
| + h := header(t, body) |
| + for _, want := range []string{ |
| + `href="` + nav + `/"`, `href="` + nav + `/team"`, `href="` + nav + `/settings"`, |
| + `href="/settings"`, // the address is the door to personal settings |
| + } { |
| + if !strings.Contains(h, want) { |
| + t.Fatalf("header missing %q in:\n%s", want, h) |
| + } |
| + } |
| + if strings.Count(h, `aria-current="page"`) != 1 { |
| + t.Fatalf("want exactly one active nav section on the apps page, got:\n%s", h) |
| + } |
| + _, _, team := get(t, c, fx.ts.URL+nav+"/team") |
| + if !strings.Contains(header(t, team), `/team" aria-current="page"`) { |
| + t.Fatal("team page does not mark Team active") |
| + } |
| +} |
diff --git a/internal/console/templates/layout.html b/internal/console/templates/layout.html| index 2acd0eecb..21494314e 100644 |
| --- a/internal/console/templates/layout.html |
| +++ b/internal/console/templates/layout.html |
| @@ -266,8 +266,12 @@ |
| <div class="brand"><a href="/">CARLOS console</a></div> |
| {{end}} |
| {{if .Address}}<nav> |
| - {{if .Account}}<a href="{{.Nav}}/">Apps</a>{{end}} |
| - <span class="addr">{{.Address}}</span> |
| + {{if .Account}} |
| + <a href="{{.Nav}}/"{{if eq .ActiveNav "apps"}} aria-current="page"{{end}}>Apps</a> |
| + <a href="{{.Nav}}/team"{{if eq .ActiveNav "team"}} aria-current="page"{{end}}>Team</a> |
| + <a href="{{.Nav}}/settings"{{if eq .ActiveNav "settings"}} aria-current="page"{{end}}>Settings</a> |
| + {{end}} |
| + <a class="addr" href="/settings" title="Your settings">{{.Address}}</a> |
| <form method="post" action="/auth/signout"> |
| <input type="hidden" name="csrf" value="{{.CSRF}}"> |
| <button class="linkish" type="submit">Sign out</button> |