feat(device-initialization, core): Unterstützung für Hilfe-Tooltips, Netzwerk-Interface-Auswahl & Discovery-Radar ergänzt
Signed-off-by: Stefan Mogeritsch <stefan.mo.co@gmail.com>
This commit is contained in:
+43
-4
@@ -1,10 +1,11 @@
|
|||||||
package at.mocode.frontend.core.designsystem.components
|
package at.mocode.frontend.core.designsystem.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.material.icons.filled.HelpOutline
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.key.*
|
import androidx.compose.ui.input.key.*
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -31,6 +32,7 @@ fun <T : Enum<T>> MsEnumDropdown(
|
|||||||
onOptionSelected: (T) -> Unit,
|
onOptionSelected: (T) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
optionLabel: (T) -> String = { it.name },
|
optionLabel: (T) -> String = { it.name },
|
||||||
|
helpDescription: String? = null,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
isError: Boolean = false,
|
isError: Boolean = false,
|
||||||
errorMessage: String? = null
|
errorMessage: String? = null
|
||||||
@@ -46,7 +48,44 @@ fun <T : Enum<T>> MsEnumDropdown(
|
|||||||
value = selectedOption?.let { optionLabel(it) } ?: "",
|
value = selectedOption?.let { optionLabel(it) } ?: "",
|
||||||
onValueChange = {},
|
onValueChange = {},
|
||||||
readOnly = true,
|
readOnly = true,
|
||||||
label = { Text(label, style = MaterialTheme.typography.bodySmall) },
|
label = {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
Text(label, style = MaterialTheme.typography.bodySmall)
|
||||||
|
if (helpDescription != null) {
|
||||||
|
var showHelp by remember { mutableStateOf(false) }
|
||||||
|
Box {
|
||||||
|
IconButton(
|
||||||
|
onClick = { showHelp = !showHelp },
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.HelpOutline,
|
||||||
|
contentDescription = "Hilfe",
|
||||||
|
tint = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f),
|
||||||
|
modifier = Modifier.size(14.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (showHelp) {
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
TooltipBox(
|
||||||
|
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
tooltip = {
|
||||||
|
PlainTooltip {
|
||||||
|
Text(helpDescription)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state = rememberTooltipState(isPersistent = true)
|
||||||
|
) {
|
||||||
|
// Tooltip wird durch Klick auf das Icon getriggert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
||||||
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
|
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
+1
@@ -13,6 +13,7 @@ expect fun MsFilePicker(
|
|||||||
onFileSelected: (String) -> Unit,
|
onFileSelected: (String) -> Unit,
|
||||||
fileExtensions: List<String> = emptyList(),
|
fileExtensions: List<String> = emptyList(),
|
||||||
directoryOnly: Boolean = false,
|
directoryOnly: Boolean = false,
|
||||||
|
helpDescription: String? = null,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
)
|
)
|
||||||
|
|||||||
+43
-4
@@ -1,10 +1,11 @@
|
|||||||
package at.mocode.frontend.core.designsystem.components
|
package at.mocode.frontend.core.designsystem.components
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.material.icons.filled.HelpOutline
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.key.*
|
import androidx.compose.ui.input.key.*
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -21,6 +22,7 @@ fun MsStringDropdown(
|
|||||||
onOptionSelected: (String) -> Unit,
|
onOptionSelected: (String) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
placeholder: String = "",
|
placeholder: String = "",
|
||||||
|
helpDescription: String? = null,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
isError: Boolean = false,
|
isError: Boolean = false,
|
||||||
errorMessage: String? = null
|
errorMessage: String? = null
|
||||||
@@ -36,7 +38,44 @@ fun MsStringDropdown(
|
|||||||
value = selectedOption,
|
value = selectedOption,
|
||||||
onValueChange = {},
|
onValueChange = {},
|
||||||
readOnly = true,
|
readOnly = true,
|
||||||
label = { Text(label, style = MaterialTheme.typography.bodySmall) },
|
label = {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
Text(label, style = MaterialTheme.typography.bodySmall)
|
||||||
|
if (helpDescription != null) {
|
||||||
|
var showHelp by remember { mutableStateOf(false) }
|
||||||
|
Box {
|
||||||
|
IconButton(
|
||||||
|
onClick = { showHelp = !showHelp },
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.HelpOutline,
|
||||||
|
contentDescription = "Hilfe",
|
||||||
|
tint = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f),
|
||||||
|
modifier = Modifier.size(14.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (showHelp) {
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
TooltipBox(
|
||||||
|
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
tooltip = {
|
||||||
|
PlainTooltip {
|
||||||
|
Text(helpDescription)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state = rememberTooltipState(isPersistent = true)
|
||||||
|
) {
|
||||||
|
// Tooltip wird durch Klick auf das Icon getriggert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
placeholder = { Text(placeholder, style = MaterialTheme.typography.bodySmall) },
|
placeholder = { Text(placeholder, style = MaterialTheme.typography.bodySmall) },
|
||||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
||||||
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
|
colors = ExposedDropdownMenuDefaults.outlinedTextFieldColors(),
|
||||||
|
|||||||
+44
-6
@@ -3,6 +3,8 @@ package at.mocode.frontend.core.designsystem.components
|
|||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.filled.HelpOutline
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -27,6 +29,7 @@ fun MsTextField(
|
|||||||
isError: Boolean = false,
|
isError: Boolean = false,
|
||||||
errorMessage: String? = null,
|
errorMessage: String? = null,
|
||||||
helperText: String? = null,
|
helperText: String? = null,
|
||||||
|
helpDescription: String? = null,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
readOnly: Boolean = false,
|
readOnly: Boolean = false,
|
||||||
singleLine: Boolean = true,
|
singleLine: Boolean = true,
|
||||||
@@ -41,12 +44,47 @@ fun MsTextField(
|
|||||||
|
|
||||||
Column(modifier = modifier) {
|
Column(modifier = modifier) {
|
||||||
if (label != null) {
|
if (label != null) {
|
||||||
Text(
|
Row(
|
||||||
text = label,
|
modifier = Modifier.padding(bottom = 4.dp, start = 4.dp),
|
||||||
style = MaterialTheme.typography.labelMedium,
|
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically,
|
||||||
color = if (isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurfaceVariant,
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
modifier = Modifier.padding(bottom = 4.dp, start = 4.dp)
|
) {
|
||||||
)
|
Text(
|
||||||
|
text = label,
|
||||||
|
style = MaterialTheme.typography.labelMedium,
|
||||||
|
color = if (isError) MaterialTheme.colorScheme.error else MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
if (helpDescription != null) {
|
||||||
|
var showHelp by remember { mutableStateOf(false) }
|
||||||
|
Box {
|
||||||
|
IconButton(
|
||||||
|
onClick = { showHelp = !showHelp },
|
||||||
|
modifier = Modifier.size(16.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.HelpOutline,
|
||||||
|
contentDescription = "Hilfe",
|
||||||
|
tint = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f),
|
||||||
|
modifier = Modifier.size(14.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (showHelp) {
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
TooltipBox(
|
||||||
|
positionProvider = TooltipDefaults.rememberPlainTooltipPositionProvider(),
|
||||||
|
tooltip = {
|
||||||
|
PlainTooltip {
|
||||||
|
Text(helpDescription)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
state = rememberTooltipState(isPersistent = true)
|
||||||
|
) {
|
||||||
|
// Tooltip wird durch Klick auf das Icon getriggert
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
|
|||||||
+2
@@ -20,6 +20,7 @@ actual fun MsFilePicker(
|
|||||||
onFileSelected: (String) -> Unit,
|
onFileSelected: (String) -> Unit,
|
||||||
fileExtensions: List<String>,
|
fileExtensions: List<String>,
|
||||||
directoryOnly: Boolean,
|
directoryOnly: Boolean,
|
||||||
|
helpDescription: String?,
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
modifier: Modifier
|
modifier: Modifier
|
||||||
) {
|
) {
|
||||||
@@ -32,6 +33,7 @@ actual fun MsFilePicker(
|
|||||||
onValueChange = { },
|
onValueChange = { },
|
||||||
readOnly = true,
|
readOnly = true,
|
||||||
label = label,
|
label = label,
|
||||||
|
helpDescription = helpDescription,
|
||||||
placeholder = if (directoryOnly) "Verzeichnis wählen..." else "Datei wählen...",
|
placeholder = if (directoryOnly) "Verzeichnis wählen..." else "Datei wählen...",
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
enabled = enabled,
|
enabled = enabled,
|
||||||
|
|||||||
+1
@@ -10,6 +10,7 @@ actual fun MsFilePicker(
|
|||||||
onFileSelected: (String) -> Unit,
|
onFileSelected: (String) -> Unit,
|
||||||
fileExtensions: List<String>,
|
fileExtensions: List<String>,
|
||||||
directoryOnly: Boolean,
|
directoryOnly: Boolean,
|
||||||
|
helpDescription: String?,
|
||||||
enabled: Boolean,
|
enabled: Boolean,
|
||||||
modifier: Modifier
|
modifier: Modifier
|
||||||
) {
|
) {
|
||||||
|
|||||||
+1
@@ -27,6 +27,7 @@ data class ExpectedClient(
|
|||||||
@Serializable
|
@Serializable
|
||||||
data class DeviceInitializationSettings(
|
data class DeviceInitializationSettings(
|
||||||
val deviceName: String = "",
|
val deviceName: String = "",
|
||||||
|
val networkInterface: String = "",
|
||||||
val sharedKey: String = "",
|
val sharedKey: String = "",
|
||||||
val backupPath: String = "",
|
val backupPath: String = "",
|
||||||
val networkRole: NetworkRole = NetworkRole.CLIENT,
|
val networkRole: NetworkRole = NetworkRole.CLIENT,
|
||||||
|
|||||||
+70
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
package at.mocode.frontend.features.device.initialization.presentation
|
package at.mocode.frontend.features.device.initialization.presentation
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.*
|
||||||
|
import androidx.compose.foundation.Canvas
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
@@ -10,6 +12,7 @@ import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
|||||||
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
import androidx.compose.material.icons.filled.Edit
|
import androidx.compose.material.icons.filled.Edit
|
||||||
|
import androidx.compose.material.icons.filled.NetworkCheck
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
@@ -19,12 +22,65 @@ import androidx.compose.ui.focus.FocusRequester
|
|||||||
import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component1
|
import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component1
|
||||||
import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component2
|
import androidx.compose.ui.focus.FocusRequester.Companion.FocusRequesterFactory.component2
|
||||||
import androidx.compose.ui.focus.focusRequester
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
import androidx.compose.ui.input.key.*
|
import androidx.compose.ui.input.key.*
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import at.mocode.frontend.features.device.initialization.domain.DeviceInitializationValidator
|
import at.mocode.frontend.features.device.initialization.domain.DeviceInitializationValidator
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun DiscoveryRadar() {
|
||||||
|
val infiniteTransition = rememberInfiniteTransition()
|
||||||
|
val radius by infiniteTransition.animateFloat(
|
||||||
|
initialValue = 0f,
|
||||||
|
targetValue = 40f,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(2000, easing = LinearEasing),
|
||||||
|
repeatMode = RepeatMode.Restart
|
||||||
|
)
|
||||||
|
)
|
||||||
|
val alpha by infiniteTransition.animateFloat(
|
||||||
|
initialValue = 1f,
|
||||||
|
targetValue = 0f,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(2000, easing = LinearEasing),
|
||||||
|
repeatMode = RepeatMode.Restart
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val color = MaterialTheme.colorScheme.primary
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.size(80.dp),
|
||||||
|
contentAlignment = androidx.compose.ui.Alignment.Center
|
||||||
|
) {
|
||||||
|
Canvas(modifier = Modifier.fillMaxSize()) {
|
||||||
|
drawCircle(
|
||||||
|
color = color,
|
||||||
|
radius = radius.dp.toPx(),
|
||||||
|
center = Offset(size.width / 2, size.height / 2),
|
||||||
|
style = Stroke(width = 2.dp.toPx()),
|
||||||
|
alpha = alpha
|
||||||
|
)
|
||||||
|
drawCircle(
|
||||||
|
color = color,
|
||||||
|
radius = (radius * 0.5f).dp.toPx(),
|
||||||
|
center = Offset(size.width / 2, size.height / 2),
|
||||||
|
style = Stroke(width = 1.dp.toPx()),
|
||||||
|
alpha = alpha * 0.5f
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.NetworkCheck,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = color,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DeviceInitializationScreen(
|
fun DeviceInitializationScreen(
|
||||||
viewModel: DeviceInitializationViewModel
|
viewModel: DeviceInitializationViewModel
|
||||||
@@ -67,6 +123,20 @@ fun DeviceInitializationScreen(
|
|||||||
style = MaterialTheme.typography.bodySmall
|
style = MaterialTheme.typography.bodySmall
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth().padding(vertical = 8.dp),
|
||||||
|
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
DiscoveryRadar()
|
||||||
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
Text(
|
||||||
|
"Suche nach Geräten im Netzwerk...",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
NetworkRoleSelector(
|
NetworkRoleSelector(
|
||||||
selectedRole = uiState.settings.networkRole,
|
selectedRole = uiState.settings.networkRole,
|
||||||
onRoleSelected = {
|
onRoleSelected = {
|
||||||
|
|||||||
+11
-1
@@ -8,7 +8,17 @@ import kotlinx.serialization.json.Json
|
|||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
actual object DeviceInitializationSettingsManager {
|
actual object DeviceInitializationSettingsManager {
|
||||||
private val settingsFile = File("settings.json")
|
private val settingsFile: File by lazy {
|
||||||
|
val os = System.getProperty("os.name").lowercase()
|
||||||
|
val appName = "Meldestelle"
|
||||||
|
val baseDir = when {
|
||||||
|
os.contains("win") -> File(System.getenv("APPDATA"), appName)
|
||||||
|
os.contains("mac") -> File(System.getProperty("user.home"), "Library/Application Support/$appName")
|
||||||
|
else -> File(System.getProperty("user.home"), ".config/$appName")
|
||||||
|
}
|
||||||
|
if (!baseDir.exists()) baseDir.mkdirs()
|
||||||
|
File(baseDir, "settings.json")
|
||||||
|
}
|
||||||
private val json = Json { prettyPrint = true; ignoreUnknownKeys = true }
|
private val json = Json { prettyPrint = true; ignoreUnknownKeys = true }
|
||||||
|
|
||||||
actual fun saveSettings(settings: DeviceInitializationSettings) {
|
actual fun saveSettings(settings: DeviceInitializationSettings) {
|
||||||
|
|||||||
+21
@@ -34,6 +34,7 @@ import at.mocode.frontend.core.designsystem.components.MsStringDropdown
|
|||||||
import at.mocode.frontend.core.designsystem.components.MsTextField
|
import at.mocode.frontend.core.designsystem.components.MsTextField
|
||||||
import at.mocode.frontend.features.device.initialization.domain.DeviceInitializationValidator
|
import at.mocode.frontend.features.device.initialization.domain.DeviceInitializationValidator
|
||||||
import at.mocode.frontend.features.device.initialization.domain.model.NetworkRole
|
import at.mocode.frontend.features.device.initialization.domain.model.NetworkRole
|
||||||
|
import java.net.NetworkInterface
|
||||||
import javax.print.PrintServiceLookup
|
import javax.print.PrintServiceLookup
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -57,6 +58,7 @@ actual fun DeviceInitializationConfig(
|
|||||||
value = settings.deviceName,
|
value = settings.deviceName,
|
||||||
onValueChange = { viewModel.updateSettings { s -> s.copy(deviceName = it) } },
|
onValueChange = { viewModel.updateSettings { s -> s.copy(deviceName = it) } },
|
||||||
label = "Gerätename",
|
label = "Gerätename",
|
||||||
|
helpDescription = "Ein eindeutiger Name für diesen PC (z.B. 'Richter-Springplatz'). Dies hilft dem Master, die Datenquellen zuzuordnen.",
|
||||||
placeholder = "z.B. Meldestelle-PC-1",
|
placeholder = "z.B. Meldestelle-PC-1",
|
||||||
isError = settings.deviceName.isNotEmpty() && !DeviceInitializationValidator.isNameValid(settings.deviceName),
|
isError = settings.deviceName.isNotEmpty() && !DeviceInitializationValidator.isNameValid(settings.deviceName),
|
||||||
errorMessage = "Mindestens ${DeviceInitializationValidator.MIN_NAME_LENGTH} Zeichen erforderlich.",
|
errorMessage = "Mindestens ${DeviceInitializationValidator.MIN_NAME_LENGTH} Zeichen erforderlich.",
|
||||||
@@ -66,11 +68,28 @@ actual fun DeviceInitializationConfig(
|
|||||||
enabled = !uiState.isLocked
|
enabled = !uiState.isLocked
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val interfaces = remember {
|
||||||
|
NetworkInterface.getNetworkInterfaces().toList()
|
||||||
|
.filter { it.isUp && !it.isLoopback && it.inetAddresses.hasMoreElements() }
|
||||||
|
.map { "${it.displayName} (${it.inetAddresses.nextElement().hostAddress})" }
|
||||||
|
}
|
||||||
|
|
||||||
|
MsStringDropdown(
|
||||||
|
label = "Netzwerk-Interface",
|
||||||
|
helpDescription = "Wähle das Netzwerk-Interface aus, über das die App kommunizieren soll (z.B. LAN für das Turnier-Netzwerk).",
|
||||||
|
options = interfaces,
|
||||||
|
selectedOption = settings.networkInterface,
|
||||||
|
onOptionSelected = { viewModel.updateSettings { s -> s.copy(networkInterface = it) } },
|
||||||
|
placeholder = "Interface wählen...",
|
||||||
|
enabled = !uiState.isLocked
|
||||||
|
)
|
||||||
|
|
||||||
var passwordVisible by remember { mutableStateOf(false) }
|
var passwordVisible by remember { mutableStateOf(false) }
|
||||||
MsTextField(
|
MsTextField(
|
||||||
value = settings.sharedKey,
|
value = settings.sharedKey,
|
||||||
onValueChange = { viewModel.updateSettings { s -> s.copy(sharedKey = it) } },
|
onValueChange = { viewModel.updateSettings { s -> s.copy(sharedKey = it) } },
|
||||||
label = "Sicherheitsschlüssel (Sync-Key)",
|
label = "Sicherheitsschlüssel (Sync-Key)",
|
||||||
|
helpDescription = "Das 'Turnier-Passwort'. Nur Geräte mit exakt diesem Schlüssel können Daten austauschen. Wichtig für die Verschlüsselung (DSGVO)!",
|
||||||
placeholder = "Mindestens 8 Zeichen",
|
placeholder = "Mindestens 8 Zeichen",
|
||||||
isError = settings.sharedKey.isNotEmpty() && !DeviceInitializationValidator.isKeyValid(settings.sharedKey),
|
isError = settings.sharedKey.isNotEmpty() && !DeviceInitializationValidator.isKeyValid(settings.sharedKey),
|
||||||
errorMessage = "Mindestens ${DeviceInitializationValidator.MIN_KEY_LENGTH} Zeichen erforderlich.",
|
errorMessage = "Mindestens ${DeviceInitializationValidator.MIN_KEY_LENGTH} Zeichen erforderlich.",
|
||||||
@@ -87,6 +106,7 @@ actual fun DeviceInitializationConfig(
|
|||||||
|
|
||||||
MsFilePicker(
|
MsFilePicker(
|
||||||
label = "Backup-Verzeichnis (Pfad)",
|
label = "Backup-Verzeichnis (Pfad)",
|
||||||
|
helpDescription = "Wähle hier deinen USB-Stick oder einen lokalen Ordner aus. Die App speichert hier laufend Sicherheitskopien für den Notfall (Plan-USB).",
|
||||||
selectedPath = settings.backupPath,
|
selectedPath = settings.backupPath,
|
||||||
onFileSelected = { selectedPath ->
|
onFileSelected = { selectedPath ->
|
||||||
viewModel.updateSettings { s -> s.copy(backupPath = selectedPath) }
|
viewModel.updateSettings { s -> s.copy(backupPath = selectedPath) }
|
||||||
@@ -102,6 +122,7 @@ actual fun DeviceInitializationConfig(
|
|||||||
|
|
||||||
MsStringDropdown(
|
MsStringDropdown(
|
||||||
label = "Standard-Drucker",
|
label = "Standard-Drucker",
|
||||||
|
helpDescription = "Der Drucker, der standardmäßig für Protokolle und Listen verwendet wird. Kann später jederzeit geändert werden.",
|
||||||
options = printers,
|
options = printers,
|
||||||
selectedOption = settings.defaultPrinter,
|
selectedOption = settings.defaultPrinter,
|
||||||
onOptionSelected = { viewModel.updateSettings { s -> s.copy(defaultPrinter = it) } },
|
onOptionSelected = { viewModel.updateSettings { s -> s.copy(defaultPrinter = it) } },
|
||||||
|
|||||||
Reference in New Issue
Block a user