## Context Currently, the frontend displays raw UUIDs for user references: - Task list shows `assigneeId` (e.g., "a1b2c3d4-e5f6...") or "Unassigned" - Task detail shows `assigneeId` and `createdById` - Shift detail shows `memberId` for each signup The backend already stores `DisplayName` in the `Member` entity but the API DTOs don't expose it. The `ShiftService` already demonstrates the pattern of joining with Members (lines 82-87), which we can replicate for Tasks. ## Goals / Non-Goals **Goals:** - Add member name fields to backend DTOs - Update TaskService to query and include member names - Update ShiftService to include member name in ShiftSignupDto - Update frontend TypeScript interfaces - Replace UUID displays with names in task/shift UIs **Non-Goals:** - No database schema changes - No changes to authentication or authorization - No changes to how tasks/shifts are created or updated - No caching layer for member names ## Decisions ### 1. Add names to existing DTOs vs create new DTOs **Decision:** Add optional fields to existing DTOs **Rationale:** - Keeps API surface simple - Backward compatible - existing clients ignore new fields - No breaking changes to existing integrations **Alternative considered:** Create new DTO versions (e.g., `TaskDetailDtoV2`) - Rejected: Unnecessary complexity for a simple additive change ### 2. Fetch member names via JOIN vs separate query **Decision:** Use JOIN in TaskService methods **Rationale:** - More efficient - single query per endpoint - Pattern already exists in ShiftService - Avoids N+1 query problem **Alternative considered:** Query members separately and build lookup dictionary - Rejected: Adds complexity and extra database round-trips ### 3. Handle missing members (orphaned IDs) **Decision:** Return null for name when member not found **Rationale:** - Data integrity issue should surface visibly - Frontend can display fallback like "Unknown" or keep showing ID - Logging can track data inconsistencies ### 4. Frontend handling of null names **Decision:** Frontend shows fallback text when name is null **Implementation:** ```typescript // Task list task.assigneeName || 'Unassigned' // Task detail task.assigneeName || 'Unassigned' task.createdByName || 'Unknown' // Shift signups signup.memberName || 'Unknown Member' ``` ## Risks / Trade-offs | Risk | Mitigation | |------|-----------| | JOIN adds query complexity | Keep JOINs simple, only on indexed columns (Member.Id) | | Larger API response payloads | Minimal impact - names are small strings | | Member names become stale | Acceptable - names rarely change; eventual consistency | | Database performance degradation | Monitor query execution plans; add caching if needed | | Partial data on member deletion | Show "Unknown" fallback; log orphaned references | ## Migration Plan 1. **Backend DTO changes** - Add new optional fields 2. **Backend service changes** - Update queries to include names 3. **Frontend type updates** - Add name fields to interfaces 4. **Frontend UI updates** - Replace ID displays with names **Rollback:** - DTO changes are backward compatible - Frontend can revert to showing IDs by changing display logic - No database changes required ## Open Questions - Should we include `externalUserId` in the signup display? (Currently available in ShiftSignupDto) - Do we need to include member email for any display purposes? - Should we add name fields to shift list items (showing creator name)?