Wire add-place, add-group and edit-place entry points into the map UI #75
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/entry-points"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Implements task 159.
PlaceFormandGroupFormexisted but nothing opened either — this hosts both, plus editing, as floating panels matchingGroupMembersPanel's own shape (ADR-0065).PlaceFormPanelandGroupFormPanelhost the existing forms;Draweropens them for add-place (general and per-group, pre-selecting the group) and add-group,Homeopens the place form for editing from the detail panel's own Edit button.@key="Place?.Id"onPlaceForminsidePlaceFormPanel— the requirement PR #72's review found.PlaceFormPanelTestsproves swapping which place is being edited on an already-rendered instance shows and submits the right place's data, and reddens if the key is removed.MapPlacesState.UpsertPlacepatches the map and drawer locally after a save — no reload, per ADR-0062's own predicted extension point.MapPlacesState(GroupMembershipLoader), closing the gap where a caller with no places anywhere yet would have no add-place affordance at all.Reasoning, alternatives and what's left unreconciled (three independent
GET /api/groupsloaders) in ADR-0067 — next free number, confirmed againstmainand the one open PR (#74, which holds 0066).Full solution build,
dotnet testanddotnet format --verify-no-changesall clean under the pinned SDK.Unverified in a browser (task 156, as ever): focus movement, whether the new panels visually clear the drawer on a narrow viewport, and screen-reader announcement of panel headings.
Verdict: changes needed
Home's own independently-loaded group/role cache never refreshes after a group created viaDrawer'sGroupFormPanel— reachable in the exact "create a group → add a place → edit it" flow this PR wires up end to end, in one session.Drawer.HandleGroupSavedAsyncreloadsPlaceAccordionandGroupsSectionafter a save, but has no reference toHome(siblings, per ADR-0062) — soHome's own_groups(loaded once inOnInitializedAsyncviaGroupMembershipLoader) never learns about the new group. Concretely: caller creates a group,PlaceAccordion's general "Add a place" button correctly appears (its own loader was reloaded), caller adds a place to the new group, it gets selected andHomeshows the detail panel — butRoleFor(newGroupId)returnsnullagainst the stale_groups, soCanEditisfalseand the Edit button is silently hidden, even though the caller is that group's own Owner. Verified this concretely with a scratch bUnit test reproducing it against the built code (not committed). This is a fail-closed bug (no security issue), but it's misleading — it reads as "you don't have permission" for something the caller just created — and it directly answers the trade-off question ADR-0067 raises: the three independentGET /api/groupsloaders aren't merely a redundant-request cost, they're a real staleness bug in this PR's own new surface, not a hypothetical about a rename days later. Suggest a small, scoped fix rather than the larger shared-service reconciliation ADR-0067 defers — e.g. haveHomere-runGroupMembershipLoader.LoadAllAsyncwhenPlacesState.Changedfires andPlacesState.SelectedPlace.GroupIdisn't in_groups.ADR-0067's Consequences section should be amended to name this concretely once fixed — as written it frames the three loaders as only "not worth avoiding" network overhead, not as a case that breaks an affordance in the caller's own just-created group.
Everything else checks out:
@key="Place?.Id"is onPlaceFormitself, not a wrapper — removing it reddensPlaceFormPanel_PlaceParameterSwappedOnAnAlreadyRenderedInstance_ShowsTheNewPlacesFieldsAndVersionNotTheOldOnes(confirmed by mutation). The manual "close one place's edit form, open a different one" scenario doesn't itself exercise the key —Home'sIsEditingSelectedPlacealways drops to view mode first, fully unmountingPlaceFormPanel, before a different place's edit can open — but ADR-0067 says this plainly ("the@keyis the correctness guarantee, not the UX nicety that usually avoids the case"), so this isn't a gap, just confirming the reasoning holds.PlaceForm's existingEligibleGroups[0]default), add-group, and edit-place from the detail panel are all wired and covered end to end (DrawerAuthenticatedTests,PlaceAccordionTests,HomeTests).MapPlacesState.UpsertPlacecorrectly patches both the new-place and edited-place cases with no duplication or loss — read the implementation and the existing tests cover both directions.PlaceForm's pre-existingApiProblemExceptionhandling (task 94, untouched here).dotnet test(315 passed),dotnet format --verify-no-changes, andPlaceMark.Architecture.Testsall clean under the pinned 10.0.100 SDK.Not verifiable without a browser (as the PR and ADR-0067 already say): whether focus actually lands visibly, whether either panel clears the drawer on a narrow viewport, and what a screen reader announces on open.
Verdict: mergeable
Re-reviewed at
1bf5537.Drift bug fixed, and correctly. Reproduced the original defect against pre-fix
Home.razor/Home.razor.cs(reverted them in a worktree): the Edit button stays hidden after creating a group and adding a place to it in one session. On1bf5537the same steps show the Edit button, andHomeAndDrawerSharedGroupStateTests.Home_AlreadyMounted_ShowsEditForAPlaceAddedToAGroupCreatedAfterwardsInTheDrawerreddens against the revertedHomeand passes against the fix — confirmed both directions myself rather than trusting the description.CallerGroupsStateis correctly scoped, andGroupFormPanelreloading it directly (rather thanDrawerreaching into siblings) is the right shape — no component reaches into another's internals to propagate the change.Cross-user reset: present and correct in the code, but untested — confirm before merge.
CallerGroupsStatemirrorsMapPlacesState'sAuthenticationStateChangedhandling correctly; I wrote a scratch test (sign in as A, load, sign out, sign in as B) and it passes, so the behaviour itself is right. But there is noCallerGroupsStateTests.csin this PR at all, unlikeMapPlacesStateTests' ownAuthenticationStateChanged_ADifferentCallerSignsInAfterSignOut_...test. I commented out theAuthenticationStateChangedsubscription inCallerGroupsState's constructor and ran the fullPlaceMark.WebUI.Testssuite (320 tests) — all still pass. Nothing in this PR would catch a regression here. Given this exact class of bug (one user's state leaking to the next sign-in) was a PR #70 finding, this needs a dedicated test before merge, not just structural resemblance toMapPlacesState.GroupsSectionresidual: judged and named honestly, and currently inert. Checked whether any live UI path can moveGroupsSectionandCallerGroupsStateout of step:GroupForm's rename affordance has no host anywhere in the WebUI, andPlaceMarkApiClient.DeleteGroupAsynchas no caller in any component — neither rename nor delete is reachable from the UI at all yet.GroupMembersPanel's role controls never render against the caller's own row (ADR-0065), so an in-app role change can't touch the caller's own role either. The only mutation currently wired end-to-end is group creation, and that's the one case already paired correctly (GroupFormPanelreloadsCallerGroupsState,Drawer.HandleGroupSavedAsyncreloadsGroupsSection, same handler). So: no, it cannot drift today. It will become live the moment someone wires up rename or delete without remembering to touch both reload call sites — exactly the shape that caused the original bug — and the rewritten ADR names that trigger condition explicitly rather than leaving it implicit. Agreed with the record's own judgement here.ADR rewrite: correct and honest. ADR-0001 binds immutability "from the moment a record is merged onto
main" — ADR-0067 has never been merged, so rewriting in place rather than superseding is the right call, consistent with the precedent ADR-0001 itself names for ADRs 0001–0011. The rewrite doesn't smooth the mistake into a design choice: it states plainly that the first version's trade-off was wrong, names the reproducing test, and keeps the rejected first version in Alternatives considered rather than deleting it.Build, full
dotnet test(316 passed, 4 pre-existing skips), anddotnet format --verify-no-changesall clean under the pinned 10.0.100 SDK. Nothing else outstanding from the first round.Verdict: changes needed
Correcting my previous review on this same commit (
1bf5537), posted moments ago with verdictmergeable— that verdict contradicted my own finding in the same comment and should not stand. Everything in that review's substance holds; only the verdict was wrong.The blocking issue:
CallerGroupsStatehas no test coverage for the cross-user reset at all. I commented out itsAuthenticationStateChangedsubscription and ran the fullPlaceMark.WebUI.Testssuite — all 320 tests (316 run, 4 pre-existing skips) still passed. Nothing in this PR would catch a regression that leaks one user's groups into the next sign-in on the same tab — the exact bug class PR #70's review found inMapPlacesState. The behaviour itself is correct (confirmed with a scratch test: sign in as A, load, sign out, sign in as B, none of A's groups remain), but "I checked it by hand and it's fine" is not a substitute for the regression testMapPlacesStateTestsitself sets as the house standard (AuthenticationStateChanged_ADifferentCallerSignsInAfterSignOut_NeverExposesThePreviousCallersPlaces). Add the equivalent forCallerGroupsStatebefore merge.Everything else from the previous review stands:
Hometo reddenHomeAndDrawerSharedGroupStateTests, restored to see it pass).GroupsSectionresidual is real but currently inert — no live UI path renames or deletes a group, or changes the caller's own role, so it cannot drift today; the rewritten ADR names the future trigger condition honestly.dotnet format --verify-no-changesall clean under the pinned SDK.Once
CallerGroupsStateTests(or equivalent) exists and asserts the cross-user reset, this is mergeable.Verdict: mergeable
Re-reviewed at
bb3f332. Diff is exactly one new file,CallerGroupsStateTests.cs(174 lines), as expected.Verified independently rather than trusting the description:
CallerGroupsState'sAuthenticationStateChangedsubscription: exactly the two cross-user tests fail, both with a genuine leaked-group assertion (namesVisibleToBcontaining"Alice's Group", andstate.Groupsnon-empty after sign-out) — not an incidental crash. Restored,git statusclean.MapPlacesState's subscription: its two equivalents fail the same way (namesVisibleToBcontaining"Alice's Place"). Restored, clean.AuthenticationStateChanged: exactlyAuthenticationStateChanged_SameCallerNotified_KeepsWhatIsAlreadyLoadedfails ("state.Groupsshould not be empty but was"), nothing else. Restored, clean. So the same-caller no-op is genuinely pinned, not merely asserted.The eight tests each pin something distinct — first load, no-op second
EnsureLoadedAsynccall, API-error handling, explicitReloadAsyncre-reading,ReloadAsyncraisingChanged, sign-out clearing, cross-user isolation, and same-caller no-op — no padding or restatement.Full test suite (324 passed, 4 pre-existing skips),
dotnet format --verify-no-changes, andPlaceMark.Architecture.Testsall clean under the pinned 10.0.100 SDK.Nothing outstanding from either of the last two rounds. This is clean.