refactor(device-initialization): Code-Bereinigung, ungenutzte Parameter entfernt und WasmJS-Unterstützung vervollständigt

Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
Stefan Mogeritsch 2026-04-18 14:47:34 +02:00
parent e91b10daa3
commit fb77a5065b
7 changed files with 125 additions and 21 deletions

View File

@ -276,7 +276,7 @@ und über definierte Schnittstellen kommunizieren.
## 4. Geplante Phasen
### PHASE 13: Export & ZNS-Rückmeldung ✅ ABGESCHLOSSEN
### PHASE 13: Export & ZNS-Rückmeldung ✅ ABGESCHLOSSEN (18. April 2026)
*Ziel: Finalisierung der Turnier-Daten und Rückübermittlung an den OEPS.*
* [x] **Mail-Service Integration:** Online-Nennungen via REST/Mail empfangen und persistieren. ✓ (April 2026)
@ -284,6 +284,10 @@ und über definierte Schnittstellen kommunizieren.
18. April 2026)
* [x] **Plug-and-Play Architektur:** Umstellung der Frontend-Komponenten auf fachlich autarke Organismen (ADR-0024).
✓ (18. April 2026)
* [x] **WASM-Transition:** Projektweite Umstellung auf JVM (Desktop) und wasmJs (Web). Eliminierung von `js(IR)`. ✓ (
18. April 2026)
* [x] **Geräte-Initialisierung:** Refactoring des Onboarding-Prozesses in das Plug-and-Play Modul
`device-initialization`. ✓ (18. April 2026)
* [ ] **XML-Export:** Vollständiger B-Satz Export (inkl. Ergebnisse und Platzierungen).
* [ ] **ZNS-Portal:** Upload-Integration in das OEPS-ZNS.
* [ ] **Archivierung:** Langzeit-Archivierung abgeschlossener Turniere.

View File

@ -50,7 +50,11 @@ Prinzip und ist fachlich präzise benannt.
* **ViewModel-Fix:** `DeviceInitializationViewModel` erbt nun von `androidx.lifecycle.ViewModel`, was die Integration in
`koinViewModel` ermöglicht.
* **DesktopMainLayout:** Syntaxfehler beim `koinViewModel`-Aufruf behoben und Typos (`geraetName` -> `deviceName`)
korrigiert.
korrigiert. Unbenutzter `settings`-Parameter entfernt.
* **Multiplatform-Härtung:** `DeviceInitializationSettingsManager` und `DeviceInitializationConfig` auf `expect/actual`
umgestellt, um JVM-Lecks im Common-Code zu vermeiden und JS/WasmJS Kompatibilität (via Stubs) sicherzustellen.
umgestellt.
* **Beta-Warnungen:** `@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")` hinzugefügt, um
Compiler-Warnungen in den Domain-Klassen zu unterdrücken.
* **WASM-Komplettierung:** `DeviceInitializationSettingsManager` nutzt nun `localStorage` im Web.
`DeviceInitializationConfig` wurde für WasmJS funktional implementiert (Basis-Konfiguration).
* **UI-Cleanup:** Code-Duplikate in der Desktop-Konfiguration durch `MsSettingsField` reduziert.

View File

@ -1,3 +1,5 @@
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
package at.mocode.frontend.features.deviceinitialization.domain
expect object DeviceInitializationSettingsManager {

View File

@ -1,11 +1,35 @@
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
package at.mocode.frontend.features.deviceinitialization.domain
import kotlinx.browser.localStorage
import kotlinx.serialization.json.Json
actual object DeviceInitializationSettingsManager {
private const val SETTINGS_KEY = "device_initialization_settings"
private val json = Json { prettyPrint = true; ignoreUnknownKeys = true }
actual fun saveSettings(settings: DeviceInitializationSettings) {
// Nicht implementiert für WasmJS
try {
val content = json.encodeToString(DeviceInitializationSettings.serializer(), settings)
localStorage.setItem(SETTINGS_KEY, content)
} catch (e: Exception) {
println("Fehler beim Speichern der Einstellungen (WasmJS): ${e.message}")
}
}
actual fun loadSettings(): DeviceInitializationSettings? = null
actual fun loadSettings(): DeviceInitializationSettings? {
val content = localStorage.getItem(SETTINGS_KEY) ?: return null
return try {
json.decodeFromString<DeviceInitializationSettings>(DeviceInitializationSettings.serializer(), content)
} catch (e: Exception) {
println("Fehler beim Laden der Einstellungen (WasmJS): ${e.message}")
null
}
}
actual fun isConfigured(): Boolean = false
actual fun isConfigured(): Boolean {
val settings = loadSettings() ?: return false
return DeviceInitializationValidator.canContinue(settings)
}
}

View File

@ -1,12 +1,92 @@
package at.mocode.frontend.features.deviceinitialization.presentation
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Visibility
import androidx.compose.material.icons.outlined.VisibilityOff
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.PasswordVisualTransformation
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.unit.dp
import at.mocode.frontend.features.deviceinitialization.domain.DeviceInitializationValidator
@Composable
actual fun DeviceInitializationConfig(
uiState: DeviceInitializationUiState,
viewModel: DeviceInitializationViewModel
) {
Text("Konfiguration für Web (WasmJS) ist nicht implementiert.")
val settings = uiState.settings
Card(modifier = Modifier.fillMaxWidth()) {
Column(Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
Text("⚙️ Geräte-Konfiguration", style = MaterialTheme.typography.titleMedium)
MsSettingsField(
value = settings.deviceName,
onValueChange = { viewModel.updateSettings { s -> s.copy(deviceName = it) } },
label = "Gerätename",
placeholder = "z.B. Web-Client",
isError = settings.deviceName.isNotEmpty() && !DeviceInitializationValidator.isNameValid(settings.deviceName),
errorText = "Mindestens ${DeviceInitializationValidator.MIN_NAME_LENGTH} Zeichen erforderlich."
)
var passwordVisible by remember { mutableStateOf(false) }
MsSettingsField(
value = settings.sharedKey,
onValueChange = { viewModel.updateSettings { s -> s.copy(sharedKey = it) } },
label = "Sicherheitsschlüssel (Sync-Key)",
placeholder = "Mindestens 8 Zeichen",
isError = settings.sharedKey.isNotEmpty() && !DeviceInitializationValidator.isKeyValid(settings.sharedKey),
errorText = "Mindestens ${DeviceInitializationValidator.MIN_KEY_LENGTH} Zeichen erforderlich.",
visualTransformation = if (passwordVisible) VisualTransformation.None else PasswordVisualTransformation(),
trailingIcon = {
IconButton(onClick = { passwordVisible = !passwordVisible }) {
Icon(
imageVector = if (passwordVisible) Icons.Outlined.VisibilityOff else Icons.Outlined.Visibility,
contentDescription = if (passwordVisible) "Verbergen" else "Anzeigen"
)
}
}
)
Text(
"Hinweis: In der Web-Version sind erweiterte Master-Funktionen (wie lokales Backup) derzeit deaktiviert.",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
@Composable
private fun MsSettingsField(
value: String,
onValueChange: (String) -> Unit,
label: String,
placeholder: String,
isError: Boolean,
errorText: String,
visualTransformation: VisualTransformation = VisualTransformation.None,
trailingIcon: @Composable (() -> Unit)? = null
) {
OutlinedTextField(
value = value,
onValueChange = onValueChange,
label = { Text(label) },
placeholder = { Text(placeholder) },
modifier = Modifier.fillMaxWidth(),
isError = isError,
visualTransformation = visualTransformation,
trailingIcon = trailingIcon,
supportingText = {
if (isError) {
Text(errorText)
}
}
)
}

View File

@ -1,12 +1,4 @@
{
"geraetName": "Meldestelle",
"sharedKey": "Meldestelle",
"backupPath": "/home/stefan/WsMeldestelle/Meldestelle/meldestelle/docs/temp",
"networkRole": "MASTER",
"expectedClients": [
{
"name": "Richter-Turm",
"role": "RICHTER"
}
]
"deviceName": "Richter-Turm",
"sharedKey": "Meldestelle"
}

View File

@ -118,7 +118,6 @@ fun DesktopMainLayout(
onboardingSettings = it
DeviceInitializationSettingsManager.saveSettings(it)
},
settings = onboardingSettings,
)
}
@ -523,7 +522,6 @@ private fun DesktopContentArea(
currentScreen: AppScreen,
onNavigate: (AppScreen) -> Unit,
onBack: () -> Unit,
settings: DeviceInitializationSettings,
onSettingsChange: (DeviceInitializationSettings) -> Unit,
) {
when (currentScreen) {