KI-AGENT: Telefonie Nebenstellen in Einstellungen integrieren

This commit is contained in:
2026-05-22 15:55:06 +02:00
parent da9cad1513
commit 520052e71a
12 changed files with 922 additions and 780 deletions

View File

@@ -562,17 +562,6 @@ const savePlanningBoardConfig = async () => {
setupPage()
onMounted(() => {
loadMcpTokens()
loadTelephonyExtensionOptions()
loadTelephonyExtensions()
loadTelephonyTrunk()
})
watch(() => telephonyTrunkForm.provider, async (provider) => {
const defaults = telephonyProviderDefaults[provider] || telephonyProviderDefaults.easybell
if (!telephonyTrunkForm.registrar || ["tel.t-online.de", "voip.easybell.de"].includes(telephonyTrunkForm.registrar)) {
telephonyTrunkForm.registrar = defaults.registrar
}
await loadTelephonyTrunk()
})
</script>
@@ -727,248 +716,14 @@ watch(() => telephonyTrunkForm.provider, async (provider) => {
</div>
<div v-else-if="item.label === 'Integrationen'">
<UCard class="mt-5">
<div class="mb-8 space-y-6">
<div class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
<div>
<h3 class="text-base font-semibold text-highlighted">Telefonie-Trunk</h3>
<p class="text-sm text-muted">Konfiguriere den SIP-Trunk für externe Anrufe über Asterisk.</p>
</div>
<UBadge :color="telephonyTrunkForm.enabled ? 'success' : 'neutral'" variant="soft">
{{ telephonyTrunkForm.enabled ? "Aktiv" : "Inaktiv" }}
</UBadge>
</div>
<UAlert
:title="activeTelephonyProvider.title"
color="neutral"
variant="outline"
>
<template #description>
<p class="text-sm">
{{ activeTelephonyProvider.description }}
</p>
</template>
</UAlert>
<div class="grid gap-4 md:grid-cols-2">
<UFormField label="Provider">
<USelectMenu
v-model="telephonyTrunkForm.provider"
:items="telephonyProviderOptions"
label-key="label"
value-key="value"
/>
</UFormField>
<UFormField label="Trunk aktivieren">
<USwitch v-model="telephonyTrunkForm.enabled" />
</UFormField>
<UFormField label="Registrar">
<UInput v-model="telephonyTrunkForm.registrar" :placeholder="activeTelephonyProvider.registrar" />
</UFormField>
<UFormField label="SIP-ID / Rufnummer">
<UInput v-model="telephonyTrunkForm.sipUser" placeholder="SIP-Benutzername" />
</UFormField>
<UFormField label="Auth-User">
<UInput v-model="telephonyTrunkForm.authUser" placeholder="Optional" />
</UFormField>
<UFormField :label="telephonyTrunkForm.passwordConfigured ? 'Kennwort ersetzen' : 'Kennwort'">
<UInput
v-model="telephonyTrunkForm.password"
type="password"
:placeholder="telephonyTrunkForm.passwordConfigured ? 'Bereits gespeichert' : 'SIP-Kennwort'"
/>
</UFormField>
<UFormField label="Gespeichertes Kennwort löschen">
<USwitch
v-model="telephonyTrunkForm.clearPassword"
:disabled="!telephonyTrunkForm.passwordConfigured"
/>
</UFormField>
<UFormField label="Absendernummer">
<UInput v-model="telephonyTrunkForm.callerId" placeholder="Optional, z. B. 49301234567" />
</UFormField>
<UFormField label="Eingehende Nebenstelle">
<UInput v-model="telephonyTrunkForm.inboundExtension" placeholder="1001" />
</UFormField>
<UFormField label="Standardroute">
<USelectMenu
v-model="telephonyTrunkForm.defaultRouteExtensionId"
:items="telephonyRouteOptions"
label-key="label"
value-key="value"
placeholder="Nebenstelle auswählen"
/>
</UFormField>
<UFormField label="Ausgehender Prefix">
<UInput v-model="telephonyTrunkForm.outboundPrefix" placeholder="0" />
</UFormField>
<UFormField label="Öffentliche Signaling-Adresse">
<UInput v-model="telephonyTrunkForm.externalSignalingAddress" placeholder="Öffentliche IP oder DNS-Name" />
</UFormField>
<UFormField label="Öffentliche Medien-Adresse">
<UInput v-model="telephonyTrunkForm.externalMediaAddress" placeholder="Leer = Signaling-Adresse" />
</UFormField>
<UFormField label="Lokale Netze">
<UInput v-model="telephonyTrunkForm.localNetworks" placeholder="172.16.0.0/12,192.168.0.0/16,10.0.0.0/8" />
</UFormField>
</div>
<div class="flex flex-wrap gap-2">
<UButton
icon="i-heroicons-arrow-path"
variant="outline"
:loading="telephonyTrunkLoading"
@click="loadTelephonyTrunk"
>
Laden
</UButton>
<UButton
icon="i-heroicons-check"
:loading="telephonyTrunkSaving"
@click="saveTelephonyTrunk"
>
Telefonie-Trunk speichern
</UButton>
<UButton
icon="i-heroicons-arrow-path-rounded-square"
color="primary"
variant="soft"
:loading="telephonyTrunkApplying"
@click="applyTelephonyTrunk"
>
In Asterisk anwenden
</UButton>
</div>
<USeparator />
<div class="space-y-4">
<div class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
<div>
<h4 class="text-sm font-semibold text-highlighted">Nebenstellen</h4>
<p class="text-sm text-muted">Ordne Benutzer, Teams und Niederlassungen routbaren Nebenstellen zu.</p>
</div>
<UButton
icon="i-heroicons-arrow-path"
variant="outline"
:loading="telephonyExtensionsLoading"
@click="loadTelephonyExtensions"
>
Aktualisieren
</UButton>
</div>
<div class="grid gap-4 md:grid-cols-3">
<UFormField label="Zieltyp">
<USelectMenu
v-model="telephonyExtensionForm.targetType"
:items="telephonyExtensionTargetTypes"
label-key="label"
value-key="value"
/>
</UFormField>
<UFormField v-if="telephonyExtensionForm.targetType === 'user'" label="Benutzer">
<USelectMenu
v-model="telephonyExtensionForm.targetUserId"
:items="activeTelephonyTargetOptions"
label-key="label"
value-key="id"
placeholder="Benutzer auswählen"
/>
</UFormField>
<UFormField v-else-if="telephonyExtensionForm.targetType === 'team'" label="Team">
<USelectMenu
v-model="telephonyExtensionForm.targetTeamId"
:items="activeTelephonyTargetOptions"
label-key="label"
value-key="id"
placeholder="Team auswählen"
/>
</UFormField>
<UFormField v-else label="Niederlassung">
<USelectMenu
v-model="telephonyExtensionForm.targetBranchId"
:items="activeTelephonyTargetOptions"
label-key="label"
value-key="id"
placeholder="Niederlassung auswählen"
/>
</UFormField>
<UFormField label="Nebenstelle">
<UInput v-model="telephonyExtensionForm.extension" placeholder="1001" />
</UFormField>
<UFormField label="Anzeigename">
<UInput v-model="telephonyExtensionForm.displayName" placeholder="Optional" />
</UFormField>
<UFormField label="SIP-Benutzername">
<UInput v-model="telephonyExtensionForm.sipUsername" placeholder="Leer = Nebenstelle" />
</UFormField>
<UFormField label="SIP-Kennwort">
<UInput v-model="telephonyExtensionForm.sipPassword" type="password" placeholder="Leer = automatisch" />
</UFormField>
<UFormField label="Aktiv">
<USwitch v-model="telephonyExtensionForm.enabled" />
</UFormField>
</div>
<div class="flex flex-wrap gap-2">
<UButton
icon="i-heroicons-check"
:loading="telephonyExtensionSaving"
@click="saveTelephonyExtension"
>
{{ telephonyExtensionForm.id ? "Nebenstelle speichern" : "Nebenstelle anlegen" }}
</UButton>
<UButton
v-if="telephonyExtensionForm.id"
variant="outline"
@click="resetTelephonyExtensionForm"
>
Abbrechen
</UButton>
</div>
<div class="divide-y divide-default rounded-md border border-default">
<div
v-for="extension in telephonyExtensions"
:key="extension.id"
class="flex flex-col gap-3 p-3 md:flex-row md:items-center md:justify-between"
>
<div>
<div class="font-medium text-highlighted">
{{ extension.extension }} - {{ extension.displayName || extension.targetType }}
</div>
<div class="text-sm text-muted">
{{ extension.targetType }} · Ziele: {{ extension.dialTargets?.length ? extension.dialTargets.join(", ") : "noch keine Benutzer-Nebenstelle" }}
</div>
</div>
<div class="flex gap-2">
<UButton
icon="i-heroicons-pencil-square"
variant="outline"
@click="editTelephonyExtension(extension)"
>
Bearbeiten
</UButton>
<UButton
icon="i-heroicons-trash"
color="error"
variant="soft"
:loading="telephonyExtensionDeletingId === extension.id"
@click="deleteTelephonyExtension(extension)"
>
Löschen
</UButton>
</div>
</div>
<div v-if="!telephonyExtensions.length" class="p-3 text-sm text-muted">
Noch keine Nebenstellen angelegt.
</div>
</div>
</div>
</div>
<USeparator class="mb-8" />
<UAlert
class="mb-8"
color="neutral"
icon="i-heroicons-phone"
title="Telefonie ist jetzt eine eigene Einstellungsseite"
description="SIP-Trunk, Standardroute und Nebenstellenübersicht findest du unter Einstellungen > Telefonie."
:actions="[{ label: 'Telefonie öffnen', to: '/settings/telephony', icon: 'i-heroicons-arrow-top-right-on-square' }]"
/>
<div class="space-y-6">
<div class="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">