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: opt-in module mp 1024 #5666

Merged
merged 16 commits into from
Nov 12, 2024
15 changes: 10 additions & 5 deletions .storybook/mock-data/receipt-data-mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ export default {
"name": "Alan",
"id": 1911067,
"image": {
"url": "https://www-dev-kiva-org.global.ssl.fastly.net/img/s100/5e041e655faa305c49c13505d1c17cba.jpg"
"url": "https://www-dev-kiva-org.global.ssl.fastly.net/img/s300/5e041e655faa305c49c13505d1c17cba.jpg",
"hash": "5e041e655faa305c49c13505d1c17cba"
},
"use": "to expand her business by purchasing more stock such as seed and manure for the expansion of her vegetable garden.",
"geocode": {
Expand All @@ -63,7 +64,8 @@ export default {
"name": "Alan",
"id": 1908530,
"image": {
"url": "https://www-dev-kiva-org.global.ssl.fastly.net/img/s100/da9ac74228e39e15cc198f8165e68c20.jpg"
"url": "https://www-dev-kiva-org.global.ssl.fastly.net/img/s300/da9ac74228e39e15cc198f8165e68c20.jpg",
"hash": "da9ac74228e39e15cc198f8165e68c20"
},
"use": "to buy taro roots (seedlings), banana tubers (seedlings), chemicals, a bag back sprayer, and a chainsaw.",
"geocode": {
Expand All @@ -87,7 +89,8 @@ export default {
"name": "Barrio Concepcion Group",
"id": 1907517,
"image": {
"url": "https://www-dev-kiva-org.global.ssl.fastly.net/img/s100/213d4f59069e192a2a4b9ee1fb56b649.jpg"
"url": "https://www-dev-kiva-org.global.ssl.fastly.net/img/s300/213d4f59069e192a2a4b9ee1fb56b649.jpg",
"hash": "213d4f59069e192a2a4b9ee1fb56b649"
},
"use": "to buy a stove with a grill, kitchen utensils, corn and firewood.",
"geocode": {
Expand All @@ -111,7 +114,8 @@ export default {
"name": "Alan",
"id": 1909791,
"image": {
"url": "https://www-dev-kiva-org.global.ssl.fastly.net/img/s100/26e15431f51b540f31cd9f011cc54f31.jpg"
"url": "https://www-dev-kiva-org.global.ssl.fastly.net/img/s300/26e15431f51b540f31cd9f011cc54f31.jpg",
"hash": "26e15431f51b540f31cd9f011cc54f31"
},
"use": "to buy farming inputs and improve her production in 2020 for an increased income.",
"geocode": {
Expand All @@ -135,7 +139,8 @@ export default {
"name": "Las Victoriosas Group",
"id": 1906824,
"image": {
"url": "https://www-dev-kiva-org.global.ssl.fastly.net/img/s100/26342071330beb8309d17a5d262da2dd.jpg"
"url": "https://www-dev-kiva-org.global.ssl.fastly.net/img/s300/26342071330beb8309d17a5d262da2dd.jpg",
"hash": "26342071330beb8309d17a5d262da2dd"
},
"use": "to buy used clothing for men, women and children in specific sizes.",
"geocode": {
Expand Down
29 changes: 29 additions & 0 deletions .storybook/stories/ThanksBadges.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -97,3 +97,32 @@ export const BadgeMultiple = story({
receipt: mockedReceiptData,
badgesAchieved: [mockOldBadge, mockTieredBadge],
});

export const UserGuestOptedOutWithOneLoan = story({
lender: mockLender,
isGuest: false,
optedIn: false,
receipt: mockedReceiptData,
loans: mockLoans.slice(0, 1),
selectedLoan: mockLoans[0],
badgesAchieved: [mockTieredBadge],
});

export const UserGuestOptedOutWithTwoLoans = story({
lender: mockLender,
isGuest: false,
optedIn: false,
receipt: mockedReceiptData,
loans: mockLoans.slice(0, 2),
badgesAchieved: [mockTieredBadge],
});

export const UserGuestOptedOutWithThreeOrMoreLoans = story({
lender: mockLender,
isGuest: false,
optedIn: false,
receipt: mockedReceiptData,
loans: mockLoans.slice(0, 3),
badgesAchieved: [mockTieredBadge],
});

253 changes: 253 additions & 0 deletions src/components/Thanks/MyKiva/OptInModule.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
<template>
<transition-group name="collapse" tag="div">
<template v-if="!newConsentAnswered">
<div class="module-container">
<h2>{{ title }}</h2>
<div class="tw-flex tw-items-center tw-justify-center">
<KvUserAvatar
v-for="loan, index in loansToDisplay"
:key="loan.id"
:lender-name="loan?.name"
:lender-image-url="loan?.image?.url"
class="borrower-image tw-rounded-full tw-shadow"
:class="{
'centered-borrower-image' : index === 1 && loansToDisplay.length === 3,
'single-pair-loans': loansToDisplay.length < 3
}"
:style="{
marginRight: getMarginRight(index),
marginLeft: getMarginLeft(index),
zIndex: index === 1 ? 2 : 1,
}"
/>
</div>
<h3 class="tw-font-book">
{{ description }}
</h3>
<div class="tw-w-full tw-flex tw-flex-col tw-gap-2">
<kv-button
class="tw-w-full btn"
@click="() => updateOptIn(true)"
v-kv-track-event="[
'thanks',
'click',
'accept-opt-in-request',
]"
>
Yes, keep me updated
</kv-button>
<kv-button
dyersituations marked this conversation as resolved.
Show resolved Hide resolved
@click="() => updateOptIn(false)"
variant="ghost"
class="tw-w-full btn ghost"
v-kv-track-event="[
'thanks',
'click',
'reject-opt-in-request',
]"
>
No, I don’t want to receive updates
</kv-button>
</div>
</div>
</template>

<template v-if="newConsentAnswered">
<OptInNotification
:loans-to-display="loansToDisplay"
:receive-news="receiveNews"
:is-mobile="isMobile"
/>
</template>
</transition-group>
</template>

<script setup>
import { computed, inject, ref } from 'vue';
import { gql } from 'graphql-tag';
import logReadQueryError from '#src/util/logReadQueryError';
import KvButton from '@kiva/kv-components/vue/KvButton';
import useIsMobile from '#src/composables/useIsMobile';
import {
MOBILE_BREAKPOINT,
} from '#src/composables/useBadgeModal';
import KvUserAvatar from '@kiva/kv-components/vue/KvUserAvatar';
import OptInNotification from './OptInNotification';

const props = defineProps({
loans: {
type: Array,
default: () => ([])
},
optedIn: {
type: Boolean,
default: false
}
});

const apollo = inject('apollo');
const newConsentAnswered = ref(false);
const receiveNews = ref(false);

const { isMobile } = useIsMobile(MOBILE_BREAKPOINT);

const title = computed(() => {
if (props.optedIn) {
return 'Thank you! You reached a milestone';
}
if (props.loans.length === 1) {
return `Thank you! You and ${props.loans[0]?.name} are in this together now.`;
}
if (props.loans.length === 2) {
const names = props.loans.map(loan => loan?.name).join(' and ');
return `Thank you! You, ${names} are in this together now.`;
}

return `Thank you! You, ${props.loans[0]?.name}, ${props.loans[1]?.name}
and ${props.loans[2]?.name} are in this together now.`;
});

const description = computed(
() => `Want to hear how you're impacting ${props.loans[0]?.name}'s life and more ways to help people like them?`
);

const loansToDisplay = computed(() => props.loans.slice(0, 3));

const getMarginRight = index => {
if (loansToDisplay.value.length > 2 && index === 0) {
if (isMobile.value) {
return '-81.5px';
}
return '-100px';
}

return '0';
};

const getMarginLeft = index => {
if (loansToDisplay.value.length > 1 && index === loansToDisplay.value.length - 1) {
if (loansToDisplay.value.length === 2) {
return '-63px';
}

if (isMobile.value) {
return '-81.5px';
}
return '-100px';
}

return '0';
};

const updateOptIn = value => {
if (value) {
try {
apollo.mutate({
mutation: gql`
mutation updateCommunicationSettings(
$lenderNews: Boolean
) {
my {
updateCommunicationSettings(
communicationSettings: {
lenderNews: $lenderNews
}
)
}
}
`,
variables: {
lenderNews: value,
},
});
} catch (error) {
logReadQueryError(error, 'OptInModule updateCommunicationSettings');
}
}
newConsentAnswered.value = true;
receiveNews.value = value;
};
</script>

<style lang="postcss" scoped>

.module-container {
max-width: 620px;
max-height: 900px;

@apply tw-flex tw-flex-col tw-mx-auto tw-overflow-hidden tw-opacity-full tw-bg-white
tw-text-center tw-px-3 md:tw-px-8 tw-gap-3 tw-rounded-lg tw-py-4;
}

.borrower-container {
animation: fadein ease-in 1s;
width: 70px;

@screen md {
width: 150px;
}

@apply tw-block tw-relative tw-mx-auto tw-z-4;
}

.borrower-container > div {
height: 100px;

@screen md {
height: 200px;
}
}

.borrower-image, .borrower-image :deep(img) {
width: 124px;
height: 124px;

@screen md {
width: 160px;
dyersituations marked this conversation as resolved.
Show resolved Hide resolved
height: 160px;
}
}

.single-pair-loans, .single-pair-loans :deep(img) {
width: 148px !important;
height: 148px !important;

@screen md {
width: 200px !important;
height: 200px !important;
}
}

.centered-borrower-image, .centered-borrower-image :deep(img) {
width: 164px !important;
height: 160px !important;

@screen md {
width: 200px !important;
height: 200px !important;
}
}

.borrower-image :deep(img), .centered-borrower-image :deep(img) {
@apply tw-border-4 tw-border-white;
}

.btn :deep(span) {
@apply tw-px-0;
}

.btn.ghost :deep(span) {
@apply tw-bg-transparent;
}

.collapse-enter-active,
.collapse-leave-active {
transition: max-height 0.5s ease, opacity 0.5s ease, padding 0.5s ease;
overflow: hidden;
}

.collapse-enter,
.collapse-leave-to {
@apply tw-max-h-0 tw-opacity-0 tw-py-0;
}
</style>
Loading
Loading