From 442caa59ff46942b73d5a18b30c13f1632f90d68 Mon Sep 17 00:00:00 2001 From: Stefan Mogeritsch Date: Tue, 31 Mar 2026 10:42:36 +0200 Subject: [PATCH] feat(design-system): add `MsValidationWrapper` component and update roadmap - Introduced `MsValidationWrapper` for consistent display of validation messages (errors, warnings, info) in input components. - Updated `Frontend_Komponenten_Roadmap.md` to mark this component as complete in Phase 3. Signed-off-by: Stefan Mogeritsch --- .../Frontend_Komponenten_Roadmap.md | 2 +- .../components/MsValidationWrapper.kt | 94 +++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 frontend/core/design-system/src/commonMain/kotlin/at/mocode/frontend/core/designsystem/components/MsValidationWrapper.kt diff --git a/docs/01_Architecture/Frontend_Komponenten_Roadmap.md b/docs/01_Architecture/Frontend_Komponenten_Roadmap.md index 8ed64444..dfbe26a5 100644 --- a/docs/01_Architecture/Frontend_Komponenten_Roadmap.md +++ b/docs/01_Architecture/Frontend_Komponenten_Roadmap.md @@ -44,7 +44,7 @@ Turniermanagement bedeutet Arbeit mit Listen. Wir benötigen mächtige, aber kom Eingabe von Stammdaten muss schnell und fehlerfrei erfolgen. * [x] **`MsEnumDropdown`:** Automatisches Mapping von Backend-Enums (ÖTO) auf UI-Auswahl. -* [ ] **`MsValidationWrapper`:** Konsistente Anzeige von Fehlern (z.B. "Lizenz für diese Klasse nicht ausreichend"). +* [x] **`MsValidationWrapper`:** Konsistente Anzeige von Fehlern und Warnungen (z.B. ÖTO-Validierungsregeln). * [ ] **`MsSearchableSelect`:** Für die Verknüpfung von Reitern/Pferden (Autocomplete). ## Phase 4: Layout-Patterns & Navigation ⚪ [ZUKUNFT] diff --git a/frontend/core/design-system/src/commonMain/kotlin/at/mocode/frontend/core/designsystem/components/MsValidationWrapper.kt b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/frontend/core/designsystem/components/MsValidationWrapper.kt new file mode 100644 index 00000000..e85d446f --- /dev/null +++ b/frontend/core/design-system/src/commonMain/kotlin/at/mocode/frontend/core/designsystem/components/MsValidationWrapper.kt @@ -0,0 +1,94 @@ +package at.mocode.frontend.core.designsystem.components + +import androidx.compose.foundation.layout.* +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ErrorOutline +import androidx.compose.material.icons.filled.Info +import androidx.compose.material.icons.filled.WarningAmber +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp + +/** + * Die Schwere einer Validierungsmeldung. + */ +enum class ValidationSeverity { + ERROR, // Blokierend (z.B. fehlende Pflichtfelder) + WARNING, // Hinweisend (z.B. § 39 ÖTO - Abteilungstrennung steht bevor) + INFO // Informativ (z.B. Reiter hat heute Geburtstag) +} + +/** + * Eine einzelne Validierungsmeldung. + */ +data class ValidationMessage( + val message: String, + val severity: ValidationSeverity = ValidationSeverity.ERROR +) + +/** + * Ein Wrapper für Eingabekomponenten, um Validierungsergebnisse (ÖTO-Regeln) anzuzeigen. + * + * @param messages Liste der anzuzeigenden Meldungen. + * @param modifier Der Modifier für den äußeren Container. + * @param content Die Eingabekomponente (z.B. MsTextField, MsEnumDropdown). + */ +@Composable +fun MsValidationWrapper( + messages: List, + modifier: Modifier = Modifier, + content: @Composable () -> Unit +) { + Column(modifier = modifier) { + // Die eigentliche Eingabekomponente + content() + + // Validierungsmeldungen unterhalb + if (messages.isNotEmpty()) { + Column( + modifier = Modifier + .padding(top = 4.dp, start = 12.dp) + .fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(2.dp) + ) { + messages.forEach { msg -> + ValidationRow(msg) + } + } + } + } +} + +/** + * Eine einzelne Zeile für eine Validierungsmeldung mit passendem Icon und Farbe. + */ +@Composable +private fun ValidationRow(msg: ValidationMessage) { + val (color, icon) = when (msg.severity) { + ValidationSeverity.ERROR -> MaterialTheme.colorScheme.error to Icons.Default.ErrorOutline + ValidationSeverity.WARNING -> Color(0xFFEF6C00) to Icons.Default.WarningAmber // Warmer Orange-Ton + ValidationSeverity.INFO -> MaterialTheme.colorScheme.primary to Icons.Default.Info + } + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + Icon( + imageVector = icon, + contentDescription = null, + modifier = Modifier.size(14.dp), + tint = color + ) + Text( + text = msg.message, + style = MaterialTheme.typography.labelSmall, + color = color + ) + } +}