feat: deploy seen-once logic for goal in review modal in my kiva and … - #7148
feat: deploy seen-once logic for goal in review modal in my kiva and …#7148christian14b wants to merge 6 commits into
Conversation
| GoalEntrypoint | ||
| }, | ||
| setup() { | ||
| const apollo = inject('apollo'); |
There was a problem hiding this comment.
Is there a way to avoid injecting apollo twice?
There was a problem hiding this comment.
Interesting question, I'll take a look
There was a problem hiding this comment.
Also, useGoalInReview({ apollo }) spins up a useGoalData internally, and setup creates a second one directly. Since userPreferences is a per-call ref(null) rather than module-scoped, the instance loadAutoOpenRecap writes goalRecapViewed/goalRecapPending into is a different reactive store from the one the page reads feedback state from. It works today only because everything reloads network-only, but it's a duplicate round-trip and a divergence trap if anything later leans on shared reactive prefs. Could we thread one useGoalData through, or have useGoalInReview re-expose the feedback helpers the page needs? (MyKiva has the same shape.)
There was a problem hiding this comment.
Good catch! and it was worse than two. MyKiva had three useGoalData instances: the page-wide one MyKivaPage provides, the one MyKivaPageContent was injecting, and the one useGoalInReview creates internally. Portfolio had two.
Indeed, userPreferences is ref(null) inside the composable, so each call gets its own store, and it only worked because every read forced network-only.
I will fix it by letting useGoalInReview accept an existing instance
| if (!year) return; | ||
| const parsedPrefs = await loadPreferences('network-only'); | ||
| const prev = parsedPrefs?.goalRecapViewed || {}; | ||
| // Year-keyed flag so seeing this year's recap does not suppress next year's. |
There was a problem hiding this comment.
I don't know if this comment is accurate.
|
|
||
| const goalRecapPendingByYear = computed(() => { | ||
| const parsedPrefs = JSON.parse(userPreferences.value?.preferences || '{}'); | ||
| return parsedPrefs.goalRecapPending || {}; |
There was a problem hiding this comment.
There are two methods here that are really similar and could be combined and passed a key.
| }); | ||
|
|
||
| function hasGoalRecapPendingForYear(year) { | ||
| return Boolean(goalRecapPendingByYear.value?.[year]); |
There was a problem hiding this comment.
Similarly this method could be combined/abstracted.
|
|
||
| // Set the first time a completed goal is seen, so the recap can open on the session | ||
| // after completion without depending on the goal card's own celebration flag. | ||
| async function setGoalRecapPendingPreference(year = GOALS_CURRENT_YEAR) { |
There was a problem hiding this comment.
Another method that is almost duplicated above.
| // In progress goal setters are reached when the feature itself goes live, so the | ||
| // flag is the only gate. | ||
| if (goalStatus === GOAL_STATUS.IN_PROGRESS) { | ||
| return true; |
There was a problem hiding this comment.
This means any in-progress goal is shown in the new modal after the feature is enabled?
There was a problem hiding this comment.
Yes, called out in the epic:
Lenders who still have a goal in progress - show on log in as a pop up when they visit MyKiva or the Portfolio mid November just pop up once (total, not once per page)
There was a problem hiding this comment.
That mentions "mid November" instead of when the feature is enabled. Is that "November" check already in place for in-progress goals?
| return; | ||
| } | ||
| await this.loadGoalPreferences('network-only'); | ||
| this.goalInReviewFeedbackSubmitted = this.hasSubmittedGoalFeedbackForYear(goalInReview.year); |
There was a problem hiding this comment.
Why is goalInReviewFeedbackSubmitted set here?
| hasCompletionPending, | ||
| }); | ||
|
|
||
| if (!shouldOpen) { |
There was a problem hiding this comment.
Why are we exiting if the user just completed a goal? Wouldn't this be true when returning to MyKiva after checking out the last loan? And I assume we'd want to show the new modal then?
There was a problem hiding this comment.
It's called out in the epic too: Lenders who completed their goal - the next session after they complete their goal, show as a pop up when they visit MyKiva or the Portfolio , but I'll reconfirm it asking in the channel
There was a problem hiding this comment.
Ah, so first load on MyKiva sets the pending, second load shows?
…portfolio pages
Opens the Goal in Review modal, once per user in total across both My Kiva and Portfolio pages.
What changed
A seen-once flag the feature owns.
goalRecapViewed[year]is written to user preferences when the recap opens, so opening and dismissing both count as seen. It is server side, so it holds across pages, sessions and devices (a cookie would not).A pending marker for the "session after completion" rule.
goalRecapPending[year]is set the first time a completed goal is seen, and the recap opens on the visit after that.** Both pages ask the same question.** Every rule about when the recap should appear lives in one place,
shouldAutoOpenRecap. It takes the things it needs as arguments and answers yes or no, so each rule can be tested on its own without standing up a page.Portfolio now renders the modal. It computed
goalInReviewEnablebut never hadGoalInReviewModalmounted.Trigger rules
goalRecapPending[year]goalRecapViewed[year]On MyKiva a
?goTo=goal-recapdeep link returns early and skips the auto-open, so an explicit link does not silently consume the once-only flag. The deep link also does not set the flag, so a link in an email keeps working however many times it is clicked.Test instructions - VQA
You need:
goal_in_review_enableswitched on.Use
/mykivaor/portfoliowith no query parameters. A?goTo=goal-recapparameter skips the auto-open path deliberately.Check your starting state
Run this in the gateway while logged in as the test account:
Look inside the
preferencesJSON forgoalRecapViewedandgoalRecapPending. They determine what happens next, and they are stored on the account (clearing cookies or changing browser will not reset them).Scenarios
In progress goal
/mykiva-> the recap opens.goalRecapViewed[year]is now set./portfolio-> nothing. This is the "once in total, not once per page".Completed goal
/mykiva-> nothing shown. This is the session they completed in.goalRecapPending[year]is now set./mykivaor/portfolioagain -> the recap opens.Portfolio page, without visiting MyKiva
Worth running as its own pass. The completed-goal rule is driven by
goalRecapPending[year], which either page can set, so a lender who only ever visits Portfolio gets the same two-step behaviour as one who uses MyKiva:/portfolio→ nothing, arms the marker./portfolioagain -> opens./mykiva-> nothing.Dismissal counts as seen
Open the recap, then close it with the X or by clicking outside it. Reload -> it does not come back. The flag is written when it opens, not when it is read.
Cases that must never pop
goal_in_review_enableoff.Resetting between runs
The flags are written to the account, so each scenario is one-shot. To run them again, clear
goalRecapViewedandgoalRecapPendingfrom that user's preferences.