Skip to content

Commit a153019

Browse files
authored
UI declutter stepper (#2013)
* stepper ui changes * refine in background when done extracting * fix updating person & drop temp tables manually * move refine reload logic to mining panel * await function refineReloadContacts * remove async keyword * ui changes: remove title, remove button, bigger title, * remove unused code
1 parent 81ff852 commit a153019

File tree

5 files changed

+104
-95
lines changed

5 files changed

+104
-95
lines changed

frontend/src/components/Mining/MiningStepper.vue

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,7 @@
55
severity="secondary"
66
unstyled
77
@click="collapsePanel = !collapsePanel"
8-
>
9-
<span class="font-semibold flex items-center gap-2">
10-
<i
11-
v-if="collapsePanel && $leadminerStore.activeTask"
12-
v-tooltip.top="spinnerText"
13-
class="pi pi-spin pi-spinner text-lg"
14-
/>
15-
{{ t('mine_contacts') }}
16-
</span>
17-
</Button>
8+
/>
189
</template>
1910
<Stepper v-model:value="$stepper.index" linear>
2011
<StepList>
@@ -94,20 +85,6 @@ const sourcePanel = ref<InstanceType<typeof SourcePanel>>();
9485
9586
const { error, provider } = $route.query;
9687
97-
const spinnerText = computed(() => {
98-
if (!(collapsePanel.value && $leadminerStore.activeTask)) return undefined;
99-
if ($leadminerStore.miningTask !== undefined) {
100-
return t('mining');
101-
}
102-
if ($leadminerStore.isLoadingBoxes) {
103-
return t('retrieving_mailboxes');
104-
}
105-
if (!$leadminerStore.cleaningFinished) {
106-
return t('cleaning');
107-
}
108-
return undefined;
109-
});
110-
11188
onNuxtReady(() => {
11289
if (provider && error === 'oauth-consent') {
11390
const newQuery = { ...useRoute().query };

frontend/src/components/Mining/StepperPanels/MinePanel.vue

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ const source = computed(() => (miningSource ? 'boxes' : 'file'));
142142
const $toast = useToast();
143143
const $stepper = useMiningStepper();
144144
const $leadminerStore = useLeadminerStore();
145+
const $contactsStore = useContactsStore();
145146
146147
const AVERAGE_EXTRACTION_RATE =
147148
parseInt(useRuntimeConfig().public.AVERAGE_EXTRACTION_RATE) || 130;
@@ -224,7 +225,20 @@ onMounted(async () => {
224225
}
225226
});
226227
227-
watch(extractionFinished, (finished) => {
228+
async function refineReloadContacts() {
229+
/**
230+
* Disable realtime; protects table from rendering multiple times
231+
*/
232+
await $contactsStore.unsubscribeFromRealtimeUpdates();
233+
await $contactsStore.refineContacts();
234+
await $contactsStore.reloadContacts();
235+
/**
236+
* Subscribe again after the table is rendered
237+
*/
238+
$contactsStore.subscribeToRealtimeUpdates();
239+
}
240+
241+
watch(extractionFinished, async (finished) => {
228242
if (canceled.value) {
229243
$toast.add({
230244
severity: 'success',
@@ -233,6 +247,7 @@ watch(extractionFinished, (finished) => {
233247
life: 3000,
234248
});
235249
$stepper.next();
250+
await refineReloadContacts();
236251
} else if (finished) {
237252
$toast.add({
238253
severity: 'success',
@@ -244,6 +259,7 @@ watch(extractionFinished, (finished) => {
244259
life: 5000,
245260
});
246261
$stepper.next();
262+
await refineReloadContacts();
247263
}
248264
});
249265

frontend/src/components/Mining/StepperPanels/SourcePanel.vue

Lines changed: 68 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,60 @@
11
<template>
2-
<div class="flex flex-col min-[1129px]:flex-row gap-2">
3-
<div
4-
v-if="sourceOptions.length"
5-
class="w-full min-[1129px]:w-1/2 flex flex-col gap-3"
6-
>
7-
<p>{{ t('pick_existing_email') }}</p>
8-
<div class="flex flex-col min-[1129px]:flex-row gap-2">
9-
<Select
10-
v-model="sourceModel"
11-
:options="sourceOptions"
12-
class="flex-grow min-w-0"
13-
option-label="email"
14-
:placeholder="t('email_address')"
15-
:pt="{
16-
trigger: {
17-
class: 'text-indigo-500 ',
18-
},
19-
input: {
20-
class: 'text-indigo-500 ',
21-
},
22-
root: {
23-
class: 'border-[#bcbdf9] ',
24-
},
25-
}"
2+
<div class="flex flex-col items-center space-y-6 p-6">
3+
<span class="text-xl">{{ t('title') }}</span>
4+
5+
<template v-if="!showOtherSources">
6+
<Select
7+
v-model="sourceModel"
8+
:options="sourceOptions"
9+
class="w-full max-w-lg"
10+
option-label="email"
11+
:placeholder="t('email_address')"
12+
:pt="{
13+
trigger: { class: 'text-indigo-500' },
14+
input: { class: 'text-indigo-500' },
15+
root: { class: 'border-[#bcbdf9]' },
16+
}"
17+
>
18+
<template #value="{ value }">
19+
<div class="flex items-center space-x-2">
20+
<i :class="getIcon(value?.type)" class="text-secondary text-sm"></i>
21+
<span>{{ value?.email }}</span>
22+
</div>
23+
</template>
24+
25+
<template #option="{ option }">
26+
<div class="flex items-center space-x-2">
27+
<i :class="getIcon(option.type)" class="text-secondary text-sm"></i>
28+
<span>{{ option.email }}</span>
29+
</div>
30+
</template>
31+
</Select>
32+
33+
<div class="flex flex-col min-[1129px]:flex-row gap-2 w-full max-w-lg">
34+
<Button
35+
id="mine-source"
36+
class="w-full"
37+
severity="secondary"
38+
:disabled="!sourceModel"
39+
:label="t('mine_new_source')"
40+
outlined
41+
@click="showOtherSources = true"
2642
/>
2743
<Button
2844
id="extract-source"
29-
:disabled="!sourceModel"
45+
class="w-full"
3046
severity="contrast"
31-
class="font-semibold flex-shrink-0"
47+
:disabled="!sourceModel"
3248
:label="t('extract_contacts')"
33-
@click="extractContacts()"
49+
@click="extractContacts"
3450
/>
3551
</div>
36-
</div>
37-
<template v-if="sourceOptions.length">
38-
<Separator
39-
layout="vertical"
40-
:content="$t('common.or')"
41-
class="hidden min-[1129px]:flex"
42-
/>
43-
<Separator
44-
layout="horizontal"
45-
:content="$t('common.or')"
46-
class="flex min-[1129px]:hidden"
47-
/>
4852
</template>
49-
<div class="shrink flex flex-col gap-3">
50-
<span>{{ t('mine_from') }}</span>
51-
<div class="flex flex-col min-[1129px]:flex-row gap-2 flex-wrap">
53+
54+
<template v-else>
55+
<div
56+
class="flex flex-col min-[1129px]:flex-row flex-wrap gap-2 w-full max-w-4xl justify-center"
57+
>
5258
<oauth-source icon="pi pi-google" label="Google" source="google" />
5359
<oauth-source
5460
icon="pi pi-microsoft"
@@ -61,14 +67,14 @@
6167
/>
6268
<Button
6369
id="import-file"
64-
outlined
6570
icon="pi pi-upload"
6671
:label="t('import_csv_excel')"
72+
outlined
6773
@click="importFileDialogRef.openModal()"
6874
/>
6975
<importFileDialog ref="importFileDialogRef" />
7076
</div>
71-
</div>
77+
</template>
7278
</div>
7379
</template>
7480

@@ -90,7 +96,7 @@ const $leadminerStore = useLeadminerStore();
9096
const $imapDialogStore = useImapDialog();
9197
const sourceModel = ref<MiningSource | undefined>();
9298
const sourceOptions = computed(() => useLeadminerStore().miningSources);
93-
99+
const showOtherSources = ref(false);
94100
const { source } = useRoute().query;
95101
96102
function onSourceChange(miningSource: MiningSource) {
@@ -105,6 +111,17 @@ function extractContacts() {
105111
}
106112
}
107113
114+
function getIcon(type: string) {
115+
switch (type) {
116+
case 'google':
117+
return 'pi pi-google';
118+
case 'azure':
119+
return 'pi pi-microsoft';
120+
default:
121+
return 'pi pi-inbox';
122+
}
123+
}
124+
108125
onMounted(async () => {
109126
try {
110127
await $leadminerStore.fetchMiningSources();
@@ -146,17 +163,19 @@ defineExpose({
146163
<i18n lang="json">
147164
{
148165
"en": {
149-
"pick_existing_email": "Pick an existing email address to mine",
150-
"mine_from": "Mine your contacts from",
166+
"title": "Mine contacts from",
167+
"mine_existing_source": "Mine from existing source",
168+
"mine_new_source": "Mine from another source",
151169
"fetch_sources_failed": "Failed to fetch mining sources",
152170
"email_address": "email address",
153171
"extract_contacts": "Extract contacts",
154172
"microsoft_or_outlook": "Microsoft or Outlook",
155173
"import_csv_excel": "Import CSV or Excel"
156174
},
157175
"fr": {
158-
"pick_existing_email": "Choisissez une adresse e-mail existante pour l’extraction",
159-
"mine_from": "Extraire vos contacts depuis",
176+
"title": "Extraire des contacts depuis",
177+
"mine_existing_source": "Extraire depuis une source existante",
178+
"mine_new_source": "Extraire depuis une autre source",
160179
"fetch_sources_failed": "Échec de la récupération des sources de minage",
161180
"email_address": "adresse e-mail",
162181
"extract_contacts": "Extraire les contacts",

frontend/src/components/Mining/Table/MiningTable.vue

Lines changed: 2 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -784,32 +784,13 @@ function onFilter(event: DataTableFilterEvent) {
784784
filteredContacts.value = event.filteredValue;
785785
}
786786
787-
watch(activeMiningTask, async (isActive) => {
787+
watch(activeMiningTask, (isActive) => {
788788
if (isActive) {
789789
$leadminerStore.cleaningFinished = false;
790790
filtersStore.clearFilter();
791791
} else {
792-
isLoading.value = true;
793-
/**
794-
* Disable realtime; protects table from rendering multiple times
795-
*/
796-
await $contactsStore.unsubscribeFromRealtimeUpdates();
797-
798-
loadingLabel.value = t('refining_contacts');
799-
await $contactsStore.refineContacts();
800-
801-
loadingLabel.value = t('syncing');
802-
await $contactsStore.reloadContacts();
803-
804-
filtersStore.toggleFilters();
805-
806-
isLoading.value = false;
807792
$leadminerStore.cleaningFinished = true;
808-
809-
/**
810-
* Subscribe again after the table is rendered
811-
*/
812-
$contactsStore.subscribeToRealtimeUpdates();
793+
filtersStore.toggleFilters();
813794
}
814795
});
815796

supabase/migrations/20250122185859_optimize_refine_persons.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,14 @@ BEGIN
9090
LEFT JOIN real_names pn ON ea.person_email = pn.person_email
9191
JOIN grouped_tags gt ON ea.person_email = gt.email;
9292

93+
UPDATE private.persons
94+
SET
95+
name = cd.name,
96+
alternate_name = cd.alternate_name,
97+
alternate_email = cd.alternate_email
98+
FROM combined_data cd
99+
WHERE private.persons.email = cd.person_email;
100+
93101
INSERT INTO private.refinedpersons (
94102
user_id, email, occurrence, recency, seniority,
95103
sender, recipient, conversations, replied_conversations, tags
@@ -116,5 +124,13 @@ BEGIN
116124
conversations = EXCLUDED.conversations,
117125
replied_conversations = EXCLUDED.replied_conversations,
118126
tags = EXCLUDED.tags;
127+
128+
-- Drop temp tables after function execution
129+
DROP TABLE IF EXISTS user_points_of_contact;
130+
DROP TABLE IF EXISTS grouped_tags;
131+
DROP TABLE IF EXISTS name_aggregates;
132+
DROP TABLE IF EXISTS real_names;
133+
DROP TABLE IF EXISTS email_aggregates;
134+
DROP TABLE IF EXISTS combined_data;
119135
END;
120136
$$;

0 commit comments

Comments
 (0)