Skip to content

Commit 0a7a04f

Browse files
uppertoeclaude
andcommitted
still-to-try: members see only the lines they can act on; open by default
A member of a shared account was offered "name the household" and "give someone access", both owner-only, and landed on a form they cannot see. Those two lines now appear for the account owner only; naming the permit and notification settings stay for everyone. The accordion opens by default; collapsing it, like dismissing it, is remembered per browser and per tab. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0121j6qgGwUSLGQ48jWZN98F
1 parent e4c468f commit 0a7a04f

7 files changed

Lines changed: 29 additions & 19 deletions

File tree

internal/server/checklist.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ type checkItem struct {
2626
// done. Every input is a count or a row the page already needs, so the card
2727
// costs a few index reads. A tab whose lines are all ticked returns nil, and so
2828
// does one with nothing to offer yet (no permit managed).
29-
func (s *Server) checklistFor(ctx context.Context, owner, user, tab string) *checklistView {
29+
// isPrimary drops the lines only the account owner can act on (naming the
30+
// household, sharing access) for a member, who would otherwise be sent to a
31+
// form they cannot see.
32+
func (s *Server) checklistFor(ctx context.Context, owner, user string, isPrimary bool, tab string) *checklistView {
3033
did := func(action string) bool {
3134
n, err := s.store.CountChanges(ctx, owner, action)
3235
return err == nil && n > 0
@@ -85,10 +88,13 @@ func (s *Server) checklistFor(ctx context.Context, owner, user, tab string) *che
8588
prefs, _ := s.store.HasNotifyPref(ctx, user)
8689
items = []checkItem{
8790
{Label: "Name the permit, as it appears on the schedule and in your emails", Href: "/schedule", Done: named},
88-
{Label: "Name the household, so visitors see it instead of your email", Href: "#household", Done: household != ""},
89-
{Label: "Give someone else in the house access", Href: "#shared", Done: members > 0},
90-
{Label: "Set how you want to be told about changes", Href: "#notifications", Done: prefs},
9191
}
92+
if isPrimary {
93+
items = append(items,
94+
checkItem{Label: "Name the household, so visitors see it instead of your email", Href: "#household", Done: household != ""},
95+
checkItem{Label: "Give someone else in the house access", Href: "#shared", Done: members > 0})
96+
}
97+
items = append(items, checkItem{Label: "Set how you want to be told about changes", Href: "#notifications", Done: prefs})
9298
default:
9399
return nil
94100
}

internal/server/checklist_test.go

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ func TestChecklistTicksAndRetires(t *testing.T) {
1414
s := newAuthzServer(t)
1515
ctx := context.Background()
1616
const owner = "own@example.com"
17-
if s.checklistFor(ctx, owner, owner, "schedule") != nil {
17+
if s.checklistFor(ctx, owner, owner, true, "schedule") != nil {
1818
t.Fatal("schedule card offered before any permit is managed")
1919
}
2020
pid, err := s.store.UpsertPermit(ctx, owner, "14576", "14", "")
2121
if err != nil {
2222
t.Fatal(err)
2323
}
24-
v := s.checklistFor(ctx, owner, owner, "schedule")
24+
v := s.checklistFor(ctx, owner, owner, true, "schedule")
2525
if v == nil || v.Done != 0 || len(v.Items) != 2 {
2626
t.Fatalf("fresh schedule card = %+v", v)
2727
}
@@ -32,35 +32,39 @@ func TestChecklistTicksAndRetires(t *testing.T) {
3232
if err := s.store.SetRule(ctx, pid, 0, time.Monday, vid); err != nil {
3333
t.Fatal(err)
3434
}
35-
v = s.checklistFor(ctx, owner, owner, "schedule")
35+
v = s.checklistFor(ctx, owner, owner, true, "schedule")
3636
if v == nil || v.Done != 1 || len(v.Items) != 3 || !v.Items[0].Done || v.Items[0].Href != "" || v.Items[1].Href == "" {
3737
t.Fatalf("after a roster day: %+v", v)
3838
}
3939
// Regos: one saved, no email yet.
40-
v = s.checklistFor(ctx, owner, owner, "vehicles")
40+
v = s.checklistFor(ctx, owner, owner, true, "vehicles")
4141
if v == nil || v.Done != 1 || !v.Items[0].Done || v.Items[1].Done {
4242
t.Fatalf("regos card = %+v", v)
4343
}
4444
if err := s.store.SetVehicleEmail(ctx, owner, vid, "nana@example.com"); err != nil {
4545
t.Fatal(err)
4646
}
47-
if s.checklistFor(ctx, owner, owner, "vehicles") != nil {
47+
if s.checklistFor(ctx, owner, owner, true, "vehicles") != nil {
4848
t.Fatal("regos card still shown with everything done")
4949
}
5050
// Guests: the three tools come from the change log.
5151
if err := s.store.RecordChange(ctx, owner, owner, store.ActionDoorQRShow, "", ""); err != nil {
5252
t.Fatal(err)
5353
}
54-
v = s.checklistFor(ctx, owner, owner, "guests")
54+
v = s.checklistFor(ctx, owner, owner, true, "guests")
5555
if v == nil || v.Done != 1 || !v.Items[0].Done {
5656
t.Fatalf("guests card = %+v", v)
5757
}
5858
// Settings: nothing done on a fresh account.
59-
v = s.checklistFor(ctx, owner, owner, "settings")
59+
v = s.checklistFor(ctx, owner, owner, true, "settings")
6060
if v == nil || v.Done != 0 || len(v.Items) != 4 {
6161
t.Fatalf("settings card = %+v", v)
6262
}
63-
if s.checklistFor(ctx, owner, owner, "activity") != nil {
63+
// A member is not offered the owner-only lines.
64+
if m := s.checklistFor(ctx, owner, owner, false, "settings"); m == nil || len(m.Items) != 2 {
65+
t.Fatalf("member settings card = %+v, want the two lines a member can act on", m)
66+
}
67+
if s.checklistFor(ctx, owner, owner, true, "activity") != nil {
6468
t.Fatal("a tab without a card returned one")
6569
}
6670
}

internal/server/guest_manage.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ func (s *Server) guestsPage(w http.ResponseWriter, r *http.Request) {
3131
if !ok {
3232
return
3333
}
34-
base.Checklist = s.checklistFor(r.Context(), base.Owner, base.User.Email, "guests")
34+
base.Checklist = s.checklistFor(r.Context(), base.Owner, base.User.Email, base.IsPrimary, "guests")
3535
// Success feedback after deciding a printed-QR request. These values land in the
3636
// green success banner — the most trusted element on a page whose whole premise is
3737
// custody of a tenant password — and although our own redirects write them, nothing

internal/server/schedule.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ func (s *Server) schedule(w http.ResponseWriter, r *http.Request) {
158158
}
159159
}
160160
}
161-
base.Checklist = s.checklistFor(ctx, owner, base.User.Email, "schedule")
161+
base.Checklist = s.checklistFor(ctx, owner, base.User.Email, base.IsPrimary, "schedule")
162162
s.render(w, base)
163163
}
164164

internal/server/settings.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func (s *Server) settingsPage(w http.ResponseWriter, r *http.Request) {
2424
owner := base.Owner
2525
user := base.User.Email // the signed-in person; notification prefs are theirs
2626
base.Settings = &settingsData{}
27-
base.Checklist = s.checklistFor(ctx, owner, user, "settings")
27+
base.Checklist = s.checklistFor(ctx, owner, user, base.IsPrimary, "settings")
2828
base.Settings.HouseholdName = s.householdOrEmpty(ctx, owner)
2929
if r.URL.Query().Get("named") == "1" {
3030
if base.Settings.HouseholdName == "" {

internal/server/templates/nav.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@
6565

6666
{{define "checklist"}}{{/* The per-tab "still to try" strip: one collapsed line in the
6767
tab's own hue, opening into the list. Rendered from server state on page
68-
load only (never swapped live), so ticks never move anything mid-visit; the
69-
open state is remembered per browser and per tab. */}}
70-
{{if .Checklist}}<section class="todo" style="--hue:var(--sec-{{.Checklist.Key}})" x-data="{open: localStorage.getItem('pstonn-todo-{{.Checklist.Key}}')==='1', hide: localStorage.getItem('pstonn-todo-{{.Checklist.Key}}-hide')==='1'}" x-show="!hide" x-cloak>
68+
load only (never swapped live), so ticks never move anything mid-visit. Open
69+
by default; collapsing or dismissing is remembered per browser and per tab. */}}
70+
{{if .Checklist}}<section class="todo" style="--hue:var(--sec-{{.Checklist.Key}})" x-data="{open: localStorage.getItem('pstonn-todo-{{.Checklist.Key}}')!=='0', hide: localStorage.getItem('pstonn-todo-{{.Checklist.Key}}-hide')==='1'}" x-show="!hide" x-cloak>
7171
<button type="button" class="todo-head" :aria-expanded="open" @click="open=!open; localStorage.setItem('pstonn-todo-{{.Checklist.Key}}', open ? '1' : '0')">
7272
<span class="todo-title">Still to try</span><span class="todo-count">{{.Checklist.Done}} of {{len .Checklist.Items}}</span>
7373
<svg class="todo-chev" :class="{open: open}" viewBox="0 0 24 24" aria-hidden="true"><path d="M6 9l6 6 6-6"/></svg>

internal/server/vehicles.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ func (s *Server) vehiclesPage(w http.ResponseWriter, r *http.Request) {
5555
base.Vehicles, _, _, _ = vehicleViews(vehicles)
5656
base.Regions = s.tenant.Regions(r.Context(), base.Owner, "")
5757
base.BookFAB = s.hasLivePermit(r.Context(), base.Owner)
58-
base.Checklist = s.checklistFor(r.Context(), base.Owner, base.User.Email, "vehicles")
58+
base.Checklist = s.checklistFor(r.Context(), base.Owner, base.User.Email, base.IsPrimary, "vehicles")
5959
if r.URL.Query().Get("added") == "1" {
6060
// The landing after adding a permit with no rego saved yet (see addPermit).
6161
base.Flash = "Permit added. Add the regos of the people who visit you, then set up the schedule."

0 commit comments

Comments
 (0)