Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: composable for tiered badge data #5620

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 5 additions & 41 deletions src/components/MyKiva/BadgesSection.vue
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<template>
<div class="tw-w-full tw-inline-flex tw-flex-wrap tw-justify-center tw-gap-2.5">
<div
v-for="badge in badgesArray"
:key="badge.fields.key"
v-for="(badge, index) in visibleBadges"
:key="index"
class="badge-container tw-flex tw-flex-col tw-justify-between tw-p-1.5 tw-rounded"
:class="{
'tw-bg-white': badge.hasStarted,
Expand Down Expand Up @@ -50,55 +50,19 @@
</template>

<script setup>
import { computed, toRefs } from 'vue';
import { computed } from 'vue';
import { defaultBadges } from '#src/util/achievementUtils';

defineEmits(['badge-clicked']);

const props = defineProps({
badgesData: {
badgeData: {
type: Array,
default: () => ([])
},
userAchievements: {
type: Array,
default: () => ([])
}
});

const { badgesData, userAchievements } = toRefs(props);

const badgesArray = computed(() => {
const badges = [];
if (badgesData.value.length > 0) {
defaultBadges.forEach(badgeKey => {
let badgeFound = badgesData.value.find(entry => entry.fields.key === `${badgeKey}-level-1`);
const userAchievement = userAchievements.value.find(entry => entry.id === badgeKey);

if (!userAchievement) {
badgeFound = {
...badgeFound,
hasStarted: false,
level: 0,
};
} else {
// TODO: Update this status field when we have the data from the backend
const hasStarted = userAchievement.status !== 'NO_PROGRESS';
// TODO: Change this to level when we have the data from the backend
const level = userAchievement.totalProgressToAchievement;
badgeFound = {
...badgeFound,
hasStarted,
level,
...userAchievement,
};
}

badges.push(badgeFound);
});
}
return badges;
});
const visibleBadges = computed(() => props.badgeData.filter(b => defaultBadges.includes(b.id)));

const getBadgeTitle = badge => badge?.fields?.challengeName ?? '';

Expand Down
125 changes: 125 additions & 0 deletions src/composables/useBadgeData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { onMounted, ref, computed } from 'vue';
import userAchievementProgressQuery from '#src/graphql/query/userAchievementProgress.graphql';
import contentfulEntriesQuery from '#src/graphql/query/contentfulEntries.graphql';
import logReadQueryError from '#src/util/logReadQueryError';

/**
* Utilities for loading and combining tiered badge data
*
* @param apollo The current Apollo client instance
* @returns Cleaned up tiered badge data
*/
export default function useBadgeData(apollo) {
const badgeAchievementData = ref();
const badgeContentfulData = ref();

const getContentfulLevelData = entry => ({
id: entry?.fields?.key?.replace(/-level-\d+/, '') ?? '',
level: +(entry?.fields?.key?.replace(/\D/g, '') ?? ''),
levelName: entry?.fields?.challengeName ?? '',
imageUrl: entry?.fields?.badgeImage?.fields?.file?.url ?? '',
});

const fetchAchievementData = () => {
apollo.query({ query: userAchievementProgressQuery })
.then(result => {
badgeAchievementData.value = result.data?.userAchievementProgress?.tieredLendingAchievements ?? [];
}).catch(e => {
logReadQueryError(e, 'useBadgeData userAchievementProgressQuery');
});
};

const fetchContentfulData = () => {
apollo.query({
query: contentfulEntriesQuery,
variables: {
contentType: 'challenge',
limit: 200,
}
})
.then(result => {
badgeContentfulData.value = (result.data?.contentful?.entries?.items ?? [])
.map(entry => getContentfulLevelData(entry));
}).catch(e => {
logReadQueryError(e, 'useBadgeData contentfulEntriesQuery');
});
};

/**
* {
* "contentfulData": [
* {
* "id": "",
* "level": 1,
* "levelName": "",
* "imageUrl": ""
* },
* ...
* ],
* "achievementData": {
* "id": "",
* "totalProgressToAchievement": 0,
* "tiers": [
* {
* "target": 1,
* "tierStatement": "",
* "completedDate": null,
* "learnMoreURL": "",
* "level": 1
* },
* ...
* ]
* },
* "hasStarted": false,
* "level": undefined
* }
*/
const badgeData = computed(() => {
const badges = [];

// Ensure data loaded from both achievement service and Contentful
if (badgeAchievementData.value && badgeContentfulData.value) {
// Currently only targeting specific tiered badges
badgeAchievementData.value.forEach(achievementData => {
const contentfulData = badgeContentfulData.value.filter(entry => entry.id === achievementData.id);

// Ensure badges are defined in both locations
if (achievementData && contentfulData) {
const sortedTiers = [...(achievementData.tiers ?? [])].map((t, i) => {
const tier = JSON.parse(JSON.stringify(t));
// Ensure achievement data includes numerical level of tier
tier.level = i + 1;
return tier;
});

sortedTiers.sort((a, b) => a.target - b.target);

// Get specific properties used in the UI
const completedTiers = sortedTiers.filter(t => !!t.completedDate);
const hasStarted = completedTiers.length > 0;
const level = hasStarted ? completedTiers[completedTiers.length - 1].level : undefined;

// Combine the achievement service and Contentful data
badges.push({
id: achievementData.id,
contentfulData,
achievementData: {
...achievementData,
tiers: sortedTiers,
},
hasStarted,
level,
});
}
});
}
return badges;
});

onMounted(() => {
fetchAchievementData();
fetchContentfulData();
});

return { badgeAchievementData, badgeData };
}
44 changes: 5 additions & 39 deletions src/pages/Portfolio/MyKiva/MyKivaPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,7 @@
</section>
</MyKivaContainer>
<section class="tw-my-2">
<MyKivaStats
:user-achievements="userAchievements"
/>
<MyKivaStats :user-achievements="badgeAchievementData" />
<MyKivaContainer>
<div class="tw-flex tw-flex-col tw-w-full lg:tw-hidden tw-mt-2">
<router-link
Expand Down Expand Up @@ -90,11 +88,7 @@
>
My impact journeys
</h3>
<BadgesSection
:badges-data="badgesData"
:user-achievements="userAchievements"
@badge-clicked="handleBadgeClicked"
/>
<BadgesSection :badge-data="badgeData" @badge-clicked="handleBadgeClicked" />

<BadgeModal
v-if="selectedBadgeData"
Expand All @@ -115,8 +109,6 @@ import WwwPage from '#src/components/WwwFrame/WwwPage';
import MyKivaNavigation from '#src/components/MyKiva/MyKivaNavigation';
import myKivaQuery from '#src/graphql/query/myKiva.graphql';
import updatesQuery from '#src/graphql/query/loanUpdates.graphql';
import userAchievementProgressQuery from '#src/graphql/query/userAchievementProgress.graphql';
import contentfulEntriesQuery from '#src/graphql/query/contentfulEntries.graphql';
import MyKivaHero from '#src/components/MyKiva/MyKivaHero';
import MyKivaProfile from '#src/components/MyKiva/MyKivaProfile';
import MyKivaContainer from '#src/components/MyKiva/MyKivaContainer';
Expand All @@ -125,6 +117,7 @@ import JournalUpdatesCarousel from '#src/components/MyKiva/JournalUpdatesCarouse
import BadgeModal from '#src/components/MyKiva/BadgeModal';
import BadgesSection from '#src/components/MyKiva/BadgesSection';
import MyKivaStats from '#src/components/MyKiva/MyKivaStats';
import useBadgeData from '#src/composables/useBadgeData';

import {
ref,
Expand All @@ -138,6 +131,8 @@ const MY_KIVA_EXP_KEY = 'my_kiva_page';
const apollo = inject('apollo');
const $kvTrackEvent = inject('$kvTrackEvent');

const { badgeAchievementData, badgeData } = useBadgeData(apollo);

const lender = ref(null);
const showNavigation = ref(false);
const userInfo = ref({});
Expand All @@ -146,8 +141,6 @@ const activeLoan = ref({});
const loanUpdates = ref([]);
const showBadgeModal = ref(false);
const selectedBadgeData = ref();
const userAchievements = ref([]);
const badgesData = ref([]);

const isLoading = computed(() => !lender.value);

Expand Down Expand Up @@ -179,30 +172,6 @@ const handleSelectedLoan = loan => {
fetchLoanUpdates(activeLoan.value.id);
};

const fetchUserAchievements = () => {
apollo.query({ query: userAchievementProgressQuery })
.then(result => {
userAchievements.value = result.data?.userAchievementProgress?.tieredLendingAchievements ?? [];
}).catch(e => {
logReadQueryError(e, 'MyKivaPage userAchievementProgressQuery');
});
};

const fetchBadgesData = () => {
apollo.query({
query: contentfulEntriesQuery,
variables: {
contentType: 'challenge',
limit: 200,
}
})
.then(result => {
badgesData.value = result.data?.contentful?.entries?.items ?? [];
}).catch(e => {
logReadQueryError(e, 'MyKivaPage contentfulEntriesQuery');
});
};

apollo.query({ query: myKivaQuery })
.then(result => {
userInfo.value = result.data?.my ?? {};
Expand All @@ -227,8 +196,5 @@ onMounted(() => {
);

$kvTrackEvent('portfolio', 'view', 'new-my-kiva');

fetchBadgesData();
fetchUserAchievements();
});
</script>
Loading