Batch 2: dashboard cluster (#486, #482, #427)#222
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b7248563b8
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| var cached = _profileService.GetCachedProfile(userId) | ||
| ?? await _profileService.GetCachedProfileAsync(userId); | ||
|
|
||
| if (cached is not null) | ||
| { | ||
| displayName = cached.DisplayName; | ||
| profilePictureUrl ??= cached.ProfilePictureUrl; | ||
| profilePictureUrl = ResolveAvatarUrl(cached); | ||
| } |
There was a problem hiding this comment.
Preserve avatar fallback when no profile cache entry exists
The new ID-based avatar resolution only populates displayName/profilePictureUrl when GetCachedProfile* returns a profile, but that API explicitly returns null for users without a profile record; in those cases this component now renders ? even when the signed-in user has a valid Google picture and display name (e.g., onboarding/guest users reached via _LoginPartial). Before this change those values were passed directly by callers, so this is a user-visible regression for profileless accounts.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed — fixed in a44d290.
Good catch. The id-based refactor dropped the caller-supplied display name and picture URL that _LoginPartial and dashboard header used to pass through directly, so any user without a Profile row (onboarding, guest, pre-consent) fell straight into the ? fallback even when their Google claims were fully populated.
Added an IsCurrentUser(userId) check. When GetCachedProfile* returns null AND the requested userId matches the currently signed-in principal, fall back to ClaimTypes.Name and the urn:google:picture claim (same claim AccountController uses on sign-in). Profileless users now see their Google picture and initial on both the nav avatar and the dashboard header. Rendering another user's avatar who has no Profile still shows the initial-letter fallback — that's the intended behavior since we only have claims for the caller.
…recedence (nobodies-collective#482) The view component now takes only a user GUID and resolves the display name and avatar URL internally via IProfileService. Resolution precedence: 1. Custom uploaded picture (served via /Profile/{profileId}/Picture) 2. Google OAuth User.ProfilePictureUrl 3. Initial-letter fallback from cached display name Avatar URL resolution is cached in IMemoryCache keyed on user id and profile UpdatedAtTicks so picture updates bust the cache automatically. All call sites (dashboard, nav, profile, team, search, admin, sync tab) migrated to pass user-id; the URL-accepting overload has been removed. Reporter: fb:f603a8c3
…ies-collective#427) Introduces IDashboardService in the Application layer, with a single GetMemberDashboardAsync method that computes the full member dashboard snapshot: membership state, application term expiry, urgent-shift and signup aggregation, ticket state, and participation status. HomeController becomes a thin adapter that calls the service and maps the DTO to its existing view model — no term expiry or shift aggregation logic remains in the controller.
…#482 review) View components cannot own cache state — caching must live in the owning service. The cached profile data (from IProfileService) is already cached; building the route URL for the custom-picture case is cheap compared to a DB hit, so no additional caching is needed. - Drop IMemoryCache field and constructor parameter - Drop the ResolveAvatarUrl cache wrapper; compute the URL inline via IUrlHelperFactory each render
1ea4f56 to
2e9fbab
Compare
…obodies-collective#482 review) Codex P2 finding on PR #222: the id-based refactor renders "?" for any user without a Profile row, which regresses onboarding/guest users in _LoginPartial who have a valid Google picture and display name on their signed-in claims but no Profile yet. When GetCachedProfile returns null AND the requested userId is the currently signed-in user, fall back to ClaimTypes.Name and the "urn:google:picture" claim (the same claim AccountController uses on login). The fallback only applies to the caller's own avatar — rendering another user's avatar without a Profile still shows the initial-letter fallback, which is the intended behavior.
Auto-executed by /execute-sprint for sprint 2026-04-13 (batch 2, lightweight tier).
Issues
nobodies-collective#486 — Gate Get Involved dashboard card on volunteer membership
IsVolunteerMemberis falseThingsToDoViewComponentunchangedOne-line condition change in
Views/Home/Dashboard.cshtml.nobodies-collective#482 — UserAvatarViewComponent refactor (id-based, custom-upload precedence)
IProfileServiceAll 11 call sites migrated: Dashboard, _LoginPartial, _HumanPopover, _ProfileCard, ProfileCard/Default, Profile/Edit, Profile/Search, Profile/AdminDetail, Team/Details, Application/ApplicationDetail, Google/_SyncTabContent. Also added
UserIdtoProfileViewModelin the Edit path so the avatar can resolve from cache.nobodies-collective#427 — Extract dashboard orchestration from HomeController
Introduces
IDashboardService.GetMemberDashboardAsync(userId, isPrivileged, ct)in the Application layer with a newMemberDashboardDataDTO.DashboardServicein Infrastructure aggregates fromIProfileService,IMembershipCalculator,IApplicationDecisionService,IShiftManagementService,IShiftSignupService,ITicketQueryService,IUserService. HomeController becomes a thin auth + mapping layer (constructor shrinks from 13 deps to 7;Indexshrinks from ~230 lines to ~80).Build / tests
dotnet build Humans.slnx -v q— clean, 0 warnings, 0 errors.dotnet test Humans.slnx --filter "FullyQualifiedName~Home|FullyQualifiedName~Dashboard|FullyQualifiedName~Avatar|FullyQualifiedName~Profile"— Domain + Application tests pass (12 + 119). Integration tests not runnable in this sandbox (Docker unavailable), unrelated to these changes.dotnet format Humans.slnx --verify-no-changes— clean.Test plan
/shows Get Involved card when no upcoming shifts.ThingsToDowizard still renders./Profile/Edit: custom picture shown in nav avatar and dashboard welcome avatar (both viaUserAvatarViewComponent).