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: iwd activity feed added #5152

Merged
merged 6 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from 5 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
43 changes: 43 additions & 0 deletions .storybook/stories/IwdActivityAvatar.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import ActivityAvatar from '@/components/Iwd/ActivityAvatar';

export default {
title: 'IWD/ActivityAvatar',
component: ActivityAvatar,
};

const story = (args) => {
const template = (_args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { ActivityAvatar },
template: `
<div>
<activity-avatar
:lender-image-url="lenderImageUrl"
:lender-name="lenderName"
/>
</div>
`,
});
template.args = args;
return template;
};

export const Default = story({
lenderImageUrl: 'https://www.development.kiva.org/img/s100/26e15431f51b540f31cd9f011cc54f31.jpg',
lenderName: 'Roger',
});

export const NoImage = story({
lenderImageUrl: '',
lenderName: 'Roger',
});

export const Anonymous = story({
lenderImageUrl: 'https://www.development.kiva.org/img/s100/26e15431f51b540f31cd9f011cc54f31.jpg',
lenderName: 'Anonymous',
});

export const DefaultProfile = story({
lenderImageUrl: 'https://www.development.kiva.org/img/s100/4d844ac2c0b77a8a522741b908ea5c32.jpg',
lenderName: 'Default Profile',
});
35 changes: 35 additions & 0 deletions .storybook/stories/IwdActivityCard.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import ActivityCard from '@/components/Iwd/ActivityCard';

export default {
title: 'IWD/ActivityCard',
component: ActivityCard,
};

const story = (args) => {
const template = (_args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { ActivityCard },
template: `
<div>
<activity-card
:activity="activity"
/>
</div>
`,
});
template.args = args;
return template;
};

export const Default = story({
activity: {
lender: {
name: 'Roger',
image: {
url: 'https://www.development.kiva.org/img/s100/26e15431f51b540f31cd9f011cc54f31.jpg',
},
},
shareAmount: '25.00',
}
});

86 changes: 86 additions & 0 deletions .storybook/stories/IwdActivityFeed.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import ActivityFeed from '@/components/Iwd/ActivityFeed';
import apolloStoryMixin from '../mixins/apollo-story-mixin';

const queryResult = {
data: {
lend: {
campaignActions: {
values: [
{
lender: {
id: 723174,
name: "TonyB",
image: {
url: "https://www-0.development.kiva.org/img/s100/6b1a24092be3aaa22216874e644a4acf.jpg"
}
},
shareAmount: "25.00"
},
{
lender: {
id: 723174,
name: "Roger",
image: {
url: ""
}
},
shareAmount: "25.00"
},
{
lender: {
id: 723174,
name: "Anonymous",
image: {
url: "https://www-0.development.kiva.org/img/s100/6b1a24092be3aaa22216874e644a4acf.jpg"
}
},
shareAmount: "25.00"
},
{
lender: {
id: 723174,
name: "Default user",
image: {
url: "https://www-0.development.kiva.org/img/s100/4d844ac2c0b77a8a522741b908ea5c32.jpg"
}
},
shareAmount: "25.00"
},
{
lender: {
id: 723174,
name: "Jessica",
image: {
url: "https://www-0.development.kiva.org/img/s100/6b1a24092be3aaa22216874e644a4acf.jpg"
}
},
shareAmount: "25.00"
},
]
}
}
}
};

export default {
title: 'IWD/ActivityFeed',
component: ActivityFeed,
};

const story = (args) => {
const template = (_args, { argTypes }) => ({
props: Object.keys(argTypes),
components: { ActivityFeed },
mixins: [apolloStoryMixin({ queryResult })],
template: `
<div>
<activity-feed />
</div>
`,
});
template.args = args;
return template;
};

export const Default = story();

79 changes: 79 additions & 0 deletions src/components/Iwd/ActivityAvatar.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<template>
<div class="tw-w-4 tw-h-4" v-if="!isDefaultProfilePic(imageFilename) && !isAnonymousUser && imageFilename">
<img
:src="lenderImageUrl"
alt="Image of lender"
class="tw-rounded-full tw-inline-block tw-w-4 tw-h-4"
dyersituations marked this conversation as resolved.
Show resolved Hide resolved
>
</div>
<div
v-else-if="!isAnonymousUser && isDefaultProfilePic(imageFilename) || !imageFilename"
class="tw-rounded-full tw-inline-flex tw-align-center tw-justify-center tw-w-4 tw-h-4"
:class="randomizedUserAvatarClass"
>
<!-- First Letter of lender name -->
<span class="tw-self-center">
{{ lenderNameFirstLetter }}
</span>
</div>
<div
v-else
class="tw-rounded-full tw-bg-brand tw-inline-flex tw-align-center tw-justify-center tw-w-4 tw-h-4"
>
<!-- Kiva K logo -->
<img :src="kivaIcon" alt="Kiva Icon">
</div>
</template>

<script>
import { isLegacyPlaceholderAvatar } from '@/util/imageUtils';
import kivaIcon from '@/assets/images/helpmechoose/kiva_mark.svg';

export default {
name: 'ActivityAvatar',
props: {
lenderImageUrl: {
type: String,
default: null
},
lenderName: {
type: String,
required: true
}
},
data() {
return {
kivaIcon,
};
},
computed: {
imageFilename() {
return this.lenderImageUrl?.split('/').pop() ?? '';
},
isAnonymousUser() {
return this.lenderName === 'Anonymous';
},
randomizedUserAvatarClass() {
const userCardStyleOptions = [
{ color: 'tw-text-action', bg: 'tw-bg-brand-100' },
{ color: 'tw-text-black', bg: 'tw-bg-brand-100' },
{ color: 'tw-text-white', bg: 'tw-bg-action' },
{ color: 'tw-text-brand-100', bg: 'tw-bg-action' },
{ color: 'tw-text-primary-inverse', bg: 'tw-bg-action' },
{ color: 'tw-text-white', bg: 'tw-bg-black' },
{ color: 'tw-text-action', bg: 'tw-bg-black' },
];
const randomStyle = userCardStyleOptions[Math.floor(Math.random() * userCardStyleOptions.length)];
return `${randomStyle.color} ${randomStyle.bg}`;
},
lenderNameFirstLetter() {
return this.lenderName?.charAt(0).toUpperCase() ?? '';
}
},
methods: {
isDefaultProfilePic(filename) {
return isLegacyPlaceholderAvatar(filename);
},
}
};
</script>
40 changes: 40 additions & 0 deletions src/components/Iwd/ActivityCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<template>
<div class="tw-flex tw-items-center tw-gap-1 tw-py-0.5 tw-px-1 tw-shadow-lg" style="border-radius: 8px;">
<activity-avatar
:lender-image-url="lenderImageUrl"
:lender-name="lenderName"
/>
<p class="tw-text-base tw-whitespace-nowrap">
<span class="data-hj-suppress">{{ lenderName }}</span> contributed ${{ amountLent }}
</p>
</div>
</template>

<script>
import ActivityAvatar from '@/components/Iwd/ActivityAvatar';

export default {
name: 'ActivityCard',
components: {
ActivityAvatar,
},
props: {
activity: {
type: Object,
required: true
}
},
computed: {
lenderName() {
return this.activity?.lender?.name ?? '';
},
lenderImageUrl() {
return this.activity?.lender?.image?.url ?? '';
},
amountLent() {
const amount = this.activity?.shareAmount ?? 0;
return parseFloat(amount).toFixed();
}
}
};
</script>
51 changes: 51 additions & 0 deletions src/components/Iwd/ActivityFeed.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<template>
<div class="tw-flex tw-gap-x-0.5 tw-overflow-x-auto tw-py-2 tw-px-1 hide-scrollbar">
<activity-card
v-for="(activity, index) in activities"
:key="index"
:activity="activity"
/>
</div>
</template>

<script>
import ActivityCard from '@/components/Iwd/ActivityCard';
import IwdActionsQuery from '@/graphql/query/IwdActions.graphql';

export default {
name: 'ActivityFeed',
components: {
ActivityCard,
},
inject: ['apollo'],
data() {
return {
activities: [],
};
},
methods: {
fetchActivities() {
// Fetch activities from API
this.apollo.query({
dyersituations marked this conversation as resolved.
Show resolved Hide resolved
query: IwdActionsQuery,
}).then(({ data }) => {
this.activities = data?.lend?.campaignActions?.values ?? [];
});
},
},
mounted() {
this.fetchActivities();
},
};
</script>

<style scoped lang="postcss">
.hide-scrollbar {
dyersituations marked this conversation as resolved.
Show resolved Hide resolved
-ms-overflow-style: none; /* IE and Edge */
scrollbar-width: none; /* Firefox */
}

.hide-scrollbar::-webkit-scrollbar {
@apply tw-hidden;
}
</style>
20 changes: 20 additions & 0 deletions src/graphql/query/IwdActions.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
query IwdActions {
lend {
campaignActions(

Check warning on line 3 in src/graphql/query/IwdActions.graphql

View workflow job for this annotation

GitHub Actions / build

Cannot query field "campaignActions" on type "Lend". Did you mean "lendingActions"?
campaignKey: "international_womens_day",
filters: {gender: female}
) {
totalCount
values {
lender {
id
name
image {
url
}
}
shareAmount
}
}
}
}
3 changes: 3 additions & 0 deletions src/pages/Lend/LoanChannelCategoryControl.vue
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<template v-if="iwdHeaderExpEnabled">
<h1>Placeholder for IWD 2024 Header</h1>
<p>More content coming soon!</p>
<activity-feed />
</template>
<template v-else>
<h1 class="tw-mb-2">
Expand Down Expand Up @@ -178,6 +179,7 @@ import EmptyState from '@/components/LoanFinding/EmptyState';
import experimentAssignmentQuery from '@/graphql/query/experimentAssignment.graphql';
import experimentVersionFragment from '@/graphql/fragments/experimentVersion.graphql';
import { trackExperimentVersion } from '@/util/experiment/experimentUtils';
import ActivityFeed from '@/components/Iwd/ActivityFeed';

const defaultLoansPerPage = 12;

Expand Down Expand Up @@ -277,6 +279,7 @@ export default {
PromoGridLoanCardExp,
KvClassicLoanCardContainer,
EmptyState,
ActivityFeed,
},
inject: ['apollo', 'cookieStore'],
mixins: [loanChannelQueryMapMixin],
Expand Down
Loading