KI-AGENT: Projekträume und Direktnachrichten integrieren

This commit is contained in:
2026-05-19 08:27:39 +02:00
parent 716de8a503
commit 5b3445c2dc
3 changed files with 338 additions and 28 deletions

View File

@@ -7,6 +7,8 @@ const { $api } = useNuxtApp()
const status = ref(null)
const identity = ref(null)
const matrixRooms = ref([])
const matrixProjectRooms = ref([])
const matrixDirectRooms = ref([])
const activeRoomKey = ref("allgemein")
const matrixMessages = ref([])
const matrixMembers = ref([])
@@ -35,6 +37,7 @@ const roomCreateForm = ref({
})
const loading = ref(false)
const roomProvisioning = ref(false)
const roomProvisioningKey = ref("")
const roomCreating = ref(false)
const roomMembersSyncing = ref(false)
const matrixMessagesLoading = ref(false)
@@ -54,7 +57,7 @@ const canUseMatrixChat = computed(() =>
)
const activeRoom = computed(() =>
matrixRooms.value.find((room) => room.key === activeRoomKey.value) || {
rooms.value.find((room) => room.key === activeRoomKey.value) || {
key: activeRoomKey.value,
name: activeRoomKey.value,
description: "Mandantenweiter Austausch",
@@ -109,26 +112,34 @@ const roomCreateKeyPreview = computed(() =>
normalizeRoomKey(roomCreateForm.value.key || roomCreateForm.value.name)
)
const rooms = computed(() => [
...matrixRooms.value.map((room) => ({
const rooms = computed(() => {
const baseRooms = matrixRooms.value
.filter((room) => !["project", "direct"].includes(room.type))
.map((room) => ({
...room,
group: "Räume",
icon: "i-heroicons-chat-bubble-left-right",
description: room.alias || room.roomId || "Mandantenweiter Austausch"
}))
const projectRooms = matrixProjectRooms.value.map((room) => ({
...room,
description: room.alias || room.roomId || "Mandantenweiter Austausch"
})),
{
key: "projects",
name: "Projekt-Chats",
description: "Nächste Ausbaustufe",
exists: false,
disabled: true
},
{
key: "direct",
name: "Direktnachrichten",
description: "Nächste Ausbaustufe",
exists: false,
disabled: true
}
])
group: "Projekte",
icon: "i-heroicons-briefcase",
description: room.projectNumber || room.topic || "Projektkommunikation",
provisionEndpoint: `/api/communication/matrix/project-rooms/${encodeURIComponent(room.projectId)}/provision`
}))
const directRooms = matrixDirectRooms.value.map((room) => ({
...room,
group: "Direkt",
icon: "i-heroicons-user-circle",
description: room.email || room.topic || "Direktnachricht",
provisionEndpoint: `/api/communication/matrix/direct-rooms/${encodeURIComponent(room.userId)}/provision`
}))
return [...baseRooms, ...projectRooms, ...directRooms]
})
const normalizeRoomKey = (value) => {
const normalized = String(value || "")
@@ -149,12 +160,61 @@ const normalizeRoomKey = (value) => {
const setActiveRoom = async (room) => {
if (room.disabled || room.key === activeRoomKey.value) return
if (!room.exists && room.provisionEndpoint) {
await provisionRoomFromList(room)
return
}
activeRoomKey.value = room.key
matrixMessages.value = []
matrixMembers.value = []
await loadRoomChat({ includeMembers: true })
}
const mergeRoomIntoLists = (room) => {
upsertRoom({ ...room, exists: true })
if (room.type === "project") {
matrixProjectRooms.value = matrixProjectRooms.value.map((item) =>
item.key === room.key ? { ...item, ...room, exists: true } : item
)
}
if (room.type === "direct") {
matrixDirectRooms.value = matrixDirectRooms.value.map((item) =>
item.key === room.key ? { ...item, ...room, exists: true } : item
)
}
}
const provisionRoomFromList = async (room) => {
roomProvisioningKey.value = room.key
try {
const createdRoom = await $api(room.provisionEndpoint, {
method: "POST"
})
mergeRoomIntoLists(createdRoom)
activeRoomKey.value = createdRoom.key
matrixMessages.value = []
matrixMembers.value = []
toast.add({
title: "Chatraum ist bereit",
color: "success"
})
await loadRoomChat({ includeMembers: true })
} catch (error) {
toast.add({
title: "Chatraum konnte nicht erstellt werden",
color: "error"
})
} finally {
roomProvisioningKey.value = ""
}
}
const upsertRoom = (room) => {
const roomWasKnown = matrixRooms.value.some((item) => item.key === room.key)
matrixRooms.value = roomWasKnown
@@ -192,15 +252,19 @@ const scrollMessagesToBottom = async () => {
const loadChatInfo = async () => {
loading.value = true
try {
const [statusRes, identityRes, roomsRes] = await Promise.all([
const [statusRes, identityRes, roomsRes, projectRoomsRes, directRoomsRes] = await Promise.all([
$api("/api/communication/matrix/status"),
$api("/api/communication/matrix/me"),
$api("/api/communication/matrix/rooms")
$api("/api/communication/matrix/rooms"),
$api("/api/communication/matrix/project-rooms"),
$api("/api/communication/matrix/direct-rooms")
])
status.value = statusRes
identity.value = identityRes
matrixRooms.value = roomsRes.rooms || []
matrixProjectRooms.value = projectRoomsRes.rooms || []
matrixDirectRooms.value = directRoomsRes.rooms || []
lastUpdated.value = new Date()
if (activeRoom.value?.exists && canUseMatrixChat.value) {
@@ -869,14 +933,22 @@ onBeforeUnmount(() => {
@click="setActiveRoom(room)"
>
<span class="flex size-9 shrink-0 items-center justify-center rounded-md bg-primary/10 text-primary">
<UIcon name="i-heroicons-chat-bubble-left-right" class="size-5" />
<UIcon :name="room.icon || 'i-heroicons-chat-bubble-left-right'" class="size-5" />
</span>
<span class="min-w-0 flex-1">
<span class="block truncate text-sm font-medium">{{ room.name }}</span>
<span class="block truncate text-xs text-muted">{{ room.description }}</span>
</span>
<UBadge
v-if="room.exists"
v-if="roomProvisioningKey === room.key"
color="primary"
variant="soft"
size="xs"
>
lädt
</UBadge>
<UBadge
v-else-if="room.exists"
color="success"
variant="soft"
size="xs"
@@ -978,7 +1050,7 @@ onBeforeUnmount(() => {
<div class="border-b border-default bg-default p-3 lg:hidden">
<div class="flex gap-2 overflow-x-auto pb-1">
<button
<button
v-for="room in rooms"
:key="room.key"
type="button"