337 lines
9.0 KiB
Vue
337 lines
9.0 KiB
Vue
<script setup lang="ts">
|
|
import { ref, shallowRef, computed, watch } from 'vue'
|
|
import { useServicesStore } from '@MailManager/stores/servicesStore'
|
|
import { useProvidersStore } from '@MailManager/stores/providersStore'
|
|
import type { ProviderObject, ServiceObject } from '@MailManager/models'
|
|
import ProviderAuxiliaryPanel from '@MailManager/components/steps/ProviderAuxiliaryPanel.vue'
|
|
import ProviderProtocolPanel from '@MailManager/components/steps/ProviderProtocolPanel.vue'
|
|
import ProviderAuthPanel from '@MailManager/components/steps/ProviderAuthPanel.vue'
|
|
import TestAndSavePanel from '@MailManager/components/steps/TestAndSavePanel.vue'
|
|
|
|
type EditTab = 'general' | 'auxiliary' | 'protocol' | 'auth'
|
|
|
|
const props = defineProps<{
|
|
modelValue: boolean
|
|
serviceProvider: string
|
|
serviceIdentifier: string | number
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
'update:modelValue': [value: boolean]
|
|
'saved': []
|
|
}>()
|
|
|
|
const servicesStore = useServicesStore()
|
|
const providersStore = useProvidersStore()
|
|
|
|
const dialogOpen = computed({
|
|
get: () => props.modelValue,
|
|
set: (val) => emit('update:modelValue', val)
|
|
})
|
|
|
|
const currentTab = ref<EditTab>('general')
|
|
const saving = ref(false)
|
|
const loading = ref(false)
|
|
const loadError = ref<string | null>(null)
|
|
|
|
const localProvider = shallowRef<ProviderObject | null>(null)
|
|
const localService = shallowRef<ServiceObject | null>(null)
|
|
|
|
// Validation states
|
|
const testAndSaveValid = ref(false)
|
|
|
|
function serviceRequiresConnectionTest(service: ServiceObject | null): boolean {
|
|
return !!(service?.location?.mutated() || service?.identity?.mutated())
|
|
}
|
|
|
|
const tabItems = [
|
|
{
|
|
title: 'General',
|
|
icon: 'mdi-view-dashboard-outline',
|
|
value: 'general' as const
|
|
},
|
|
{
|
|
title: 'Auxiliary Settings',
|
|
icon: 'mdi-tune-variant',
|
|
value: 'auxiliary' as const
|
|
},
|
|
{
|
|
title: 'Protocol',
|
|
icon: 'mdi-tune-vertical',
|
|
value: 'protocol' as const
|
|
},
|
|
{
|
|
title: 'Authentication',
|
|
icon: 'mdi-shield-key-outline',
|
|
value: 'auth' as const
|
|
}
|
|
]
|
|
|
|
const canSave = computed(() => {
|
|
return !serviceRequiresConnectionTest(localService.value) || testAndSaveValid.value
|
|
})
|
|
|
|
const showSaveButton = computed(() => currentTab.value === 'general')
|
|
const accountReady = computed(() => localProvider.value !== null && localService.value !== null)
|
|
|
|
// Load service data when the dialog is open and the target account is available.
|
|
watch(
|
|
() => [props.modelValue, props.serviceProvider, props.serviceIdentifier] as const,
|
|
async ([isOpen, serviceProvider, serviceIdentifier]) => {
|
|
if (!isOpen || !serviceProvider || !serviceIdentifier) {
|
|
return
|
|
}
|
|
|
|
await load()
|
|
},
|
|
{ immediate: true }
|
|
)
|
|
|
|
async function load() {
|
|
loading.value = true
|
|
loadError.value = null
|
|
localProvider.value = null
|
|
localService.value = null
|
|
|
|
if (!props.serviceProvider || !props.serviceIdentifier) {
|
|
console.error('[Mail Manager][Edit Account Dialog] - Cannot open dialog missing service or provider identifier')
|
|
loadError.value = 'missing service or provider identifier'
|
|
loading.value = false
|
|
return
|
|
}
|
|
|
|
try {
|
|
const [provider, service] = await Promise.all([
|
|
providersStore.provider(props.serviceProvider) ?? providersStore.fetch(props.serviceProvider),
|
|
servicesStore.service(props.serviceProvider, props.serviceIdentifier) ?? servicesStore.fetch(props.serviceProvider, props.serviceIdentifier)
|
|
])
|
|
|
|
localProvider.value = provider.clone()
|
|
localService.value = service.clone()
|
|
} catch (error) {
|
|
console.error('[Mail Manager][Edit Account Dialog] - Failed to load service:', error)
|
|
loadError.value = 'Failed to load service details'
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
function close() {
|
|
dialogOpen.value = false
|
|
// Reset state after animation
|
|
setTimeout(resetForm, 300)
|
|
}
|
|
|
|
function resetForm() {
|
|
currentTab.value = 'general'
|
|
localService.value = null
|
|
localProvider.value = null
|
|
loadError.value = null
|
|
}
|
|
|
|
function isTabDisabled(tab: EditTab) {
|
|
if (tab === 'auth') {
|
|
return !localService.value?.location
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
function handleUpdate(mutatedService: ServiceObject) {
|
|
localService.value = mutatedService
|
|
|
|
if (serviceRequiresConnectionTest(mutatedService)) {
|
|
testAndSaveValid.value = false
|
|
}
|
|
}
|
|
|
|
async function testConnection() {
|
|
try {
|
|
if (!localService.value) {
|
|
return {
|
|
success: false,
|
|
message: 'Missing service configuration'
|
|
}
|
|
}
|
|
|
|
let testResult = null
|
|
|
|
if (serviceRequiresConnectionTest(localService.value)) {
|
|
testResult = await servicesStore.test(
|
|
localService.value.provider,
|
|
null,
|
|
localService.value.location,
|
|
localService.value.identity
|
|
)
|
|
} else {
|
|
testResult = await servicesStore.test(
|
|
localService.value.provider,
|
|
localService.value.identifier
|
|
)
|
|
}
|
|
|
|
testAndSaveValid.value = testResult.success
|
|
return testResult
|
|
} catch (error) {
|
|
console.error('[Mail Manager][Edit Account Dialog] - Test connection failed:', error)
|
|
return {
|
|
success: false,
|
|
message: 'Test failed due to an unexpected error'
|
|
}
|
|
}
|
|
}
|
|
|
|
async function saveAccount() {
|
|
// No changes made, just close the dialog
|
|
if (!localService.value.mutated() && !localService.value.location?.mutated() && !localService.value.identity?.mutated()) {
|
|
close()
|
|
return
|
|
}
|
|
|
|
saving.value = true
|
|
|
|
try {
|
|
await servicesStore.update(
|
|
localService.value.provider,
|
|
localService.value.identifier as string | number,
|
|
true, // delta update
|
|
localService.value
|
|
)
|
|
|
|
emit('saved')
|
|
close()
|
|
} catch (error) {
|
|
console.error('[Mail Manager][Edit Account Dialog] - Failed to save service:', error)
|
|
// TODO: Show error message to user
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<v-dialog
|
|
v-model="dialogOpen"
|
|
max-width="900"
|
|
persistent
|
|
scrollable
|
|
>
|
|
<v-card>
|
|
<v-card-title class="d-flex justify-space-between align-center pa-6">
|
|
<span class="text-h5">Edit Mail Account</span>
|
|
<v-btn
|
|
icon="mdi-close"
|
|
variant="text"
|
|
@click="close"
|
|
/>
|
|
</v-card-title>
|
|
|
|
<v-divider />
|
|
|
|
<v-card-text v-if="loading || (!loadError && !accountReady)" class="text-center py-8">
|
|
<v-progress-circular indeterminate color="primary" />
|
|
<p class="text-caption text-medium-emphasis mt-2">Loading account...</p>
|
|
</v-card-text>
|
|
|
|
<v-card-text v-else-if="loadError" class="pa-6">
|
|
<v-alert type="error" variant="tonal">
|
|
{{ loadError }}
|
|
</v-alert>
|
|
</v-card-text>
|
|
|
|
<v-card-text v-else class="pa-0">
|
|
<v-tabs
|
|
v-model="currentTab"
|
|
bg-color="transparent"
|
|
grow
|
|
class="px-4 pt-2"
|
|
>
|
|
<v-tab
|
|
v-for="item in tabItems"
|
|
:key="item.value"
|
|
:value="item.value"
|
|
:disabled="isTabDisabled(item.value)"
|
|
>
|
|
<v-icon start>{{ item.icon }}</v-icon>
|
|
{{ item.title }}
|
|
</v-tab>
|
|
</v-tabs>
|
|
|
|
<v-divider />
|
|
|
|
<v-window v-model="currentTab">
|
|
<v-window-item value="general">
|
|
<v-card flat class="pa-6">
|
|
<TestAndSavePanel
|
|
v-if="localProvider && localService"
|
|
:provider="localProvider!"
|
|
:service="localService!"
|
|
:on-test="testConnection"
|
|
@update:service="handleUpdate"
|
|
/>
|
|
</v-card>
|
|
</v-window-item>
|
|
|
|
<v-window-item value="auxiliary">
|
|
<v-card flat class="pa-6">
|
|
<ProviderAuxiliaryPanel
|
|
v-if="localProvider && localService"
|
|
:provider="localProvider!"
|
|
:service="localService!"
|
|
@update:service="handleUpdate"
|
|
/>
|
|
</v-card>
|
|
</v-window-item>
|
|
|
|
<v-window-item value="protocol">
|
|
<v-card flat class="pa-6">
|
|
<ProviderProtocolPanel
|
|
v-if="localProvider && localService"
|
|
:provider="localProvider!"
|
|
:service="localService!"
|
|
@update:service="handleUpdate"
|
|
/>
|
|
</v-card>
|
|
</v-window-item>
|
|
|
|
<v-window-item value="auth">
|
|
<v-card flat class="pa-6">
|
|
<ProviderAuthPanel
|
|
v-if="localProvider && localService"
|
|
:provider="localProvider!"
|
|
:service="localService!"
|
|
@update:service="handleUpdate"
|
|
/>
|
|
</v-card>
|
|
</v-window-item>
|
|
</v-window>
|
|
</v-card-text>
|
|
|
|
<v-divider />
|
|
|
|
<v-card-actions class="pa-6">
|
|
<v-spacer />
|
|
|
|
<v-btn
|
|
variant="text"
|
|
@click="close"
|
|
>
|
|
Cancel
|
|
</v-btn>
|
|
|
|
<!-- Save Button -->
|
|
<v-btn
|
|
v-if="showSaveButton"
|
|
color="primary"
|
|
:loading="saving"
|
|
:disabled="!canSave"
|
|
@click="saveAccount"
|
|
>
|
|
<v-icon start>mdi-content-save</v-icon>
|
|
Save Changes
|
|
</v-btn>
|
|
</v-card-actions>
|
|
</v-card>
|
|
</v-dialog>
|
|
</template>
|