Skip to content

Commit d3bc417

Browse files
authored
Organization enrichment frontend (#864)
1 parent 789faf8 commit d3bc417

15 files changed

Lines changed: 713 additions & 25 deletions

frontend/src/assets/scss/badge.scss

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@
5353
}
5454
}
5555

56+
&--border {
57+
@apply text-gray-900 gap-1.5 border px-1.5 rounded-md h-6 truncate block;
58+
}
59+
5660
&--interactive {
5761
@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;
5862
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<template>
2+
<div class="grid gap-x-12 grid-cols-3 mb-16">
3+
<div v-if="showHeader">
4+
<h6>Attributes</h6>
5+
<p class="text-gray-500 text-2xs leading-normal mt-1">
6+
Data points to enhance the organization profile
7+
</p>
8+
</div>
9+
<div :class="showHeader ? 'col-span-2' : 'col-span-3'">
10+
<div class="flex gap-3 border-b h-8 items-center">
11+
<span
12+
class="uppercase text-gray-400 text-2xs font-semibold tracking-wide w-1/3"
13+
>Name</span>
14+
<span
15+
class="uppercase text-gray-400 text-2xs font-semibold tracking-wide grow"
16+
>Value</span>
17+
</div>
18+
<div
19+
class="custom-attributes-form flex mt-4 mb-2 flex-col gap-4"
20+
>
21+
<app-organization-form-item
22+
v-for="attribute in visibleAttributes"
23+
:key="attribute.name"
24+
:type="attribute.type"
25+
:label="attribute.label"
26+
:is-enrichment-field="true"
27+
>
28+
<app-autocomplete-many-input
29+
v-if="attribute.type === attributesTypes.multiSelect"
30+
v-model="model[attribute.name]"
31+
disabled
32+
input-class="w-full multi-select-field"
33+
placeholder=" "
34+
:collapse-tags="true"
35+
/>
36+
<el-input
37+
v-else
38+
v-model="model[attribute.name]"
39+
:type="attribute.type"
40+
disabled
41+
clearable
42+
/><template #error>
43+
<div class="el-form-item__error">
44+
Value is required
45+
</div>
46+
</template>
47+
</app-organization-form-item>
48+
</div>
49+
</div>
50+
</div>
51+
</template>
52+
53+
<script setup>
54+
import { computed } from 'vue';
55+
import enrichmentAttributes, { attributesTypes } from '@/modules/organization/config/organization-enrichment-attributes';
56+
import AppOrganizationFormItem from './organization-form-item.vue';
57+
58+
const props = defineProps({
59+
showHeader: {
60+
type: Boolean,
61+
default: true,
62+
},
63+
modelValue: {
64+
type: Object,
65+
default: () => {},
66+
},
67+
organization: {
68+
type: Object,
69+
default: () => {},
70+
},
71+
});
72+
73+
const model = computed(() => props.modelValue);
74+
const visibleAttributes = computed(() => enrichmentAttributes.filter((a) => a.showInForm));
75+
</script>
76+
77+
<script>
78+
export default {
79+
name: 'AppOrganizationFormAttributes',
80+
};
81+
</script>
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<template>
2+
<div class="flex gap-3">
3+
<div
4+
class="w-1/3 flex flex-col gap-1 justify-start mt-0.5"
5+
>
6+
<div class="flex items-center leading-tight">
7+
<div
8+
class="text-gray-900 text-xs font-medium mr-2"
9+
>
10+
{{ label }}
11+
</div>
12+
<el-tooltip
13+
v-if="isEnrichmentField"
14+
content="Organization enrichment"
15+
placement="top"
16+
>
17+
<div class="form-enrichment-badge">
18+
<app-svg name="enrichment" />
19+
</div>
20+
</el-tooltip>
21+
</div>
22+
<span
23+
class="text-2xs text-gray-500 leading-none"
24+
>{{ type }}</span>
25+
</div>
26+
<el-form-item class="grow">
27+
<slot />
28+
</el-form-item>
29+
</div>
30+
</template>
31+
32+
<script setup>
33+
import AppSvg from '@/shared/svg/svg.vue';
34+
35+
defineProps({
36+
label: {
37+
type: String,
38+
default: null,
39+
},
40+
type: {
41+
type: String,
42+
default: null,
43+
},
44+
isEnrichmentField: {
45+
type: Boolean,
46+
default: false,
47+
},
48+
});
49+
</script>
50+
51+
<script>
52+
export default {
53+
name: 'AppOrganizationFormItem',
54+
};
55+
</script>
56+
57+
<style lang="scss">
58+
.form-enrichment-badge {
59+
@apply w-5 h-5 bg-gray-100 rounded-full flex items-center justify-center;
60+
svg {
61+
@apply h-4 w-4 overflow-visible flex items-center justify-center leading-none;
62+
}
63+
}
64+
65+
.multi-select-field .el-select__tags {
66+
@apply h-7;
67+
}
68+
</style>

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

Lines changed: 186 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -173,24 +173,24 @@
173173
</template>
174174
</el-table-column>
175175

176-
<!-- Number of employees
177-
TODO: Uncomment when we support enrichment
176+
<!-- Number of employees -->
178177
<el-table-column
179178
label="# Employees"
180179
width="150"
181180
prop="employees"
182181
sortable
183-
><template #default="scope">
184-
<div class="text-gray-900 text-sm">
185-
{{
186-
formatNumberToRange(
187-
scope.row.employees
188-
)
189-
}}
190-
</div></template
191-
></el-table-column
192182
>
193-
-->
183+
<template #default="scope">
184+
<div class="text-sm h-full flex items-center">
185+
<span v-if="scope.row.employees" class="text-gray-900">
186+
{{
187+
formatNumber(scope.row.employees)
188+
}}
189+
</span>
190+
<span v-else class="text-gray-500">-</span>
191+
</div>
192+
</template>
193+
</el-table-column>
194194

195195
<!-- Number of activities -->
196196
<el-table-column
@@ -331,6 +331,161 @@
331331
</template>
332332
</el-table-column>
333333

334+
<!-- Location -->
335+
<el-table-column
336+
label="Location"
337+
width="150"
338+
prop="location"
339+
sortable
340+
>
341+
<template #default="scope">
342+
<router-link
343+
:to="{
344+
name: 'organizationView',
345+
params: { id: scope.row.id },
346+
}"
347+
class="block"
348+
>
349+
<div
350+
class="text-sm h-full flex items-center"
351+
>
352+
<span v-if="scope.row.location" class="text-gray-900">
353+
{{
354+
scope.row.location
355+
}}
356+
</span>
357+
<span v-else class="text-gray-500">-</span>
358+
</div>
359+
</router-link>
360+
</template>
361+
</el-table-column>
362+
363+
<!-- Industry -->
364+
<el-table-column
365+
label="Industry"
366+
width="150"
367+
prop="industry"
368+
sortable
369+
>
370+
<template #default="scope">
371+
<router-link
372+
:to="{
373+
name: 'organizationView',
374+
params: { id: scope.row.id },
375+
}"
376+
class="block"
377+
>
378+
<div
379+
class="text-sm h-full flex items-center"
380+
>
381+
<span v-if="scope.row.industry" class="text-gray-900">
382+
{{
383+
toSentenceCase(scope.row.industry)
384+
}}
385+
</span>
386+
<span v-else class="text-gray-500">-</span>
387+
</div>
388+
</router-link>
389+
</template>
390+
</el-table-column>
391+
392+
<!-- Size -->
393+
<el-table-column
394+
label="Size"
395+
width="150"
396+
prop="size"
397+
sortable
398+
>
399+
<template #default="scope">
400+
<router-link
401+
:to="{
402+
name: 'organizationView',
403+
params: { id: scope.row.id },
404+
}"
405+
class="block"
406+
>
407+
<div
408+
class="text-sm h-full flex items-center"
409+
>
410+
<span v-if="scope.row.size" class="text-gray-900">
411+
{{
412+
scope.row.size
413+
}}
414+
</span>
415+
<span v-else class="text-gray-500">-</span>
416+
</div>
417+
</router-link>
418+
</template>
419+
</el-table-column>
420+
421+
<!-- Founded -->
422+
<el-table-column
423+
label="Founded"
424+
width="150"
425+
prop="founded"
426+
sortable
427+
>
428+
<template #default="scope">
429+
<router-link
430+
:to="{
431+
name: 'organizationView',
432+
params: { id: scope.row.id },
433+
}"
434+
class="block"
435+
>
436+
<div
437+
class="text-sm h-full flex items-center"
438+
>
439+
<span v-if="scope.row.founded" class="text-gray-900">
440+
{{
441+
scope.row.founded
442+
}}
443+
</span>
444+
<span v-else class="text-gray-500">-</span>
445+
</div>
446+
</router-link>
447+
</template>
448+
</el-table-column>
449+
450+
<!-- Profiles -->
451+
<el-table-column
452+
label="Profiles"
453+
:width="profilesColumnWidth"
454+
>
455+
<template #default="scope">
456+
<router-link
457+
:to="{
458+
name: 'organizationView',
459+
params: { id: scope.row.id },
460+
}"
461+
class="block"
462+
>
463+
<div
464+
v-if="scope.row.profiles?.length && scope.row.profiles?.some((e) => !!e)"
465+
class="text-sm cursor-auto flex flex-wrap gap-1"
466+
>
467+
<app-tags
468+
:tags="scope.row.profiles"
469+
:interactive="true"
470+
:collapse-tags="true"
471+
:collapse-tags-tooltip="true"
472+
>
473+
<template #tagTooltipContent>
474+
<span>Open profile
475+
<i
476+
class="ri-external-link-line text-gray-400"
477+
/></span>
478+
</template>
479+
</app-tags>
480+
</div>
481+
<span
482+
v-else
483+
class="text-gray-500"
484+
>-</span>
485+
</router-link>
486+
</template>
487+
</el-table-column>
488+
334489
<!-- Actions -->
335490
<el-table-column fixed="right">
336491
<template #default="scope">
@@ -388,8 +543,9 @@ import {
388543
mapActions,
389544
} from '@/shared/vuex/vuex.helpers';
390545
import { formatDateToTimeAgo } from '@/utils/date';
391-
import { formatNumberToCompact } from '@/utils/number';
392-
import { withHttp } from '@/utils/string';
546+
import { formatNumberToCompact, formatNumber } from '@/utils/number';
547+
import { withHttp, toSentenceCase } from '@/utils/string';
548+
import AppTags from '@/shared/tags/tags.vue';
393549
import AppOrganizationIdentities from '../organization-identities.vue';
394550
import AppOrganizationListToolbar from './organization-list-toolbar.vue';
395551
import AppOrganizationName from '../organization-name.vue';
@@ -515,6 +671,22 @@ const emailsColumnWidth = computed(() => {
515671
return maxTabWidth;
516672
});
517673
674+
const profilesColumnWidth = computed(() => {
675+
let maxTabWidth = 150;
676+
677+
rows.value.forEach((row) => {
678+
const tabWidth = row.profiles
679+
?.map((profile) => (profile ? profile.length * 12 : 0))
680+
.reduce((a, b) => a + b, 0);
681+
682+
if (tabWidth > maxTabWidth) {
683+
maxTabWidth = tabWidth > 400 ? 400 : tabWidth;
684+
}
685+
});
686+
687+
return maxTabWidth;
688+
});
689+
518690
const trackEmailClick = () => {
519691
window.analytics.track('Click Organization Contact', {
520692
channel: 'Email',

0 commit comments

Comments
 (0)