Repository: kiva/ui
Pull request #5867: feat: payment history modal
Author: login: michelleinez
Labels: name: released on @staging
name: released
Created: 2025-03-12T18:48:02Z | Merged: 2025-03-12T19:40:11Z



Diff:
diff --git a/src/components/Portfolio/LoanList.vue b/src/components/Portfolio/LoanList.vue
index 4eb9cfbdffb..4c12ad4e650 100644
--- a/src/components/Portfolio/LoanList.vue
+++ b/src/components/Portfolio/LoanList.vue
@@ -125,12 +125,10 @@
 									{{ loan.lenderRepaymentTerm || '-' }} mos
 								</div>
 							</td>
-							<td class="tw-text-right tw-px-2">
-								<div>
-									<div class="tw-mb-1">
-										${{ loan.userProperties.loanBalance.amountRepaidToLender }} repaid to you
-									</div>
-								</div>
+							<td class="tw-px-2">
+								<paid-amount-modal
+									:amount="loan.userProperties.loanBalance.amountRepaidToLender"
+								/>
 							</td>
 							<td class="team-cell tw-whitespace-normal tw-break-words tw-px-2">
 								<div class="tw-items-center">
@@ -167,6 +165,7 @@ import {
 	RAISED,
 	REFUNDED,
 } from '#src/api/fixtures/LoanStatusEnum';
+import PaidAmountModal from '#src/components/Portfolio/PaidAmountModal';
 
 const DELINQUENT = 'payingBackDelinquent';
 
@@ -185,7 +184,8 @@ export default {
 	},
 	components: {
 		KvFlag,
-		KvLoadingPlaceholder
+		KvLoadingPlaceholder,
+		PaidAmountModal
 	},
 	methods: {
 		formatDate(date) {
diff --git a/src/components/Portfolio/PaidAmountModal.vue b/src/components/Portfolio/PaidAmountModal.vue
new file mode 100644
index 00000000000..38c28b8b36a
--- /dev/null
+++ b/src/components/Portfolio/PaidAmountModal.vue
@@ -0,0 +1,57 @@
+<template>
+	<div>
+		<span
+			class="tw-text-link tw-cursor-pointer"
+			@click="showModal = true"
+		>
+			${{ formattedAmount }} repaid to you
+		</span>
+
+		<kv-lightbox
+			:visible="showModal"
+			@lightbox-closed="showModal = false"
+			title="Payment history"
+		>
+			<div class="tw-px-4">
+				<div class="tw-grid tw-grid-cols-2">
+					<div v-for="(payment, index) in paymentHistory" :key="index" class="tw-contents">
+						<span>{{ payment.date }}:</span>
+						<span class="tw-relative">
+							<span v-if="payment.type === 'loss'" class="tw-absolute tw-right-full">-</span>
+							${{ payment.amount }}
+							{{ payment.type === 'loss' ? 'currency loss' : 'repaid' }}
+						</span>
+					</div>
+				</div>
+			</div>
+		</kv-lightbox>
+	</div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+import numeral from 'numeral';
+import { KvLightbox } from '@kiva/kv-components';
+
+const props = defineProps({
+	amount: {
+		type: String,
+		required: true
+	},
+	paymentHistory: {
+		type: Array,
+		default: () => ([
+			// Fake hardcoded example data to use while API endpoints are still being worked on
+			{ date: 'Dec 18, 2024', amount: '2.37', type: 'repaid' },
+			{ date: 'Jan 28, 2025', amount: '0.89', type: 'repaid' },
+			{ date: 'Jan 28, 2025', amount: '0.01', type: 'loss' }
+		])
+	}
+});
+
+const showModal = ref(false);
+
+const formattedAmount = computed(() => {
+	return numeral(props.amount).format('0,0.00');
+});
+</script>


Reviews:
user: login: dyersituations
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-12T18:52:22Z
user: login: dyersituations
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-12T18:52:45Z
user: login: michelleinez
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T19:12:22Z
user: login: michelleinez
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-12T19:12:46Z
user: login: dyersituations
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-03-12T19:14:10Z
user: login: dyersituations
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-03-12T19:14:19Z

Review comments:
diff_hunk: @@ -0,0 +1,66 @@
+<template>
+	<div>
+		<span
+			class="tw-text-link tw-cursor-pointer"
+			@click="showModal = true"
+		>
+			${{ formattedAmount }} repaid to you
+		</span>
+
+		<kv-lightbox
+			:visible="showModal"
+			@lightbox-closed="showModal = false"
+			title="Payment history"
+		>
+			<div class="tw-px-4">
+				<div class="tw-grid tw-grid-cols-2">
+					<div v-for="(payment, index) in paymentHistory" :key="index" class="tw-contents">
+						<span>{{ payment.date }}:</span>
+						<span class="tw-relative">
+							<span v-if="payment.type === 'loss'" class="tw-absolute tw-right-full">-</span>
+							${{ payment.amount }}
+							{{ payment.type === 'loss' ? 'currency loss' : 'repaid' }}
+						</span>
+					</div>
+				</div>
+			</div>
+		</kv-lightbox>
+	</div>
+</template>
+
+<script>
+import numeral from 'numeral';
+import { KvLightbox } from '@kiva/kv-components';
+
+export default {
path: src/components/Portfolio/PaidAmountModal.vue
user: login: dyersituations
body: While this component doesn't necessarily need to change now, we want to use `script setup` and composition API for new Vue components.
created_at: 2025-03-12T18:52:22Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,66 @@
+<template>
+	<div>
+		<span
+			class="tw-text-link tw-cursor-pointer"
+			@click="showModal = true"
+		>
+			${{ formattedAmount }} repaid to you
+		</span>
+
+		<kv-lightbox
+			:visible="showModal"
+			@lightbox-closed="showModal = false"
+			title="Payment history"
+		>
+			<div class="tw-px-4">
+				<div class="tw-grid tw-grid-cols-2">
+					<div v-for="(payment, index) in paymentHistory" :key="index" class="tw-contents">
+						<span>{{ payment.date }}:</span>
+						<span class="tw-relative">
+							<span v-if="payment.type === 'loss'" class="tw-absolute tw-right-full">-</span>
+							${{ payment.amount }}
+							{{ payment.type === 'loss' ? 'currency loss' : 'repaid' }}
+						</span>
+					</div>
+				</div>
+			</div>
+		</kv-lightbox>
+	</div>
+</template>
+
+<script>
+import numeral from 'numeral';
+import { KvLightbox } from '@kiva/kv-components';
+
+export default {
+	name: 'PaidAmountModal',
+	components: {
+		KvLightbox
+	},
+	props: {
+		amount: {
+			type: [Number, String],
path: src/components/Portfolio/PaidAmountModal.vue
user: login: dyersituations
body: Is this prop really two possible types?
created_at: 2025-03-12T18:52:45Z
author_association: COLLABORATOR
diff_hunk: @@ -0,0 +1,66 @@
+<template>
+	<div>
+		<span
+			class="tw-text-link tw-cursor-pointer"
+			@click="showModal = true"
+		>
+			${{ formattedAmount }} repaid to you
+		</span>
+
+		<kv-lightbox
+			:visible="showModal"
+			@lightbox-closed="showModal = false"
+			title="Payment history"
+		>
+			<div class="tw-px-4">
+				<div class="tw-grid tw-grid-cols-2">
+					<div v-for="(payment, index) in paymentHistory" :key="index" class="tw-contents">
+						<span>{{ payment.date }}:</span>
+						<span class="tw-relative">
+							<span v-if="payment.type === 'loss'" class="tw-absolute tw-right-full">-</span>
+							${{ payment.amount }}
+							{{ payment.type === 'loss' ? 'currency loss' : 'repaid' }}
+						</span>
+					</div>
+				</div>
+			</div>
+		</kv-lightbox>
+	</div>
+</template>
+
+<script>
+import numeral from 'numeral';
+import { KvLightbox } from '@kiva/kv-components';
+
+export default {
path: src/components/Portfolio/PaidAmountModal.vue
user: login: michelleinez
body: okay, I updated it anyway.
created_at: 2025-03-12T19:12:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,66 @@
+<template>
+	<div>
+		<span
+			class="tw-text-link tw-cursor-pointer"
+			@click="showModal = true"
+		>
+			${{ formattedAmount }} repaid to you
+		</span>
+
+		<kv-lightbox
+			:visible="showModal"
+			@lightbox-closed="showModal = false"
+			title="Payment history"
+		>
+			<div class="tw-px-4">
+				<div class="tw-grid tw-grid-cols-2">
+					<div v-for="(payment, index) in paymentHistory" :key="index" class="tw-contents">
+						<span>{{ payment.date }}:</span>
+						<span class="tw-relative">
+							<span v-if="payment.type === 'loss'" class="tw-absolute tw-right-full">-</span>
+							${{ payment.amount }}
+							{{ payment.type === 'loss' ? 'currency loss' : 'repaid' }}
+						</span>
+					</div>
+				</div>
+			</div>
+		</kv-lightbox>
+	</div>
+</template>
+
+<script>
+import numeral from 'numeral';
+import { KvLightbox } from '@kiva/kv-components';
+
+export default {
+	name: 'PaidAmountModal',
+	components: {
+		KvLightbox
+	},
+	props: {
+		amount: {
+			type: [Number, String],
path: src/components/Portfolio/PaidAmountModal.vue
user: login: michelleinez
body: ah no, I was testing both types and meant to change this back.  should be ok now.
created_at: 2025-03-12T19:12:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,57 @@
+<template>
+	<div>
+		<span
+			class="tw-text-link tw-cursor-pointer"
+			@click="showModal = true"
+		>
+			${{ formattedAmount }} repaid to you
+		</span>
+
+		<kv-lightbox
+			:visible="showModal"
+			@lightbox-closed="showModal = false"
+			title="Payment history"
+		>
+			<div class="tw-px-4">
+				<div class="tw-grid tw-grid-cols-2">
+					<div v-for="(payment, index) in paymentHistory" :key="index" class="tw-contents">
+						<span>{{ payment.date }}:</span>
+						<span class="tw-relative">
+							<span v-if="payment.type === 'loss'" class="tw-absolute tw-right-full">-</span>
+							${{ payment.amount }}
+							{{ payment.type === 'loss' ? 'currency loss' : 'repaid' }}
+						</span>
+					</div>
+				</div>
+			</div>
+		</kv-lightbox>
+	</div>
+</template>
+
+<script setup>
+import { ref, computed } from 'vue';
+import numeral from 'numeral';
+import { KvLightbox } from '@kiva/kv-components';
+
+const props = defineProps({
+	amount: {
+		type: [String],
path: src/components/Portfolio/PaidAmountModal.vue
user: login: dyersituations
body: This can just be `type: String`
created_at: 2025-03-12T19:14:10Z
author_association: COLLABORATOR

PR comments:
user: login: kiva-robot
created_at: 2025-03-25T17:25:33Z
body: :tada: This PR is included in version 3.14.0-rc.2 :tada:

The release is available on [GitHub release](https://github.com/kiva/ui/releases/tag/v3.14.0-rc.2)

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
author_association: COLLABORATOR
user: login: kiva-robot
created_at: 2025-03-25T19:41:07Z
body: :tada: This PR is included in version 3.14.0 :tada:

The release is available on [GitHub release](https://github.com/kiva/ui/releases/tag/v3.14.0)

Your **[semantic-release](https://github.com/semantic-release/semantic-release)** bot :package::rocket:
author_association: COLLABORATOR