feat(veranstaltung): Wizard für neue Veranstaltung implementiert und ZNS-Light-Integration hinzugefügt

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
2026-04-16 13:26:39 +02:00
parent 10f9e82718
commit cb4f2f855c
11 changed files with 318 additions and 121 deletions
@@ -6,6 +6,8 @@ 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.domain.zns.ZnsImportProvider
import at.mocode.frontend.core.domain.zns.ZnsImportState
import at.mocode.frontend.core.network.NetworkConfig
import io.ktor.client.*
import io.ktor.client.request.*
@@ -20,17 +22,6 @@ import kotlinx.serialization.json.Json
import java.io.File
import kotlin.time.Duration.Companion.milliseconds
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)
@@ -51,19 +42,19 @@ private const val MAX_VISIBLE_ERRORS = 50
class ZnsImportViewModel(
private val httpClient: HttpClient,
private val authTokenManager: AuthTokenManager,
) : ViewModel() {
) : ViewModel(), ZnsImportProvider {
var state by mutableStateOf(ZnsImportState())
override var state by mutableStateOf(ZnsImportState())
private set
private var pollingJob: Job? = null
private val json = Json { ignoreUnknownKeys = true }
fun onFileSelected(path: String) {
override fun onFileSelected(path: String) {
state = ZnsImportState(selectedFilePath = path)
}
fun startImport() {
override fun startImport(mode: String) {
val filePath = state.selectedFilePath ?: return
val file = File(filePath)
if (!file.exists() || !file.name.endsWith(".zip", ignoreCase = true)) {
@@ -79,6 +70,7 @@ class ZnsImportViewModel(
try {
val token = authTokenManager.authState.value.token
val response: HttpResponse = httpClient.post("${NetworkConfig.baseUrl}/api/v1/import/zns") {
parameter("mode", mode)
if (token != null) header(HttpHeaders.Authorization, "Bearer $token")
setBody(MultiPartFormDataContent(formData {
append("file", file.readBytes(), Headers.build {
@@ -129,7 +121,7 @@ class ZnsImportViewModel(
}
}
fun reset() {
override fun reset() {
pollingJob?.cancel()
state = ZnsImportState()
}
@@ -1,9 +1,11 @@
package at.mocode.zns.feature.di
import at.mocode.frontend.core.domain.zns.ZnsImportProvider
import at.mocode.zns.feature.ZnsImportViewModel
import org.koin.core.qualifier.named
import org.koin.dsl.module
val znsImportModule = module {
factory<ZnsImportProvider> { ZnsImportViewModel(get(named("apiClient")), get()) }
factory { ZnsImportViewModel(get(named("apiClient")), get()) }
}
@@ -114,7 +114,10 @@ fun StammdatenImportScreen(
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
Icon(Icons.Default.Error, contentDescription = null, tint = MaterialTheme.colorScheme.error)
Text(state.errorMessage, color = MaterialTheme.colorScheme.onErrorContainer)
val errorMsg = state.errorMessage
if (errorMsg != null) {
Text(errorMsg, color = MaterialTheme.colorScheme.onErrorContainer)
}
}
}
}
@@ -129,7 +132,10 @@ fun StammdatenImportScreen(
verticalAlignment = Alignment.CenterVertically,
) {
Text("Status", style = MaterialTheme.typography.titleMedium)
StatusChip(state.jobStatus)
val statusMsg = state.jobStatus
if (statusMsg != null) {
StatusChip(statusMsg)
}
}
LinearProgressIndicator(