Files
mail_manager/src/components/steps/DiscoveryStatusPanel.vue
T
Sebastian 3362afb7ec
JS Unit Tests / test (pull_request) Successful in 33s
Build Test / test (pull_request) Successful in 36s
PHP Unit Tests / test (pull_request) Successful in 1m12s
chore: bunch of improvements
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
2026-04-23 22:00:50 -04:00

241 lines
6.4 KiB
Vue

<script setup lang="ts">
import { computed } from 'vue'
import type { ProviderDiscoveryStatus } from '@MailManager/types'
const props = defineProps<{
address: string
status: Record<string, ProviderDiscoveryStatus>
}>()
const emit = defineEmits<{
'select': [providerId: string]
'advanced': [providerId: string]
'manual': []
'back': []
}>()
const sortedStatus = computed(() => {
const statusArray = Object.values(props.status)
const order: Record<string, number> = { success: 0, discovering: 1, pending: 2, failed: 3 }
return statusArray.sort((a, b) => order[a.status] - order[b.status])
})
const progressPercent = computed(() => {
const total = Object.keys(props.status).length
if (total === 0) return 0
const completed = Object.values(props.status).filter(
s => s.status === 'success' || s.status === 'failed'
).length
return (completed / total) * 100
})
const isDiscovering = computed(() => {
return Object.values(props.status).some(
s => s.status === 'discovering' || s.status === 'pending'
)
})
const successCount = computed(() => {
return Object.values(props.status).filter(s => s.status === 'success').length
})
function getStatusColor(status: string): string {
const colors: Record<string, string> = {
success: 'success',
failed: 'error',
discovering: 'primary',
pending: 'grey'
}
return colors[status] || 'grey'
}
function getProviderLabel(providerId: string): string {
const labels: Record<string, string> = {
jmap: 'JMAP',
smtp: 'SMTP/IMAP',
imap: 'IMAP',
exchange: 'Microsoft Exchange'
}
return labels[providerId] || providerId.toUpperCase()
}
</script>
<template>
<div class="discovery-status-step">
<h3 class="text-h6 mb-2">Discovering Mail Servers</h3>
<p class="text-body-2 text-medium-emphasis mb-6">
Testing {{ address }} across multiple providers...
</p>
<!-- Overall Progress -->
<v-progress-linear
:model-value="progressPercent"
color="primary"
height="8"
rounded
class="mb-6"
/>
<!-- Provider Status List -->
<v-list class="provider-status-list">
<v-list-item
v-for="status in sortedStatus"
:key="status.provider"
class="provider-status-item mb-2"
:class="`status-${status.status}`"
>
<template #prepend>
<v-avatar :color="getStatusColor(status.status)" size="40">
<v-icon v-if="status.status === 'success'" color="white">
mdi-check
</v-icon>
<v-icon v-else-if="status.status === 'failed'" color="white">
mdi-close
</v-icon>
<v-progress-circular
v-else-if="status.status === 'discovering'"
indeterminate
size="24"
width="3"
color="white"
/>
<v-icon v-else color="white">
mdi-clock-outline
</v-icon>
</v-avatar>
</template>
<v-list-item-title class="d-flex align-center gap-2">
<span class="font-weight-medium">{{ getProviderLabel(status.provider) }}</span>
<v-chip
v-if="status.status === 'success'"
size="x-small"
color="success"
variant="flat"
>
Found
</v-chip>
<v-chip
v-else-if="status.status === 'failed'"
size="x-small"
color="error"
variant="flat"
>
{{ status.error || 'Failed' }}
</v-chip>
<v-chip
v-else-if="status.status === 'discovering'"
size="x-small"
color="primary"
variant="flat"
>
Testing...
</v-chip>
</v-list-item-title>
<v-list-item-subtitle v-if="status.status === 'success' && status.metadata">
<div class="text-caption">
<v-icon size="14" class="mr-1">mdi-server</v-icon>
{{ status.metadata.host }}{{ status.metadata.port ? ':' + status.metadata.port : '' }}
<span v-if="status.metadata.protocol" class="ml-2">
<v-icon size="14" class="mr-1">mdi-shield-lock</v-icon>
{{ status.metadata.protocol.toUpperCase() }}
</span>
</div>
</v-list-item-subtitle>
<template #append>
<div v-if="status.status === 'success'" class="d-flex gap-2">
<v-btn
size="small"
variant="tonal"
color="primary"
prepend-icon="mdi-check"
@click="$emit('select', status.provider)"
>
Select
</v-btn>
<v-tooltip text="Advanced configuration">
<template #activator="{ props }">
<v-btn
v-bind="props"
size="small"
variant="outlined"
icon="mdi-tune"
@click="$emit('advanced', status.provider)"
/>
</template>
</v-tooltip>
</div>
</template>
</v-list-item>
</v-list>
<!-- No Results Message -->
<v-alert
v-if="!isDiscovering && successCount === 0"
type="warning"
variant="tonal"
class="mt-6"
>
<template #prepend>
<v-icon>mdi-alert</v-icon>
</template>
<div>
<div class="font-weight-medium mb-1">No configurations found</div>
<div class="text-caption">
We couldn't automatically discover server settings for {{ address }}.
You can try manual configuration instead.
</div>
</div>
</v-alert>
<div
v-if="!isDiscovering && successCount === 0"
class="mt-6"
>
<v-btn
size="large"
variant="text"
block
class="manual-action-btn"
prepend-icon="mdi-tune"
@click="$emit('manual')"
>
Manual Configuration
</v-btn>
</div>
</div>
</template>
<style scoped>
.provider-status-item {
border: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
border-radius: 8px;
transition: all 0.2s;
}
.provider-status-item.status-success {
background-color: rgba(var(--v-theme-success), 0.05);
}
.provider-status-item.status-discovering {
background-color: rgba(var(--v-theme-primary), 0.05);
}
.gap-2 {
gap: 8px;
}
.gap-3 {
gap: 12px;
}
.manual-action-btn {
background-color: rgba(var(--v-theme-on-surface), 0.06);
}
</style>