8000 feat(ui): POC B — open the agenda on everything my groups have on by MichaelDimmitt · Pull Request #675 · huddlz-hq/huddlz · GitHub
[go: up one dir, main page]

Skip to content

feat(ui): POC B — open the agenda on everything my groups have on - #675

Closed
MichaelDimmitt wants to merge 7 commits into
huddlz-hq:mainfrom
MichaelDimmitt:poc/670-default-landing
Closed

MichaelDimmitt wants to merge 7 commits into
huddlz-hq:mainfrom
MichaelDimmitt:poc/670-default-landing

Conversation

@MichaelDimmitt
@MichaelDimmitt MichaelDimmitt commented Oct 9, 2026 •
Copy link
Copy Markdown
Contributor

POC variant B — Better default landing for #670. The agenda is the signed-in home, but it opened on a person's own RSVPs — so the huddl they arrived for was absent from it by definition: their group had it scheduled, and they had not responded to it yet. Seeing it cost a click on the Groups chip, on every visit.

Summary

The flip itself is two functions, and the second one is the subtle half:

 handle_params(params)
-  scope = parse_scope(params["scope"])          # "groups" -> :groups, else :mine
+  scope = parse_scope(params["scope"], user.agenda_landing)
+                                                # explicit ?scope= wins; else the
+                                                # person's preference, default :groups

 calendar_path(nav, overrides)                   # the page's own URL
-  scope: scope == :groups && "groups"           # omit the hardcoded default
+  scope: scope != nav.landing && to_string(scope)
+                                                # omit THEIR default, emit the other

That second line is load-bearing. Left alone, the RSVPs chip would link to a bare /agenda, which now resolves back to Groups — the chip becomes a no-op and the filter is genuinely unreachable, which is the failure mode the spec's "Watch out" names. Chip counts needed no change: handle_params/3 already computes both scopes every render.

The preference is shaped on theme_preference, and is a filter preference, not a destination:

Huddlz.Accounts.AgendaLanding        :groups | :mine
user.ex
├── attribute :agenda_landing        default :groups, allow_nil? false
├── update :update_agenda_landing    accept [:agenda_landing]
└── policy                           authorize_if id == ^actor(:id)

profile_live.ex   "Open my agenda on" — two cards, saves on choice
auth_controller.ex
└── return_to(conn, user)
      [params["return_to"], session[:return_to], agenda_landing_path(user)]
      |> Enum.find_value(&AuthReturnTo.validate/1)   # preference fills the blank only

An explicit return_to still wins, so someone who clicked RSVP on a huddl goes back to that huddl, not to their agenda. sign_out keeps the old user-less return_to/1.

Clicks from sign-in to an un-RSVP'd huddl in one of your groups: 3 → 2. Before: land on the agenda showing own RSVPs (huddl absent) → Groups chip (1) → the huddl (2) → RSVP (3). After: the agenda already shows it → the huddl (1) → RSVP (2).

Evidence

Screenshot

Variant B

The agenda opening on Groups rather than RSVPs: RSVPs 0 · Groups 1. Under the old default this page would have been empty, because the huddl belongs to a group the person has joined but not yet responded to.

Before — the three scenarios describing the new behaviour fail; the three describing behaviour that must be preserved already pass:

1) The agenda opens on everything my groups have on
   Then the agenda lists "Hands-on with Ash Framework" without an RSVP status
   Could not find any elements with selector
     "#calendar-agenda .cal-agenda-entry[data-status=open] .cal-agenda-title"
     and text "Hands-on with Ash Framework"

2) Choosing to land on my RSVPs instead
   And I choose to open the agenda on "Just my RSVPs"
   Could not find element with label "Just my RSVPs"

3) Landing after sign in follows my agenda preference
   Then the user lands on their own RSVPs
   PhoenixTest.Assertions.assert_path/3

After — mix test test/features_test.exs --only agenda_default_scope is green, including the three preserved scenarios (RSVPs reachable, counts right from either filter, person in zero groups). mix compile --force --warnings-as-errors is clean.

The displaced scenario: calendar_group_scope.feature's "The calendar starts with what I have responded to" asserted the old starting point. It is reframed as "Narrowing to what I have responded to", starting from an explicit ?scope=mine — same behaviour, same coverage, different entry point.

Merge Danger

Door: two-way for the default; one-way-ish for the column.

Reverting parse_scope/2 and calendar_path/2 restores the old landing exactly — no data is involved. The agenda_landing column is additive with a default, so a revert leaves an unused column rather than breaking anything; dropping it needs its own migration.

Blast Radius: every signed-in agenda view.

  • Every signed-in person's landing changes on deploy with no opt-in. People who liked the old behaviour must find the new Profile → Agenda panel, which is discovery this POC does not solve (that is variant E's question).
  • Generated calendar URLs change shape: groups is now the omitted scope and mine the emitted one, so links, the Week/Month nav and the day panel's close link all shift. Seven unit tests and three scenarios pinned the old shape and are updated here.
  • Empty-state copy changes under the Groups filter, where the old "huddlz you RSVP to show up here" was only half true.
  • No new query path and no data-loading change: both scopes were already loaded on every render for the chip counts.

Part of #670

This is a POC for comparison, not intended to merge as-is. Verified by
scenario (failing -> passing) and a clean --warnings-as-errors compile
only; mix precommit is a deliberate follow-up after review.

Model: Claude Opus 5.5 (claude-opus-5-5)

🤖 Generated with Claude Code

Issue huddlz-hq#670 asks for several competing proofs of concept, each as its own
draft PR, so one can be chosen and the rest declined. This is the shared
brief those PRs are built from: the problem, what the code already does,
one section per variant with its own task list, and the rubric they are
all judged against.

Records three findings that shape the variants: the return_to plumbing is
already complete and safe but only the huddl page's RSVP button produces
such a link; the agenda already loads both scopes every render to feed its
chip counts; and the agenda has no query for huddlz outside a person's
groups.

Part of huddlz-hq#670
The POC variants are compared partly on whether people want to use them,
so a variant that looks unfinished loses Desirable and Delightful for
reasons unrelated to its idea. Ask each one to polish the surface it
changes, and keep the scope discipline pointed at everything else.

Part of huddlz-hq#670
Six scenarios for variant B of the post-login landing POCs: the agenda
opening on everything a person's groups have scheduled rather than only
their own RSVPs, the RSVPs filter staying one click away, the filter
counts staying right from either filter, the person in zero groups, and
the preference that opts back out.

These fail against the current default, which is the point: they are the
outside-in specification the implementation follows.

Part of huddlz-hq#670
The agenda was the signed-in home but opened on a person's own RSVPs, so
the huddl someone arrived for was absent from it by definition: their
groups had it scheduled, and they had not responded to it yet. Seeing it
cost a click on the Groups chip, every visit.

Groups is now the filter the agenda opens on, from a new per-person
preference that defaults to it.

- `parse_scope/2` takes the person's `agenda_landing` as the fallback. An
  explicit `?scope=` in the address still wins, so both filters stay
  linkable and bookmarkable.
- `calendar_path/2` now omits whichever scope matches the person's
  preference rather than a hardcoded `:mine`. Without this the RSVPs
  chip would link to a bare `/agenda`, which resolves back to Groups,
  making the chip a no-op and the filter unreachable.
- the chip counts needed no change: `handle_params/3` already computes
  both scopes on every render.
- the empty states say what is true under the Groups filter. "huddlz you
  RSVP to show up here" was only half the story, so an empty Groups
  agenda now names the action that fills it: joining a group.

One existing scenario asserted the old starting point and is reframed to
start from an explicit `?scope=mine`; its behaviour is unchanged and
still covered.

Part of huddlz-hq#670
Groups is the better default, but it is not everyone's. `agenda_landing`
is a per-person choice between everything their groups have scheduled and
just their own RSVPs, shaped on `theme_preference`: an atom-typed
attribute with a default, a narrow update action accepting one field, and
a policy allowing a person to update only their own.

It is a filter preference, not a destination. The agenda stays the one
signed-in home, so the choice never changes which page someone lands on,
only what that page opens showing. An enum of two atoms cannot become a
bad redirect the way a stored path could.

- Profile gains an Agenda panel: two cards that save on choice, the way
  the appearance menu does. Validation is the Ash enum's, so there is no
  HTML5 `required` and a bad value comes back as an error.
- the post-auth redirect honours it by adding it as the last candidate in
  the existing `return_to` chain, through `AuthReturnTo.validate/1`. An
  explicit `return_to` still wins: someone who clicked RSVP on a huddl
  goes back to that huddl, not to their agenda. Signing out keeps the
  old user-less default.

Part of huddlz-hq#670
Clicks, the scope-vs-page decision and its reasoning, what the default
flip actually touched, how B compares with A's baseline, and how its
preference attribute relates to E's.

Part of huddlz-hq#670
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

0