Conversation
Every club settings section (image, general info, location & contact, opening hours, disciplines, membership, privacy, regional defaults) saves through an endpoint the backend gates on manage_settings, but the frontend never threaded the viewer's permissions through to disable them — any club moderator saw fully live, editable forms and got an unexplained "Failed to save" on every attempt if they lacked the capability. LeagueSettings.tsx already fixed this exact bug for leagues; port the same canManage/ CapabilityNote pattern to its club counterpart. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016fa2ueKYTDTQCG2ZqKgzJa
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
ClubSettings.tsxrendered every settings section (image, general info, location & contact, opening hours, disciplines, membership, privacy, regional defaults) fully live and editable for any club moderator, even though the backend gates every one of those save endpoints onPermManageSettings(service/club.go:UpdateClub,UpdateImageURL,ReplaceOpeningHours,RegenerateJoinCode). A moderator promoted with onlymanage_members(the default promotion grant explicitly excludesmanage_settings) would see a normal, live form and get a bare "Failed to save" toast with no explanation on every attempted save.LeagueSettings.tsx— the structural sibling built to the same pattern — already fixed this exact bug, and CLAUDE.md documents the fix in detail ("A control the viewer's grant doesn't cover is drawn read-only, with the reason"). It just never made it across to the club side.📋 Findings
Functional
ClubSettings.tsxnever gates any section onmanage_settings— the fixed defect.permissionData(fromgetModeratorPermissions) was fetched at the page level but only used forMembersSection'scanDelegateand the announcement composer'scanSend; all 8 profile/settings sections rendered live regardless of the viewer's actual capability, silently 403'ing on save with no explanation. See "Fixed" below.ScoreCardDetail.tsx'supdateMutation.onErroronly shows a toast forApiError(403 or other), with noelsebranch for a non-ApiErrorfailure (e.g. a raw networkTypeErrorfromfetch). There is a fallback inline{updateMutation.isError && <p>Failed to save changes…</p>}message, so the user isn't left with literally zero feedback, but the toast path is inconsistent withScoreEntry.tsx's equivalent handler, which always falls back to a generic toast. Not fixed — low impact given the existing inline fallback, and touching shared error-toast conventions felt like a larger, less clearly-scoped change than the primary fix.contextChangePlan/score-card context switching, notification fan-out, gear-comparison opt-in — traced these flows end-to-end (backend service + frontend call sites) looking for the class of bug the brief calls out (region/permission gaps, stale closures, float money math, race conditions). All were consistent with CLAUDE.md's documented behaviour and already covered by existing tests; no defects found.UX
CapabilityNoteanywhere inClubSettings.tsxprior to this fix, so a moderator with a partial grant had no way to tell a save failure from a permission gap vs. a real error.ConfirmDialogis used consistently for every destructive action across both settings pages, and nowindow.confirm/alert/promptusage exists in the frontend.🐛 Fixed
Ported
LeagueSettings.tsx'scanManage/CapabilityNotepattern toClubSettings.tsx:canManage: booleanprop toClubImageSection,GeneralInfoSection,LocationContactSection,OpeningHoursSection,DisciplinesSection,MembershipInfoSection,PrivacySection,RegionalSection.CapabilityNote("Read-only — … needs the "Manage settings" permission. The club owner grants it from Members, below.") when the viewer lacksmanage_settings.ClubSettings()now computescanManageSettings = can(permissionData?.role, PERM.manageSettings)and passes it to all 8 sections — mirroringLeagueSettings.tsxexactly.disabledprop to the sharedTagInputcomponent (used by Disciplines' Distances/Facilities editors) so it can be gated too.Added
describe('ClubSettings capability gating', …)inClubSettings.test.tsx(mirroring the existingLeagueSettings capability gatingsuite) covering both branches: read-only withoutmanage_settings, editable with it.Verified:
cd frontend && npm run check— cleancd frontend && npm run lint— cleancd frontend && npm test— 59 files / 367 tests pass (including the 2 new tests)cd frontend && npm run build— clean (confirmstsc -bsolution-file build mode)❓ Questions
📝 Noted, not fixed
ScoreCardDetail.tsxerror-toast fallback) — left as a follow-up given the existing inline fallback message and to keep this PR to one clearly-scoped fix.🤖 Generated with Claude Code
https://claude.ai/code/session_016fa2ueKYTDTQCG2ZqKgzJa
Generated by Claude Code