From 21c3b6cc89609613cccfea54bdb3043146075efe Mon Sep 17 00:00:00 2001 From: SAUL Date: Wed, 23 Oct 2024 12:18:43 +0400 Subject: [PATCH] Password Form Field validation --- .../core/form/validation/CommonRules.kt | 7 + .../ui/components/forms/PasswordForm.kt | 214 +++++++++++++----- .../kotlin/ui/screens/PasswordFormScreen.kt | 14 +- .../ui/validators/PasswordFormValidator.kt | 63 ++++++ 4 files changed, 239 insertions(+), 59 deletions(-) create mode 100644 src/main/kotlin/ui/validators/PasswordFormValidator.kt diff --git a/src/main/kotlin/core/form/validation/CommonRules.kt b/src/main/kotlin/core/form/validation/CommonRules.kt index dc21670..0c45392 100644 --- a/src/main/kotlin/core/form/validation/CommonRules.kt +++ b/src/main/kotlin/core/form/validation/CommonRules.kt @@ -9,6 +9,13 @@ val emailRule: ValidationRule = ValidationRule( errorMessage = "Invalid email address" ) +val urlRule: ValidationRule = ValidationRule( + condition = {url -> + url.matches(Regex("^https?://(?:www\\.)?[-a-zA-Z0-9@:%._+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b[-a-zA-Z0-9()@:%_+.~#?&/=]*$")) + }, + errorMessage = "Invalid URL" +) + val passwordRule: ValidationRule = ValidationRule( condition = { password -> password.length >= 8 && diff --git a/src/main/kotlin/ui/components/forms/PasswordForm.kt b/src/main/kotlin/ui/components/forms/PasswordForm.kt index 42d64bb..5d0fca5 100644 --- a/src/main/kotlin/ui/components/forms/PasswordForm.kt +++ b/src/main/kotlin/ui/components/forms/PasswordForm.kt @@ -17,25 +17,39 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import core.form.validation.FormValidator import ui.components.FormTextField import ui.components.PasswordTextField import ui.theme.Font import ui.theme.primary import ui.theme.secondary +import ui.validators.PasswordFormFieldName @Preview @Composable -fun PasswordForm() { +fun PasswordForm( + formValidator: FormValidator, + isFormValid: Boolean, + onSaveClick: () -> Unit +) { + + val userName = formValidator.getField(PasswordFormFieldName.USERNAME) + val email = formValidator.getField(PasswordFormFieldName.EMAIL) + val password = formValidator.getField(PasswordFormFieldName.PASSWORD) + + val name = formValidator.getField(PasswordFormFieldName.NAME) + val webSiteUrl = formValidator.getField(PasswordFormFieldName.WEBSITE_URL) + val icon = formValidator.getField(PasswordFormFieldName.WEBSITE_ICON_URL) Column( modifier = Modifier.fillMaxSize() - .background(secondary) + .background(secondary) ) { Row( modifier = Modifier.weight(1f) - .fillMaxSize(), + .fillMaxSize(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Start ) { @@ -52,86 +66,170 @@ fun PasswordForm() { Row( modifier = Modifier.weight(7.5f) - .fillMaxSize() + .fillMaxSize() ) { Column( modifier = Modifier.weight(1f) - .fillMaxSize() - .background(primary) - .padding(PaddingValues(end = 20.dp)), + .fillMaxSize() + .background(primary) + .padding(PaddingValues(end = 20.dp)), horizontalAlignment = Alignment.End, - verticalArrangement = Arrangement.spacedBy(30.dp, Alignment.CenterVertically) + verticalArrangement = Arrangement.spacedBy(5.dp, Alignment.CenterVertically) ) { - FormTextField( - value = "fe", - onValueChange = {}, - label = "Username", - icon = Icons.Filled.AccountCircle, - modifier = Modifier.height(45.dp).width(400.dp) - ) - FormTextField( - value = "fe", - onValueChange = {}, - label = "Email", - icon = Icons.Filled.Email, - modifier = Modifier.height(45.dp).width(400.dp) - ) - PasswordTextField( - value = "fe", - onValueChange = {}, - label = "Password", - modifier = Modifier.height(45.dp).width(400.dp) - ) + Column(modifier = Modifier.height(80.dp)) { + FormTextField( + value = userName?.value?.value ?: "", + onValueChange = { newValue -> + userName?.value?.value = newValue + formValidator.validateField(PasswordFormFieldName.USERNAME) + }, + label = "Username", + icon = Icons.Filled.AccountCircle, + modifier = Modifier.height(45.dp).width(400.dp) + ) + userName?.error?.value?.let { + Text( + text = it, + color = Color.Red, + fontFamily = Font.RobotoRegular, + fontSize = 10.sp, + modifier = Modifier.padding(start = 8.dp) + ) + } + } + Column(modifier = Modifier.height(80.dp)) { + FormTextField( + value = email?.value?.value ?: "", + onValueChange = { newValue -> + email?.value?.value = newValue + formValidator.validateField(PasswordFormFieldName.EMAIL) + }, + label = "Email", + icon = Icons.Filled.Email, + modifier = Modifier.height(45.dp).width(400.dp) + ) + email?.error?.value?.let { + Text( + text = it, + color = Color.Red, + fontFamily = Font.RobotoRegular, + fontSize = 10.sp, + modifier = Modifier.padding(start = 8.dp) + ) + } + } + Column(modifier = Modifier.height(80.dp)) { + PasswordTextField( + value = password?.value?.value ?: "", + onValueChange = { newValue -> + password?.value?.value = newValue + formValidator.validateField(PasswordFormFieldName.PASSWORD) + }, + label = "Password", + modifier = Modifier.height(45.dp).width(400.dp) + ) + password?.error?.value?.let { + Text( + text = it, + color = Color.Red, + fontFamily = Font.RobotoRegular, + fontSize = 10.sp, + modifier = Modifier.padding(start = 8.dp) + ) + } + } } Column( modifier = Modifier.weight(1f) - .fillMaxSize() - .background(primary) - .padding(PaddingValues(start = 20.dp)), + .fillMaxSize() + .background(primary) + .padding(PaddingValues(start = 20.dp)), horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.spacedBy(30.dp, Alignment.CenterVertically) + verticalArrangement = Arrangement.spacedBy(5.dp, Alignment.CenterVertically) ) { - FormTextField( - value = "fe", - onValueChange = {}, - label = "Name", - icon = Icons.Filled.Web, - modifier = Modifier.height(45.dp).width(400.dp) - ) - FormTextField( - value = "fe", - onValueChange = {}, - label = "Website Url", - icon = Icons.Filled.Link, - modifier = Modifier.height(45.dp).width(400.dp) - ) - FormTextField( - value = "fe", - onValueChange = {}, - label = "Website Icon Url", - icon = Icons.Filled.Info, - modifier = Modifier.height(45.dp).width(400.dp) - ) + Column(modifier = Modifier.height(80.dp)) { + FormTextField( + value = name?.value?.value ?: "", + onValueChange = { newValue -> + name?.value?.value = newValue + formValidator.validateField(PasswordFormFieldName.NAME) + }, + label = "Name", + icon = Icons.Filled.Web, + modifier = Modifier.height(45.dp).width(400.dp) + ) + name?.error?.value?.let { + Text( + text = it, + color = Color.Red, + fontFamily = Font.RobotoRegular, + fontSize = 10.sp, + modifier = Modifier.padding(start = 8.dp) + ) + } + } + Column(modifier = Modifier.height(80.dp)) { + FormTextField( + value = webSiteUrl?.value?.value ?: "", + onValueChange = { newValue -> + webSiteUrl?.value?.value = newValue + formValidator.validateField(PasswordFormFieldName.WEBSITE_URL) + }, + label = "Website Url", + icon = Icons.Filled.Link, + modifier = Modifier.height(45.dp).width(400.dp) + ) + webSiteUrl?.error?.value?.let { + Text( + text = it, + color = Color.Red, + fontFamily = Font.RobotoRegular, + fontSize = 10.sp, + modifier = Modifier.padding(start = 8.dp) + ) + } + } + Column(modifier = Modifier.height(80.dp)) { + FormTextField( + value = icon?.value?.value ?: "", + onValueChange = { newValue -> + icon?.value?.value = newValue + formValidator.validateField(PasswordFormFieldName.WEBSITE_ICON_URL) + }, + label = "Website Icon Url", + icon = Icons.Filled.Info, + modifier = Modifier.height(45.dp).width(400.dp) + ) + icon?.error?.value?.let { + Text( + text = it, + color = Color.Red, + fontFamily = Font.RobotoRegular, + fontSize = 10.sp, + modifier = Modifier.padding(start = 8.dp) + ) + } + } } } Row( modifier = Modifier.weight(1.5f) - .fillMaxSize() - .background(primary), + .fillMaxSize() + .background(primary), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(30.dp, Alignment.CenterHorizontally) ) { Button( onClick = {}, modifier = Modifier.width(175.dp), - enabled = true, + enabled = isFormValid, colors = ButtonColors( containerColor = secondary, contentColor = Color.White, - disabledContentColor = secondary, + disabledContentColor = Color.Gray, disabledContainerColor = secondary, ) ) @@ -146,7 +244,7 @@ fun PasswordForm() { ) } Button( - onClick = {}, + onClick = onSaveClick, modifier = Modifier.width(175.dp), colors = ButtonColors( containerColor = secondary, diff --git a/src/main/kotlin/ui/screens/PasswordFormScreen.kt b/src/main/kotlin/ui/screens/PasswordFormScreen.kt index 07ed5a8..a5afe81 100644 --- a/src/main/kotlin/ui/screens/PasswordFormScreen.kt +++ b/src/main/kotlin/ui/screens/PasswordFormScreen.kt @@ -1,14 +1,26 @@ package ui.screens import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import cafe.adriel.voyager.core.screen.Screen import ui.components.forms.PasswordForm +import ui.validators.passwordFormValidator class PasswordFormScreen : Screen { @Composable override fun Content() { - PasswordForm() + + val formValidator = remember { passwordFormValidator() } + + val isFormValid by formValidator.isValid + + PasswordForm( + formValidator, + isFormValid, + onSaveClick = {} + ) } } \ No newline at end of file diff --git a/src/main/kotlin/ui/validators/PasswordFormValidator.kt b/src/main/kotlin/ui/validators/PasswordFormValidator.kt new file mode 100644 index 0000000..47b6872 --- /dev/null +++ b/src/main/kotlin/ui/validators/PasswordFormValidator.kt @@ -0,0 +1,63 @@ +package ui.validators + +import core.form.FormField +import core.form.FormFieldName +import core.form.validation.* + +fun passwordFormValidator(): FormValidator { + return FormValidator() + .addField( + PasswordFormFieldName.USERNAME, FormField( + validator = Validator() + .addRule(notNullRule(PasswordFormFieldName.USERNAME.fieldName)) + .addRule(lengthRule(PasswordFormFieldName.USERNAME.fieldName, 1)) + ) + ) + .addField( + PasswordFormFieldName.EMAIL, FormField( + validator = Validator() + .addRule(notNullRule(PasswordFormFieldName.EMAIL.fieldName)) + .addRule(lengthRule(PasswordFormFieldName.EMAIL.fieldName, 1)) + .addRule(emailRule) + ) + ) + .addField( + PasswordFormFieldName.PASSWORD, FormField( + validator = Validator() + .addRule(notNullRule(PasswordFormFieldName.PASSWORD.fieldName)) + .addRule(passwordRule) + ) + ) + .addField( + PasswordFormFieldName.NAME, FormField( + validator = Validator() + .addRule(notNullRule(PasswordFormFieldName.NAME.fieldName)) + .addRule(lengthRule(PasswordFormFieldName.NAME.fieldName, 1)) + ) + ) + .addField( + PasswordFormFieldName.WEBSITE_URL, FormField( + validator = Validator() + .addRule(notNullRule(PasswordFormFieldName.WEBSITE_URL.fieldName)) + .addRule(lengthRule(PasswordFormFieldName.WEBSITE_URL.fieldName, 1)) + .addRule(urlRule) + ) + ) + .addField( + PasswordFormFieldName.WEBSITE_ICON_URL, FormField( + validator = Validator() + .addRule(notNullRule(PasswordFormFieldName.WEBSITE_ICON_URL.fieldName)) + .addRule(lengthRule(PasswordFormFieldName.WEBSITE_ICON_URL.fieldName, 1)) + .addRule(urlRule) + ) + ).validateAllFields() +} + +enum class PasswordFormFieldName(val fieldName: String) : FormFieldName { + USERNAME("Username"), + EMAIL("Email"), + PASSWORD("Password"), + NAME("Name"), + WEBSITE_URL("Website URL"), + WEBSITE_ICON_URL("Website Icon URL"); +} \ No newline at end of file