chore: remove unused meldestelle-portal module

- Deleted obsolete `meldestelle-portal` module, including all associated screens, configurations, tests, and assets.
- Includes removal of Compose multiplatform dependencies in `build.gradle.kts`.
- Cleaned up redundant files such as `AppPreview`, `AuthStatusScreen`, `DashboardScreen`, and associated core implementations.
- Streamlined module references in `settings.gradle.kts`.

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
2026-03-25 15:46:48 +01:00
parent 9d08cb0f72
commit 3fe850d914
47 changed files with 578 additions and 2814 deletions
@@ -0,0 +1,28 @@
/**
* Feature-Modul: ZNS-Stammdaten-Import (Desktop-only)
* Kapselt ViewModel, State und API-Kommunikation für den ZNS-Import.
*/
plugins {
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.composeCompiler)
}
group = "at.mocode.clients"
version = "1.0.0"
kotlin {
jvm()
sourceSets {
jvmMain.dependencies {
implementation(projects.frontend.core.network)
implementation(projects.frontend.core.auth)
implementation(libs.bundles.kmp.common)
implementation(libs.koin.core)
implementation(libs.ktor.client.core)
implementation(libs.ktor.client.contentNegotiation)
implementation(libs.ktor.client.serialization.kotlinx.json)
implementation(libs.androidx.lifecycle.viewmodelCompose)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.serialization.json)
}
}
}
@@ -0,0 +1,140 @@
package at.mocode.zns.feature
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import at.mocode.frontend.core.auth.data.AuthTokenManager
import at.mocode.frontend.core.network.NetworkConfig
import io.ktor.client.*
import io.ktor.client.request.*
import io.ktor.client.request.forms.*
import io.ktor.client.statement.*
import io.ktor.http.*
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import java.io.File
data class ZnsImportState(
val selectedFilePath: String? = null,
val isUploading: Boolean = false,
val jobId: String? = null,
val jobStatus: String? = null,
val progress: Int = 0,
val progressDetail: String = "",
val errors: List<String> = emptyList(),
val errorMessage: String? = null,
val isFinished: Boolean = false,
)
@Serializable
internal data class JobIdResponse(val jobId: String)
@Serializable
internal data class JobStatusResponse(
val jobId: String,
val status: String,
val progress: Int = 0,
val progressDetail: String = "",
val errors: List<String> = emptyList(),
)
private val TERMINAL_STATES = setOf("ABGESCHLOSSEN", "FEHLER")
private const val POLLING_INTERVAL_MS = 2000L
private const val MAX_VISIBLE_ERRORS = 50
class ZnsImportViewModel(
private val httpClient: HttpClient,
private val authTokenManager: AuthTokenManager,
) : ViewModel() {
var state by mutableStateOf(ZnsImportState())
private set
private var pollingJob: Job? = null
private val json = Json { ignoreUnknownKeys = true }
fun onFileSelected(path: String) {
state = ZnsImportState(selectedFilePath = path)
}
fun startImport() {
val filePath = state.selectedFilePath ?: return
val file = File(filePath)
if (!file.exists() || !file.name.endsWith(".zip", ignoreCase = true)) {
state = state.copy(errorMessage = "Bitte eine gültige .zip-Datei auswählen.")
return
}
viewModelScope.launch {
state = state.copy(
isUploading = true, errorMessage = null, isFinished = false,
jobId = null, progress = 0, progressDetail = "", errors = emptyList()
)
try {
val token = authTokenManager.authState.value.token
val response: HttpResponse = httpClient.post("${NetworkConfig.baseUrl}/api/v1/import/zns") {
if (token != null) header(HttpHeaders.Authorization, "Bearer $token")
setBody(MultiPartFormDataContent(formData {
append("file", file.readBytes(), Headers.build {
append(HttpHeaders.ContentDisposition, "filename=\"${file.name}\"")
append(HttpHeaders.ContentType, "application/zip")
})
}))
}
if (response.status == HttpStatusCode.Accepted) {
val body = json.decodeFromString<JobIdResponse>(response.bodyAsText())
state = state.copy(isUploading = false, jobId = body.jobId, jobStatus = "AUSSTEHEND")
startPolling(body.jobId)
} else {
state = state.copy(isUploading = false, errorMessage = "Upload fehlgeschlagen: HTTP ${response.status.value}")
}
} catch (e: Exception) {
state = state.copy(isUploading = false, errorMessage = "Fehler beim Upload: ${e.message}")
}
}
}
private fun startPolling(jobId: String) {
pollingJob?.cancel()
pollingJob = viewModelScope.launch {
while (true) {
try {
val token = authTokenManager.authState.value.token
val response: HttpResponse = httpClient.get("${NetworkConfig.baseUrl}/api/v1/import/zns/$jobId/status") {
if (token != null) header(HttpHeaders.Authorization, "Bearer $token")
}
if (response.status.isSuccess()) {
val status = json.decodeFromString<JobStatusResponse>(response.bodyAsText())
state = state.copy(
jobStatus = status.status,
progress = status.progress,
progressDetail = status.progressDetail,
errors = status.errors.takeLast(MAX_VISIBLE_ERRORS),
isFinished = status.status in TERMINAL_STATES,
)
if (status.status in TERMINAL_STATES) break
}
} catch (e: Exception) {
state = state.copy(errorMessage = "Polling-Fehler: ${e.message}", isFinished = true)
break
}
delay(POLLING_INTERVAL_MS)
}
}
}
fun reset() {
pollingJob?.cancel()
state = ZnsImportState()
}
override fun onCleared() {
super.onCleared()
pollingJob?.cancel()
}
}
@@ -0,0 +1,9 @@
package at.mocode.zns.feature.di
import at.mocode.zns.feature.ZnsImportViewModel
import org.koin.core.qualifier.named
import org.koin.dsl.module
val znsImportModule = module {
factory { ZnsImportViewModel(get(named("apiClient")), get()) }
}