Skip to content

Commit c77f40c

Browse files
authored
Add enrich gdpr dialog (#1992)
* database: add column `gdpr_details` in `profiles` * enrich gdpr dialog * i18n * fix dialog width sizes * fix i18n * deepsource fix
1 parent 52783b0 commit c77f40c

File tree

4 files changed

+204
-3
lines changed

4 files changed

+204
-3
lines changed

frontend/src/components/Mining/Buttons/EnrichButton.vue

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
:label="t('button.start_enrichment')"
6363
pt:label:class="hidden md:block"
6464
:disabled="isEnrichDisabled"
65-
@click="openEnrichmentConfirmationDialog"
65+
@click="onClickEnrich"
6666
>
6767
<template #icon>
6868
<span class="p-button-icon p-button-icon-right">
@@ -71,6 +71,10 @@
7171
</span>
7272
</template>
7373
</Button>
74+
<EnrichGdprDialog
75+
ref="enrichGdprDialogRef"
76+
@has-given-consent="onAcceptEnrich"
77+
/>
7478
</template>
7579
<script setup lang="ts">
7680
import type {
@@ -85,6 +89,7 @@ import {
8589
openCreditsDialog,
8690
} from '@/utils/credits';
8791
import type { Contact } from '~/types/contact';
92+
import EnrichGdprDialog from '../EnrichGdprDialog.vue';
8893
8994
const { t } = useI18n({
9095
useScope: 'local',
@@ -310,7 +315,30 @@ onMounted(async () => {
310315
}
311316
});
312317
313-
const openEnrichmentConfirmationDialog = () => {
318+
const enrichGdprDialogRef = ref();
319+
const $profile = useSupabaseUserProfile();
320+
const hasAcceptedEnriching = computed(
321+
() => $profile.value?.gdpr_details.hasAcceptedEnriching,
322+
);
323+
function onAcceptEnrich() {
324+
const justAcceptedEnrich = true;
325+
openEnrichmentConfirmationDialog(justAcceptedEnrich);
326+
}
327+
328+
function onClickEnrich(_: MouseEvent) {
329+
openEnrichmentConfirmationDialog();
330+
}
331+
332+
/**
333+
* Verifies if user has accepted enriching conditions (using `hasAcceptedEnriching` of `$profile`), then proceeds to the enrichment confirmation dialog
334+
* @param justAcceptedEnrich : is a workaround as `hasAcceptedEnriching` of `$profile` can still be not updated from the realtime
335+
*/
336+
function openEnrichmentConfirmationDialog(justAcceptedEnrich?: boolean) {
337+
if (!justAcceptedEnrich && !hasAcceptedEnriching.value) {
338+
enrichGdprDialogRef.value.openModal();
339+
return;
340+
}
341+
314342
const creditsDialogOpened = useCreditsDialog(
315343
CreditsDialogEnrichRef,
316344
contactsToEnrich.value?.map(({ email }) => email as string),
@@ -320,7 +348,7 @@ const openEnrichmentConfirmationDialog = () => {
320348
if (skipDialog.value) {
321349
startEnrichment(false);
322350
} else dialogVisible.value = true;
323-
};
351+
}
324352
325353
const closeEnrichmentConfirmationDialog = () => {
326354
dialogVisible.value = false;
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
<template>
2+
<Dialog
3+
ref="dialog"
4+
v-model:visible="visible"
5+
modal
6+
:closable="false"
7+
:header="t('beforeYouProceed')"
8+
pt:content:class="grow p-3 border-y border-slate-200 font-serif"
9+
pt:footer:class="p-3"
10+
:draggable="false"
11+
:pt:root:class="{ 'p-dialog-maximized': !$screenStore?.size?.md }"
12+
class="h-[70vh] 2xl:w-[40vw] w-[70vw]"
13+
>
14+
<div>
15+
{{ t('acknowledge') }}
16+
</div>
17+
<ul class="my-3">
18+
<li class="mb-3">
19+
<div class="text-lg font-semibold">
20+
{{ t('purposeOfEnrichment.title') }}
21+
</div>
22+
<div>
23+
{{ t('purposeOfEnrichment.content') }}
24+
</div>
25+
</li>
26+
<li class="mb-3">
27+
<div class="text-lg font-semibold">
28+
{{ t('thirdPartyTools.title') }}
29+
</div>
30+
<div>
31+
{{ t('thirdPartyTools.content') }}
32+
</div>
33+
</li>
34+
<li>
35+
<div class="text-lg font-semibold">
36+
{{ t('yourResponsibilities.title') }}
37+
</div>
38+
<ul>
39+
<li>
40+
<Checkbox v-model="checked1" binary :invalid="!checked1" disabled />
41+
{{ t('yourResponsibilities.content_1') }}
42+
</li>
43+
<li>
44+
<Checkbox v-model="checked2" binary :invalid="!checked2" disabled />
45+
{{ t('yourResponsibilities.content_2') }}
46+
</li>
47+
<li>
48+
<Checkbox v-model="checked3" binary :invalid="!checked3" disabled />
49+
{{ t('yourResponsibilities.content_3') }}
50+
</li>
51+
</ul>
52+
</li>
53+
</ul>
54+
<div>
55+
{{ t('proceedConfirmation') }}
56+
</div>
57+
<template #footer>
58+
<Button
59+
severity="secondary"
60+
:label="$t('common.cancel')"
61+
@click="closeModal"
62+
/>
63+
<Button
64+
severity="secondary"
65+
class="text-center"
66+
:label="t('privacyPolicyButton')"
67+
as="a"
68+
href="https://www.leadminer.io/data-privacy'"
69+
target="_blank"
70+
rel="noopener noreferrer"
71+
/>
72+
73+
<Button
74+
class="border-solid border-2 border-black"
75+
:label="t('enrichButton')"
76+
severity="contrast"
77+
@click="confirm"
78+
/>
79+
</template>
80+
</Dialog>
81+
</template>
82+
<script setup lang="ts">
83+
const { t } = useI18n({
84+
useScope: 'local',
85+
});
86+
const $screenStore = useScreenStore();
87+
const $profile = useSupabaseUserProfile();
88+
89+
const dialog = ref();
90+
const visible = ref(false);
91+
function openModal() {
92+
visible.value = true;
93+
}
94+
const emit = defineEmits(['hasGivenConsent']);
95+
96+
const checked1 = ref(true);
97+
const checked2 = ref(true);
98+
const checked3 = ref(true);
99+
100+
async function acceptEnriching() {
101+
const { error } = await useSupabaseClient()
102+
// @ts-expect-error: Issue with nuxt/supabase
103+
.schema('private')
104+
.from('profiles')
105+
.update({ gdpr_details: { hasAcceptedEnriching: true } })
106+
.eq('user_id', $profile.value?.user_id);
107+
108+
if (error) {
109+
throw error;
110+
}
111+
}
112+
113+
function closeModal() {
114+
visible.value = false;
115+
}
116+
117+
async function confirm() {
118+
closeModal();
119+
await acceptEnriching();
120+
emit('hasGivenConsent');
121+
}
122+
123+
defineExpose({ openModal });
124+
</script>
125+
<i18n lang="json">
126+
{
127+
"en": {
128+
"beforeYouProceed": "Before You Proceed: Enrich My Contacts",
129+
"acknowledge": "By clicking \"Enrich My Contacts\", you acknowledge and agree to the following:",
130+
"purposeOfEnrichment": {
131+
"title": "Purpose of Enrichment",
132+
"content": "This service is intended to enrich information about your existing contacts. You must have a legitimate interest or have gathered their explicit consent to perform this enrichment for a specific and lawful purpose."
133+
},
134+
"thirdPartyTools": {
135+
"title": "Third-Party Tools",
136+
"content": "The enrichment process uses TheDig, Proxycurl, and VoilaNorbert. Results are cached securely for 90 days to improve efficiency and ensure data integrity."
137+
},
138+
"yourResponsibilities": {
139+
"title": "Your Responsibilities",
140+
"content_1": "It is your responsibility to ensure compliance with applicable laws, including data protection regulations.",
141+
"content_2": "You must inform your contacts about the use of this service and any data you enrich on their behalf.",
142+
"content_3": "Your contacts hold their full rights including the rights to access and delete any information about them."
143+
},
144+
"proceedConfirmation": "By proceeding, you confirm that you have a legitimate interest or proper consent to enrich data on the selected contacts and accept your responsibility for compliance.",
145+
"privacyPolicyButton": "Read the data privacy policy",
146+
"enrichButton": "Enrich My Contacts"
147+
},
148+
"fr": {
149+
"beforeYouProceed": "Avant de continuer : Enrichissez mes contacts",
150+
"acknowledge": "En cliquant sur \"Enrichissez mes contacts\", vous reconnaissez et acceptez les éléments suivants :",
151+
"purposeOfEnrichment": {
152+
"title": "Objectif de l'enrichissement",
153+
"content": "Ce service est destiné à enrichir les informations sur vos contacts existants. Vous devez avoir un intérêt légitime ou avoir obtenu leur consentement explicite pour effectuer cet enrichissement à des fins spécifiques et légales."
154+
},
155+
"thirdPartyTools": {
156+
"title": "Outils tiers",
157+
"content": "Le processus d'enrichissement utilise TheDig, Proxycurl et VoilaNorbert. Les résultats sont mis en cache en toute sécurité pendant 90 jours pour améliorer l'efficacité et garantir l'intégrité des données."
158+
},
159+
"yourResponsibilities": {
160+
"title": "Vos responsabilités",
161+
"content_1": "Il est de votre responsabilité de vous conformer aux lois applicables, y compris les réglementations sur la protection des données.",
162+
"content_2": "Vous devez informer vos contacts de l'utilisation de ce service et de toute donnée que vous enrichissez en leur nom.",
163+
"content_3": "Vos contacts conservent tous leurs droits, y compris les droits d'accès et de suppression de toute information les concernant."
164+
},
165+
"proceedConfirmation": "En continuant, vous confirmez que vous avez un intérêt légitime ou un consentement approprié pour enrichir les données des contacts sélectionnés et acceptez votre responsabilité en matière de conformité.",
166+
"privacyPolicyButton": "Lire la politique de confidentialité des données",
167+
"enrichButton": "Enrichissez mes contacts"
168+
}
169+
}
170+
</i18n>

frontend/src/types/profile.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ export interface Profile {
33
email: string;
44
full_name: string;
55
credits: number;
6+
gdpr_details: { hasAcceptedEnriching: boolean };
67
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ALTER TABLE private.profiles
2+
ADD COLUMN gdpr_details JSONB DEFAULT '{"hasAcceptedEnriching": false }' NOT NULL;

0 commit comments

Comments
 (0)