Goals PR2 companion — relationship-scoped goals with per-session display#330
Merged
Conversation
…n display
Update Goal type from session-scoped (coaching_session_id) to relationship-scoped
(coaching_relationship_id, created_in_session_id nullable, target_date nullable).
Use GET /coaching_sessions/{id}/goals join table endpoint for per-session goal
display in dashboard cards and coaching session goal drawer. Send
created_in_session_id on goal creation to trigger backend auto-link.
Goal SSE events (created/updated/deleted) now also invalidate
/coaching_sessions SWR keys, covering the GET /coaching_sessions/{id}/goals
join table endpoint used for per-session goal display.
Extract DEFAULT_GOAL_TITLE constant and goalTitle() helper in goal.ts to consistently handle empty/null goal titles across 6 UI call sites.
…sion - Add missing field checks (title, body, target_date, status_changed_at, completed_at) to isGoal type guard to prevent malformed data at boundary - Conditionally construct URL in useGoalsBySession to avoid embedding "null" string when session ID is absent - Remove as-any casts in goal API tests, remove test that asserted null-in-URL implementation detail
invalidateEndpoint('/coaching_sessions') was too broad — it revalidated
every SWR key containing that path, causing the coaching session title
to flash on goal updates. Replace with a targeted invalidator that only
matches /coaching_sessions/{id}/goals keys.
calebbourg
requested changes
Mar 10, 2026
calebbourg
left a comment
Collaborator
There was a problem hiding this comment.
Just a couple of thoughts. Not sure if they're valid
| // any other local code using the KeyedMutator will also update with this new data. | ||
| refresh(); | ||
| } | ||
| if (goal.id) { |
Collaborator
There was a problem hiding this comment.
Is goal here the goal that's defined on line 30? If so would it make sense to pass it in as an argument here? I've seen referencing variables from outside of the function scope in JS before but not sure what the best practice is.
| * @param coachingSessionId The ID of the coaching session | ||
| * @returns Promise resolving to an array of Goal objects linked to the session | ||
| */ | ||
| listBySession: async (coachingSessionId: Id): Promise<Goal[]> => { |
Collaborator
There was a problem hiding this comment.
I think it would be more conventional to this app for this to be called listNested similar to createNested
Address PR review feedback: use listNested convention with EntityApi.listNestedFn and make handleGoalChange accept the current goal explicitly instead of closing over component scope.
Surface a destructive toast when the backend returns HTTP 409 because the coaching relationship already has 3 active goals. Adds ActiveGoalLimitError types and extractActiveGoalLimitError helper for reuse in future swap dialog work.
"Active" means InProgress only — NotStarted does not count. Use max_active_goals from 409 response instead of hardcoding. Rename ACTIVE_GOAL_LIMIT to DEFAULT_MAX_ACTIVE_GOALS. Return ActiveGoalLimitInfo (with limit + goals) from the extractor.
calebbourg
approved these changes
Mar 11, 2026
jhodapp
added a commit
that referenced
this pull request
Mar 12, 2026
Mark backend PR1/PR2 as merged with links to PRs (#242, #330). Update API gap analysis with PR2 completion status. Correct active goal limit from frontend-only to backend-enforced (409 Conflict). Update join table endpoints to nested routes. Add Layer 2 PR2 companion deliverables and remaining work blockers. Document auto-link on creation interim behavior.
Contributor
🚀 PR Preview Environment Deployed!🔗 Access URLs
📊 Environment Details
🧪 Testing# Health check
curl http://neo/pr-330/health
# Frontend
curl http://neo/pr-330/
# API test
curl http://neo/pr-330/api/v1/users🧹 CleanupEnvironment auto-cleaned when PR closes/merges Deployed: 2026-03-13T16:48:47.808Z |
jhodapp
added a commit
that referenced
this pull request
Mar 15, 2026
Mark backend PR1/PR2 as merged with links to PRs (#242, #330). Update API gap analysis with PR2 completion status. Correct active goal limit from frontend-only to backend-enforced (409 Conflict). Update join table endpoints to nested routes. Add Layer 2 PR2 companion deliverables and remaining work blockers. Document auto-link on creation interim behavior.
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.
Description
Frontend companion for backend PR2 (goals relationship scoping). Updates the Goal type, API layer, and UI components to work with the new relationship-scoped goals and
coaching_sessions_goalsjoin table.Coordinated deploy: must merge alongside backend PR #242.
Changes
Goalinterface: removecoaching_session_id, addcoaching_relationship_id(required),created_in_session_id(nullable),target_date(nullable)GoalApi.list()to sendcoaching_relationship_idquery paramGoalApi.listBySession()anduseGoalsBySessionhook forGET /coaching_sessions/{id}/goals(join table endpoint)created_in_session_idon goal creation to trigger backend auto-linkGoalContainerScreenshots / Videos Showing UI Changes (if applicable)
None
Testing Strategy
Concerns
include=goalon enriched sessions endpoint still works for existing data but will become incomplete as new join table links are created — migration to join table endpoint is done in this PR