Skip to content

Commit ef91741

Browse files
authored
Frontend support for members emails as arrays (#632)
1 parent 70c7974 commit ef91741

15 files changed

Lines changed: 162 additions & 99 deletions

frontend/src/assets/scss/badge.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,8 @@
5252
@apply bg-yellow-100;
5353
}
5454
}
55+
56+
&--interactive {
57+
@apply text-gray-900 hover:text-brand-500 transition text-ellipsis truncate flex items-center hover:cursor-pointer gap-1.5 border px-1.5 rounded-md h-6;
58+
}
5559
}

frontend/src/modules/member/components/form/member-form-identities.vue

Lines changed: 13 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -69,30 +69,18 @@
6969
</el-form-item>
7070
</div>
7171
</div>
72-
73-
<div class="flex items-center justify-between mt-24">
72+
<div class="flex items-start justify-between mt-24">
7473
<div class="flex items-center flex-1">
7574
<app-platform platform="email" />
7675
<div class="font-medium text-sm ml-3">
7776
Email address
7877
</div>
7978
</div>
80-
<el-form-item
81-
prop="email"
79+
<app-string-array-input
80+
v-model="computedModelEmails"
8281
class="flex-1"
83-
:rules="[
84-
{
85-
type: 'email',
86-
message: 'Please input correct email address',
87-
trigger: ['blur', 'change']
88-
}
89-
]"
90-
>
91-
<el-input
92-
v-model="computedModelEmail"
93-
placeholder="john.doe@gmail.com"
94-
/>
95-
</el-form-item>
82+
add-row-label="Add e-email address"
83+
/>
9684
</div>
9785
</div>
9886
</div>
@@ -133,12 +121,14 @@ const model = computed({
133121
}
134122
})
135123
136-
const computedModelEmail = computed({
124+
const computedModelEmails = computed({
137125
get() {
138-
return model.value.email
126+
return model.value.emails?.length > 0
127+
? model.value.emails
128+
: ['']
139129
},
140-
set(newEmail) {
141-
model.value.email = newEmail
130+
set(emails) {
131+
model.value.emails = emails
142132
}
143133
})
144134
@@ -150,8 +140,8 @@ watch(
150140
151141
if (platforms.length) {
152142
model.value.platform = platforms[0]
153-
} else if (newValue.email) {
154-
model.value.platform = 'email'
143+
} else if (newValue.emails) {
144+
model.value.platform = 'emails'
155145
} else {
156146
model.value.platform = null
157147
}

frontend/src/modules/member/components/list/member-list-table.vue

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,10 @@
300300
</template>
301301
</el-table-column>
302302

303-
<el-table-column label="Email" width="240">
303+
<el-table-column
304+
label="Emails"
305+
:width="emailsColumnWidth"
306+
>
304307
<template #default="scope">
305308
<router-link
306309
:to="{
@@ -309,41 +312,45 @@
309312
}"
310313
class="block"
311314
>
312-
<div class="text-sm cursor-auto">
315+
<div
316+
v-if="scope.row.emails.length"
317+
class="text-sm cursor-auto flex flex-wrap gap-1"
318+
>
313319
<el-tooltip
314-
v-if="scope.row.email"
315-
:disabled="!scope.row.email"
320+
v-for="email of scope.row.emails"
321+
:key="email"
322+
:disabled="!email"
316323
popper-class="custom-identity-tooltip"
317324
placement="top"
318325
>
319326
<template #content
320327
><span
321328
>Send email
322329
<i
323-
v-if="scope.row.email"
330+
v-if="email"
324331
class="ri-external-link-line text-gray-400"
325332
></i></span
326333
></template>
327334
<div @click.prevent>
328335
<a
329336
target="_blank"
330-
class="text-gray-500 hover:!text-brand-500"
331-
:href="`mailto:${scope.row.email}`"
337+
class="badge--interactive"
338+
:href="`mailto:${email}`"
332339
@click.stop="trackEmailClick"
333-
>{{ scope.row.email }}</a
340+
>{{ email }}</a
334341
>
335342
</div>
336343
</el-tooltip>
337-
<span v-else class="text-gray-500"
338-
>-</span
339-
>
340344
</div>
345+
<span v-else class="text-gray-500"
346+
>-</span
347+
>
341348
</router-link>
342349
</template>
343350
</el-table-column>
344351

345352
<el-table-column
346-
:width="maxTabWidth"
353+
:width="tagsColumnWidth"
347354
:label="
348355
translate('entities.member.fields.tag')
349356
"
@@ -498,7 +505,7 @@ const loading = computed(
498505
store.state.member.list.loading || props.isPageLoading
499506
)
500507
501-
const maxTabWidth = computed(() => {
508+
const tagsColumnWidth = computed(() => {
502509
let maxTabWidth = 0
503510
for (const row of rows.value) {
504511
if (row.tags) {
@@ -515,6 +522,21 @@ const maxTabWidth = computed(() => {
515522
return Math.min(maxTabWidth + 100, 500)
516523
})
517524
525+
const emailsColumnWidth = computed(() => {
526+
let maxTabWidth = 0
527+
for (const row of rows.value) {
528+
const tabWidth = row.emails
529+
.map((email) => email.length * 12)
530+
.reduce((a, b) => a + b, 0)
531+
532+
if (tabWidth > maxTabWidth) {
533+
maxTabWidth = tabWidth > 400 ? 400 : tabWidth
534+
}
535+
}
536+
537+
return maxTabWidth
538+
})
539+
518540
const selectedRows = computed(
519541
() => store.getters['member/selectedRows']
520542
)

frontend/src/modules/member/components/member-manage-identities-drawer.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ const handleSubmit = async () => {
8383
loading.value = true
8484
await MemberService.update(props.member.id, {
8585
username: memberModel.value.username,
86-
email: memberModel.value.email
86+
emails: memberModel.value.emails
8787
})
8888
await store.dispatch('member/doFind', props.member.id)
8989
Message.success('Member identities updated successfully')

frontend/src/modules/member/components/member-organizations.vue

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@
7676
name: 'organizationView',
7777
params: { id: organization.id }
7878
}"
79-
class="text-gray-900 hover:text-brand-500 transition text-ellipsis truncate flex items-center hover:cursor-pointer gap-1.5 border px-1.5 rounded-md h-6"
79+
class="badge--interactive"
8080
@click.stop
8181
>
8282
<img
@@ -92,10 +92,6 @@
9292
</div>
9393
</div>
9494
<div v-else class="text-gray-900">-</div>
95-
<app-paywall-modal
96-
v-model="isUpgradeModalOpen"
97-
module="organizations"
98-
/>
9995
</template>
10096

10197
<script>
@@ -105,8 +101,7 @@ export default {
105101
</script>
106102

107103
<script setup>
108-
import { defineProps, ref } from 'vue'
109-
import AppPaywallModal from '@/modules/layout/components/paywall-modal.vue'
104+
import { defineProps } from 'vue'
110105
111106
const props = defineProps({
112107
member: {
@@ -122,6 +117,4 @@ const props = defineProps({
122117
default: () => 'vertical'
123118
}
124119
})
125-
126-
const isUpgradeModalOpen = ref(false)
127120
</script>

frontend/src/modules/member/components/view/_aside/_aside-custom-attributes.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,8 @@ const computedCustomAttributes = computed(() => {
121121
'bio',
122122
'url',
123123
'location',
124+
'emails',
124125
'jobTitle',
125-
// 'emails' TODO: should we actually render emails here?
126126
'workExperiences', // we render them in _aside-work-experience
127127
'certifications', // we render them in _aside-work-certifications
128128
'education', // we render them in _aside-work-education

frontend/src/modules/member/components/view/_aside/_aside-identities.vue

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -61,23 +61,32 @@
6161
</a>
6262
</div>
6363
<div
64-
v-if="Object.keys(socialIdentities).length && email"
64+
v-if="
65+
Object.keys(socialIdentities).length &&
66+
emails.length
67+
"
6568
class="mt-2"
6669
>
6770
<el-divider class="border-t-gray-200"></el-divider>
68-
<a
69-
class="py-2 px-6 -mx-6 mt-4 flex justify-between items-center relative hover:bg-gray-50 transition-colors cursor-pointer"
70-
:href="`mailto:${email}`"
71-
target="_blank"
72-
>
73-
<div class="flex gap-3 items-center">
74-
<app-platform platform="email" />
75-
<span class="text-gray-900 text-xs">
76-
{{ email }}</span
77-
>
78-
</div>
79-
<i class="ri-external-link-line text-gray-300"></i>
80-
</a>
71+
<div class="mt-4">
72+
<a
73+
v-for="email of emails"
74+
:key="email"
75+
class="py-2 px-6 -mx-6 flex justify-between items-center relative hover:bg-gray-50 transition-colors cursor-pointer"
76+
:href="`mailto:${email}`"
77+
target="_blank"
78+
>
79+
<div class="flex gap-3 items-center">
80+
<app-platform platform="email" />
81+
<span class="text-gray-900 text-xs">
82+
{{ email }}</span
83+
>
84+
</div>
85+
<i
86+
class="ri-external-link-line text-gray-300"
87+
></i>
88+
</a>
89+
</div>
8190
</div>
8291
<app-member-manage-identities-drawer
8392
v-model="identitiesDrawer"
@@ -103,13 +112,13 @@ const { currentTenant, currentUser } = mapGetters('auth')
103112
104113
const identitiesDrawer = ref(false)
105114
106-
const email = computed(() => {
107-
return props.member.email
115+
const emails = computed(() => {
116+
return props.member.emails
108117
})
109118
110119
const socialIdentities = computed(() => {
111120
const identities = { ...props.member.username }
112-
delete identities.email
121+
delete identities.emails
113122
114123
return identities
115124
})

frontend/src/modules/member/member-list-exporter-fields.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const { fields } = MemberModel
55
export default [
66
fields.id,
77
fields.username,
8-
fields.email,
8+
fields.emails,
99
fields.createdAt,
1010
fields.updatedAt
1111
]

frontend/src/modules/member/member-model.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import SearchField from '@/shared/fields/search-field'
1414
import MemberIdentitiesField from './member-identities-field'
1515
import SentimentField from '@/shared/fields/sentiment-field'
1616
import ActivityTypeField from '@/modules/activity/activity-type-field'
17+
import StringArrayField from '@/shared/fields/string-array-field'
1718

1819
function label(name) {
1920
return i18n(`entities.member.fields.${name}`)
@@ -64,9 +65,7 @@ const fields = {
6465
tags: TagField.relationToMany('tags', label('tags'), {
6566
filterable: true
6667
}),
67-
email: new StringField('email', label('email'), {
68-
email: true
69-
}),
68+
emails: new StringArrayField('emails', 'Emails'),
7069
noMerge: MemberField.relationToMany(
7170
'noMerge',
7271
label('noMerge'),

frontend/src/modules/member/pages/member-form-page.vue

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ const formSchema = computed(
150150
() =>
151151
new FormSchema([
152152
fields.displayName,
153-
fields.email,
153+
fields.emails,
154154
fields.joinedAt,
155155
fields.tags,
156156
fields.username,
@@ -280,7 +280,7 @@ function getInitialModel(record) {
280280
JSON.stringify(
281281
formSchema.value.initialValues({
282282
displayName: record ? record.displayName : '',
283-
email: record ? record.email : '',
283+
emails: record ? record.emails : '',
284284
joinedAt: record ? record.joinedAt : '',
285285
attributes: record
286286
? filteredAttributes(record.attributes)
@@ -301,6 +301,7 @@ function filteredAttributes(attributes) {
301301
return Object.keys(attributes).reduce((acc, item) => {
302302
if (
303303
![
304+
'emails',
304305
'workExperiences',
305306
'education',
306307
'certifications',
@@ -337,8 +338,8 @@ async function onSubmit() {
337338
formModel.value.displayName && {
338339
displayName: formModel.value.displayName
339340
},
340-
formModel.value.email && {
341-
email: formModel.value.email
341+
formModel.value.emails && {
342+
emails: formModel.value.emails
342343
},
343344
formModel.value.joinedAt && {
344345
joinedAt: formModel.value.joinedAt

0 commit comments

Comments
 (0)