Owner profile: Organizations becomes a third tab with route-specific rendering #430
Labels
No labels
actions
bug
cli
duplicate
enhancement
fork
forum
git storage
help wanted
insights
invalid
issues
moderation
oidc
ownership transfer
packages
pr/merge protection rules
projects
pull requests
question
releases
sponsorships
tags
webhooks
wiki
wontfix
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
crueber/walhub#430
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Owner profile: Organizations becomes a third tab with route-specific rendering
What's requested
The owner surface currently has a two-tab strip (
OwnerTabs, Profile | Repositories). Extend it to three tabs — Profile | Repositories | Organizations — and make the pages route-specific:/:owner— the profile view as it renders today (identity header, edit form, membership rail, repo-count teaser)./:owner/repositories— the repositories tab only (toolbar + grid + import footer), as today./:owner/organizations— a new Organizations tab: the membership list moves out of the profile view into this dedicated tab. Route shows ONLY the org membership list under the tab strip (org name linking to/:org, explicit "No organizations" empty state — the GitHub-parity empty state the rail already has).Evidence (current tree)
web/src/pages/Repos.jsx:88-133—OwnerTabsrenders two<A>links and derives the active tab fromloc.pathname === '/{owner}/repositories'. A third tab needs the same pattern for/{owner}/organizations.web/src/pages/Repos.jsx:301-310— the membership data already loads as a separate cached keymemberorgs:{owner}viarepos.users.orgs(owner())(GET/api/v1/users/{principal}/orgs), so the new tab consumes existing machinery — no backend change required.web/src/pages/Repos.jsx:440-465— the current membership rail renders inside the profile view (view() === "profile"branch). This ticket moves that list into the new tab; decide whether the profile view keeps a compact rail as well or goes rail-free (planner's call — either is acceptable; the tab must always exist for user profiles).web/src/pages/Repos.jsx:512-518— the tab strip renders on both views outside the isOrg gate. Org profiles (isOrg()branch) deliberately omit the membership section because member principals are email spellings, not routable owner slugs (#370) — keep the Organizations tab user-profiles only (org profiles still render Profile | Repositories).web/src/pages/Repos.jsx:681-695— route components:Repos→OwnerPage view="profile",OwnerRepositories→view="repos". Add/:owner/organizationsinweb/src/index.jsx(next to the/:owner/repositoriesregistration) mapped to a newview="orgs"variant; note the same reservation class as/repositories— an org literally namedorganizationsloses its UI page, client-side only.Architecture notes
users.orgs,memberorgs:{owner}cache key, explicit empty state) is landed and stays; only its presentation surface moves from the profile rail to a dedicated tab. Reference this ticket in the closed issue with a comment.Acceptance criteria
/:ownershows the profile (identity, edit form, teaser) — no repos grid, no full membership list replacing the profile./:owner/repositoriesshows only the repos toolbar/grid/import listing under the tab strip./:owner/organizationsshows the membership list (links to/:org) with the explicit "No organizations" empty state; loading state preserved.aria-currentcorrect per route.web/test/unit/) updated; a unit test covers the active-tab derivation for all three routes.Fix PR: #434 (branch fix/issue-430). Client-only: /:owner/organizations → view="orgs" with the membership list moved verbatim from the profile rail; OwnerTabs reads Profile | Repositories | Organizations (same classes/underline/aria-current, three-way derivation); isOrg hides the third tab on org profiles (#370); /:owner goes rail-free (planner's call). node --test 903 total / 901 pass / 2 pre-existing live-server smoke failures (identical pristine); vite build green. No backend change, no new deps.
REVIEW PR #434 (fix/issue-430, commit
5001acc) — verified in scratch worktree /tmp/pr434 (main worktree untouched, still clean on main apart from pre-existing untracked .opencode/).(1) Route — web/src/index.jsx:79-89: /:owner/repositories (79) → /:owner/organizations (85) → /:owner (86) → /:owner/teams/:slug (88) → /:owner/:name (89). Static-before-dynamic holds; reservation comment present (names 'organizations', cites #430, same class as /repositories + /orgs/new). Named import updated (line 18). PASS.
(2) Tab strip — Repos.jsx:103-152: three-way pathname derivation (organizations probed before repositories, fallback profile); all three tabs share one cls (3x class={cls}), same active-underline classList + aria-current pattern on the third tab. Strip call site :519 feeds isOrg. PASS.
(3) Orgs tab content — Repos.jsx:602-644 (view=="orgs" gate): verbatim move — orgs-rail hook, heading, normalizeMemberOrgs, /:org links, 'No organizations' empty state, loading… fallback all kept; same memberorgs:{owner} key + repos.users.orgs fetch; no toolbar/grid/teaser in branch (pinned by test). PASS.
(4) Profile rail-free — Repos.jsx:468-476: rail removed, explanatory comment left; only remaining orgs-rail is the orgs-view one (:603). Teaser intact (View all → deep-links /repositories). Matches the issue's planner's-call option. PASS.
(5) Org profiles excluded — :142 Show when={!props.isOrg} gates only the third tab; Profile/Repositories unconditional. #370 rationale cited in code + docs. (Direct-URL /:org/organizations on an org slug still renders the branch, but the tab never links there — acknowledged in code comment :600; out of scope.) PASS.
(6) Count badge — still rides the Repositories tab only (test pins badge between repos/orgs links), derives from shared repos:{owner} payload, no new fetch. PASS.
(7) #422 views intact — owner-repos-tab-422.test.js updated (2 pins: import line, strip call site with isOrg) and passing; profile/repos gating untouched. PASS.
(8) #423 cross-ref — comment 4329 on #423 verified via API: full body references #430 + PR #434 + endpoint-stays rationale. PASS.
(9) No backend change (6 files: 2 docs + 2 web src + 2 web tests; go.mod/package.json/lock untouched, no new deps); docs accurate (12_web_ui.md §2.3 route table + FIXED #430 entry; features/01_identity_permissions.md membership-move decision — law 12 satisfied). Laws 1/7/8 hold (no deps, N/A long-work, existing OwnerPage(view)+route seam).
VERIFY: node --test web/test/unit/*.test.js → 901 pass / 2 fail; the 2 fails are both smoke.test.js live-server shell-serve tests (fail identically pre/post vite build — environmental, no server in worktree, unrelated to this client-route change). Targeted owner-orgs-tab-430 + owner-repos-tab-422 → 17/17 pass. vite build green (2.15s). No browser drive per review instructions (headless tests + source reasoning only) — browser proof stays open as with #422.
No fixes needed — nothing pushed.
MERGE RECOMMENDATION: ready to merge.
Fixed by PR #434 (review clean — all 9 checks pass, #422 intact, #423 cross-ref posted), merged. Closing.