feat: change email, export my data, delete account (with data anonymization)

This commit is contained in:
Florian Sylvain committed 2026-10-30 20:19:07 +01:00
1 parent bb191c389a
commit 10a66cd979
123 files changed
+10789 -76

No files matched your search

+49 -2
View File
@@ -7,10 +7,15 @@ import { useLocalizedOverlay, useLocalizedToast } from '@/i18n/presentation'
import AuthorProfileCard from '@/components/AuthorProfileCard.vue'
import CharacterCounter from '@/components/CharacterCounter.vue'
import ConfirmDialog from '@/components/ConfirmDialog.vue'
import DangerZoneCard from '@/components/DangerZoneCard.vue'
import DataExportCard from '@/components/DataExportCard.vue'
import DashboardShell from '@/components/DashboardShell.vue'
import EmailChangeForm from '@/components/EmailChangeForm.vue'
import PendingEmailNotice from '@/components/PendingEmailNotice.vue'
import ProfileCard from '@/components/ProfileCard.vue'
import YourRoleCard from '@/components/YourRoleCard.vue'
import { ApiError, authRedirectPending } from '@/composables/useApi'
import { useEmailChange } from '@/composables/useEmailChange'
import { useProfile } from '@/composables/useProfile'
import { useRetryAfter } from '@/composables/useRetryAfter'
import { usePermissionsStore } from '@/stores/permissions'
@@ -39,6 +44,7 @@ const toast = useLocalizedToast()
const overlay = useLocalizedOverlay()
const { updateDisplayName, sendPasswordReset } = useProfile()
const retryAfter = useRetryAfter()
const emailChange = useEmailChange()
const account = ref<Session | null>(session.session)
const field = ref('')
@@ -102,6 +108,8 @@ const readOnlyUi = {
base: 'h-11 text-lg disabled:text-toned lg:h-10 lg:text-base',
}
const readOnlyLockUi = { trailingIcon: 'size-3.5 text-dimmed' }
const changeEmailLinkClass =
'h-auto min-h-11 p-0 font-medium text-highlighted underline underline-offset-3 hover:underline lg:min-h-5'
const resetNotes = {
sent: {
@@ -266,7 +274,7 @@ onBeforeRouteLeave(async () => {
<template>
<DashboardShell compact-padding render-while-checking>
<div class="flex max-w-164 flex-col gap-4">
<div class="flex max-w-168 flex-col gap-4">
<div class="flex flex-col gap-0.5">
<h1 class="text-xl font-semibold text-highlighted">
{{ t('Profile') }}
@@ -281,7 +289,7 @@ onBeforeRouteLeave(async () => {
v-for="(card, cardIndex) in SKELETON_CARDS"
:key="cardIndex"
data-testid="profile-skeleton-card"
class="flex flex-col gap-3 border border-default bg-default p-4 lg:p-5"
class="flex flex-col gap-3 border border-default bg-default p-4"
>
<USkeleton class="h-3.5 rounded-none bg-accented" :class="card.titleWidth" />
<USkeleton
@@ -385,6 +393,16 @@ onBeforeRouteLeave(async () => {
/>
</UFormField>
<UFormField :label="t('Email')">
<template v-if="emailChange.showChangeLink.value" #hint>
<UButton
variant="link"
color="neutral"
:class="changeEmailLinkClass"
@click="emailChange.openForm"
>
{{ t('Change email') }}
</UButton>
</template>
<UInput
:model-value="email"
disabled
@@ -394,6 +412,31 @@ onBeforeRouteLeave(async () => {
/>
</UFormField>
</div>
<EmailChangeForm
v-if="emailChange.formOpen.value"
v-model:email="emailChange.newEmail.value"
v-model:password="emailChange.password.value"
:email-error="emailChange.emailError.value"
:password-error="emailChange.passwordError.value"
:notice="emailChange.notice.value"
:sending="emailChange.sending.value"
:send-disabled="emailChange.sendLimited.value || emailChange.sending.value"
:focus-request="emailChange.focusRequest.value"
@submit="emailChange.submit"
@cancel="emailChange.resetForm"
@blur-email="emailChange.checkEmailOnBlur"
/>
<PendingEmailNotice
v-else-if="emailChange.pendingEmail.value"
:pending-email="emailChange.pendingEmail.value"
:current-email="email"
:resend-state="emailChange.resendState.value"
:resend-countdown="emailChange.resendCountdown.value"
:note="emailChange.pendingNote.value"
:busy="emailChange.cancelling.value"
@resend="emailChange.resend"
@cancel-change="emailChange.cancelChange"
/>
</ProfileCard>
<YourRoleCard v-if="permissions.role" />
@@ -443,6 +486,10 @@ onBeforeRouteLeave(async () => {
:close="false"
/>
</ProfileCard>
<DataExportCard />
<DangerZoneCard />
</template>
</div>
</DashboardShell>