• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

kiva / ui / 18959440641

31 Oct 2025 01:00AM UTC coverage: 50.115%. First build
18959440641

Pull #6410

github

web-flow
Merge 1c9df5739 into f4a55f874
Pull Request #6410: fix: analytics and impact goal fixes

1840 of 3912 branches covered (47.03%)

Branch coverage included in aggregate %.

0 of 4 new or added lines in 1 file covered. (0.0%)

2723 of 5193 relevant lines covered (52.44%)

259.62 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/composables/useGoalData.js
1
import {
2
        computed,
3
        inject,
4
        ref,
5
} from 'vue';
6

7
import useGoalDataQuery from '#src/graphql/query/useGoalData.graphql';
8
import useGoalDataProgressQuery from '#src/graphql/query/useGoalDataProgress.graphql';
9
import logFormatter from '#src/util/logFormatter';
10
import { createUserPreferences, updateUserPreferences } from '#src/util/userPreferenceUtils';
11

12
import {
13
        ID_BASIC_NEEDS,
14
        ID_CLIMATE_ACTION,
15
        ID_REFUGEE_EQUALITY,
16
        ID_SUPPORT_ALL,
17
        ID_US_ECONOMIC_EQUALITY,
18
        ID_WOMENS_EQUALITY,
19
} from '#src/composables/useBadgeData';
20

21
const GOAL_DISPLAY_MAP = {
×
22
        [ID_BASIC_NEEDS]: 'basic needs loans',
23
        [ID_CLIMATE_ACTION]: 'eco-friendly loans',
24
        [ID_REFUGEE_EQUALITY]: 'refugees',
25
        [ID_SUPPORT_ALL]: 'loans',
26
        [ID_US_ECONOMIC_EQUALITY]: 'U.S. entrepreneurs',
27
        [ID_WOMENS_EQUALITY]: 'women',
28
};
29

30
function getGoalDisplayName(category) {
31
        return GOAL_DISPLAY_MAP[category] || 'loans';
×
32
}
33

34
/**
35
 * Vue composable for loading and managing user goal data
36
 *
37
 * @param {Object} options - Configuration options
38
 * @param {Array} options.loans - List of loans to count toward goals
39
 * @param {Object} options.apollo - Apollo client instance (optional, will use inject if not provided)
40
 * @returns Goal data and utilities
41
 */
42
export default function useGoalData({ loans, apollo: apolloParam }) {
43
        const apollo = apolloParam || inject('apollo');
×
44
        const $kvTrackEvent = inject('$kvTrackEvent');
×
45

46
        const allTimeProgress = ref([]);
×
47
        const loading = ref(true);
×
48
        const totalLoanCount = ref(null);
×
49
        const userGoal = ref(null);
×
50
        const userPreferences = ref(null);
×
51
        const userGoalAchievedNow = ref(false);
×
52

53
        async function loadPreferences(fetchPolicy = 'cache-first') {
×
54
                try {
×
55
                        const response = await apollo.query({ query: useGoalDataQuery, fetchPolicy });
×
56
                        const prefsData = response.data?.my?.userPreferences || null;
×
57
                        totalLoanCount.value = response.data?.my?.loans?.totalCount || 0;
×
58
                        userPreferences.value = prefsData;
×
59
                        return prefsData ? JSON.parse(prefsData.preferences || '{}') : {};
×
60
                } catch (error) {
61
                        logFormatter(error, 'Failed to load preferences');
×
62
                        return null;
×
63
                }
64
        }
65

66
        async function loadProgress(fetchPolicy = 'cache-first') {
×
67
                try {
×
68
                        const loanIds = loans.map(loan => loan.id);
×
69
                        const response = await apollo.query({
×
70
                                query: useGoalDataProgressQuery,
71
                                variables: { loanIds },
72
                                fetchPolicy
73
                        });
74
                        allTimeProgress.value = response?.data?.postCheckoutAchievements?.allTimeProgress || [];
×
75
                        return true;
×
76
                } catch (error) {
77
                        logFormatter(error, 'Failed to load progress');
×
78
                        return null;
×
79
                }
80
        }
81

82
        function setGoalState(parsedPrefs) {
83
                if (!parsedPrefs) return;
×
84
                const goals = parsedPrefs.goals || [];
×
85
                userGoal.value = { ...goals[0] };
×
86
        }
87

88
        async function storeGoalPreferences(updates) {
89
                if (!userPreferences.value?.id) {
×
90
                        await createUserPreferences(apollo, { goals: [] });
×
91
                        await loadPreferences('network-only'); // Reload after create
×
92
                }
93
                const parsedPrefs = JSON.parse(userPreferences.value?.preferences || '{}');
×
94
                const goals = parsedPrefs.goals || [];
×
95
                const goalIndex = goals.findIndex(g => g.goalName === updates.goalName);
×
96
                if (goalIndex !== -1) goals[goalIndex] = { ...goals[goalIndex], ...updates };
×
97
                else goals.push(updates);
×
98
                await updateUserPreferences(apollo, userPreferences.value, parsedPrefs, { goals });
×
99
                setGoalState({ goals }); // Refresh local state after update
×
100
        }
101

102
        const goalProgress = computed(() => {
×
NEW
103
                if (userGoal.value?.category === ID_SUPPORT_ALL) {
×
NEW
104
                        const currentTotal = totalLoanCount.value || 0;
×
NEW
105
                        const startTotal = userGoal.value?.loanTotalAtStart || 0;
×
NEW
106
                        return Math.max(currentTotal - startTotal, 0);
×
107
                }
108
                const totalProgress = allTimeProgress.value.find(
×
109
                        entry => entry.achievementId === userGoal.value?.category
×
110
                )?.totalProgress || 0;
111
                const adjustedProgress = totalProgress - (userGoal.value?.loanTotalAtStart || 0);
×
112
                return Math.max(adjustedProgress, 0);
×
113
        });
114

115
        const userGoalAchieved = computed(() => goalProgress.value >= userGoal.value?.target);
×
116

117
        const checkCompletedGoal = async (category = 'post-checkout') => {
×
118
                if (userGoal.value && userGoalAchieved.value && userGoal.value.status !== 'completed') {
×
119
                        await storeGoalPreferences({
×
120
                                goalName: userGoal.value.goalName,
121
                                dateStarted: userGoal.value.dateStarted,
122
                                target: userGoal.value.target,
123
                                count: userGoal.value.count,
124
                                status: 'completed',
125
                        });
126
                        $kvTrackEvent(
×
127
                                category,
128
                                'show',
129
                                'annual-goal-complete',
130
                                userGoal.value.category,
131
                                userGoal.value.target
132
                        );
133
                        userGoalAchievedNow.value = true;
×
134
                }
135
        };
136

137
        async function loadGoalData() {
138
                loading.value = true;
×
139
                const parsedPrefs = await loadPreferences();
×
140
                await loadProgress();
×
141
                setGoalState(parsedPrefs);
×
142
                loading.value = false;
×
143
        }
144

145
        return {
×
146
                getGoalDisplayName,
147
                goalProgress,
148
                loading,
149
                loadGoalData,
150
                storeGoalPreferences,
151
                userGoal,
152
                userGoalAchieved,
153
                userGoalAchievedNow,
154
                checkCompletedGoal,
155
        };
156
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc