@kiva/kv-components 3.88.1 → 3.89.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +36 -0
- package/package.json +3 -2
- package/utils/loanCard.js +2 -0
- package/vue/KvCartModal.vue +1 -1
- package/vue/KvFlag.vue +120 -0
- package/vue/KvIntroductionLoanCard.vue +418 -0
- package/vue/KvLoanProgressGroup.vue +26 -6
- package/vue/KvLoanUse.vue +10 -0
- package/vue/stories/KvFlag.stories.js +36 -0
- package/vue/stories/KvIntroductionLoanCard.stories.js +132 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,42 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
|
5
5
|
|
|
6
|
+
## [3.89.1](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.89.0...@kiva/kv-components@3.89.1) (2024-07-24)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Bug Fixes
|
|
10
|
+
|
|
11
|
+
* remove top margin from cart modal ([d789a53](https://github.com/kiva/kv-ui-elements/commit/d789a53b4ce498d1945d7160613af4d5e11431ab))
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# [3.89.0](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.88.1...@kiva/kv-components@3.89.0) (2024-07-23)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
### Bug Fixes
|
|
21
|
+
|
|
22
|
+
* flag-icons import ([0b58418](https://github.com/kiva/kv-ui-elements/commit/0b58418bf5aeb58cb8eefc265c228092e6e761e4))
|
|
23
|
+
* indent, use kvFlag and some refactor ([4fbc11a](https://github.com/kiva/kv-ui-elements/commit/4fbc11a03fa2970e7d8c3cc340aa144b9da35c24))
|
|
24
|
+
* line-clamp in loan use ([f4cf963](https://github.com/kiva/kv-ui-elements/commit/f4cf963a567adc6ba23349cbe0da1bc2d15f24d1))
|
|
25
|
+
* loan name clickable ([18f63ca](https://github.com/kiva/kv-ui-elements/commit/18f63ca60d08f92e034cae73e3eb326b2a61f735))
|
|
26
|
+
* loan use template ([4efe849](https://github.com/kiva/kv-ui-elements/commit/4efe8493ec1dce8a3edb73d17e47c1fb8e308be2))
|
|
27
|
+
* remove class props for deep ([c6923dc](https://github.com/kiva/kv-ui-elements/commit/c6923dc3ff576be80efc69f72a9cf6d35974aef7))
|
|
28
|
+
* remove kvflag from loan card ([1e2bba8](https://github.com/kiva/kv-ui-elements/commit/1e2bba8c726e7702aa52efb765651a138feb2220))
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
### Features
|
|
32
|
+
|
|
33
|
+
* introduction card component and story ([7115800](https://github.com/kiva/kv-ui-elements/commit/71158004b0b9a18e9bf1ba8375c213c1c3ca251f))
|
|
34
|
+
* kvflag component and new loan card for homepage ([8cf61bb](https://github.com/kiva/kv-ui-elements/commit/8cf61bb62820c6f9ce280d0f864f5b58d5279694))
|
|
35
|
+
* toggle flag border ([2a782a1](https://github.com/kiva/kv-ui-elements/commit/2a782a1c9bec4ca9d0d3f60a7e9a6d44a310956c))
|
|
36
|
+
* using flag emoticons ([159497b](https://github.com/kiva/kv-ui-elements/commit/159497b08bb1efb5989544df068624cca3c6929b))
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
|
|
6
42
|
## [3.88.1](https://github.com/kiva/kv-ui-elements/compare/@kiva/kv-components@3.88.0...@kiva/kv-components@3.88.1) (2024-07-22)
|
|
7
43
|
|
|
8
44
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kiva/kv-components",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.89.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -66,6 +66,7 @@
|
|
|
66
66
|
"change-case": "^4.1.2",
|
|
67
67
|
"date-fns": "^2.30.0",
|
|
68
68
|
"embla-carousel": "^4.5.3",
|
|
69
|
+
"flag-icons": "^7.2.3",
|
|
69
70
|
"focus-trap": "^6.7.2",
|
|
70
71
|
"moment": "^2.29.4",
|
|
71
72
|
"nanoid": "^3.1.23",
|
|
@@ -81,5 +82,5 @@
|
|
|
81
82
|
"optional": true
|
|
82
83
|
}
|
|
83
84
|
},
|
|
84
|
-
"gitHead": "
|
|
85
|
+
"gitHead": "ffe94e4d7bb773db757012754b5a753dd32ecf51"
|
|
85
86
|
}
|
package/utils/loanCard.js
CHANGED
|
@@ -28,6 +28,7 @@ export function loanCardComputedProperties(props) {
|
|
|
28
28
|
const isLoading = computed(() => !loanId.value || !loan.value);
|
|
29
29
|
const borrowerName = computed(() => loan.value?.name || '');
|
|
30
30
|
const countryName = computed(() => loan.value?.geocode?.country?.name || '');
|
|
31
|
+
const countryCode = computed(() => loan.value?.geocode?.country?.isoCode || '');
|
|
31
32
|
const city = computed(() => loan.value?.geocode?.city || '');
|
|
32
33
|
const state = computed(() => loan.value?.geocode?.state || '');
|
|
33
34
|
const distributionModel = computed(() => loan.value?.distributionModel || '');
|
|
@@ -161,6 +162,7 @@ export function loanCardComputedProperties(props) {
|
|
|
161
162
|
isLoading,
|
|
162
163
|
borrowerName,
|
|
163
164
|
countryName,
|
|
165
|
+
countryCode,
|
|
164
166
|
city,
|
|
165
167
|
state,
|
|
166
168
|
distributionModel,
|
package/vue/KvCartModal.vue
CHANGED
package/vue/KvFlag.vue
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
v-if="country"
|
|
4
|
+
:class="`kv-flag kv-flag--${aspectRatio}`"
|
|
5
|
+
:style="{ maxWidth: spriteWidth, minWidth: spriteWidth }"
|
|
6
|
+
>
|
|
7
|
+
<div
|
|
8
|
+
class="
|
|
9
|
+
kv-flag__wrapper
|
|
10
|
+
kv-flag-svg
|
|
11
|
+
tw-bg-gray-100
|
|
12
|
+
tw-relative
|
|
13
|
+
tw-overflow-hidden
|
|
14
|
+
tw-h-0
|
|
15
|
+
tw-w-full
|
|
16
|
+
tw-border
|
|
17
|
+
tw-border-gray-600
|
|
18
|
+
!tw-bg-cover
|
|
19
|
+
fib
|
|
20
|
+
"
|
|
21
|
+
:class="classes"
|
|
22
|
+
>
|
|
23
|
+
<span class="tw-sr-only">{{ countryName }}</span>
|
|
24
|
+
</div>
|
|
25
|
+
<span
|
|
26
|
+
v-if="showName"
|
|
27
|
+
class="tw-text-h4 tw-my-2"
|
|
28
|
+
>{{ getNameByCode(country) }}</span>
|
|
29
|
+
</div>
|
|
30
|
+
</template>
|
|
31
|
+
|
|
32
|
+
<script>
|
|
33
|
+
export default {
|
|
34
|
+
name: 'KvFlag',
|
|
35
|
+
props: {
|
|
36
|
+
/**
|
|
37
|
+
* 2 letter ISO country code of the flag to show
|
|
38
|
+
* */
|
|
39
|
+
country: {
|
|
40
|
+
type: String,
|
|
41
|
+
required: true,
|
|
42
|
+
},
|
|
43
|
+
/**
|
|
44
|
+
* Aspect Ratio of the flag image
|
|
45
|
+
* `4x3, 1x1`
|
|
46
|
+
* */
|
|
47
|
+
aspectRatio: {
|
|
48
|
+
type: String,
|
|
49
|
+
default: '4x3',
|
|
50
|
+
},
|
|
51
|
+
/**
|
|
52
|
+
* Show the name of the country next to the flag
|
|
53
|
+
*/
|
|
54
|
+
showName: {
|
|
55
|
+
type: Boolean,
|
|
56
|
+
default: false,
|
|
57
|
+
},
|
|
58
|
+
/**
|
|
59
|
+
* Override the width of the flag
|
|
60
|
+
*/
|
|
61
|
+
widthOverride: {
|
|
62
|
+
type: String,
|
|
63
|
+
default: null,
|
|
64
|
+
},
|
|
65
|
+
/**
|
|
66
|
+
* Hide the border around the flag
|
|
67
|
+
*/
|
|
68
|
+
hideBorder: {
|
|
69
|
+
type: Boolean,
|
|
70
|
+
default: false,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
async setup() {
|
|
74
|
+
const countryList = await import('../../../node_modules/flag-icons/country.json');
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
countryList,
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
computed: {
|
|
81
|
+
spriteWidth() {
|
|
82
|
+
if (this.widthOverride) {
|
|
83
|
+
return this.widthOverride;
|
|
84
|
+
}
|
|
85
|
+
return '100%';
|
|
86
|
+
},
|
|
87
|
+
countryName() {
|
|
88
|
+
return `Flag of ${this.getNameByCode(this.country)}`;
|
|
89
|
+
},
|
|
90
|
+
classes() {
|
|
91
|
+
return {
|
|
92
|
+
[`fi-${this.country.toLowerCase()}`]: true,
|
|
93
|
+
'tw-border-0': this.hideBorder,
|
|
94
|
+
};
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
methods: {
|
|
98
|
+
getNameByCode(code) {
|
|
99
|
+
const uppercaseCode = code?.toLowerCase() ?? '';
|
|
100
|
+
return this.countryList?.default?.find((country) => country.code === uppercaseCode)?.name ?? '';
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
</script>
|
|
105
|
+
|
|
106
|
+
<style lang="postcss" scoped>
|
|
107
|
+
@import "../../../node_modules/flag-icons/css/flag-icons.min.css";
|
|
108
|
+
|
|
109
|
+
.kv-flag__wrapper {
|
|
110
|
+
line-height: 0;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.kv-flag--4x3 .kv-flag__wrapper.kv-flag-svg {
|
|
114
|
+
padding-bottom: 71%;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.kv-flag--1x1 .kv-flag__wrapper.kv-flag-svg {
|
|
118
|
+
padding-bottom: 96%;
|
|
119
|
+
}
|
|
120
|
+
</style>
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="card-container"
|
|
4
|
+
:class="{ 'tw-pointer-events-none' : isLoading }"
|
|
5
|
+
style="box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);"
|
|
6
|
+
:style="{ minWidth: '230px', maxWidth: '20.5rem' }"
|
|
7
|
+
>
|
|
8
|
+
<div class="tw-grow">
|
|
9
|
+
<div class="loan-card-active-hover">
|
|
10
|
+
<!-- Borrower image -->
|
|
11
|
+
<kv-loading-placeholder
|
|
12
|
+
v-if="isLoading"
|
|
13
|
+
class="tw-w-full tw-rounded-t"
|
|
14
|
+
:style="{ height: '11rem' }"
|
|
15
|
+
/>
|
|
16
|
+
<div
|
|
17
|
+
v-else
|
|
18
|
+
class="tw-relative"
|
|
19
|
+
>
|
|
20
|
+
<component
|
|
21
|
+
:is="tag"
|
|
22
|
+
:to="readMorePath"
|
|
23
|
+
:href="readMorePath"
|
|
24
|
+
class="tw-flex"
|
|
25
|
+
aria-label="Borrower image"
|
|
26
|
+
@click.native="clickReadMore('Photo', $event)"
|
|
27
|
+
>
|
|
28
|
+
<kv-borrower-image
|
|
29
|
+
class="
|
|
30
|
+
tw-relative
|
|
31
|
+
tw-w-full
|
|
32
|
+
tw-bg-black
|
|
33
|
+
tw-rounded-t
|
|
34
|
+
borrower-image
|
|
35
|
+
"
|
|
36
|
+
:alt="`Photo of ${borrowerName}`"
|
|
37
|
+
:aspect-ratio="imageAspectRatio"
|
|
38
|
+
:default-image="{ width: imageDefaultWidth }"
|
|
39
|
+
:hash="imageHash"
|
|
40
|
+
:images="imageSizes"
|
|
41
|
+
:photo-path="photoPath"
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
<div v-if="countryName">
|
|
45
|
+
<p
|
|
46
|
+
class="
|
|
47
|
+
tw-absolute
|
|
48
|
+
tw-bottom-1
|
|
49
|
+
tw-left-1
|
|
50
|
+
tw-text-primary
|
|
51
|
+
tw-bg-white
|
|
52
|
+
tw-rounded
|
|
53
|
+
tw-p-1
|
|
54
|
+
tw-mb-0
|
|
55
|
+
tw-mr-2
|
|
56
|
+
tw-text-h4
|
|
57
|
+
tw-inline-flex
|
|
58
|
+
tw-items-center
|
|
59
|
+
!tw-capitalize
|
|
60
|
+
"
|
|
61
|
+
style="padding: 2px 6px;"
|
|
62
|
+
>
|
|
63
|
+
<kv-flag
|
|
64
|
+
class="tw-mr-0.5"
|
|
65
|
+
:country="countryCode"
|
|
66
|
+
width-override="0.725rem"
|
|
67
|
+
hide-border
|
|
68
|
+
/>
|
|
69
|
+
{{ formattedLocation }}
|
|
70
|
+
</p>
|
|
71
|
+
</div>
|
|
72
|
+
</component>
|
|
73
|
+
</div>
|
|
74
|
+
|
|
75
|
+
<!-- Loan name -->
|
|
76
|
+
|
|
77
|
+
<kv-loading-placeholder
|
|
78
|
+
v-if="isLoading || typeof loanCallouts === 'undefined'"
|
|
79
|
+
class="tw-mt-1 tw-mx-auto"
|
|
80
|
+
:style="{ width: '60%', height: '1.75rem', 'border-radius': '500rem' }"
|
|
81
|
+
/>
|
|
82
|
+
|
|
83
|
+
<h2
|
|
84
|
+
class="loan-card-name"
|
|
85
|
+
:class="{'tw-text-center': borrowerName.length < 20}"
|
|
86
|
+
@click="clickReadMore('Name', $event)"
|
|
87
|
+
>
|
|
88
|
+
{{ borrowerName }}
|
|
89
|
+
</h2>
|
|
90
|
+
|
|
91
|
+
<!-- Loan tag -->
|
|
92
|
+
|
|
93
|
+
<div
|
|
94
|
+
v-if="isLoading"
|
|
95
|
+
class="tw-flex tw-justify-center tw-gap-2"
|
|
96
|
+
>
|
|
97
|
+
<kv-loading-placeholder
|
|
98
|
+
v-if="isLoading || typeof loanCallouts === 'undefined'"
|
|
99
|
+
class="tw-mt-0.5 tw-mb-1"
|
|
100
|
+
:style="{ width: '20%', height: '1.75rem', 'border-radius': '500rem' }"
|
|
101
|
+
/>
|
|
102
|
+
<kv-loading-placeholder
|
|
103
|
+
v-if="isLoading || typeof loanCallouts === 'undefined'"
|
|
104
|
+
class="tw-mt-0.5 tw-mb-1"
|
|
105
|
+
:style="{ width: '20%', height: '1.75rem', 'border-radius': '500rem' }"
|
|
106
|
+
/>
|
|
107
|
+
<kv-loading-placeholder
|
|
108
|
+
v-if="isLoading || typeof loanCallouts === 'undefined'"
|
|
109
|
+
class="tw-mt-0.5 tw-mb-1"
|
|
110
|
+
:style="{ width: '20%', height: '1.75rem', 'border-radius': '500rem' }"
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
|
|
114
|
+
<component
|
|
115
|
+
:is="tag"
|
|
116
|
+
v-else
|
|
117
|
+
:to="readMorePath"
|
|
118
|
+
:href="readMorePath"
|
|
119
|
+
class="tw-flex hover:tw-no-underline focus:tw-no-underline tw-justify-center"
|
|
120
|
+
aria-label="Loan tag"
|
|
121
|
+
@click.native="clickReadMore('Tag', $event)"
|
|
122
|
+
>
|
|
123
|
+
<kv-loan-callouts
|
|
124
|
+
:callouts="loanCallouts"
|
|
125
|
+
:add-bg-color="false"
|
|
126
|
+
class="loan-callouts"
|
|
127
|
+
/>
|
|
128
|
+
</component>
|
|
129
|
+
|
|
130
|
+
<component
|
|
131
|
+
:is="tag"
|
|
132
|
+
:to="readMorePath"
|
|
133
|
+
:href="readMorePath"
|
|
134
|
+
class="loan-card-use tw-text-primary"
|
|
135
|
+
aria-label="Loan use"
|
|
136
|
+
@click.native="clickReadMore('Use', $event)"
|
|
137
|
+
>
|
|
138
|
+
<!-- Loan use -->
|
|
139
|
+
<div
|
|
140
|
+
class="tw-pt-1 tw-px-1"
|
|
141
|
+
:class="{'tw-mb-1.5': !isLoading}"
|
|
142
|
+
>
|
|
143
|
+
<div
|
|
144
|
+
v-if="isLoading"
|
|
145
|
+
class="tw-w-full"
|
|
146
|
+
style="height: 5.5rem;"
|
|
147
|
+
>
|
|
148
|
+
<div
|
|
149
|
+
v-for="(_n, i) in [...Array(3)]"
|
|
150
|
+
:key="i"
|
|
151
|
+
class="tw-h-2 tw-mb-1"
|
|
152
|
+
>
|
|
153
|
+
<kv-loading-placeholder />
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
<div v-else>
|
|
157
|
+
<kv-loan-use
|
|
158
|
+
:use="loanUse"
|
|
159
|
+
:loan-amount="loanAmount"
|
|
160
|
+
:status="loanStatus"
|
|
161
|
+
:borrower-count="loanBorrowerCount"
|
|
162
|
+
:name="borrowerName"
|
|
163
|
+
:distribution-model="distributionModel"
|
|
164
|
+
hide-loan-amount
|
|
165
|
+
class="!tw-line-clamp-3"
|
|
166
|
+
/>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
</component>
|
|
170
|
+
</div>
|
|
171
|
+
</div>
|
|
172
|
+
|
|
173
|
+
<div
|
|
174
|
+
class="tw-px-1 tw-grow"
|
|
175
|
+
>
|
|
176
|
+
<!-- Fundraising -->
|
|
177
|
+
<div
|
|
178
|
+
v-if="!hasProgressData"
|
|
179
|
+
class="tw-w-full tw-pr-1"
|
|
180
|
+
>
|
|
181
|
+
<div class="tw-flex tw-justify-between">
|
|
182
|
+
<kv-loading-placeholder
|
|
183
|
+
class="tw-mb-0.5"
|
|
184
|
+
:style="{ width: '20%', height: '1.3rem' }"
|
|
185
|
+
/>
|
|
186
|
+
<kv-loading-placeholder
|
|
187
|
+
class="tw-mb-0.5"
|
|
188
|
+
:style="{ width: '20%', height: '1.3rem' }"
|
|
189
|
+
/>
|
|
190
|
+
</div>
|
|
191
|
+
|
|
192
|
+
<kv-loading-placeholder
|
|
193
|
+
class="tw-rounded"
|
|
194
|
+
:style="{ width: '100%', height: '0.5rem' }"
|
|
195
|
+
/>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<div>
|
|
199
|
+
<component
|
|
200
|
+
:is="tag"
|
|
201
|
+
v-if="unreservedAmount > 0"
|
|
202
|
+
:to="readMorePath"
|
|
203
|
+
:href="readMorePath"
|
|
204
|
+
class="loan-card-progress tw-mt-1"
|
|
205
|
+
aria-label="Loan progress"
|
|
206
|
+
@click.native="clickReadMore('Progress', $event)"
|
|
207
|
+
>
|
|
208
|
+
<kv-loan-progress-group
|
|
209
|
+
id="loanProgress"
|
|
210
|
+
:money-left="`${unreservedAmount}`"
|
|
211
|
+
:amount-goal="`${loanAmount}`"
|
|
212
|
+
:progress-percent="fundraisingPercent"
|
|
213
|
+
class="tw-text-secondary"
|
|
214
|
+
/>
|
|
215
|
+
</component>
|
|
216
|
+
</div>
|
|
217
|
+
|
|
218
|
+
<!-- Loan Match -->
|
|
219
|
+
<kv-loading-placeholder
|
|
220
|
+
v-if="isLoading"
|
|
221
|
+
class="tw-rounded tw-mx-auto tw-mt-1"
|
|
222
|
+
:style="{ width: '9rem', height: '1rem' }"
|
|
223
|
+
/>
|
|
224
|
+
|
|
225
|
+
<div class="tw-pb-2.5">
|
|
226
|
+
<kv-loan-tag
|
|
227
|
+
v-if="matchingText"
|
|
228
|
+
:loan="loan"
|
|
229
|
+
variation="matched-loan"
|
|
230
|
+
class="tw-text-center !tw-text-brand"
|
|
231
|
+
/>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
</template>
|
|
236
|
+
|
|
237
|
+
<script>
|
|
238
|
+
import numeral from 'numeral';
|
|
239
|
+
import { loanCardComputedProperties, loanCardMethods } from '../utils/loanCard';
|
|
240
|
+
|
|
241
|
+
import KvLoanUse from './KvLoanUse.vue';
|
|
242
|
+
import KvBorrowerImage from './KvBorrowerImage.vue';
|
|
243
|
+
import KvLoanProgressGroup from './KvLoanProgressGroup.vue';
|
|
244
|
+
import KvLoanCallouts from './KvLoanCallouts.vue';
|
|
245
|
+
import KvLoanTag from './KvLoanTag.vue';
|
|
246
|
+
import KvMaterialIcon from './KvMaterialIcon.vue';
|
|
247
|
+
import KvLoadingPlaceholder from './KvLoadingPlaceholder.vue';
|
|
248
|
+
import KvFlag from './KvFlag.vue';
|
|
249
|
+
|
|
250
|
+
export default {
|
|
251
|
+
name: 'KvIntroductionLoanCard',
|
|
252
|
+
components: {
|
|
253
|
+
KvBorrowerImage,
|
|
254
|
+
KvLoadingPlaceholder,
|
|
255
|
+
KvLoanUse,
|
|
256
|
+
KvLoanProgressGroup,
|
|
257
|
+
KvMaterialIcon,
|
|
258
|
+
KvLoanTag,
|
|
259
|
+
KvLoanCallouts,
|
|
260
|
+
KvFlag,
|
|
261
|
+
},
|
|
262
|
+
props: {
|
|
263
|
+
loanId: {
|
|
264
|
+
type: Number,
|
|
265
|
+
default: undefined,
|
|
266
|
+
},
|
|
267
|
+
loan: {
|
|
268
|
+
type: Object,
|
|
269
|
+
default: null,
|
|
270
|
+
},
|
|
271
|
+
customLoanDetails: {
|
|
272
|
+
type: Boolean,
|
|
273
|
+
default: false,
|
|
274
|
+
},
|
|
275
|
+
categoryPageName: {
|
|
276
|
+
type: String,
|
|
277
|
+
default: '',
|
|
278
|
+
},
|
|
279
|
+
customCallouts: {
|
|
280
|
+
type: Array,
|
|
281
|
+
default: () => ([]),
|
|
282
|
+
},
|
|
283
|
+
kvTrackFunction: {
|
|
284
|
+
type: Function,
|
|
285
|
+
required: true,
|
|
286
|
+
},
|
|
287
|
+
photoPath: {
|
|
288
|
+
type: String,
|
|
289
|
+
required: true,
|
|
290
|
+
},
|
|
291
|
+
errorMsg: {
|
|
292
|
+
type: String,
|
|
293
|
+
default: '',
|
|
294
|
+
},
|
|
295
|
+
externalLinks: {
|
|
296
|
+
type: Boolean,
|
|
297
|
+
default: false,
|
|
298
|
+
},
|
|
299
|
+
route: {
|
|
300
|
+
type: Object,
|
|
301
|
+
default: undefined,
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
setup(props, { emit }) {
|
|
305
|
+
const {
|
|
306
|
+
allDataLoaded,
|
|
307
|
+
borrowerName,
|
|
308
|
+
city,
|
|
309
|
+
countryName,
|
|
310
|
+
countryCode,
|
|
311
|
+
distributionModel,
|
|
312
|
+
formattedLocation,
|
|
313
|
+
fundraisingPercent,
|
|
314
|
+
hasProgressData,
|
|
315
|
+
imageHash,
|
|
316
|
+
isLoading,
|
|
317
|
+
loanAmount,
|
|
318
|
+
loanBorrowerCount,
|
|
319
|
+
loanCallouts,
|
|
320
|
+
loanStatus,
|
|
321
|
+
loanUse,
|
|
322
|
+
mdiMapMarker,
|
|
323
|
+
readMorePath,
|
|
324
|
+
state,
|
|
325
|
+
tag,
|
|
326
|
+
unreservedAmount,
|
|
327
|
+
} = loanCardComputedProperties(props);
|
|
328
|
+
|
|
329
|
+
const {
|
|
330
|
+
clickReadMore,
|
|
331
|
+
} = loanCardMethods(props, emit);
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
allDataLoaded,
|
|
335
|
+
borrowerName,
|
|
336
|
+
city,
|
|
337
|
+
countryName,
|
|
338
|
+
countryCode,
|
|
339
|
+
distributionModel,
|
|
340
|
+
formattedLocation,
|
|
341
|
+
fundraisingPercent,
|
|
342
|
+
hasProgressData,
|
|
343
|
+
imageHash,
|
|
344
|
+
isLoading,
|
|
345
|
+
loanAmount,
|
|
346
|
+
loanBorrowerCount,
|
|
347
|
+
loanCallouts,
|
|
348
|
+
loanStatus,
|
|
349
|
+
loanUse,
|
|
350
|
+
mdiMapMarker,
|
|
351
|
+
readMorePath,
|
|
352
|
+
state,
|
|
353
|
+
tag,
|
|
354
|
+
unreservedAmount,
|
|
355
|
+
clickReadMore,
|
|
356
|
+
};
|
|
357
|
+
},
|
|
358
|
+
computed: {
|
|
359
|
+
imageAspectRatio() {
|
|
360
|
+
return 178 / 328;
|
|
361
|
+
},
|
|
362
|
+
imageDefaultWidth() {
|
|
363
|
+
return 336;
|
|
364
|
+
},
|
|
365
|
+
imageSizes() {
|
|
366
|
+
return [
|
|
367
|
+
{ width: this.imageDefaultWidth, viewSize: 1024 },
|
|
368
|
+
{ width: this.imageDefaultWidth, viewSize: 768 },
|
|
369
|
+
{ width: 416, viewSize: 480 },
|
|
370
|
+
{ width: 374, viewSize: 414 },
|
|
371
|
+
{ width: 335, viewSize: 375 },
|
|
372
|
+
];
|
|
373
|
+
},
|
|
374
|
+
lendersNumber() {
|
|
375
|
+
return this.loan?.lenders?.totalCount ?? 0;
|
|
376
|
+
},
|
|
377
|
+
amountLent() {
|
|
378
|
+
const amount = this.loan?.loanFundraisingInfo?.fundedAmount ?? 0;
|
|
379
|
+
return numeral(parseFloat(amount)).format('$0,0');
|
|
380
|
+
},
|
|
381
|
+
matchingText() {
|
|
382
|
+
return this.loan?.matchingText ?? '';
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
</script>
|
|
387
|
+
|
|
388
|
+
<style lang="postcss" scoped>
|
|
389
|
+
.card-container {
|
|
390
|
+
@apply tw-flex tw-flex-col tw-bg-white tw-rounded tw-w-full tw-pb-1;
|
|
391
|
+
height: 415px;
|
|
392
|
+
}
|
|
393
|
+
@screen md {
|
|
394
|
+
.card-container {
|
|
395
|
+
height: 423px;
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
.loan-callouts >>> span {
|
|
399
|
+
@apply !tw-bg-transparent tw-text-brand;
|
|
400
|
+
}
|
|
401
|
+
.loan-card-use:hover,
|
|
402
|
+
.loan-card-use:focus {
|
|
403
|
+
@apply tw-text-primary;
|
|
404
|
+
}
|
|
405
|
+
.loan-card-active-hover:hover .loan-card-use, .loan-card-active-hover:hover .loan-card-name {
|
|
406
|
+
@apply tw-underline;
|
|
407
|
+
}
|
|
408
|
+
.loan-card-progress:hover,
|
|
409
|
+
.loan-card-progress:focus {
|
|
410
|
+
@apply tw-no-underline;
|
|
411
|
+
}
|
|
412
|
+
.borrower-image >>> img {
|
|
413
|
+
@apply tw-object-cover;
|
|
414
|
+
}
|
|
415
|
+
.loan-card-name {
|
|
416
|
+
@apply tw-pt-1 tw-px-3 tw-text-ellipsis tw-overflow-hidden tw-line-clamp-1 tw-cursor-pointer;
|
|
417
|
+
}
|
|
418
|
+
</style>
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<figure>
|
|
3
|
-
<
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
3
|
+
<div :class="{'tw-flex tw-justify-between': amountGoal}">
|
|
4
|
+
<h4
|
|
5
|
+
class="tw-lowercase tw-mb-0.5"
|
|
6
|
+
:class="{ 'progress-group-amount-low': amountLow}"
|
|
7
|
+
>
|
|
8
|
+
{{ fundingText }}
|
|
9
|
+
</h4>
|
|
10
|
+
<h4
|
|
11
|
+
v-if="amountGoal"
|
|
12
|
+
class="tw-lowercase tw-mb-0.5"
|
|
13
|
+
>
|
|
14
|
+
{{ goalText }}
|
|
15
|
+
</h4>
|
|
16
|
+
</div>
|
|
9
17
|
<kv-progress-bar
|
|
10
18
|
aria-label="Percent the loan has funded"
|
|
11
19
|
:value="progressPercent * 100"
|
|
@@ -31,6 +39,10 @@ export default {
|
|
|
31
39
|
type: Number,
|
|
32
40
|
default: 0,
|
|
33
41
|
},
|
|
42
|
+
amountGoal: {
|
|
43
|
+
type: String,
|
|
44
|
+
default: '',
|
|
45
|
+
},
|
|
34
46
|
},
|
|
35
47
|
computed: {
|
|
36
48
|
numeralLeft() {
|
|
@@ -45,6 +57,14 @@ export default {
|
|
|
45
57
|
const exclamationMark = this.amountLow ? '!' : '';
|
|
46
58
|
return `${formattedMoneyLeft} to go${exclamationMark}`;
|
|
47
59
|
},
|
|
60
|
+
numeralGoal() {
|
|
61
|
+
return numeral(this.amountGoal);
|
|
62
|
+
},
|
|
63
|
+
goalText() {
|
|
64
|
+
if (!this.numeralGoal.value()) return '';
|
|
65
|
+
const formattedGoal = this.numeralGoal.format('$0,0[.]00');
|
|
66
|
+
return `${formattedGoal} goal`;
|
|
67
|
+
},
|
|
48
68
|
},
|
|
49
69
|
};
|
|
50
70
|
</script>
|
package/vue/KvLoanUse.vue
CHANGED
|
@@ -46,6 +46,10 @@ export default {
|
|
|
46
46
|
type: String,
|
|
47
47
|
default: '',
|
|
48
48
|
},
|
|
49
|
+
hideLoanAmount: {
|
|
50
|
+
type: Boolean,
|
|
51
|
+
default: false,
|
|
52
|
+
},
|
|
49
53
|
},
|
|
50
54
|
computed: {
|
|
51
55
|
helpLanguage() {
|
|
@@ -67,6 +71,12 @@ export default {
|
|
|
67
71
|
return 'For the borrower\'s privacy, this loan has been made anonymous.';
|
|
68
72
|
}
|
|
69
73
|
|
|
74
|
+
if (this.hideLoanAmount) {
|
|
75
|
+
return `Help <span class="data-hj-suppress">${this.name}</span> `
|
|
76
|
+
+ `${this.use.charAt(0).toLowerCase() + this.use.slice(1)} `
|
|
77
|
+
+ `${this.whySpecialSentence}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
70
80
|
const isGroup = this.borrowerCount > 1;
|
|
71
81
|
|
|
72
82
|
return `${numeral(this.loanAmount).format('$0,0')} `
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import KvFlag from '../KvFlag.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'Components/KvFlag',
|
|
5
|
+
component: KvFlag,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const story = (args) => {
|
|
9
|
+
const template = (templateArgs, { argTypes }) => ({
|
|
10
|
+
props: Object.keys(argTypes),
|
|
11
|
+
components: { KvFlag },
|
|
12
|
+
setup() { return { args: templateArgs }; },
|
|
13
|
+
template: `
|
|
14
|
+
<div style="width: 100px; height: 100px;">
|
|
15
|
+
<KvFlag v-bind="args" />
|
|
16
|
+
</div>
|
|
17
|
+
`,
|
|
18
|
+
});
|
|
19
|
+
template.args = args;
|
|
20
|
+
return template;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// SVG based on the country code
|
|
24
|
+
export const InlineSvg = story({ country: 'TO' });
|
|
25
|
+
// SVG based on the country code with custom width
|
|
26
|
+
export const InlineSvgCustomWidth = story({ country: 'DE', widthOverride: '40px' });
|
|
27
|
+
|
|
28
|
+
// SVG Sqaure based on the country code
|
|
29
|
+
export const SquareInlineSvg = story({ country: 'ME', aspectRatio: '1x1', showName: true });
|
|
30
|
+
// SVG Sqaure based on the country code with custom width
|
|
31
|
+
export const SquareInlineSvgCustomWidth = story({
|
|
32
|
+
country: 'SA',
|
|
33
|
+
inlineSvg: true,
|
|
34
|
+
widthOverride: '40px',
|
|
35
|
+
aspectRatio: '1x1',
|
|
36
|
+
});
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import KvIntroductionLoanCard from '../KvIntroductionLoanCard.vue';
|
|
2
|
+
|
|
3
|
+
export default {
|
|
4
|
+
title: 'KvIntroductionLoanCard',
|
|
5
|
+
component: KvIntroductionLoanCard,
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
const story = (args) => {
|
|
9
|
+
const template = (_args, { argTypes }) => ({
|
|
10
|
+
props: Object.keys(argTypes),
|
|
11
|
+
components: { KvIntroductionLoanCard },
|
|
12
|
+
template: `
|
|
13
|
+
<div style="width: 600px;">
|
|
14
|
+
<kv-introduction-loan-card
|
|
15
|
+
:loanId="loanId"
|
|
16
|
+
:loan="loan"
|
|
17
|
+
:category-page-name="categoryPageName"
|
|
18
|
+
:custom-callouts="customCallouts"
|
|
19
|
+
:kv-track-function="kvTrackFunction"
|
|
20
|
+
:photo-path="photoPath"
|
|
21
|
+
@show-loan-details="showLoanDetails"
|
|
22
|
+
/>
|
|
23
|
+
</div>
|
|
24
|
+
`,
|
|
25
|
+
methods: {
|
|
26
|
+
showLoanDetails() {
|
|
27
|
+
console.log('show-loan-details');
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
template.args = args;
|
|
32
|
+
return template;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const nextWeek = new Date();
|
|
36
|
+
nextWeek.setDate(new Date().getDate() + 7);
|
|
37
|
+
|
|
38
|
+
const loan = {
|
|
39
|
+
id: 1,
|
|
40
|
+
name: 'Alan',
|
|
41
|
+
geocode: {
|
|
42
|
+
city: 'Lyantonde',
|
|
43
|
+
state: 'Central Region',
|
|
44
|
+
country: {
|
|
45
|
+
isoCode: 'UG',
|
|
46
|
+
name: 'Uganda',
|
|
47
|
+
region: 'Africa',
|
|
48
|
+
__typename: 'Country',
|
|
49
|
+
},
|
|
50
|
+
__typename: 'Geocode',
|
|
51
|
+
},
|
|
52
|
+
image: { hash: '9673d0722a7675b9b8d11f90849d9b44' },
|
|
53
|
+
fundraisingPercent: 0.5,
|
|
54
|
+
unreservedAmount: '500.00',
|
|
55
|
+
use: 'to purchase heifers to increase headcount of cattle and sales of organic milk. The profits from the loan will be used wisely.',
|
|
56
|
+
status: 'fundraising',
|
|
57
|
+
loanAmount: '1000.00',
|
|
58
|
+
borrowerCount: 1,
|
|
59
|
+
activity: {
|
|
60
|
+
id: 61,
|
|
61
|
+
name: 'Dairy',
|
|
62
|
+
__typename: 'Activity',
|
|
63
|
+
},
|
|
64
|
+
sector: {
|
|
65
|
+
id: 1,
|
|
66
|
+
name: 'Agriculture',
|
|
67
|
+
__typename: 'Sector',
|
|
68
|
+
},
|
|
69
|
+
plannedExpirationDate: nextWeek.toISOString(),
|
|
70
|
+
tags: [
|
|
71
|
+
'user_favorite',
|
|
72
|
+
'#Woman-Owned Business',
|
|
73
|
+
'#Animals',
|
|
74
|
+
'#Repeat Borrower',
|
|
75
|
+
'#Supporting Family',
|
|
76
|
+
'#Eco-friendly',
|
|
77
|
+
'#Single parent',
|
|
78
|
+
],
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const kvTrackFunction = () => { };
|
|
82
|
+
|
|
83
|
+
const photoPath = 'https://www-kiva-org.freetls.fastly.net/img/';
|
|
84
|
+
|
|
85
|
+
export const Default = story({
|
|
86
|
+
loanId: loan.id,
|
|
87
|
+
loan: {
|
|
88
|
+
...loan,
|
|
89
|
+
loanFundraisingInfo: {
|
|
90
|
+
fundedAmount: '200.00',
|
|
91
|
+
isExpiringSoon: false,
|
|
92
|
+
reservedAmount: '0.00',
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
kvTrackFunction,
|
|
96
|
+
photoPath,
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
export const Loading = story({
|
|
100
|
+
loanId: loan.id,
|
|
101
|
+
loan: undefined,
|
|
102
|
+
kvTrackFunction,
|
|
103
|
+
photoPath,
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
export const PartialLoading = story({
|
|
107
|
+
loanId: loan.id,
|
|
108
|
+
loan: {
|
|
109
|
+
...loan,
|
|
110
|
+
unreservedAmount: undefined,
|
|
111
|
+
fundraisingPercent: undefined,
|
|
112
|
+
},
|
|
113
|
+
kvTrackFunction,
|
|
114
|
+
photoPath,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
export const Matched = story({
|
|
118
|
+
loanId: loan.id,
|
|
119
|
+
loan: {
|
|
120
|
+
...loan,
|
|
121
|
+
matchingText: 'Ebay',
|
|
122
|
+
matchRatio: 1,
|
|
123
|
+
loanFundraisingInfo: {
|
|
124
|
+
fundedAmount: '200.00',
|
|
125
|
+
isExpiringSoon: false,
|
|
126
|
+
reservedAmount: '0.00',
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
kvTrackFunction,
|
|
130
|
+
photoPath,
|
|
131
|
+
loanCallouts: [{ label: 'callout 1' }, { label: 'callout 2' }, { label: 'callout 3' }],
|
|
132
|
+
});
|