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:
@@ -276,7 +276,7 @@ und über definierte Schnittstellen kommunizieren.
|
|||||||
|
|
||||||
## 4. Geplante Phasen
|
## 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.*
|
*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)
|
* [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)
|
18. April 2026)
|
||||||
* [x] **Plug-and-Play Architektur:** Umstellung der Frontend-Komponenten auf fachlich autarke Organismen (ADR-0024).
|
* [x] **Plug-and-Play Architektur:** Umstellung der Frontend-Komponenten auf fachlich autarke Organismen (ADR-0024).
|
||||||
✓ (18. April 2026)
|
✓ (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).
|
* [ ] **XML-Export:** Vollständiger B-Satz Export (inkl. Ergebnisse und Platzierungen).
|
||||||
* [ ] **ZNS-Portal:** Upload-Integration in das OEPS-ZNS.
|
* [ ] **ZNS-Portal:** Upload-Integration in das OEPS-ZNS.
|
||||||
* [ ] **Archivierung:** Langzeit-Archivierung abgeschlossener Turniere.
|
* [ ] **Archivierung:** Langzeit-Archivierung abgeschlossener Turniere.
|
||||||
|
|||||||
@@ -50,7 +50,11 @@ Prinzip und ist fachlich präzise benannt.
|
|||||||
* **ViewModel-Fix:** `DeviceInitializationViewModel` erbt nun von `androidx.lifecycle.ViewModel`, was die Integration in
|
* **ViewModel-Fix:** `DeviceInitializationViewModel` erbt nun von `androidx.lifecycle.ViewModel`, was die Integration in
|
||||||
`koinViewModel` ermöglicht.
|
`koinViewModel` ermöglicht.
|
||||||
* **DesktopMainLayout:** Syntaxfehler beim `koinViewModel`-Aufruf behoben und Typos (`geraetName` -> `deviceName`)
|
* **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`
|
* **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.
|
* **UI-Cleanup:** Code-Duplikate in der Desktop-Konfiguration durch `MsSettingsField` reduziert.
|
||||||
|
|||||||
+2
@@ -1,3 +1,5 @@
|
|||||||
|
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
|
||||||
|
|
||||||
package at.mocode.frontend.features.deviceinitialization.domain
|
package at.mocode.frontend.features.deviceinitialization.domain
|
||||||
|
|
||||||
expect object DeviceInitializationSettingsManager {
|
expect object DeviceInitializationSettingsManager {
|
||||||
|
|||||||
+28
-4
@@ -1,11 +1,35 @@
|
|||||||
|
@file:Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
|
||||||
|
|
||||||
package at.mocode.frontend.features.deviceinitialization.domain
|
package at.mocode.frontend.features.deviceinitialization.domain
|
||||||
|
|
||||||
|
import kotlinx.browser.localStorage
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
|
||||||
actual object DeviceInitializationSettingsManager {
|
actual object DeviceInitializationSettingsManager {
|
||||||
|
private const val SETTINGS_KEY = "device_initialization_settings"
|
||||||
|
private val json = Json { prettyPrint = true; ignoreUnknownKeys = true }
|
||||||
|
|
||||||
actual fun saveSettings(settings: DeviceInitializationSettings) {
|
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
|
||||||
actual fun isConfigured(): Boolean = false
|
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 {
|
||||||
|
val settings = loadSettings() ?: return false
|
||||||
|
return DeviceInitializationValidator.canContinue(settings)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+83
-3
@@ -1,12 +1,92 @@
|
|||||||
package at.mocode.frontend.features.deviceinitialization.presentation
|
package at.mocode.frontend.features.deviceinitialization.presentation
|
||||||
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.runtime.Composable
|
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
|
@Composable
|
||||||
actual fun DeviceInitializationConfig(
|
actual fun DeviceInitializationConfig(
|
||||||
uiState: DeviceInitializationUiState,
|
uiState: DeviceInitializationUiState,
|
||||||
viewModel: DeviceInitializationViewModel
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,4 @@
|
|||||||
{
|
{
|
||||||
"geraetName": "Meldestelle",
|
"deviceName": "Richter-Turm",
|
||||||
"sharedKey": "Meldestelle",
|
"sharedKey": "Meldestelle"
|
||||||
"backupPath": "/home/stefan/WsMeldestelle/Meldestelle/meldestelle/docs/temp",
|
|
||||||
"networkRole": "MASTER",
|
|
||||||
"expectedClients": [
|
|
||||||
{
|
|
||||||
"name": "Richter-Turm",
|
|
||||||
"role": "RICHTER"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
-2
@@ -118,7 +118,6 @@ fun DesktopMainLayout(
|
|||||||
onboardingSettings = it
|
onboardingSettings = it
|
||||||
DeviceInitializationSettingsManager.saveSettings(it)
|
DeviceInitializationSettingsManager.saveSettings(it)
|
||||||
},
|
},
|
||||||
settings = onboardingSettings,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -523,7 +522,6 @@ private fun DesktopContentArea(
|
|||||||
currentScreen: AppScreen,
|
currentScreen: AppScreen,
|
||||||
onNavigate: (AppScreen) -> Unit,
|
onNavigate: (AppScreen) -> Unit,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
settings: DeviceInitializationSettings,
|
|
||||||
onSettingsChange: (DeviceInitializationSettings) -> Unit,
|
onSettingsChange: (DeviceInitializationSettings) -> Unit,
|
||||||
) {
|
) {
|
||||||
when (currentScreen) {
|
when (currentScreen) {
|
||||||
|
|||||||
Reference in New Issue
Block a user