chore: bunch of improvements
Signed-off-by: Sebastian Krupinski <krupinski01@gmail.com>
This commit is contained in:
@@ -1,15 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue'
|
||||
import { useIntegrationStore } from '@KTXC/stores/integrationStore'
|
||||
import { useServicesStore } from '@MailManager/stores/servicesStore'
|
||||
import { useProvidersStore } from '@MailManager/stores/providersStore'
|
||||
import type { ProviderDiscoveryStatus, ServiceLocation, ServiceIdentity } from '@MailManager/types'
|
||||
import type { ServiceObject } from '@MailManager/models/service'
|
||||
import DiscoveryStatusStep from '@MailManager/components/steps/DiscoveryStatusStep.vue'
|
||||
import ProviderSelectionStep from '@MailManager/components/steps/ProviderSelectionStep.vue'
|
||||
import ProviderConfigStep from '@MailManager/components/steps/ProviderConfigStep.vue'
|
||||
import ProviderAuthStep from '@MailManager/components/steps/ProviderAuthStep.vue'
|
||||
import TestAndSaveStep from '@MailManager/components/steps/TestAndSaveStep.vue'
|
||||
import DiscoveryEntryStep from '@MailManager/components/steps/DiscoveryEntryStep.vue'
|
||||
import { ServiceObject, type ProviderObject } from '@MailManager/models'
|
||||
import type { ProviderDiscoveryStatus, ServiceInterface, ServiceLocation } from '@MailManager/types'
|
||||
import DiscoveryEntryPanel from '@MailManager/components/steps/DiscoveryEntryPanel.vue'
|
||||
import DiscoveryStatusPanel from '@MailManager/components/steps/DiscoveryStatusPanel.vue'
|
||||
import ProviderSelectionPanel from '@MailManager/components/steps/ProviderSelectionPanel.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'
|
||||
|
||||
// ==================== Step Constants ====================
|
||||
// Discovery flow: Entry → Discovery → Auth → Test
|
||||
@@ -38,6 +39,7 @@ const emit = defineEmits<{
|
||||
'saved': []
|
||||
}>()
|
||||
|
||||
const integrationStore = useIntegrationStore()
|
||||
const servicesStore = useServicesStore()
|
||||
const providersStore = useProvidersStore()
|
||||
|
||||
@@ -56,19 +58,10 @@ const discoverSecret = ref<string | null>(null)
|
||||
const discoverHostname = ref<string | null>(null)
|
||||
|
||||
// Step 2: Discovery Status / Provider Selection
|
||||
const selectedProviderId = ref<string | undefined>(undefined)
|
||||
const selectedProviderLabel = ref<string>('')
|
||||
|
||||
// Step 3: Config (manual only) OR Auth (both paths)
|
||||
const configuredLocation = ref<ServiceLocation | null>(null)
|
||||
|
||||
// Step 4: Auth (both paths)
|
||||
const configuredIdentity = ref<ServiceIdentity | null>(null)
|
||||
const authValid = ref(false)
|
||||
const selectedProvider = ref<ProviderObject | null>(null)
|
||||
const selectedService = ref<ServiceObject | null>(null)
|
||||
|
||||
// Step 5: Test & Save
|
||||
const accountLabel = ref<string>('')
|
||||
const accountEnabled = ref(true)
|
||||
const testAndSaveValid = ref(false)
|
||||
|
||||
// Local discovery state (not stored in global store)
|
||||
@@ -137,19 +130,61 @@ const showSaveButton = computed(() => {
|
||||
const canProceedToNext = computed(() => {
|
||||
if (isManualMode.value) {
|
||||
if (currentStep.value === MANUAL_STEPS.CONFIG) {
|
||||
return !!configuredLocation.value
|
||||
return !!selectedService.value?.location
|
||||
}
|
||||
if (currentStep.value === MANUAL_STEPS.AUTH) {
|
||||
return authValid.value
|
||||
return !!selectedService.value?.identity
|
||||
}
|
||||
} else {
|
||||
if (currentStep.value === DISCOVERY_STEPS.AUTH) {
|
||||
return authValid.value
|
||||
return !!selectedService.value?.identity
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
|
||||
function createServiceObject(
|
||||
providerId: string,
|
||||
data: Partial<ServiceInterface> = {}
|
||||
): ServiceObject {
|
||||
const model: ServiceInterface = {
|
||||
'@type': 'mail:service',
|
||||
version: 1,
|
||||
provider: providerId,
|
||||
identifier: null,
|
||||
label: data.label ?? null,
|
||||
enabled: data.enabled ?? true,
|
||||
primaryAddress: data.primaryAddress ?? (discoverAddress.value || null),
|
||||
secondaryAddresses: data.secondaryAddresses ?? null,
|
||||
location: data.location ?? null,
|
||||
identity: data.identity ?? null,
|
||||
capabilities: data.capabilities ?? {},
|
||||
auxiliary: data.auxiliary ?? {}
|
||||
}
|
||||
|
||||
const factoryItem = integrationStore.getItemById('mail_service_factory', providerId) as any
|
||||
const factory = factoryItem?.factory
|
||||
return factory ? factory(model) : new ServiceObject().fromJson(model)
|
||||
}
|
||||
|
||||
function setSelectedProviderAndService(providerId: string, service: ServiceObject) {
|
||||
selectedProvider.value = providersStore.provider(providerId)
|
||||
selectedService.value = service
|
||||
testAndSaveValid.value = false
|
||||
}
|
||||
|
||||
function handleServiceUpdate(service: ServiceObject) {
|
||||
selectedService.value = service
|
||||
}
|
||||
|
||||
function handleServiceTested(success: boolean) {
|
||||
testAndSaveValid.value = success
|
||||
}
|
||||
|
||||
watch(selectedService, () => {
|
||||
testAndSaveValid.value = false
|
||||
}, { deep: true })
|
||||
|
||||
// Navigation methods
|
||||
function handlePreviousStep() {
|
||||
if (currentStep.value > 1) {
|
||||
@@ -259,12 +294,18 @@ function extractLocationMetadata(location: ServiceLocation) {
|
||||
|
||||
async function handleProviderSelect(identifier: string) {
|
||||
// User clicked "Select" on discovered provider - skip config, go to auth
|
||||
const service = discoveredServices.value.find(s => s.provider === identifier)
|
||||
if (!service || !service.location) return
|
||||
|
||||
selectedProviderId.value = identifier
|
||||
selectedProviderLabel.value = providersStore.provider(identifier)?.label || identifier
|
||||
configuredLocation.value = service.location
|
||||
const discovered = discoveredServices.value.find(s => s.provider === identifier)
|
||||
if (!discovered || !discovered.location) return
|
||||
|
||||
const discoveredJson = discovered.toJson()
|
||||
const service = createServiceObject(identifier, {
|
||||
...discoveredJson,
|
||||
label: discoveredJson.label || discoverAddress.value,
|
||||
enabled: discoveredJson.enabled ?? true,
|
||||
primaryAddress: discoveredJson.primaryAddress || discoverAddress.value,
|
||||
location: discoveredJson.location
|
||||
})
|
||||
setSelectedProviderAndService(identifier, service)
|
||||
|
||||
// Discovery path: Entry → Discovery → Auth → Test
|
||||
currentStep.value = DISCOVERY_STEPS.AUTH // Go to auth step
|
||||
@@ -272,11 +313,17 @@ async function handleProviderSelect(identifier: string) {
|
||||
|
||||
function handleProviderAdvanced(identifier: string) {
|
||||
// User clicked "Advanced" - show manual config with pre-filled values
|
||||
selectedProviderId.value = identifier
|
||||
selectedProviderLabel.value = providersStore.provider(identifier)?.label || identifier
|
||||
const service = discoveredServices.value.find(s => s.provider === identifier)
|
||||
|
||||
configuredLocation.value = service?.location || null
|
||||
const discovered = discoveredServices.value.find(s => s.provider === identifier)
|
||||
const discoveredJson = discovered?.toJson()
|
||||
const service = createServiceObject(identifier, {
|
||||
...discoveredJson,
|
||||
label: discoveredJson?.label || discoverAddress.value,
|
||||
enabled: discoveredJson?.enabled ?? true,
|
||||
primaryAddress: discoveredJson?.primaryAddress || discoverAddress.value,
|
||||
location: discoveredJson?.location ?? null
|
||||
})
|
||||
|
||||
setSelectedProviderAndService(identifier, service)
|
||||
isManualMode.value = true
|
||||
|
||||
// Manual path: Entry → Discovery → Config → Auth → Test
|
||||
@@ -293,8 +340,15 @@ function handleManualMode() {
|
||||
|
||||
function handleProviderManualSelect(identifier: string) {
|
||||
// User selected a provider in manual mode
|
||||
selectedProviderId.value = identifier
|
||||
selectedProviderLabel.value = providersStore.provider(identifier)?.label || identifier
|
||||
const service = createServiceObject(identifier, {
|
||||
label: discoverAddress.value,
|
||||
enabled: true,
|
||||
primaryAddress: discoverAddress.value,
|
||||
location: null,
|
||||
identity: null
|
||||
})
|
||||
|
||||
setSelectedProviderAndService(identifier, service)
|
||||
currentStep.value = MANUAL_STEPS.CONFIG // Go to manual config
|
||||
}
|
||||
|
||||
@@ -303,10 +357,21 @@ function goBackToIdentity() {
|
||||
isManualMode.value = false
|
||||
discoveredServices.value = []
|
||||
discoveryStatus.value = {}
|
||||
selectedProvider.value = null
|
||||
selectedService.value = null
|
||||
testAndSaveValid.value = false
|
||||
}
|
||||
|
||||
async function testConnection() {
|
||||
if (!selectedProviderId.value || !configuredLocation.value || !configuredIdentity.value) {
|
||||
if (!selectedProvider.value || !selectedService.value) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Missing configuration'
|
||||
}
|
||||
}
|
||||
|
||||
const serviceData = selectedService.value.toJson()
|
||||
if (!serviceData.location || !serviceData.identity) {
|
||||
return {
|
||||
success: false,
|
||||
message: 'Missing configuration'
|
||||
@@ -314,31 +379,35 @@ async function testConnection() {
|
||||
}
|
||||
|
||||
const testResult = await servicesStore.test(
|
||||
selectedProviderId.value,
|
||||
selectedProvider.value.identifier,
|
||||
null,
|
||||
configuredLocation.value,
|
||||
configuredIdentity.value
|
||||
serviceData.location,
|
||||
serviceData.identity
|
||||
)
|
||||
|
||||
return testResult
|
||||
}
|
||||
|
||||
async function saveAccount() {
|
||||
if (!selectedProviderId.value || !configuredLocation.value || !configuredIdentity.value) return
|
||||
if (!selectedProvider.value || !selectedService.value) return
|
||||
|
||||
const serviceData = selectedService.value.toJson()
|
||||
if (!serviceData.location || !serviceData.identity) return
|
||||
|
||||
saving.value = true
|
||||
|
||||
try {
|
||||
const accountData = {
|
||||
label: accountLabel.value || discoverAddress.value,
|
||||
email: discoverAddress.value,
|
||||
enabled: accountEnabled.value,
|
||||
location: configuredLocation.value,
|
||||
identity: configuredIdentity.value
|
||||
label: serviceData.label || discoverAddress.value,
|
||||
primaryAddress: serviceData.primaryAddress || discoverAddress.value,
|
||||
enabled: serviceData.enabled,
|
||||
location: serviceData.location,
|
||||
identity: serviceData.identity,
|
||||
auxiliary: serviceData.auxiliary
|
||||
}
|
||||
|
||||
await servicesStore.create(
|
||||
selectedProviderId.value,
|
||||
selectedProvider.value.identifier,
|
||||
accountData
|
||||
)
|
||||
|
||||
@@ -364,13 +433,8 @@ function resetForm() {
|
||||
discoverAddress.value = ''
|
||||
discoverSecret.value = null
|
||||
discoverHostname.value = null
|
||||
selectedProviderId.value = undefined
|
||||
selectedProviderLabel.value = ''
|
||||
configuredLocation.value = null
|
||||
configuredIdentity.value = null
|
||||
authValid.value = false
|
||||
accountLabel.value = ''
|
||||
accountEnabled.value = true
|
||||
selectedProvider.value = null
|
||||
selectedService.value = null
|
||||
testAndSaveValid.value = false
|
||||
discoveredServices.value = []
|
||||
discoveryStatus.value = {}
|
||||
@@ -407,7 +471,7 @@ function resetForm() {
|
||||
<!-- Step 1: Discovery Entry -->
|
||||
<template #item.1>
|
||||
<v-card flat class="pa-6">
|
||||
<DiscoveryEntryStep
|
||||
<DiscoveryEntryPanel
|
||||
v-model:address="discoverAddress"
|
||||
v-model:secret="discoverSecret"
|
||||
v-model:hostname="discoverHostname"
|
||||
@@ -421,7 +485,7 @@ function resetForm() {
|
||||
<template #item.2>
|
||||
<v-card flat class="pa-6">
|
||||
<!-- Discovery path -->
|
||||
<DiscoveryStatusStep
|
||||
<DiscoveryStatusPanel
|
||||
v-if="!isManualMode"
|
||||
:address="discoverAddress"
|
||||
:status="discoveryStatus"
|
||||
@@ -432,7 +496,7 @@ function resetForm() {
|
||||
/>
|
||||
|
||||
<!-- Manual path - provider picker -->
|
||||
<ProviderSelectionStep
|
||||
<ProviderSelectionPanel
|
||||
v-else
|
||||
@select="handleProviderManualSelect"
|
||||
@back="goBackToIdentity"
|
||||
@@ -444,24 +508,18 @@ function resetForm() {
|
||||
<template #item.3>
|
||||
<v-card flat class="pa-6">
|
||||
<!-- Manual path: Protocol Configuration -->
|
||||
<ProviderConfigStep
|
||||
v-if="isManualMode && selectedProviderId"
|
||||
:provider-id="selectedProviderId"
|
||||
:discovered-location="configuredLocation || undefined"
|
||||
v-model="configuredLocation"
|
||||
@valid="() => { /* Can proceed to next step */ }"
|
||||
<ProviderProtocolPanel
|
||||
v-if="isManualMode && selectedProvider && selectedService"
|
||||
:provider="selectedProvider"
|
||||
:service="selectedService"
|
||||
@update:service="handleServiceUpdate"
|
||||
/>
|
||||
|
||||
<ProviderAuthStep
|
||||
v-else-if="!isManualMode && selectedProviderId"
|
||||
:provider-id="selectedProviderId"
|
||||
:provider-label="selectedProviderLabel"
|
||||
:email-address="discoverAddress"
|
||||
:discovered-location="configuredLocation || undefined"
|
||||
:prefilled-identity="discoverAddress"
|
||||
:prefilled-secret="discoverSecret || undefined"
|
||||
v-model="configuredIdentity"
|
||||
@valid="(valid) => authValid = valid"
|
||||
<ProviderAuthPanel
|
||||
v-else-if="!isManualMode && selectedProvider && selectedService"
|
||||
:provider="selectedProvider"
|
||||
:service="selectedService"
|
||||
@update:service="handleServiceUpdate"
|
||||
/>
|
||||
</v-card>
|
||||
</template>
|
||||
@@ -469,31 +527,21 @@ function resetForm() {
|
||||
<!-- Step 4: Auth (manual) OR Test (discovery) -->
|
||||
<template #item.4>
|
||||
<v-card flat class="pa-6">
|
||||
<ProviderAuthStep
|
||||
v-if="isManualMode && selectedProviderId"
|
||||
:provider-id="selectedProviderId"
|
||||
:provider-label="selectedProviderLabel"
|
||||
:email-address="discoverAddress"
|
||||
:discovered-location="configuredLocation || undefined"
|
||||
:prefilled-identity="discoverAddress"
|
||||
:prefilled-secret="discoverSecret || undefined"
|
||||
v-model="configuredIdentity"
|
||||
@valid="(valid) => authValid = valid"
|
||||
<ProviderAuthPanel
|
||||
v-if="isManualMode && selectedProvider && selectedService"
|
||||
:provider="selectedProvider"
|
||||
:service="selectedService"
|
||||
@update:service="handleServiceUpdate"
|
||||
/>
|
||||
|
||||
<!-- Discovery path: Test & Save -->
|
||||
<TestAndSaveStep
|
||||
v-else-if="!isManualMode && selectedProviderId"
|
||||
:provider-id="selectedProviderId"
|
||||
:provider-label="selectedProviderLabel"
|
||||
:email-address="discoverAddress"
|
||||
:location="configuredLocation"
|
||||
:identity="configuredIdentity"
|
||||
:prefilled-label="discoverAddress"
|
||||
<TestAndSavePanel
|
||||
v-else-if="!isManualMode && selectedProvider && selectedService"
|
||||
:provider="selectedProvider"
|
||||
:service="selectedService"
|
||||
:on-test="testConnection"
|
||||
@update:label="(val) => accountLabel = val"
|
||||
@update:enabled="(val) => accountEnabled = val"
|
||||
@valid="(valid) => testAndSaveValid = valid"
|
||||
@update:service="handleServiceUpdate"
|
||||
@tested="handleServiceTested"
|
||||
/>
|
||||
</v-card>
|
||||
</template>
|
||||
@@ -501,18 +549,13 @@ function resetForm() {
|
||||
<!-- Step 5: Test & Save (manual only) -->
|
||||
<template #item.5>
|
||||
<v-card flat class="pa-6">
|
||||
<TestAndSaveStep
|
||||
v-if="selectedProviderId"
|
||||
:provider-id="selectedProviderId"
|
||||
:provider-label="selectedProviderLabel"
|
||||
:email-address="discoverAddress"
|
||||
:location="configuredLocation"
|
||||
:identity="configuredIdentity"
|
||||
:prefilled-label="discoverAddress"
|
||||
<TestAndSavePanel
|
||||
v-if="selectedProvider && selectedService"
|
||||
:provider="selectedProvider"
|
||||
:service="selectedService"
|
||||
:on-test="testConnection"
|
||||
@update:label="(val) => accountLabel = val"
|
||||
@update:enabled="(val) => accountEnabled = val"
|
||||
@valid="(valid) => testAndSaveValid = valid"
|
||||
@update:service="handleServiceUpdate"
|
||||
@tested="handleServiceTested"
|
||||
/>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user