diff --git a/app/src/main/java/com/mob/utsmyanmar/ui/password_Input/InputPassword.kt b/app/src/main/java/com/mob/utsmyanmar/ui/password_Input/InputPassword.kt new file mode 100644 index 0000000..31c0981 --- /dev/null +++ b/app/src/main/java/com/mob/utsmyanmar/ui/password_Input/InputPassword.kt @@ -0,0 +1,334 @@ +package com.mob.utsmyanmar.ui.password_Input + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.navigationBarsPadding +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.statusBarsPadding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBackIosNew +import androidx.compose.material.icons.rounded.Backspace +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.shadow +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 com.mob.utsmyanmar.ui.components.appbar.AppBar +import com.mob.utsmyanmar.ui.preview.P2Preview +import com.mob.utsmyanmar.ui.theme.Color + + +@Composable +fun InputPassword( + title: String = "Password", + onBackClick: () -> Unit = {}, + onChargeClick: (String) -> Unit = {} +) { + var amount by remember { mutableStateOf("") } + + Scaffold( + topBar = { + AppBar(title = title, icon = Icons.Default.ArrowBackIosNew, onIconClick = onBackClick) + }, + containerColor = Color.IvoryBeige + ) { paddingValues -> + + NumericEntryScreen( + modifier = Modifier.padding(paddingValues), + title = title, + prompt = "Enter $title", + supportingText = "Enter your secure password", + confirmText = "Next", + prefixLabel = "* * * * * *", + onBackClick = onBackClick, + onCancelClick = onBackClick, + onConfirmClick = { + if (amount.isNotEmpty()) { + onChargeClick(amount) + } + }, + onKeyClick = { value -> + amount = appendAmountValue(amount, value) + }, + onDeleteClick = { + amount = amount.dropLast(1) + }, + confirmEnabled = amount.isNotEmpty(), + canDelete = amount.isNotEmpty() + ) + } +} + +@Composable +fun NumericEntryScreen( + title: String, + prompt: String, + supportingText: String, + confirmText: String, + onBackClick: () -> Unit, + onCancelClick: () -> Unit, + onConfirmClick: () -> Unit, + onKeyClick: (String) -> Unit, + onDeleteClick: () -> Unit, + modifier: Modifier = Modifier, + prefixLabel: String? = null, + errorMessage: String? = null, + confirmEnabled: Boolean = true, + canDelete: Boolean = true, + keys: List> = listOf( + listOf("1", "2", "3"), + listOf("4", "5", "6"), + listOf("7", "8", "9"), + listOf( ".","0","00") + ) +) { + Box( + modifier = modifier + .fillMaxSize() + .background(Color.White) + .navigationBarsPadding() + .statusBarsPadding() + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 20.dp) + ) { + Text( + text = prompt, + color = Color.Gray, + fontSize = 11.sp, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Row( + modifier = Modifier.align(Alignment.CenterHorizontally), + verticalAlignment = Alignment.Bottom + ) { + if (!prefixLabel.isNullOrBlank()) { + Text( + text = prefixLabel, + color = Color.LegacyRed, + fontSize = 25.sp, + fontWeight = FontWeight.Medium, + modifier = Modifier.padding(top = 25.dp) + ) + } + + + } + + Spacer(modifier = Modifier.height(20.dp)) + + Text( + text = supportingText, + color = if (errorMessage == null) Color.Gray else Color.LegacyRed, + fontSize = 11.sp, + modifier = Modifier.align(Alignment.CenterHorizontally) + ) + + Spacer(modifier = Modifier.height(40.dp)) + + NumericKeypad( + keys = keys, + onKeyClick = onKeyClick + ) + + Spacer(modifier = Modifier.weight(1f)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + Button( + onClick = onCancelClick, + modifier = Modifier + .weight(1f) + .height(56.dp), + shape = RoundedCornerShape(8.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color.White, + contentColor = Color.LegacyRed + ) + ) { + Text("Cancel") + } + + Button( + onClick = onConfirmClick, + modifier = Modifier + .weight(1f) + .height(56.dp), + enabled = confirmEnabled, + shape = RoundedCornerShape(8.dp), + colors = ButtonDefaults.buttonColors( + containerColor = Color.LegacyRed, + contentColor = Color.White, + disabledContainerColor = Color.LegacyRed.copy(alpha = 0.5f), + disabledContentColor = Color.White + ) + ) { + Text( + text = confirmText, + fontSize = 14.sp, + fontWeight = FontWeight.Medium + ) + } + } + + Spacer(modifier = Modifier.height(18.dp)) + } + + Card( + modifier = Modifier + .align(Alignment.TopEnd) + .padding(top = 32.dp, end = 20.dp) + .clickable(enabled = canDelete) { + onDeleteClick() + }, + shape = RoundedCornerShape(18.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + elevation = CardDefaults.cardElevation(defaultElevation = 6.dp) + ) { + Icon( + imageVector = Icons.Rounded.Backspace, + contentDescription = "Delete", + tint = if (canDelete) Color.LegacyRed else Color.Gray, + modifier = Modifier.padding(horizontal = 14.dp, vertical = 12.dp) + ) + } + } +} + +@Composable +private fun NumericKeypad( + keys: List>, + onKeyClick: (String) -> Unit +) { + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(6.dp) + ) { + keys.forEach { row -> + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + row.forEach { key -> + KeypadButton( + text = key, + modifier = Modifier.weight(1f), + onClick = { onKeyClick(key) } + ) + } + } + } + } +} + +@Composable +private fun KeypadButton( + text: String, + modifier: Modifier = Modifier, + onClick: () -> Unit +) { + val enabled = text.isNotBlank() + + Box( + modifier = modifier + .height(66.dp) + .shadow( + elevation = 2.dp, + shape = RoundedCornerShape(8.dp), + clip = false + ) + .background( + color = Color.White, + shape = RoundedCornerShape(8.dp) + ) + .clickable(enabled = enabled) { onClick() }, + contentAlignment = Alignment.Center + ) { + Text( + text = text, + color = if (enabled) Color.LegacyRed else Color.White, + fontSize = 24.sp, + fontWeight = FontWeight.Normal, + textAlign = TextAlign.Center + ) + } +} + +private fun formatAmount(value: String): String { + val normalized = value.ifEmpty { "0" } + val wholePart = normalized.substringBefore(".").ifEmpty { "0" } + val groupedWholePart = "%,d".format(wholePart.toLongOrNull() ?: 0L) + + if (!normalized.contains(".")) { + return groupedWholePart + } + + val decimalPart = normalized.substringAfter(".", "") + return if (normalized.endsWith(".")) { + "$groupedWholePart." + } else { + "$groupedWholePart.$decimalPart" + } +} + +private fun appendAmountValue(current: String, value: String): String { + if (value == ".") { + if (current.contains(".")) return current + return if (current.isEmpty()) "0." else "$current." + } + + val decimalIndex = current.indexOf('.') + return if (decimalIndex >= 0) { + val decimalPart = current.substring(decimalIndex + 1) + val remainingDecimalDigits = 2 - decimalPart.length + if (remainingDecimalDigits <= 0) { + current + } else { + current + value.take(remainingDecimalDigits) + } + } else { + val wholeDigitsCount = current.filter(Char::isDigit).length + val remainingWholeDigits = 9 - wholeDigitsCount + if (remainingWholeDigits <= 0) { + current + } else { + current + value.take(remainingWholeDigits) + } + } +} + +@P2Preview +@Composable +fun PreviewInputPassword() { + InputPassword() +}