feat(mail) : AdminMailTab — form IMAP/SMTP/credentials + test connexion + indicateur hasPassword

This commit is contained in:
2026-05-20 00:57:10 +02:00
parent 28b673eec8
commit d36429f058

View File

@@ -0,0 +1,231 @@
<template>
<div>
<h2 class="text-lg font-bold text-neutral-900">{{ $t('mail.admin.title') }}</h2>
<form class="mt-6 max-w-lg space-y-6" @submit.prevent="handleSave">
<!-- Section IMAP (réception) -->
<fieldset class="space-y-4">
<legend class="text-sm font-bold text-neutral-700">{{ $t('mail.admin.imapSection') }}</legend>
<div>
<MalioInputText
v-model="form.imapHost"
:label="$t('mail.admin.host')"
input-class="w-full"
/>
<p class="mt-1 text-xs text-neutral-500">{{ $t('mail.admin.ovhDefaultsHelp') }}</p>
</div>
<div>
<label class="block text-sm font-semibold text-neutral-700">{{ $t('mail.admin.port') }}</label>
<input
v-model.number="form.imapPort"
type="number"
min="1"
max="65535"
class="mt-1 w-full rounded-md border border-neutral-300 px-3 py-2 text-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
/>
</div>
<div>
<label class="block text-sm font-semibold text-neutral-700">{{ $t('mail.admin.encryption') }}</label>
<select
v-model="form.imapEncryption"
class="mt-1 w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
>
<option value="ssl">SSL</option>
<option value="tls">TLS</option>
<option value="none">Aucun</option>
</select>
</div>
</fieldset>
<!-- Section SMTP (envoi) -->
<fieldset class="space-y-4">
<legend class="text-sm font-bold text-neutral-700">{{ $t('mail.admin.smtpSection') }}</legend>
<MalioInputText
v-model="form.smtpHost"
:label="$t('mail.admin.host')"
input-class="w-full"
/>
<div>
<label class="block text-sm font-semibold text-neutral-700">{{ $t('mail.admin.port') }}</label>
<input
v-model.number="form.smtpPort"
type="number"
min="1"
max="65535"
class="mt-1 w-full rounded-md border border-neutral-300 px-3 py-2 text-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
/>
</div>
<div>
<label class="block text-sm font-semibold text-neutral-700">{{ $t('mail.admin.encryption') }}</label>
<select
v-model="form.smtpEncryption"
class="mt-1 w-full rounded-md border border-neutral-300 bg-white px-3 py-2 text-sm focus:border-primary-500 focus:outline-none focus:ring-1 focus:ring-primary-500"
>
<option value="ssl">SSL</option>
<option value="tls">TLS</option>
<option value="none">Aucun</option>
</select>
</div>
</fieldset>
<!-- Credentials -->
<fieldset class="space-y-4">
<legend class="text-sm font-bold text-neutral-700">{{ $t('mail.admin.username') }}</legend>
<MalioInputText
v-model="form.username"
:label="$t('mail.admin.username')"
input-class="w-full"
/>
<div>
<MalioInputPassword
v-model="form.password"
:label="$t('mail.admin.password')"
input-class="w-full"
/>
<p v-if="hasPassword && !form.password" class="mt-1 text-xs text-green-600">
{{ $t('mail.admin.passwordSet') }}
</p>
</div>
<MalioInputText
v-model="form.sentFolderPath"
:label="$t('mail.admin.sentFolderPath')"
placeholder="Sent Messages"
input-class="w-full"
/>
</fieldset>
<label class="flex cursor-pointer items-center gap-2">
<input v-model="form.enabled" type="checkbox" class="rounded border-neutral-300" />
<span class="text-sm">{{ $t('mail.admin.enabled') }}</span>
</label>
<div class="flex gap-3">
<MalioButton
:label="$t('mail.admin.save')"
button-class="w-auto px-4"
:disabled="isSaving"
@click="handleSave"
/>
<MalioButton
variant="tertiary"
:label="$t('mail.admin.test')"
button-class="w-auto px-4"
:disabled="isTesting"
@click="handleTest"
/>
</div>
<div v-if="testResult !== null">
<p
class="text-sm font-medium"
:class="testResult ? 'text-green-600' : 'text-red-600'"
>
{{ testResult ? $t('mail.admin.testSuccess') : $t('mail.admin.testFailed') }}
</p>
<p v-if="testResult === false && testError" class="mt-1 text-xs text-neutral-500">
{{ testError }}
</p>
</div>
</form>
</div>
</template>
<script setup lang="ts">
import { useMailService } from '~/services/mail'
const { getConfiguration, updateConfiguration, testConfiguration } = useMailService()
const form = reactive({
protocol: 'imap',
imapHost: '',
imapPort: 993,
imapEncryption: 'ssl',
smtpHost: '',
smtpPort: 465,
smtpEncryption: 'ssl',
username: '',
password: '',
sentFolderPath: '',
enabled: false,
})
const hasPassword = ref<boolean>(false)
const isSaving = ref<boolean>(false)
const isTesting = ref<boolean>(false)
const testResult = ref<boolean | null>(null)
const testError = ref<string | null>(null)
async function loadSettings(): Promise<void> {
const config = await getConfiguration()
form.protocol = config.protocol ?? 'imap'
form.imapHost = config.imapHost ?? ''
form.imapPort = config.imapPort ?? 993
form.imapEncryption = config.imapEncryption ?? 'ssl'
form.smtpHost = config.smtpHost ?? ''
form.smtpPort = config.smtpPort ?? 465
form.smtpEncryption = config.smtpEncryption ?? 'ssl'
form.username = config.username ?? ''
form.sentFolderPath = config.sentFolderPath ?? ''
form.enabled = config.enabled
hasPassword.value = config.hasPassword
// password jamais pré-rempli
}
async function handleSave(): Promise<void> {
isSaving.value = true
testResult.value = null
testError.value = null
try {
const payload: Record<string, unknown> = {
protocol: form.protocol,
imapHost: form.imapHost.trim() || null,
imapPort: form.imapPort,
imapEncryption: form.imapEncryption,
smtpHost: form.smtpHost.trim() || null,
smtpPort: form.smtpPort,
smtpEncryption: form.smtpEncryption,
username: form.username.trim() || null,
sentFolderPath: form.sentFolderPath.trim() || null,
enabled: form.enabled,
}
if (form.password) {
payload.password = form.password
}
const result = await updateConfiguration(payload)
hasPassword.value = result.hasPassword
form.password = ''
} finally {
isSaving.value = false
}
}
async function handleTest(): Promise<void> {
isTesting.value = true
testResult.value = null
testError.value = null
try {
const result = await testConfiguration()
testResult.value = result.ok
if (!result.ok && result.error) {
testError.value = result.error
}
} catch {
testResult.value = false
} finally {
isTesting.value = false
}
}
onMounted(() => {
loadSettings()
})
</script>