KI-AGENT: Push Test im Admin Dashboard ergänzen

This commit is contained in:
2026-05-22 18:32:15 +02:00
parent d73209a150
commit 0328a4586a
2 changed files with 171 additions and 0 deletions

View File

@@ -1,11 +1,19 @@
<script setup lang="ts">
const route = useRoute();
const pushApi = usePushApi();
const toast = useToast();
const id = route.params.id as string;
const testSubmitting = ref(false);
const selectedDeviceIds = ref<string[]>([]);
const testForm = reactive({
title: "FEDEO Push-Test",
body: "Diese Testnachricht wurde über das Push-Admin-Dashboard gesendet.",
});
const { data: instance, refresh: refreshInstance } = await useAsyncData(`instance-${id}`, () => pushApi.request<Record<string, any>>(`/admin/instances/${id}`), { immediate: false });
const { data: devices, refresh: refreshDevices } = await useAsyncData(`devices-${id}`, () => pushApi.request<Record<string, any>[]>(`/admin/instances/${id}/devices`), { default: () => [], immediate: false });
const { data: jobs, refresh: refreshJobs } = await useAsyncData(`jobs-${id}`, () => pushApi.request<Record<string, any>[]>(`/admin/instances/${id}/jobs`), { default: () => [], immediate: false });
const activeDevices = computed(() => (devices.value || []).filter((device) => device.status === "active"));
onMounted(async () => {
pushApi.hydrateToken();
@@ -17,6 +25,47 @@ onMounted(async () => {
async function refreshAll() {
await Promise.all([refreshInstance(), refreshDevices(), refreshJobs()]);
}
function toggleDevice(centralDeviceId: string, checked: boolean) {
if (checked) {
selectedDeviceIds.value = [...new Set([...selectedDeviceIds.value, centralDeviceId])];
} else {
selectedDeviceIds.value = selectedDeviceIds.value.filter((id) => id !== centralDeviceId);
}
}
function selectAllActiveDevices() {
selectedDeviceIds.value = activeDevices.value.map((device) => device.centralDeviceId);
}
async function sendTestPush() {
testSubmitting.value = true;
try {
const result = await pushApi.request<Record<string, any>>(`/admin/instances/${id}/test-push`, {
method: "POST",
body: {
title: testForm.title,
body: testForm.body,
deviceIds: selectedDeviceIds.value.length ? selectedDeviceIds.value : undefined,
priority: "high",
},
});
await Promise.all([refreshInstance(), refreshJobs(), refreshDevices()]);
toast.add({
title: "Test-Push gesendet",
description: `${result.sent ?? 0} gesendet, ${result.failed ?? 0} fehlgeschlagen`,
color: result.failed ? "warning" : "success",
});
} catch (error: any) {
toast.add({
title: "Test-Push fehlgeschlagen",
description: error?.data?.message || error?.message || "Die Testnachricht konnte nicht gesendet werden.",
color: "error",
});
} finally {
testSubmitting.value = false;
}
}
</script>
<template>
@@ -47,6 +96,54 @@ async function refreshAll() {
</UCard>
</div>
<UCard>
<template #header>
<div class="flex flex-wrap items-center justify-between gap-3">
<h3 class="font-bold">Testnachricht</h3>
<UBadge color="neutral" variant="soft">{{ selectedDeviceIds.length || activeDevices.length }} Zielgeräte</UBadge>
</div>
</template>
<div class="grid gap-4 lg:grid-cols-[1fr_240px]">
<div class="space-y-4">
<UFormField label="Titel">
<UInput v-model="testForm.title" maxlength="120" />
</UFormField>
<UFormField label="Nachricht">
<UTextarea v-model="testForm.body" :rows="3" maxlength="240" />
</UFormField>
<div class="flex flex-wrap gap-2">
<UButton
icon="i-lucide-send"
:loading="testSubmitting"
:disabled="!activeDevices.length || !testForm.title.trim() || !testForm.body.trim()"
@click="sendTestPush"
>
Test senden
</UButton>
<UButton icon="i-lucide-check-check" variant="soft" :disabled="!activeDevices.length" @click="selectAllActiveDevices">
Alle aktiven auswählen
</UButton>
<UButton icon="i-lucide-x" variant="ghost" :disabled="!selectedDeviceIds.length" @click="selectedDeviceIds = []">
Auswahl leeren
</UButton>
</div>
</div>
<div class="rounded-lg border border-gray-200 p-3">
<p class="mb-3 text-sm font-medium text-gray-700">Zielgeräte</p>
<div class="max-h-48 space-y-2 overflow-auto">
<label v-for="device in activeDevices" :key="device.id" class="flex cursor-pointer items-center gap-2 text-sm">
<UCheckbox
:model-value="selectedDeviceIds.includes(device.centralDeviceId)"
@update:model-value="toggleDevice(device.centralDeviceId, Boolean($event))"
/>
<span class="min-w-0 truncate font-mono">{{ device.centralDeviceId }}</span>
</label>
<p v-if="!activeDevices.length" class="text-sm text-gray-500">Keine aktiven Geräte vorhanden.</p>
</div>
</div>
</div>
</UCard>
<UCard>
<template #header>
<h3 class="font-bold">Geräte</h3>