Skip to content

Commit

Permalink
fix: conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
Christian Bedon committed Oct 29, 2024
2 parents d15c5f3 + 6b6a110 commit 2040c1a
Show file tree
Hide file tree
Showing 7 changed files with 10,076 additions and 92 deletions.
3 changes: 1 addition & 2 deletions src/components/MyKiva/BadgeModalContentJourney.vue
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,7 @@ const positions = ref(getTierPositions());
const tierCaption = index => {
const tier = sortedTiers.value[index];
if (tier.completedDate) {
// Date is in format "2024-10-22T18:49:21Z[UTC]"
return format(new Date(tier.completedDate.replace('[UTC]', '')), 'MMMM do, yyyy');
return format(new Date(tier.completedDate), 'MMMM do, yyyy');
}
if (tier.target) {
return `${props.badge.totalProgressToAchievement} of ${tier.target} loans`;
Expand Down
61 changes: 12 additions & 49 deletions src/components/MyKiva/BadgesSection.vue
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
<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,
'tw-border-4 tw-border-tertiary tw-border-dashed': !badge.hasStarted
}"
>
<span class="tw-text-base !tw-font-medium tw-text-center tw-mb-1">
{{ getBadgeTitle(badge) }}
{{ getCurrentTierData(badge).challengeName }}
</span>
<div
class="tw-p-1"
Expand All @@ -20,24 +20,24 @@
style="height: 148px;"
>
<img
:src="getBadgeImgUrl(badge)"
:src="getCurrentTierData(badge).imageUrl"
class="tw-h-full tw-mx-auto"
>
</div>
<div class="tw-flex tw-flex-col tw-gap-0.5 tw-mt-2 tw-font-medium">
<div class="tw-flex tw-flex-col tw-gap-0.5 tw-font-medium tw-grow">
<span
v-if="badge.hasStarted"
class="tw-mx-auto"
>
Level {{ badge.level }}/5
</span>
<button
class="tw-text-action hover:tw-underline"
class="tw-text-action hover:tw-underline tw-mt-auto"
v-kv-track-event="[
'portfolio',
'click',
badge.hasStarted ? 'Continue' : 'Start this journey',
getBadgeTitle(badge),
getCurrentTierData(badge).challengeName,
badge.level
]"
@click="() => $emit('badge-clicked', badge)"
Expand All @@ -50,59 +50,22 @@
</template>

<script setup>
import { computed, toRefs } from 'vue';
import { computed } from 'vue';
import { defaultBadges } from '#src/util/achievementUtils';
import useBadgeData from '#src/composables/useBadgeData';
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 getBadgeTitle = badge => badge?.fields?.challengeName ?? '';
const { getCurrentTierData } = useBadgeData();
const getBadgeImgUrl = badge => badge?.fields?.badgeImage?.fields?.file?.url ?? '';
const visibleBadges = computed(() => props.badgeData.filter(b => defaultBadges.includes(b.id)));
</script>

<style lang="postcss" scoped>
Expand Down
190 changes: 190 additions & 0 deletions src/composables/useBadgeData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import { 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
*
* @returns Badge data and utilities
*/
export default function useBadgeData() {
const badgeAchievementData = ref();
const badgeContentfulData = ref();

/**
* Gets a cleaned up version of Contentful badge data
*
* @param entry The Contentful entry
* @returns The cleaned up Contentful badge data
*/
const getContentfulLevelData = entry => ({
id: entry?.fields?.key?.replace(/-level-\d+/, '') ?? '',
level: +(entry?.fields?.key?.replace(/\D/g, '') ?? ''),
levelName: entry?.fields?.challengeName ?? '',
challengeName: (entry?.fields?.challengeName ?? '').replace(/\s*✨\d+✨/, ''),
imageUrl: entry?.fields?.badgeImage?.fields?.file?.url ?? '',
});

/**
* Calls Apollo to get the badge achievement service data
*/
const fetchAchievementData = apollo => {
apollo.query({ query: userAchievementProgressQuery })
.then(result => {
badgeAchievementData.value = [
...(result.data?.userAchievementProgress?.lendingAchievements ?? []),
...(result.data?.userAchievementProgress?.tieredLendingAchievements ?? [])
];
}).catch(e => {
logReadQueryError(e, 'useBadgeData userAchievementProgressQuery');
});
};

/**
* Calls Apollo to get the badge Contentful data
*/
const fetchContentfulData = apollo => {
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');
});
};

/**
* Combines the badge data into a more usable form
*
* @param allAchievementData All of the data for the user from the achievement service
* @param allContentfulData All of the badge data from Contentful
* @returns Combined and cleaned up badge data
*/
const combineBadgeData = (allAchievementData, allContentfulData) => {
const badges = [];

// Ensure data loaded from both achievement service and Contentful
if (allAchievementData && allContentfulData) {
// Currently only targeting specific tiered badges
allAchievementData.forEach(achievementData => {
const contentfulData = allContentfulData.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.map(t => ({
...t,
// Date is in format "2024-10-22T18:49:21Z[UTC]"
completedDate: t.completedDate?.replace('[UTC]', ''),
})),
},
hasStarted,
level,
});
}
});
}

return badges;
};

/**
* Gets the current (incomplete) tier for the provided badge
*
* @param badge The badge to get the current tier for
* @returns The current tier of the badge
*/
const getCurrentTierData = badge => {
let currentTier;
badge.achievementData.tiers.forEach(t => {
if (!currentTier) {
currentTier = t;
} else if (!!currentTier.completedDate && !t.completedDate) {
currentTier = t;
}
});
/**
* {
* "id": "",
* "challengeName": "",
* "level": 1,
* "levelName": "",
* "imageUrl": "",
* "target": 1,
* "tierStatement": "",
* "learnMoreURL": ""
* }
*/
return {
...badge.contentfulData.find(t => t.level === currentTier.level),
...currentTier,
};
};

/**
* {
* "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(() => combineBadgeData(badgeAchievementData.value, badgeContentfulData.value));

return {
fetchAchievementData,
fetchContentfulData,
combineBadgeData,
getContentfulLevelData,
getCurrentTierData,
badgeAchievementData,
badgeData
};
}
5 changes: 2 additions & 3 deletions src/composables/useBadgeModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,11 @@ export const ID_US_ECONOMIC_EQUALITY = 'us-economic-equality';
export const ID_CLIMATE_ACTION = 'climate-action';
export const ID_REFUGEE_EQUALITY = 'refugee-equality';
export const ID_BASIC_NEEDS = 'basic-needs';
// TODO: /lend/filter does not support the "OR" filter needed between different filters. BASIC_NEEDS and CLIMATE_ACTION
export const US_ECONOMIC_EQUALITY_FILTER = 'country=PR,US';
export const CLIMATE_ACTION_FILTER = 'attribute=32&tag=9,8';
export const CLIMATE_ACTION_FILTER = 'tag=9';
export const REFUGEE_EQUALITY_FILTER = 'attribute=28';
export const WOMENS_EQUALITY_FILTER = 'gender=female';
export const BASIC_NEEDS_FILTER = 'sector=6,10&attribute=8';
export const BASIC_NEEDS_FILTER = 'sector=6,10';

/**
* General utilities for the MyKiva badge modal
Expand Down
Loading

0 comments on commit 2040c1a

Please sign in to comment.