diff --git a/src/assets/images/thanks-page/hands-plant-v3.png b/src/assets/images/thanks-page/hands-plant-v3.png new file mode 100644 index 00000000000..4b9d34b99b2 Binary files /dev/null and b/src/assets/images/thanks-page/hands-plant-v3.png differ diff --git a/src/assets/images/thanks-page/hands-plant.gif b/src/assets/images/thanks-page/hands-plant.gif deleted file mode 100644 index e3c066a682d..00000000000 Binary files a/src/assets/images/thanks-page/hands-plant.gif and /dev/null differ diff --git a/src/components/GoalSetting/GoalSettingContainer.vue b/src/components/GoalSetting/GoalSettingContainer.vue index cfc67c84e4c..f98fb47245c 100644 --- a/src/components/GoalSetting/GoalSettingContainer.vue +++ b/src/components/GoalSetting/GoalSettingContainer.vue @@ -83,6 +83,7 @@ :goal-progress="goalProgress" :is-goal-completed="isGoalCompleted" show-goal-value-props-copy + use-direct-question-title @set-goal-target="setTarget($event)" @set-goal="setGoal($event)" @update-goal="updateGoal($event)" diff --git a/src/components/MyKiva/FeaturedGoalCard.vue b/src/components/MyKiva/FeaturedGoalCard.vue index 470f1b0ae21..024d4784a23 100644 --- a/src/components/MyKiva/FeaturedGoalCard.vue +++ b/src/components/MyKiva/FeaturedGoalCard.vue @@ -28,8 +28,10 @@ >
- {{ goalCopy.TITLE_HOW_MANY_LOANS_GENERIC }} +
- How many loans will you make this year?
+
+ +
@@ -257,8 +270,8 @@ import {
KvButton, KvMaterialIcon, KvLoadingPlaceholder, KvAccordionItem
} from '@kiva/kv-components';
-import { ID_WOMENS_EQUALITY, ID_SUPPORT_ALL, ID_US_ECONOMIC_EQUALITY } from '#src/composables/useBadgeData';
-import HandsPlant from '#src/assets/images/thanks-page/hands-plant.gif';
+import { ID_WOMENS_EQUALITY, ID_SUPPORT_ALL } from '#src/composables/useBadgeData';
+import HandsPlant from '#src/assets/images/thanks-page/hands-plant-v3.png';
import LoanNumberSelector from '#src/components/MyKiva/GoalSetting/LoanNumberSelector';
import GoalProgressRing from '#src/components/MyKiva/GoalProgressRing';
import GoalCustomAmountInput from '#src/components/MyKiva/GoalSetting/GoalCustomAmountInput';
@@ -385,6 +398,34 @@ const props = defineProps({
type: Boolean,
default: false,
},
+ /**
+ * Whether the selector should use the direct loan question instead of entrypoint-style headline copy.
+ */
+ useDirectQuestionTitle: {
+ type: Boolean,
+ default: false,
+ },
+ /**
+ * Whether the no-goal-yet intro line should render smaller than the main title.
+ */
+ compactNoGoalYetTitle: {
+ type: Boolean,
+ default: false,
+ },
+ /**
+ * Whether to use the tighter thank-you page goal selector spacing.
+ */
+ compactLayout: {
+ type: Boolean,
+ default: false,
+ },
+ /**
+ * Whether the current-year progress subtitle should render before the loan options.
+ */
+ progressSubtitleBeforeOptions: {
+ type: Boolean,
+ default: false,
+ },
});
const emit = defineEmits([
@@ -441,11 +482,19 @@ const loansLastYear = computed(() => {
});
const showLoanQuestionPrompt = computed(() => {
+ if (props.useDirectQuestionTitle) return false;
return goalSignupCopyVariant === GOAL_SIGNUP_COPY_NO_GOAL_YET
|| (!props.showGoalValuePropsCopy
&& (loansLastYear.value > 0 || props.selectedCategoryId === ID_WOMENS_EQUALITY));
});
+const loanQuestionPrompt = computed(() => {
+ if (goalSignupCopyVariant === GOAL_SIGNUP_COPY_NO_GOAL_YET && !props.useDirectQuestionTitle) {
+ return goalCopy.titleLoanQuestionForCategory(props.selectedCategoryId, props.selectedCategoryName);
+ }
+ return goalCopy.TITLE_HOW_MANY_LOANS_GENERIC;
+});
+
// Use progressForCurrentYear from tieredAchievements if available (set on Thanks page),
// otherwise use fetched current year data (for MyKiva goal-setting page and modal)
const loansThisYear = computed(() => {
@@ -485,8 +534,14 @@ const loadLoansThisYear = async () => {
};
const titleText = computed(() => {
+ if (props.useDirectQuestionTitle) {
+ return goalCopy.titleLoanQuestionForCategory(props.selectedCategoryId, props.selectedCategoryName, {
+ splitQuestion: true,
+ });
+ }
+
if (goalSignupCopyVariant === GOAL_SIGNUP_COPY_NO_GOAL_YET) {
- return goalCopy.CARD_NO_GOAL_YET_EXPERIMENT;
+ return goalCopy.titleNoGoalYetSelectorEntrypoint({ compactIntro: props.compactNoGoalYetTitle });
}
// Default title if no lending history and category is ID_WOMENS_EQUALITY
@@ -503,14 +558,7 @@ const titleText = computed(() => {
return goalCopy.titleLastYearForCategory(loansLastYear.value, props.selectedCategoryId, props.selectedCategoryName);
}
- // Support All is not a specific category, so use generic language
- if (props.selectedCategoryId === ID_SUPPORT_ALL) {
- return goalCopy.TITLE_HOW_MANY_LOANS_GENERIC;
- }
- if (props.selectedCategoryId === ID_US_ECONOMIC_EQUALITY) {
- return goalCopy.TITLE_US_ENTREPRENEURS_HOW_MANY_LOANS;
- }
- return goalCopy.titleCategoryHowManyLoans(props.selectedCategoryName?.toLowerCase());
+ return goalCopy.titleLoanQuestionForCategory(props.selectedCategoryId, props.selectedCategoryName);
});
const subtitleText = computed(() => {
@@ -778,6 +826,50 @@ watch(() => props.selectedCategoryId, async newCategory => {
diff --git a/src/util/goalCopy.js b/src/util/goalCopy.js
index 56673c2c21b..5d783e8c809 100644
--- a/src/util/goalCopy.js
+++ b/src/util/goalCopy.js
@@ -40,6 +40,14 @@ const goalCopy = {
return this.titleNoHistoryWomensDefault(cssClass);
},
+ /** Date-aware no-goal subtitle for goal card entrypoints */
+ subtitleNoGoalYetEntrypoint() {
+ if (this.getGoalSignupCopyVariant() === GOAL_SIGNUP_COPY_NO_GOAL_YET) {
+ return this.CARD_HABIT_PROMPT_SINGLE_LINE;
+ }
+ return this.TITLE_HOW_MANY_LOANS_GENERIC;
+ },
+
/** User helped exactly 1 woman last year */
// eslint-disable-next-line max-len
titleLastYearSingleWoman: (count, cssClass = 'tw-text-eco-green-3') => `Last year, you helped ${highlight(`${count} woman`, cssClass)} shape her future!`,
@@ -82,7 +90,32 @@ const goalCopy = {
TITLE_US_ENTREPRENEURS_HOW_MANY_LOANS: `How many loans to ${ecoGreen('U.S. entrepreneurs')} will you make this year?`,
/** Specific named category */
- titleCategoryHowManyLoans: categoryName => `How many loans to ${ecoGreen(categoryName)} will you make this year?`,
+ titleCategoryHowManyLoans(categoryName, { splitQuestion = false } = {}) {
+ const separator = splitQuestion ? '
' : ' ';
+ return `How many loans to ${ecoGreen(categoryName)}${separator}will you make this year?`;
+ },
+
+ /** Direct no-goal-yet title used by selector entrypoints */
+ titleNoGoalYetSelectorEntrypoint({ compactIntro = false } = {}) {
+ const intro = compactIntro
+ ? `${this.CARD_NO_GOAL_YET_EXPERIMENT}`
+ : this.CARD_NO_GOAL_YET_EXPERIMENT;
+ return `${intro}
${this.CARD_HABIT_PROMPT_SHORT}`;
+ },
+
+ /** Generic category question used by direct goal-selector flows */
+ titleLoanQuestionForCategory(categoryId, categoryName, options = {}) {
+ if (categoryId === ID_SUPPORT_ALL) {
+ return this.TITLE_HOW_MANY_LOANS_GENERIC;
+ }
+ if (categoryId === ID_US_ECONOMIC_EQUALITY) {
+ if (options.splitQuestion) {
+ return this.titleCategoryHowManyLoans('U.S. entrepreneurs', options);
+ }
+ return this.TITLE_US_ENTREPRENEURS_HOW_MANY_LOANS;
+ }
+ return this.titleCategoryHowManyLoans(categoryName?.toLowerCase(), options);
+ },
// ─── Goal selector subtitle ────────────────────────────────────────────────
@@ -124,6 +157,12 @@ const goalCopy = {
/** Experiment: motivational subtitle in the empty goal tile */
CARD_HABIT_PROMPT_EXPERIMENT: "Make helping others a habit.
We'll help you make it happen.",
+ /** Single-line habit prompt used where the subtitle should not force a line break */
+ CARD_HABIT_PROMPT_SINGLE_LINE: "Make helping others a habit. We'll help you make it happen.",
+
+ /** Short habit prompt used inside the goal selector title */
+ CARD_HABIT_PROMPT_SHORT: 'Make helping others a habit.',
+
// ─── GoalProgressRing — modal description text ───────────────────────────────
/** Modal: goal completed — "Thank you for supporting X [category]..." */
diff --git a/test/unit/specs/components/MyKiva/FeaturedGoalCard.spec.js b/test/unit/specs/components/MyKiva/FeaturedGoalCard.spec.js
index 6acfe40ca34..33659af81fb 100644
--- a/test/unit/specs/components/MyKiva/FeaturedGoalCard.spec.js
+++ b/test/unit/specs/components/MyKiva/FeaturedGoalCard.spec.js
@@ -4,6 +4,8 @@ import FeaturedGoalCard from '#src/components/MyKiva/FeaturedGoalCard';
import { GOALS_CURRENT_YEAR } from '#src/composables/useGoalData';
import goalCopy from '#src/util/goalCopy';
+const stripHtml = html => html.replace(/<[^>]*>/g, '');
+
vi.mock('#src/util/animation/confettiUtils', () => ({
showConfetti: vi.fn(),
}));
@@ -89,23 +91,36 @@ describe('FeaturedGoalCard copy', () => {
vi.setSystemTime(new Date('2026-03-31T12:00:00'));
const wrapper = buildNoGoal();
- expect(wrapper.html()).toContain('Lenders like you help');
- expect(wrapper.html()).toContain('3 women');
- expect(wrapper.html()).toContain('a year!');
+ expect(wrapper.text()).toContain(stripHtml(goalCopy.titleNoHistoryWomensDefault()));
expect(wrapper.text()).not.toContain(goalCopy.CARD_NO_GOAL_YET_EXPERIMENT);
});
- it('renders the no-goal-yet title starting April 1', () => {
+ it('renders the last-year women title from January 1 through March 31 when previous-year loans exist', () => {
+ vi.useFakeTimers();
+ vi.setSystemTime(new Date('2026-03-31T12:00:00'));
+
+ const wrapper = mountCard({ state: 'no-goal', prevYearLoans: 2 });
+ expect(wrapper.text()).toContain('Last year, you helped 2 women shape their futures!');
+ expect(wrapper.text()).not.toContain(stripHtml(goalCopy.titleNoHistoryWomensDefault()));
+ });
+
+ it('renders the no-goal-yet title and habit prompt starting April 1', () => {
vi.useFakeTimers();
vi.setSystemTime(new Date('2026-04-01T12:00:00'));
const wrapper = buildNoGoal();
expect(wrapper.text()).toContain(goalCopy.CARD_NO_GOAL_YET_EXPERIMENT);
- expect(wrapper.html()).not.toContain('Lenders like you help');
+ expect(wrapper.text()).toContain(goalCopy.CARD_HABIT_PROMPT_SINGLE_LINE);
+ expect(wrapper.html()).not.toContain('
');
+ expect(wrapper.text()).not.toContain(stripHtml(goalCopy.titleNoHistoryWomensDefault()));
+ expect(wrapper.text()).not.toContain(goalCopy.TITLE_HOW_MANY_LOANS_GENERIC);
});
- it('renders the generic loans-this-year subtitle', () => {
- expect(buildNoGoal().text()).toContain('How many loans will you make this year?');
+ it('renders the generic loans-this-year subtitle from January 1 through March 31', () => {
+ vi.useFakeTimers();
+ vi.setSystemTime(new Date('2026-03-31T12:00:00'));
+
+ expect(buildNoGoal().text()).toContain(goalCopy.TITLE_HOW_MANY_LOANS_GENERIC);
});
it('renders the year-stamped Set Goal CTA', () => {
@@ -322,8 +337,11 @@ describe('FeaturedGoalCard copy', () => {
describe('state validator fallback', () => {
it('renders no-goal copy when an unrecognized state value is passed', () => {
+ vi.useFakeTimers();
+ vi.setSystemTime(new Date('2026-03-31T12:00:00'));
+
const wrapper = mountCard({ state: 'something-unsupported' });
- expect(wrapper.text()).toContain('How many loans will you make this year?');
+ expect(wrapper.text()).toContain(goalCopy.TITLE_HOW_MANY_LOANS_GENERIC);
expect(wrapper.text()).toContain(`Set ${GOALS_CURRENT_YEAR} goal`);
});
});
diff --git a/test/unit/specs/components/MyKiva/GoalSelector.spec.js b/test/unit/specs/components/MyKiva/GoalSelector.spec.js
index c8732bfa4c4..378ce7d645b 100644
--- a/test/unit/specs/components/MyKiva/GoalSelector.spec.js
+++ b/test/unit/specs/components/MyKiva/GoalSelector.spec.js
@@ -56,6 +56,22 @@ describe('GoalSelector', () => {
type: Array,
default: () => [],
},
+ useDirectQuestionTitle: {
+ type: Boolean,
+ default: false,
+ },
+ compactNoGoalYetTitle: {
+ type: Boolean,
+ default: false,
+ },
+ compactLayout: {
+ type: Boolean,
+ default: false,
+ },
+ progressSubtitleBeforeOptions: {
+ type: Boolean,
+ default: false,
+ },
},
data() {
return {
@@ -104,6 +120,10 @@ describe('GoalSelector', () => {
:tiered-achievements="tieredAchievements"
:selected-category-id="selectedCategoryId"
:selected-category-name="selectedCategoryName"
+ :use-direct-question-title="useDirectQuestionTitle"
+ :compact-no-goal-yet-title="compactNoGoalYetTitle"
+ :compact-layout="compactLayout"
+ :progress-subtitle-before-options="progressSubtitleBeforeOptions"
/>