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/s100/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/s100/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/s100/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/s100/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/s100/26342071330beb8309d17a5d262da2dd.jpg",
"hash": "26342071330beb8309d17a5d262da2dd"
},
"use": "to buy used clothing for men, women and children in specific sizes.",
"geocode": {
Expand Down
22 changes: 22 additions & 0 deletions .storybook/stories/ThanksBadges.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ const story = (args = {}) => {
return template;
};

const loans = mockedReceiptData.items.values?.map((item) => item?.loan).filter(loan => !!loan?.id);
christian14b marked this conversation as resolved.
Show resolved Hide resolved

export const UserGuest = story({
lender: mockLender,
loans: mockLoans,
Expand Down Expand Up @@ -97,3 +99,23 @@ export const BadgeMultiple = story({
receipt: mockedReceiptData,
badgesAchieved: [mockOldBadge, mockTieredBadge],
});

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

export const UserGuestOptedOutWithTwoLoans = story({
lender: mockLender,
isGuest: false,
optedIn: false,
receipt: mockedReceiptData,
loans: loans.slice(0, 2),
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="borrower-container">
<div
class="tw-flex tw-justify-center tw-items-center tw-z-1 image-container"
>
<borrower-image
v-for="loan, index in loansToDisplay"
:key="loan.id"
class="borrower-image"
:class="{'centered-borrower' : index === 1 && loansToDisplay.length === 3}"
:style="{
marginLeft: `${loans.length === 2 && index === 1
? (index * marginLeftWeight)
: (index - 1) * marginLeftWeight}rem`,
zIndex: `${index + 1}`,
}"
:aspect-ratio="1"
:default-image="{ width: 200, faceZoom: 30 }"
:hash="hash(loan)"
:images="[
{ width: 200, faceZoom: 30 },
]"
/>
</div>
</div>
<p>{{ description }}</p>
christian14b marked this conversation as resolved.
Show resolved Hide resolved
<div class="tw-w-full tw-flex tw-flex-col tw-gap-2">
<kv-button
class="tw-w-full"
@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 ghost-button"
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 BorrowerImage from '#src/components/BorrowerProfile/BorrowerImage';
import KvButton from '@kiva/kv-components/vue/KvButton';
import useIsMobile from '#src/composables/useIsMobile';
import {
MOBILE_BREAKPOINT,
} from '#src/composables/useBadgeModal';
import OptInNotification from './OptInNotification';

const props = defineProps({
selectedLoan: {
type: Object,
default: () => ({})
},
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 borrowerName = computed(() => {
return props.selectedLoan?.name ?? '';
});

const title = computed(() => {
if (props.optedIn) {
return 'Thank you! You reached a milestone';
}
if (props.loans.length === 1) {
return `You and ${borrowerName.value} are in this together now.`;
christian14b marked this conversation as resolved.
Show resolved Hide resolved
}
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 ${borrowerName.value}'s life and more ways to help people like them?`
);

const loansToDisplay = computed(() => {
const loans = [...props.loans];
if (props.loans.length === 3) {
const indexToRemove = loans.indexOf(loan => loan.id === props.selectedLoan.id);
dyersituations marked this conversation as resolved.
Show resolved Hide resolved
const removedLoan = loans.splice(indexToRemove, 1)[0];
loans.splice(1, 0, removedLoan);
}
return loans.slice(0, 3);
});

const marginLeftWeight = computed(() => {
if (loansToDisplay.value.length === 1) {
return 0;
}
if (loansToDisplay.value.length === 2) {
return 6;
}

if (isMobile.value) {
return 7;
}

return 12;
});

const hash = loan => {
return loan?.image?.hash ?? '';
};

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, 'error');
dyersituations marked this conversation as resolved.
Show resolved Hide resolved
}
}
newConsentAnswered.value = true;
receiveNews.value = value;
};
</script>

<style lang="postcss" scoped>

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

@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 {
width: 80px !important;
height: 80px !important;

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

box-shadow: '0px 4.42px 22.1px 0px #D1DCD6';
christian14b marked this conversation as resolved.
Show resolved Hide resolved
@apply tw-absolute tw-w-full tw-rounded-full tw-bg-black tw-border-4 tw-border-white tw-z-2 tw-pb-0;
}

.centered-borrower {
width: 100px !important;
height: 100px !important;

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

@apply !tw-z-5;
}

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

.collapse-enter-active,
.collapse-leave-active {
transition: max-height 1s ease, opacity 1s ease;
dyersituations marked this conversation as resolved.
Show resolved Hide resolved
overflow: hidden;
}

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