KI-AGENT: Statusanzeige für Tenant-Import ergänzen
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 45s
Build and Push Docker Images / build-frontend (push) Successful in 1m21s
Build and Push Docker Images / build-central-services-api (push) Successful in 22s
Build and Push Docker Images / build-website (push) Successful in 23s
Build and Push Docker Images / build-central-services-admin (push) Successful in 22s
Build and Push Docker Images / build-docs (push) Successful in 21s
All checks were successful
Build and Push Docker Images / build-backend (push) Successful in 45s
Build and Push Docker Images / build-frontend (push) Successful in 1m21s
Build and Push Docker Images / build-central-services-api (push) Successful in 22s
Build and Push Docker Images / build-website (push) Successful in 23s
Build and Push Docker Images / build-central-services-admin (push) Successful in 22s
Build and Push Docker Images / build-docs (push) Successful in 21s
This commit is contained in:
@@ -63,6 +63,24 @@ export type TenantImportResult = {
|
||||
error?: string | null
|
||||
}
|
||||
|
||||
export type TenantImportJob = {
|
||||
importId: string
|
||||
tenantId?: number | null
|
||||
operation?: "import" | string
|
||||
status: "queued" | "running" | "completed" | "failed" | string
|
||||
statusMessage?: string | null
|
||||
filename: string
|
||||
fileSize?: number | null
|
||||
filesDone?: number
|
||||
filesTotal?: number
|
||||
error?: string | null
|
||||
result?: TenantImportResult | null
|
||||
statusUrl?: string
|
||||
createdAt?: string
|
||||
updatedAt?: string | null
|
||||
completedAt?: string | null
|
||||
}
|
||||
|
||||
export type TenantExportJob = {
|
||||
exportId: string
|
||||
importId?: string
|
||||
@@ -215,13 +233,17 @@ export const useAdmin = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const importTenant = async (body: Record<string, any> | FormData): Promise<TenantImportResult> => {
|
||||
const importTenant = async (body: Record<string, any> | FormData): Promise<TenantImportJob> => {
|
||||
return await $api("/api/admin/tenant-imports", {
|
||||
method: "POST",
|
||||
body,
|
||||
})
|
||||
}
|
||||
|
||||
const getTenantImport = async (importId: string): Promise<TenantImportJob> => {
|
||||
return await $api(`/api/admin/tenant-imports/${importId}`)
|
||||
}
|
||||
|
||||
const getSystemStatus = async (): Promise<SystemStatus> => {
|
||||
return await $api("/api/admin/system-status")
|
||||
}
|
||||
@@ -262,5 +284,6 @@ export const useAdmin = () => {
|
||||
getTenantExport,
|
||||
downloadTenantExport,
|
||||
importTenant,
|
||||
getTenantImport,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { AdminTenant } from "~/composables/useAdmin"
|
||||
import type { AdminTenant, TenantImportJob } from "~/composables/useAdmin"
|
||||
|
||||
const auth = useAuthStore()
|
||||
const toast = useToast()
|
||||
@@ -9,6 +9,7 @@ const admin = useAdmin()
|
||||
const loading = ref(true)
|
||||
const creatingTenant = ref(false)
|
||||
const importingTenant = ref(false)
|
||||
const tenantImportProgress = ref<TenantImportJob | null>(null)
|
||||
const createTenantModalOpen = ref(false)
|
||||
const importFileInput = ref<HTMLInputElement | null>(null)
|
||||
const tenants = ref<AdminTenant[]>([])
|
||||
@@ -101,19 +102,42 @@ const importTenantExport = async (event: Event) => {
|
||||
if (!file || importingTenant.value) return
|
||||
|
||||
importingTenant.value = true
|
||||
tenantImportProgress.value = {
|
||||
importId: "",
|
||||
status: "uploading",
|
||||
statusMessage: "Exportdatei wird hochgeladen",
|
||||
filename: file.name,
|
||||
filesDone: 0,
|
||||
filesTotal: 0,
|
||||
}
|
||||
|
||||
try {
|
||||
const isZipExport = file.name.endsWith(".zip") || file.name.endsWith(".fedeo-export.zip") || file.type === "application/zip"
|
||||
let result
|
||||
let job
|
||||
|
||||
if (isZipExport) {
|
||||
const formData = new FormData()
|
||||
formData.append("file", file)
|
||||
result = await admin.importTenant(formData)
|
||||
job = await admin.importTenant(formData)
|
||||
} else {
|
||||
result = await admin.importTenant(JSON.parse(await file.text()))
|
||||
job = await admin.importTenant(JSON.parse(await file.text()))
|
||||
}
|
||||
|
||||
tenantImportProgress.value = job
|
||||
|
||||
while (!["completed", "failed"].includes(job.status)) {
|
||||
await new Promise((resolve) => setTimeout(resolve, 1500))
|
||||
job = await admin.getTenantImport(job.importId)
|
||||
tenantImportProgress.value = job
|
||||
}
|
||||
|
||||
if (job.status === "failed") {
|
||||
throw new Error(job.error || "Mandantenimport fehlgeschlagen")
|
||||
}
|
||||
|
||||
const result = job.result
|
||||
if (!result) throw new Error("Importergebnis fehlt")
|
||||
|
||||
const rowCount = (result.tables || []).reduce((sum, table) => sum + table.rows, 0)
|
||||
|
||||
await fetchTenants()
|
||||
@@ -194,6 +218,36 @@ onMounted(async () => {
|
||||
</template>
|
||||
</UTable>
|
||||
|
||||
<UCard v-if="tenantImportProgress" class="mt-4">
|
||||
<div class="space-y-2">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<div>
|
||||
<div class="font-medium">Mandantenimport</div>
|
||||
<p class="text-sm text-gray-500">
|
||||
{{ tenantImportProgress.statusMessage || 'Import wird verarbeitet' }}
|
||||
</p>
|
||||
</div>
|
||||
<UBadge
|
||||
:color="tenantImportProgress.status === 'failed' ? 'error' : tenantImportProgress.status === 'completed' ? 'success' : 'warning'"
|
||||
variant="soft"
|
||||
>
|
||||
{{ tenantImportProgress.status === 'uploading' ? 'Upload' : tenantImportProgress.status === 'queued' ? 'Wartet' : tenantImportProgress.status === 'running' ? 'Läuft' : tenantImportProgress.status === 'completed' ? 'Abgeschlossen' : 'Fehlgeschlagen' }}
|
||||
</UBadge>
|
||||
</div>
|
||||
<UProgress
|
||||
:model-value="tenantImportProgress.filesTotal ? Math.round(((tenantImportProgress.filesDone || 0) / tenantImportProgress.filesTotal) * 100) : undefined"
|
||||
:color="tenantImportProgress.status === 'failed' ? 'error' : tenantImportProgress.status === 'completed' ? 'success' : 'warning'"
|
||||
:animation="tenantImportProgress.filesTotal ? undefined : 'carousel'"
|
||||
/>
|
||||
<p v-if="tenantImportProgress.filesTotal" class="text-xs text-gray-500">
|
||||
{{ tenantImportProgress.filesDone || 0 }} / {{ tenantImportProgress.filesTotal }} Schritte
|
||||
</p>
|
||||
<p v-if="tenantImportProgress.error" class="text-sm text-red-600">
|
||||
{{ tenantImportProgress.error }}
|
||||
</p>
|
||||
</div>
|
||||
</UCard>
|
||||
|
||||
<UModal v-model:open="createTenantModalOpen">
|
||||
<template #content>
|
||||
<UCard>
|
||||
|
||||
Reference in New Issue
Block a user