diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 305a9c4..9d57ed1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -51,6 +51,7 @@ dependencies { implementation(libs.androidx.material3) implementation(libs.androidx.navigation.fragment.ktx) implementation(libs.androidx.navigation.ui.ktx) + implementation(libs.androidx.ui) implementation(libs.material) testImplementation(libs.junit) androidTestImplementation(platform(libs.androidx.compose.bom)) diff --git a/app/src/main/java/com/example/dattingapp/FriendProfile.kt b/app/src/main/java/com/example/dattingapp/FriendProfile.kt index fc5d832..3e65795 100644 --- a/app/src/main/java/com/example/dattingapp/FriendProfile.kt +++ b/app/src/main/java/com/example/dattingapp/FriendProfile.kt @@ -45,7 +45,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.example.dattingapp.ui.theme.AccentPurple import com.example.dattingapp.ui.theme.DattingAPPTheme -import com.example.dattingapp.ui.theme.TextDark +import com.example.dattingapp.ui.theme.textDark import com.example.dattingapp.ui.theme.BadgePink import java.nio.file.WatchEvent @@ -147,13 +147,13 @@ fun FriendProfileScreen( text = "Elena", fontSize = 24.sp, fontWeight = FontWeight.Bold, - color = TextDark + color = textDark ) Text( text = "24", fontSize = 24.sp, fontWeight = FontWeight.Bold, - color = TextDark + color = textDark ) Surface( shape = RoundedCornerShape(12.dp), diff --git a/app/src/main/java/com/example/dattingapp/Login/IdVerification.kt b/app/src/main/java/com/example/dattingapp/Login/IdVerification.kt index ea4a44c..e18b17e 100644 --- a/app/src/main/java/com/example/dattingapp/Login/IdVerification.kt +++ b/app/src/main/java/com/example/dattingapp/Login/IdVerification.kt @@ -135,7 +135,7 @@ fun IdVerificationScreen( Icon( imageVector = Icons.Outlined.ArrowBack, contentDescription = "Back", - tint = TextDark, + tint = textDark, modifier = Modifier.size(18.dp) ) } @@ -186,7 +186,7 @@ fun IdVerificationScreen( text = "ID Verification", fontSize = 20.sp, fontWeight = FontWeight.Bold, - color = TextDark + color = textDark ) Spacer(modifier = Modifier.height(6.dp)) @@ -221,7 +221,7 @@ fun IdVerificationScreen( Icon( imageVector = Icons.Default.AddAPhoto, contentDescription = "Upload", - tint = TextDark, + tint = textDark, modifier = Modifier.size(32.dp) ) Spacer(modifier = Modifier.height(8.dp)) @@ -229,7 +229,7 @@ fun IdVerificationScreen( text = "Upload your ID", fontSize = 16.sp, fontWeight = FontWeight.Bold, - color = TextDark + color = textDark ) Spacer(modifier = Modifier.height(4.dp)) Text( @@ -346,7 +346,7 @@ private fun GuidelineItem( Text( text = text, fontSize = 11.sp, - color = TextDark, + color = textDark, lineHeight = 14.sp ) } diff --git a/app/src/main/java/com/example/dattingapp/Login/ui/theme/Color.kt b/app/src/main/java/com/example/dattingapp/Login/ui/theme/Color.kt deleted file mode 100644 index 7b2bd68..0000000 --- a/app/src/main/java/com/example/dattingapp/Login/ui/theme/Color.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.example.dattingapp.Login.ui.theme - -import androidx.compose.ui.graphics.Color - -val Purple80 = Color(0xFFD0BCFF) -val PurpleGrey80 = Color(0xFFCCC2DC) -val Pink80 = Color(0xFFEFB8C8) - -val Purple40 = Color(0xFF6650a4) -val PurpleGrey40 = Color(0xFF625b71) -val Pink40 = Color(0xFF7D5260) \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/Login/ui/theme/Theme.kt b/app/src/main/java/com/example/dattingapp/Login/ui/theme/Theme.kt deleted file mode 100644 index 2ca16c6..0000000 --- a/app/src/main/java/com/example/dattingapp/Login/ui/theme/Theme.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.example.dattingapp.Login.ui.theme - -import android.app.Activity -import android.os.Build -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.darkColorScheme -import androidx.compose.material3.dynamicDarkColorScheme -import androidx.compose.material3.dynamicLightColorScheme -import androidx.compose.material3.lightColorScheme -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext - -private val DarkColorScheme = darkColorScheme( - primary = Purple80, - secondary = PurpleGrey80, - tertiary = Pink80 -) - -private val LightColorScheme = lightColorScheme( - primary = Purple40, - secondary = PurpleGrey40, - tertiary = Pink40 - - /* Other default colors to override - background = Color(0xFFFFFBFE), - surface = Color(0xFFFFFBFE), - onPrimary = Color.White, - onSecondary = Color.White, - onTertiary = Color.White, - onBackground = Color(0xFF1C1B1F), - onSurface = Color(0xFF1C1B1F), - */ -) - -@Composable -fun DattingAPPTheme( - darkTheme: Boolean = isSystemInDarkTheme(), - // Dynamic color is available on Android 12+ - dynamicColor: Boolean = true, - content: @Composable () -> Unit -) { - val colorScheme = when { - dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { - val context = LocalContext.current - if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) - } - - darkTheme -> DarkColorScheme - else -> LightColorScheme - } - - MaterialTheme( - colorScheme = colorScheme, - typography = Typography, - content = content - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/Login/ui/theme/Type.kt b/app/src/main/java/com/example/dattingapp/Login/ui/theme/Type.kt deleted file mode 100644 index a47b73f..0000000 --- a/app/src/main/java/com/example/dattingapp/Login/ui/theme/Type.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.example.dattingapp.Login.ui.theme - -import androidx.compose.material3.Typography -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.font.FontFamily -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.sp - -// Set of Material typography styles to start with -val Typography = Typography( - bodyLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 16.sp, - lineHeight = 24.sp, - letterSpacing = 0.5.sp - ) - /* Other default text styles to override - titleLarge = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Normal, - fontSize = 22.sp, - lineHeight = 28.sp, - letterSpacing = 0.sp - ), - labelSmall = TextStyle( - fontFamily = FontFamily.Default, - fontWeight = FontWeight.Medium, - fontSize = 11.sp, - lineHeight = 16.sp, - letterSpacing = 0.5.sp - ) - */ -) \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/MyProfile.kt b/app/src/main/java/com/example/dattingapp/MyProfile.kt index 6d69940..94e96a9 100644 --- a/app/src/main/java/com/example/dattingapp/MyProfile.kt +++ b/app/src/main/java/com/example/dattingapp/MyProfile.kt @@ -35,7 +35,7 @@ import androidx.compose.ui.unit.sp import com.example.dattingapp.ui.theme.BrandMagenta import com.example.dattingapp.ui.theme.BrandPurple import com.example.dattingapp.ui.theme.DattingAPPTheme -import com.example.dattingapp.ui.theme.TextDark +import com.example.dattingapp.ui.theme.textDark import com.example.dattingapp.ui.theme.TextMuted @@ -832,7 +832,7 @@ private fun StatCard( modifier: Modifier = Modifier, - numberColor: Color = TextDark + numberColor: Color = textDark ) { diff --git a/app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt b/app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt index 563ffd8..bc352de 100644 --- a/app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt +++ b/app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt @@ -1,352 +1,767 @@ package com.example.dattingapp.registration -import android.content.Intent import android.os.Bundle import androidx.activity.ComponentActivity +import androidx.activity.compose.BackHandler import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge -import androidx.compose.animation.* -import androidx.compose.animation.core.tween +import androidx.compose.animation.AnimatedContent +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background -import androidx.compose.foundation.clickable +import androidx.compose.foundation.border import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material.icons.automirrored.filled.ArrowForward +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.AddCircle +import androidx.compose.material.icons.filled.AlternateEmail +import androidx.compose.material.icons.filled.ArrowDropDown +import androidx.compose.material.icons.filled.Badge +import androidx.compose.material.icons.filled.Check +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Female +import androidx.compose.material.icons.filled.Groups +import androidx.compose.material.icons.filled.LocationOn +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.Mail +import androidx.compose.material.icons.filled.Male +import androidx.compose.material.icons.filled.PhoneIphone +import androidx.compose.material.icons.filled.Public +import androidx.compose.material.icons.filled.RadioButtonUnchecked +import androidx.compose.material.icons.filled.SmartToy +import androidx.compose.material.icons.filled.VerifiedUser +import androidx.compose.material.icons.filled.Visibility +import androidx.compose.material.icons.filled.VisibilityOff import androidx.compose.material3.* import androidx.compose.runtime.* -import androidx.compose.runtime.snapshots.SnapshotStateList +import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.PasswordVisualTransformation +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import java.text.SimpleDateFormat -import java.util.* -import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.outlined.Explore -import androidx.compose.material.icons.outlined.FavoriteBorder -import androidx.compose.material.icons.outlined.Group -import androidx.compose.material.icons.outlined.LocalBar -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.style.TextAlign +import com.example.dattingapp.ui.theme.InputBg +import com.example.dattingapp.ui.theme.LightBorderColor +import com.example.dattingapp.ui.theme.PinkGradientStart +import com.example.dattingapp.ui.theme.PurpleGradientEnd +import com.example.dattingapp.ui.theme.textDark +import com.example.dattingapp.ui.theme.textPink -// Global Email Validation Regex -val EMAIL_PATTERN_REGEX = Regex("^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$") - -data class VibeOption( - val title: String, - val subtitle: String, - val icon: ImageVector, - val iconBgColor: Color, - val iconTintColor: Color -) class RegistrationScreen : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { enableEdgeToEdge() super.onCreate(savedInstanceState) setContent { MaterialTheme { - FullscreenRegistrationWizard() + RegistrationFlowContainer() } } } } @Composable -fun FullscreenRegistrationWizard() { - // Active step state +fun RegistrationFlowContainer() { var currentStep by remember { mutableIntStateOf(1) } - val totalSteps = 7 + val totalSteps = 6 + BackHandler(enabled = currentStep > 1) { currentStep-- } + + var nameInput by rememberSaveable { mutableStateOf("") } + var nameError by rememberSaveable { mutableStateOf(null) } + val nameRegex = remember { Regex("^[a-zA-Z\\s]+$") } - // Form Field States - var isPhoneSelected by remember { mutableStateOf(false) } - var countryCode by remember { mutableStateOf("+880") } - var contactInput by remember { mutableStateOf("") } - var contactError by remember { mutableStateOf(null) } + var selectedTab by rememberSaveable { mutableIntStateOf(0) } + var emailInput by rememberSaveable { mutableStateOf("") } + var phoneInput by rememberSaveable { mutableStateOf("") } + var countryCode by rememberSaveable { mutableStateOf("+86") } + var emailError by rememberSaveable { mutableStateOf(null) } + var phoneError by rememberSaveable { mutableStateOf(null) } var passwordInput by remember { mutableStateOf("") } var confirmPasswordInput by remember { mutableStateOf("") } var passwordError by remember { mutableStateOf(null) } + var confirmPasswordError by remember { mutableStateOf(null) } - var nameInput by remember { mutableStateOf("") } - var nameError by remember { mutableStateOf(null) } - - var dobInput by remember { mutableStateOf("") } - var dobError by remember { mutableStateOf(null) } - - var genderInput by remember { mutableStateOf("") } - var genderError by remember { mutableStateOf(null) } - - val selectedInterests = remember { mutableStateListOf() } - var interestsError by remember { mutableStateOf(null) } - - - var vibeInput by remember { mutableStateOf("") } - var vibeError by remember { mutableStateOf(null) } - - // Inside your FullscreenRegistrationWizard composable: - val context = LocalContext.current - - // Validation & Navigation - fun validateAndProceed() { - when (currentStep) { - + val scrollState = rememberScrollState() + Box( + modifier = Modifier + .fillMaxSize() + .statusBarsPadding() + .navigationBarsPadding() + .imePadding() + ) { + // ------------------------------------------------------------- + // SINGLE SCROLLABLE COLUMN — everything scrolls together + // ------------------------------------------------------------- + Column( + modifier = Modifier + .fillMaxSize() + .verticalScroll(scrollState) + .padding(horizontal = 24.dp) + ) { + Spacer(modifier = Modifier.height(16.dp)) - 1 -> { - if (nameInput.trim().isBlank()) { - nameError = "Please enter your name" - } else { - nameError = null - currentStep = 2 + // 1. Static Top App Bar + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + IconButton( + onClick = { if (currentStep > 1) currentStep-- }, + enabled = currentStep > 1 + ) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back", + tint = if (currentStep > 1) textDark else Color.Transparent + ) + } + Text( + text = "VibeCheck", + style = MaterialTheme.typography.titleLarge.copy( + fontWeight = FontWeight.Bold, + color = PurpleGradientEnd, + fontSize = 24.sp + ) + ) + TextButton(onClick = { /* Skip logic */ }) { + Text(text = "SKIP", color = Color.Gray, fontWeight = FontWeight.Medium) } - } - 2 -> { - if (isPhoneSelected) { - when { - contactInput.isBlank() -> contactError = "Please enter a phone number" - contactInput.length < 7 || contactInput.length > 15 -> contactError = "Enter 7-15 digits" - else -> { contactError = null; currentStep = 3 } - } - } else { - val trimmedEmail = contactInput.trim() - when { - trimmedEmail.isBlank() -> contactError = "Please enter an email address" - !EMAIL_PATTERN_REGEX.matches(trimmedEmail) -> contactError = "Invalid email format" - else -> { contactError = null; currentStep = 3 } - } - } + Spacer(modifier = Modifier.height(16.dp)) + // 2. Dynamic Step Counter Header + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween + ) { + Text( + text = "STEP $currentStep OF $totalSteps", + color = textPink, + fontWeight = FontWeight.Bold, + fontSize = 12.sp + ) + Text( + text = when (currentStep) { + 1 -> "Getting Started" + 2 -> "Contact Details" + 3 -> "Gender Identity" + 4 -> "Date of Birth" + 5 -> "Location" + 6 -> "Interests" + else -> "" + }, + color = Color.Gray, + fontSize = 12.sp + ) } - 3 -> { - when { - passwordInput.length < 6 -> passwordError = "Password must be at least 6 characters" - passwordInput != confirmPasswordInput -> passwordError = "Passwords do not match" - else -> { passwordError = null; currentStep = 4 } - } - } + Spacer(modifier = Modifier.height(8.dp)) - 4 -> { - if (dobInput.isBlank()) { - dobError = "Please select your date of birth" - } else { - dobError = null - currentStep = 5 - } - } - 5 -> { - if (genderInput.isBlank()) { - genderError = "Please select your gender" - } else { - genderError = null - currentStep = 6 + // 3. Step Progress Bar + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(6.dp) + ) { + for (i in 1..totalSteps) { + Box( + modifier = Modifier + .weight(1f) + .height(6.dp) + .clip(RoundedCornerShape(3.dp)) + .background( + if (i <= currentStep) + Brush.horizontalGradient(listOf(PinkGradientStart, PurpleGradientEnd)) + else + Brush.horizontalGradient(listOf(Color(0xFFE0E0E0), Color(0xFFE0E0E0))) + ) + ) } } - 6 -> { - if (selectedInterests.size < 2) { - interestsError = "Please select at least 2 interests" - } else { - interestsError = null - // Submit registration payload - currentStep = 7 + + Spacer(modifier = Modifier.height(20.dp)) + + // 4. Mascot Frame + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + Box( + modifier = Modifier + .size(90.dp) + .clip(RoundedCornerShape(20.dp)) + .background(Color(0xFFFDE8ED)), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = Icons.Default.SmartToy, + contentDescription = "Mascot", + tint = PinkGradientStart, + modifier = Modifier.size(50.dp) + ) } + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "VIBEBOT", + color = textPink, + fontSize = 10.sp, + fontWeight = FontWeight.Bold + ) + } - 7 -> { - if (vibeInput.isBlank()) { - vibeError = "Please select your vibe to continue" - } else { - vibeError = null - - // Launch UploadImageActivity with all collected data - val intent = Intent(context, UploadProfileImageActivity::class.java).apply { - putExtra("EXTRA_IS_PHONE", isPhoneSelected) - putExtra("EXTRA_COUNTRY_CODE", countryCode) - putExtra("EXTRA_CONTACT_VALUE", contactInput) - putExtra("EXTRA_PASSWORD", passwordInput) - putExtra("EXTRA_NAME", nameInput) - putExtra("EXTRA_DOB", dobInput) - putExtra("EXTRA_GENDER", genderInput) - putExtra("EXTRA_INTERESTS", ArrayList(selectedInterests)) - putExtra("EXTRA_VIBE", vibeInput) + Spacer(modifier = Modifier.height(20.dp)) + + // 5. Dynamic Middle Content Block + AnimatedContent( + targetState = currentStep, + label = "StepAnimation" + ) { step -> + when (step) { + 1 -> StepOneContent( + name = nameInput, + errorMessage = nameError, + onNameChange = { updatedName -> + nameInput = updatedName + nameError = null } - context.startActivity(intent) + ) + 2 -> StepTwoContent( + selectedTab = selectedTab, + onTabSelected = { newTab -> + selectedTab = newTab + emailError = null + phoneError = null + }, + emailText = emailInput, + onEmailChange = { updatedEmail -> + emailInput = updatedEmail + emailError = null + }, + phoneText = phoneInput, + onPhoneChange = { updatedPhone -> + if (updatedPhone.all { it.isDigit() }) { + phoneInput = updatedPhone + phoneError = null + } + }, + countryCode = countryCode, + emailError = emailError, + phoneError = phoneError, + isEmailValid = emailInput.trim().matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$".toRegex()), + isPhoneValid = phoneInput.trim().length in 10..15 + ) + 3 -> StepThreeContent( + passwordInput = passwordInput, + onPasswordChange = { + passwordInput = it + passwordError = null + }, + confirmPasswordInput = confirmPasswordInput, + onConfirmPasswordChange = { + confirmPasswordInput = it + confirmPasswordError = null + }, + passwordError = passwordError, + confirmPasswordError = confirmPasswordError + ) + 4 -> StepFourContent() + 5 -> StepFiveContent() + 6 -> StepSixContent() + else -> Text("Step $step content coming soon!") } } - } - } - Box( - modifier = Modifier - .fillMaxSize() - .background(Color(0xFFF8F9FA)) - .systemBarsPadding() - ) { - Column(modifier = Modifier.fillMaxSize()) { - // TOP BAR & PROGRESS - Column( + Spacer(modifier = Modifier.height(100.dp)) + + // ------------------------------------------------------------- + // CONTINUE BUTTON — inside scroll, scrolls with content + // ------------------------------------------------------------- + Button( + onClick = { + when (currentStep) { + 1 -> { + val trimmed = nameInput.trim() + when { + trimmed.isEmpty() -> nameError = "Name cannot be empty" + !nameRegex.matches(trimmed) -> nameError = "Numbers and special characters are not allowed" + else -> { + nameError = null + currentStep++ + } + } + } + 2 -> { + if (selectedTab == 0) { + val trimmedEmail = emailInput.trim() + val emailRegex = "^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$".toRegex() + when { + trimmedEmail.isEmpty() -> emailError = "Email cannot be empty" + !emailRegex.matches(trimmedEmail) -> emailError = "Enter a valid email address" + else -> { + emailError = null + currentStep++ + } + } + } else { + val trimmedPhone = phoneInput.trim() + val phoneDigits = trimmedPhone.filter { it.isDigit() } + when { + trimmedPhone.isEmpty() -> phoneError = "Phone number cannot be empty" + phoneDigits.length !in 10..15 -> phoneError = "Enter a valid phone number (10-15 digits)" + else -> { + phoneError = null + currentStep++ + } + } + } + } + 3 -> { + val trimmedPassword = passwordInput.trim() + val trimmedConfirmPassword = confirmPasswordInput.trim() + val hasLength = trimmedPassword.length in 8..20 + val hasLetterAndDigit = trimmedPassword.any { it.isLetter() } && trimmedPassword.any { it.isDigit() } + val hasSpecialChar = trimmedPassword.any { !it.isLetterOrDigit() } + + var isValid = true + + when { + trimmedPassword.isEmpty() -> { passwordError = "Password cannot be empty"; isValid = false } + !hasLength -> { passwordError = "Password must be between 8 and 20 characters"; isValid = false } + !hasLetterAndDigit -> { passwordError = "Password must contain both letters and numbers"; isValid = false } + !hasSpecialChar -> { passwordError = "Password must contain at least one special character"; isValid = false } + else -> passwordError = null + } + + when { + trimmedConfirmPassword.isEmpty() -> { confirmPasswordError = "Please confirm your password"; isValid = false } + trimmedPassword != trimmedConfirmPassword -> { confirmPasswordError = "Passwords do not match"; isValid = false } + else -> confirmPasswordError = null + } + + if (isValid) currentStep++ + } + else -> { + if (currentStep < totalSteps) currentStep++ + } + } + }, modifier = Modifier .fillMaxWidth() - .padding(horizontal = 20.dp, vertical = 16.dp) + .height(52.dp) + .background( + brush = Brush.horizontalGradient(listOf(PinkGradientStart, PurpleGradientEnd)), + shape = RoundedCornerShape(26.dp) + ), + colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent), + contentPadding = PaddingValues() ) { Row( - modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween + horizontalArrangement = Arrangement.Center ) { - if (currentStep > 1) { - TextButton( - onClick = { currentStep-- }, - contentPadding = PaddingValues(0.dp) - ) { - Text("Back", color = Color.Gray, fontSize = 16.sp) - } - } else { - Spacer(modifier = Modifier.width(48.dp)) - } - Text( - text = "Step $currentStep of $totalSteps", - fontWeight = FontWeight.Bold, + text = if (currentStep < totalSteps) "Continue" else "Finish Registration", fontSize = 16.sp, - color = Color.Black + fontWeight = FontWeight.Bold, + color = Color.White + ) + Spacer(modifier = Modifier.width(8.dp)) + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowForward, + contentDescription = null, + tint = Color.White ) - - Spacer(modifier = Modifier.width(48.dp)) } + } + + Spacer(modifier = Modifier.height(24.dp)) + } + } +} +// --- Step Content Components --- +@Composable +fun StepOneContent( + name: String, + errorMessage: String?, + onNameChange: (String) -> Unit +) { + Column { + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + border = BorderStroke(1.dp, LightBorderColor) + ) { + Text( + text = "Welcome! What should we call you?", + style = MaterialTheme.typography.titleMedium.copy( + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + color = textDark + ), + modifier = Modifier.padding(20.dp) + ) + } - Spacer(modifier = Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(25.dp)) - LinearProgressIndicator( - progress = { currentStep / totalSteps.toFloat() }, + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + border = BorderStroke( + width = 2.dp, + color = if (errorMessage != null) Color.Red else textPink + ) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = "YOUR FULL NAME OR FIRST NAME", + color = if (errorMessage != null) Color.Red else textPink, + fontSize = 11.sp, + fontWeight = FontWeight.Bold + ) + Spacer(modifier = Modifier.height(8.dp)) + Row( modifier = Modifier .fillMaxWidth() - .height(6.dp), - color = Color.Black, - trackColor = Color(0xFFE0E0E0) - ) + .clip(RoundedCornerShape(12.dp)) + .background(InputBg) + .border( + width = 1.dp, + color = if (errorMessage != null) Color.Red else Color(0xFFE5E7EB), + shape = RoundedCornerShape(12.dp) + ) + .padding(horizontal = 12.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Badge, + contentDescription = null, + tint = Color.Gray + ) + Spacer(modifier = Modifier.width(8.dp)) + BasicTextField( + value = name, + onValueChange = onNameChange, + modifier = Modifier.weight(1f), + singleLine = true + ) + } + + // Display the error message if present + if (errorMessage != null) { + Spacer(modifier = Modifier.height(6.dp)) + Text( + text = errorMessage, + color = Color.Red, + fontSize = 12.sp, + fontWeight = FontWeight.Medium, + modifier = Modifier.padding(start = 4.dp) + ) + } } + } + } +} - // SLIDING CONTENT CONTAINER - Box( - modifier = Modifier - .weight(1f) - .fillMaxWidth() - .padding(horizontal = 24.dp), - contentAlignment = Alignment.Center +@Composable +fun StepTwoContent(selectedTab: Int, onTabSelected: (Int) -> Unit, emailText: String, onEmailChange: (String) -> Unit, + phoneText: String, onPhoneChange: (String) -> Unit, countryCode: String, emailError: String?, + phoneError: String?, isEmailValid: Boolean = false, isPhoneValid: Boolean = false +) { + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + // Title Header Card + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + border = BorderStroke(1.dp, LightBorderColor) + ) { + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally ) { - AnimatedContent( - targetState = currentStep, - transitionSpec = { - if (targetState > initialState) { - slideIntoContainer( - AnimatedContentTransitionScope.SlideDirection.Left, - animationSpec = tween(400) - ) + fadeIn(animationSpec = tween(400)) togetherWith - slideOutOfContainer( - AnimatedContentTransitionScope.SlideDirection.Left, - animationSpec = tween(400) - ) + fadeOut(animationSpec = tween(400)) - } else { - slideIntoContainer( - AnimatedContentTransitionScope.SlideDirection.Right, - animationSpec = tween(400) - ) + fadeIn(animationSpec = tween(400)) togetherWith - slideOutOfContainer( - AnimatedContentTransitionScope.SlideDirection.Right, - animationSpec = tween(400) - ) + fadeOut(animationSpec = tween(400)) - } - }, - label = "WizardSlideAnimation" - ) { step -> - Column( - modifier = Modifier.fillMaxWidth(), - horizontalAlignment = Alignment.CenterHorizontally + Text( + text = if (selectedTab == 0) "How should we reach you?" else "What is your phone number?", + style = MaterialTheme.typography.titleMedium.copy( + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + color = textDark + ) + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = if (selectedTab == 0) + "Choose whether you'd like to register with your email or phone number." + else + "We'll send a quick verification code to confirm it's you.", + fontSize = 12.sp, + color = Color.Gray, + textAlign = TextAlign.Center + ) + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + // Tab Selector (Email / Phone Number) + Surface( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(16.dp), + color = Color(0xFFEEF0F4) + ) { + Row(modifier = Modifier.padding(4.dp)) { + Surface( + onClick = { onTabSelected(0) }, + modifier = Modifier.weight(1f), + shape = RoundedCornerShape(12.dp), + color = if (selectedTab == 0) Color.White else Color.Transparent, + shadowElevation = if (selectedTab == 0) 2.dp else 0.dp + ) { + Row( + modifier = Modifier.padding(vertical = 12.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically ) { - when (step) { + Icon( + imageVector = Icons.Default.Mail, + contentDescription = null, + tint = if (selectedTab == 0) textPink else Color.Gray, + modifier = Modifier.size(18.dp) + ) + Spacer(modifier = Modifier.width(6.dp)) + Text( + text = "Email", + fontSize = 14.sp, + fontWeight = FontWeight.Bold, + color = if (selectedTab == 0) textPink else Color.Gray + ) + } + } - 1 -> NameInputWidget( - name = nameInput, - onNameChange = { nameInput = it; nameError = null }, - errorMessage = nameError - ) - 2 -> ContactInputWidget( - isPhoneSelected = isPhoneSelected, - onModeChange = { isPhoneSelected = it; contactError = null }, - countryCode = countryCode, - onCountryCodeChange = { countryCode = it }, - contactValue = contactInput, - onContactChange = { contactInput = it; contactError = null }, - errorMessage = contactError + Surface( + onClick = { onTabSelected(1) }, + modifier = Modifier.weight(1f), + shape = RoundedCornerShape(12.dp), + color = if (selectedTab == 1) Color.White else Color.Transparent, + shadowElevation = if (selectedTab == 1) 2.dp else 0.dp + ) { + Row( + modifier = Modifier.padding(vertical = 12.dp), + horizontalArrangement = Arrangement.Center, + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.PhoneIphone, + contentDescription = null, + tint = if (selectedTab == 1) textPink else Color.Gray, + modifier = Modifier.size(18.dp) + ) + Spacer(modifier = Modifier.width(6.dp)) + Text( + text = "Phone Number", + fontSize = 14.sp, + fontWeight = FontWeight.Bold, + color = if (selectedTab == 1) textPink else Color.Gray + ) + } + } + } + } + + Spacer(modifier = Modifier.height(16.dp)) + + // Dynamic Input Section Card + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + border = BorderStroke( + width = 1.dp, + color = if ((selectedTab == 0 && emailError != null) || (selectedTab == 1 && phoneError != null)) Color.Red else LightBorderColor + ) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Text( + text = if (selectedTab == 0) "YOUR EMAIL ADDRESS" else "YOUR PHONE NUMBER", + color = if ((selectedTab == 0 && emailError != null) || (selectedTab == 1 && phoneError != null)) Color.Red else textPink, + fontSize = 11.sp, + fontWeight = FontWeight.Bold + ) + + Spacer(modifier = Modifier.height(8.dp)) + + if (selectedTab == 0) { + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(InputBg) + .border( + width = 1.dp, + color = if (emailError != null) Color.Red else Color(0xFFE5E7EB), + shape = RoundedCornerShape(12.dp) ) - 3 -> PasswordInputWidget( - password = passwordInput, - onPasswordChange = { passwordInput = it; passwordError = null }, - confirmPassword = confirmPasswordInput, - onConfirmPasswordChange = { confirmPasswordInput = it; passwordError = null }, - errorMessage = passwordError + .padding(horizontal = 12.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.AlternateEmail, + contentDescription = null, + tint = Color.Gray, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + BasicTextField( + value = emailText, + onValueChange = onEmailChange, + modifier = Modifier.weight(1f), + textStyle = LocalTextStyle.current.copy( + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = textDark + ), + singleLine = true + ) + if (isEmailValid) { + Icon( + imageVector = Icons.Default.CheckCircle, + contentDescription = "Valid", + tint = Color(0xFF00C853), + modifier = Modifier.size(20.dp) ) + } + } - 4 -> DobInputWidget( - dob = dobInput, - onDobChange = { dobInput = it; dobError = null }, - errorMessage = dobError - ) - 5 -> GenderInputWidget( - selectedGender = genderInput, - onGenderSelect = { genderInput = it; genderError = null }, - errorMessage = genderError + if (emailError != null) { + Spacer(modifier = Modifier.height(6.dp)) + Text( + text = emailError, + color = Color.Red, + fontSize = 12.sp, + fontWeight = FontWeight.Medium, + modifier = Modifier.padding(start = 4.dp) + ) + } + } else { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Row( + modifier = Modifier + .clip(RoundedCornerShape(12.dp)) + .background(InputBg) + .border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(12.dp)) + .padding(horizontal = 10.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text(text = "🇨🇳", fontSize = 16.sp) + Spacer(modifier = Modifier.width(4.dp)) + Text( + text = countryCode, + fontSize = 14.sp, + fontWeight = FontWeight.SemiBold, + color = textDark ) - 6 -> InterestsInputWidget( - selectedInterests = selectedInterests, - errorMessage = interestsError, - onSelectionChanged = { interestsError = null } + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = "Select Country", + tint = Color.Gray, + modifier = Modifier.size(18.dp) ) - 7 -> VibeCheckWidget( - selectedVibe = vibeInput, - onVibeSelected = { - vibeInput = it - vibeError = null - }, - errorMessage = vibeError + } + + Row( + modifier = Modifier + .weight(1f) + .clip(RoundedCornerShape(12.dp)) + .background(InputBg) + .border( + width = 1.dp, + color = if (phoneError != null) Color.Red else Color(0xFFE5E7EB), + shape = RoundedCornerShape(12.dp) + ) + .padding(horizontal = 12.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + BasicTextField( + value = phoneText, + onValueChange = onPhoneChange, + modifier = Modifier.weight(1f), + textStyle = LocalTextStyle.current.copy( + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = textDark + ), + singleLine = true ) + if (isPhoneValid) { + Icon( + imageVector = Icons.Default.CheckCircle, + contentDescription = "Valid", + tint = Color(0xFF00C853), + modifier = Modifier.size(20.dp) + ) + } } } + + if (phoneError != null) { + Spacer(modifier = Modifier.height(6.dp)) + Text( + text = phoneError, + color = Color.Red, + fontSize = 12.sp, + fontWeight = FontWeight.Medium, + modifier = Modifier.padding(start = 4.dp) + ) + } } - } - // BOTTOM ACTION BUTTON - Box( - modifier = Modifier - .fillMaxWidth() - .padding(20.dp) - ) { - Button( - onClick = { validateAndProceed() }, - modifier = Modifier - .fillMaxWidth() - .height(54.dp), - shape = RoundedCornerShape(16.dp), - colors = ButtonDefaults.buttonColors(containerColor = Color.Black) - ) { + Spacer(modifier = Modifier.height(8.dp)) + + Row(verticalAlignment = Alignment.CenterVertically) { + Icon( + imageVector = Icons.Default.Lock, + contentDescription = null, + tint = Color.Gray, + modifier = Modifier.size(12.dp) + ) + Spacer(modifier = Modifier.width(4.dp)) Text( - text = if (currentStep == totalSteps) "Complete Registration" else "Continue", - color = Color.White, - fontSize = 16.sp, - fontWeight = FontWeight.Bold + text = if (selectedTab == 0) + "We'll send an instant verification link. Never shared publicly." + else + "We'll text you a 6-digit verification code. Standard rates may apply.", + fontSize = 10.sp, + color = Color.Gray ) } } @@ -354,513 +769,753 @@ fun FullscreenRegistrationWizard() { } } -// ==================== STEP WIDGET COMPOSABLES ==================== - @Composable -fun ContactInputWidget( - isPhoneSelected: Boolean, - onModeChange: (Boolean) -> Unit, - countryCode: String, - onCountryCodeChange: (String) -> Unit, - contactValue: String, - onContactChange: (String) -> Unit, - errorMessage: String? +fun StepThreeContent( + passwordInput: String, + onPasswordChange: (String) -> Unit, + confirmPasswordInput: String, + onConfirmPasswordChange: (String) -> Unit, + passwordError: String?, + confirmPasswordError: String? ) { + var isPasswordVisible by remember { mutableStateOf(false) } + var isConfirmPasswordVisible by remember { mutableStateOf(false) } + + // Real-time requirement trackers for the UI indicators + val hasValidLength = passwordInput.length in 8..20 + val hasLetterAndNumber = passwordInput.any { it.isLetter() } && passwordInput.any { it.isDigit() } + val hasSpecialChar = passwordInput.any { !it.isLetterOrDigit() } + val passwordsMatch = passwordInput.isNotEmpty() && passwordInput == confirmPasswordInput + Column( modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(12.dp) + horizontalAlignment = Alignment.CenterHorizontally ) { - Text( - text = "ENTER YOUR CONTACT INFORMATION", - fontSize = 11.sp, - fontWeight = FontWeight.Bold, - color = Color.Gray - ) - - Row( - modifier = Modifier - .fillMaxWidth() - .height(48.dp) - .clip(RoundedCornerShape(24.dp)) - .background(Color(0xFFEFEFEF)) - .padding(4.dp), - verticalAlignment = Alignment.CenterVertically + // 1. Header Card + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + border = BorderStroke(1.dp, LightBorderColor) ) { - Box( - modifier = Modifier - .weight(1f) - .fillMaxHeight() - .clip(RoundedCornerShape(20.dp)) - .background(if (isPhoneSelected) Color.Black else Color.Transparent) - .clickable { onModeChange(true) }, - contentAlignment = Alignment.Center + Column( + modifier = Modifier.padding(10.dp), + horizontalAlignment = Alignment.CenterHorizontally ) { Text( - text = "Phone", - fontWeight = FontWeight.Bold, - fontSize = 14.sp, - color = if (isPhoneSelected) Color.White else Color.DarkGray + text = "Create a secure password", + style = MaterialTheme.typography.titleMedium.copy( + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + color = textDark + ) ) - } - - Box( - modifier = Modifier - .weight(1f) - .fillMaxHeight() - .clip(RoundedCornerShape(20.dp)) - .background(if (!isPhoneSelected) Color.Black else Color.Transparent) - .clickable { onModeChange(false) }, - contentAlignment = Alignment.Center - ) { + Spacer(modifier = Modifier.height(4.dp)) Text( - text = "Email", - fontWeight = FontWeight.Bold, - fontSize = 14.sp, - color = if (!isPhoneSelected) Color.White else Color.DarkGray + text = "Choose a strong password to protect your VibeCheck account.", + fontSize = 12.sp, + color = Color.Gray, + textAlign = TextAlign.Center ) } } - if (isPhoneSelected) { - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - OutlinedTextField( - value = countryCode, - onValueChange = { if (it.length <= 4) onCountryCodeChange(it) }, - placeholder = { Text("+880") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone), - singleLine = true, - isError = errorMessage != null, - modifier = Modifier.width(90.dp), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White + Spacer(modifier = Modifier.height(5.dp)) + + // 2. Main Input Card + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + border = BorderStroke(1.dp, LightBorderColor) + ) { + Column(modifier = Modifier.padding(16.dp)) { + + // --- PASSWORD FIELD --- + Text( + text = "PASSWORD", + color = textPink, + fontSize = 11.sp, + fontWeight = FontWeight.Bold + ) + Spacer(modifier = Modifier.height(4.dp)) + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(InputBg) + .border( + width = 1.dp, + color = if (passwordError != null) Color.Red else Color(0xFFE5E7EB), + shape = RoundedCornerShape(12.dp) + ) + .padding(horizontal = 12.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Lock, + contentDescription = null, + tint = Color.Gray, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(3.dp)) + BasicTextField( + value = passwordInput, + onValueChange = onPasswordChange, + modifier = Modifier.weight(1f), + textStyle = LocalTextStyle.current.copy( + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = textDark + ), + singleLine = true, + visualTransformation = if (isPasswordVisible) VisualTransformation.None else PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), + decorationBox = { innerTextField -> + if (passwordInput.isEmpty()) { + Text( + text = "Enter secure password", + color = Color.LightGray, + fontSize = 14.sp + ) + } + innerTextField() + } + ) + IconButton( + onClick = { isPasswordVisible = !isPasswordVisible }, + modifier = Modifier.size(24.dp) + ) { + Icon( + imageVector = if (isPasswordVisible) Icons.Default.Visibility else Icons.Default.VisibilityOff, + contentDescription = "Toggle Password Visibility", + tint = Color.Gray, + modifier = Modifier.size(20.dp) + ) + } + } + + // Password Error Message + if (passwordError != null) { + Text( + text = passwordError, + color = Color.Red, + fontSize = 12.sp, + modifier = Modifier.padding(top = 4.dp, start = 4.dp) + ) + } + + Spacer(modifier = Modifier.height(5.dp)) + + // --- CONFIRM PASSWORD FIELD --- + Text( + text = "CONFIRM PASSWORD", + color = textPink, + fontSize = 11.sp, + fontWeight = FontWeight.Bold + ) + Spacer(modifier = Modifier.height(8.dp)) + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(InputBg) + .border( + width = 1.dp, + color = if (confirmPasswordError != null) Color.Red else Color(0xFFE5E7EB), + shape = RoundedCornerShape(12.dp) + ) + .padding(horizontal = 12.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Lock, + contentDescription = null, + tint = Color.Gray, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + BasicTextField( + value = confirmPasswordInput, + onValueChange = onConfirmPasswordChange, + modifier = Modifier.weight(1f), + textStyle = LocalTextStyle.current.copy( + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = textDark + ), + singleLine = true, + visualTransformation = if (isConfirmPasswordVisible) VisualTransformation.None else PasswordVisualTransformation(), + keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), + decorationBox = { innerTextField -> + if (confirmPasswordInput.isEmpty()) { + Text( + text = "Re-enter password", + color = Color.LightGray, + fontSize = 14.sp + ) + } + innerTextField() + } + ) + IconButton( + onClick = { isConfirmPasswordVisible = !isConfirmPasswordVisible }, + modifier = Modifier.size(24.dp) + ) { + Icon( + imageVector = if (passwordsMatch) Icons.Default.CheckCircle else if (isConfirmPasswordVisible) Icons.Default.Visibility else Icons.Default.VisibilityOff, + contentDescription = "Toggle Password Visibility", + tint = if (passwordsMatch) Color(0xFF00C853) else Color.Gray, + modifier = Modifier.size(20.dp) + ) + } + } + + // Confirm Password Error Message + if (confirmPasswordError != null) { + Text( + text = confirmPasswordError, + color = Color.Red, + fontSize = 12.sp, + modifier = Modifier.padding(top = 4.dp, start = 4.dp) ) - ) + } - OutlinedTextField( - value = contactValue, - onValueChange = { if (it.length <= 15) onContactChange(it) }, - placeholder = { Text("Phone number") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), - singleLine = true, - isError = errorMessage != null, - modifier = Modifier.weight(1f), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White + Spacer(modifier = Modifier.height(5.dp)) + + // --- PASSWORD REQUIREMENTS BOX --- + Column( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(Color(0xFFF8F9FA)) + .padding(12.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + ValidationRequirementRow( + isMet = hasValidLength, + label = "8 to 20 characters long" ) - ) + ValidationRequirementRow( + isMet = hasLetterAndNumber, + label = "Includes letters and numbers" + ) + ValidationRequirementRow( + isMet = hasSpecialChar, + label = "Includes at least one special character" + ) + } } - } else { - OutlinedTextField( - value = contactValue, - onValueChange = onContactChange, - placeholder = { Text("Enter email address") }, - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Email), - singleLine = true, - isError = errorMessage != null, - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White - ) - ) - } - - if (errorMessage != null) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium, - modifier = Modifier.padding(start = 4.dp) - ) } } } @Composable -fun PasswordInputWidget( - password: String, - onPasswordChange: (String) -> Unit, - confirmPassword: String, - onConfirmPasswordChange: (String) -> Unit, - errorMessage: String? +private fun ValidationRequirementRow( + isMet: Boolean, + label: String ) { - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(12.dp) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - Text( - text = "CREATE A PASSWORD", - fontSize = 11.sp, - fontWeight = FontWeight.Bold, - color = Color.Gray - ) - - OutlinedTextField( - value = password, - onValueChange = onPasswordChange, - placeholder = { Text("Enter password") }, - visualTransformation = PasswordVisualTransformation(), - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), - singleLine = true, - isError = errorMessage != null, - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White - ) + Icon( + imageVector = if (isMet) Icons.Default.CheckCircle else Icons.Default.RadioButtonUnchecked, + contentDescription = null, + tint = if (isMet) Color(0xFF00C853) else Color.Gray, + modifier = Modifier.size(16.dp) ) - - OutlinedTextField( - value = confirmPassword, - onValueChange = onConfirmPasswordChange, - placeholder = { Text("Confirm password") }, - visualTransformation = PasswordVisualTransformation(), - keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password), - singleLine = true, - isError = errorMessage != null, - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White - ) + Text( + text = label, + fontSize = 12.sp, + color = if (isMet) textDark else Color.Gray, + fontWeight = if (isMet) FontWeight.Medium else FontWeight.Normal ) - - if (errorMessage != null) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium, - modifier = Modifier.padding(start = 4.dp) - ) - } } } +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun NameInputWidget( - name: String, - onNameChange: (String) -> Unit, - errorMessage: String? -) { +fun StepFiveContent() { + var cityText by remember { mutableStateOf("") } + var countryText by remember { mutableStateOf("China") } + var isCountryDropdownExpanded by remember { mutableStateOf(false) } + + val countryList = listOf( + "United States", + "United Kingdom", + "Canada", + "Australia", + "Germany", + "France", + "Japan" + ) + Column( modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(12.dp) + horizontalAlignment = Alignment.CenterHorizontally ) { - Text( - text = "WHAT IS YOUR NAME?", - fontSize = 11.sp, - fontWeight = FontWeight.Bold, - color = Color.Gray - ) - - OutlinedTextField( - value = name, - onValueChange = onNameChange, - placeholder = { Text("Enter your full name") }, - singleLine = true, - isError = errorMessage != null, + // 1. Header Title Card + Card( modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White - ) - ) - - if (errorMessage != null) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium, - modifier = Modifier.padding(start = 4.dp) - ) - } - } -} - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun DobInputWidget( - dob: String, - onDobChange: (String) -> Unit, - errorMessage: String? -) { - var showDatePicker by remember { mutableStateOf(false) } - val datePickerState = rememberDatePickerState() - - if (showDatePicker) { - DatePickerDialog( - onDismissRequest = { showDatePicker = false }, - confirmButton = { - TextButton(onClick = { - datePickerState.selectedDateMillis?.let { millis -> - val formatter = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) - onDobChange(formatter.format(Date(millis))) - } - showDatePicker = false - }) { - Text("OK") - } - }, - dismissButton = { - TextButton(onClick = { showDatePicker = false }) { - Text("Cancel") - } - } + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + border = BorderStroke(1.dp, LightBorderColor) ) { - DatePicker(state = datePickerState) + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "Where are you located?", + style = MaterialTheme.typography.titleMedium.copy( + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + color = textDark + ) + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = "City and country name only to help discover vibes nearby.", + fontSize = 12.sp, + color = Color.Gray, + textAlign = TextAlign.Center + ) + } } - } - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - Text( - text = "YOUR DATE OF BIRTH", - fontSize = 11.sp, - fontWeight = FontWeight.Bold, - color = Color.Gray - ) + Spacer(modifier = Modifier.height(16.dp)) - Box(modifier = Modifier.fillMaxWidth()) { - OutlinedTextField( - value = dob, - onValueChange = {}, - readOnly = true, - placeholder = { Text("DD/MM/YYYY") }, - isError = errorMessage != null, - modifier = Modifier.fillMaxWidth(), - shape = RoundedCornerShape(12.dp), - colors = OutlinedTextFieldDefaults.colors( - focusedContainerColor = Color.White, - unfocusedContainerColor = Color.White + // 2. Input Fields Card + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + border = BorderStroke(1.dp, LightBorderColor) + ) { + Column(modifier = Modifier.padding(16.dp)) { + + // --- CITY FIELD --- + Text( + text = "CITY", + color = textPink, + fontSize = 11.sp, + fontWeight = FontWeight.Bold ) - ) - Box( - modifier = Modifier - .matchParentSize() - .clickable { showDatePicker = true } - ) - } + Spacer(modifier = Modifier.height(8.dp)) + Row( + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(InputBg) + .border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(12.dp)) + .padding(horizontal = 12.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.LocationOn, + contentDescription = null, + tint = Color.Gray, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + BasicTextField( + value = cityText, + onValueChange = { cityText = it }, + modifier = Modifier.weight(1f), + textStyle = LocalTextStyle.current.copy( + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = textDark + ), + singleLine = true, + decorationBox = { innerTextField -> + if (cityText.isEmpty()) { + Text( + text = "Enter your city", + color = Color.LightGray, + fontSize = 14.sp + ) + } + innerTextField() + } + ) + } - if (errorMessage != null) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium, - modifier = Modifier.padding(start = 4.dp) - ) - } - } -} + Spacer(modifier = Modifier.height(16.dp)) -@Composable -fun GenderInputWidget( - selectedGender: String, - onGenderSelect: (String) -> Unit, - errorMessage: String? -) { - val options = listOf("Male", "Female", "Other") + // --- COUNTRY FIELD (EXPOSED DROPDOWN MENU) --- + Text( + text = "COUNTRY", + color = textPink, + fontSize = 11.sp, + fontWeight = FontWeight.Bold + ) + Spacer(modifier = Modifier.height(8.dp)) + ExposedDropdownMenuBox( + expanded = isCountryDropdownExpanded, + onExpandedChange = { isCountryDropdownExpanded = !isCountryDropdownExpanded } + ) { + Row( + modifier = Modifier + .menuAnchor() + .fillMaxWidth() + .clip(RoundedCornerShape(12.dp)) + .background(InputBg) + .border(1.dp, Color(0xFFE5E7EB), RoundedCornerShape(12.dp)) + .padding(horizontal = 12.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Icon( + imageVector = Icons.Default.Public, + contentDescription = null, + tint = Color.Gray, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + BasicTextField( + value = countryText, + onValueChange = { countryText = it }, + modifier = Modifier.weight(1f), + textStyle = LocalTextStyle.current.copy( + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = textDark + ), + singleLine = true, + decorationBox = { innerTextField -> + if (countryText.isEmpty()) { + Text( + text = "Select or enter your country", + color = Color.LightGray, + fontSize = 14.sp + ) + } + innerTextField() + } + ) + Icon( + imageVector = Icons.Default.ArrowDropDown, + contentDescription = "Dropdown", + tint = Color.Gray, + modifier = Modifier.size(20.dp) + ) + } - Column( - modifier = Modifier.fillMaxWidth(), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - Text( - text = "SELECT YOUR GENDER", - fontSize = 11.sp, - fontWeight = FontWeight.Bold, - color = Color.Gray - ) + ExposedDropdownMenu( + expanded = isCountryDropdownExpanded, + onDismissRequest = { isCountryDropdownExpanded = false }, + modifier = Modifier.background(Color.White) + ) { + countryList.forEach { country -> + DropdownMenuItem( + text = { Text(text = country, fontSize = 14.sp, color = textDark) }, + onClick = { + countryText = country + isCountryDropdownExpanded = false + } + ) + } + } + } - options.forEach { gender -> - val isSelected = selectedGender == gender - Card( - shape = RoundedCornerShape(12.dp), - colors = CardDefaults.cardColors( - containerColor = if (isSelected) Color.Black else Color.White - ), - modifier = Modifier - .fillMaxWidth() - .clickable { onGenderSelect(gender) } - ) { - Box( - modifier = Modifier.padding(16.dp), - contentAlignment = Alignment.CenterStart + Spacer(modifier = Modifier.height(16.dp)) + + // Privacy Note Row + Row( + modifier = Modifier.fillMaxWidth(), + verticalAlignment = Alignment.Top ) { + Icon( + imageVector = Icons.Default.VerifiedUser, + contentDescription = null, + tint = Color(0xFF1976D2), + modifier = Modifier.size(16.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) Text( - text = gender, - color = if (isSelected) Color.White else Color.Black, - fontWeight = FontWeight.SemiBold, - fontSize = 15.sp + text = "We only display your general city and country. Your exact address is never stored.", + fontSize = 11.sp, + color = Color.Gray, + lineHeight = 15.sp ) } } } - - if (errorMessage != null) { - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium, - modifier = Modifier.padding(start = 4.dp) - ) - } } } +data class InterestOption( + val id: String, + val emoji: String, + val label: String +) + @OptIn(ExperimentalLayoutApi::class) @Composable -fun InterestsInputWidget( - selectedInterests: SnapshotStateList, - errorMessage: String? = null, - onSelectionChanged: () -> Unit = {} -) { - val allInterests = listOf("Photography", "Dogs", "Travel", "Music", "Gaming", "Fitness", "Cooking") +fun StepFourContent() { + val availableInterests = listOf( + InterestOption("1", "🎮", "Gaming"), + InterestOption("2", "⚽", "Football"), + InterestOption("3", "📚", "Reading Books"), + InterestOption("4", "☕", "Coffee Culture"), + InterestOption("5", "🎵", "Music & Concerts"), + InterestOption("6", "✈️", "Travel"), + InterestOption("7", "🍕", "Foodie"), + InterestOption("8", "🎨", "Art & Design") + ) - Card( - shape = RoundedCornerShape(16.dp), - colors = CardDefaults.cardColors(containerColor = Color.White), - modifier = Modifier.fillMaxWidth() - ) { - Column(modifier = Modifier.padding(16.dp)) { - Text( - text = "INTERESTS (SELECT AT LEAST 2)", - fontSize = 11.sp, - fontWeight = FontWeight.Bold, - color = Color.Gray - ) - Spacer(modifier = Modifier.height(12.dp)) + // Tracks selected interest IDs + val selectedIds = remember { mutableStateListOf("1", "2", "3") } - FlowRow( - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalArrangement = Arrangement.spacedBy(8.dp) + Column( + modifier = Modifier.fillMaxWidth(), + horizontalAlignment = Alignment.CenterHorizontally + ) { + // 1. Header Card + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + border = BorderStroke(1.dp, LightBorderColor) + ) { + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally ) { - allInterests.forEach { interest -> - val isSelected = selectedInterests.contains(interest) - FilterChip( - selected = isSelected, - onClick = { - if (isSelected) selectedInterests.remove(interest) - else selectedInterests.add(interest) - onSelectionChanged() - }, - label = { Text(interest) } + Text( + text = "What are you into?", + style = MaterialTheme.typography.titleMedium.copy( + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + color = textDark ) - } - } - - if (errorMessage != null) { - Spacer(modifier = Modifier.height(8.dp)) + ) + Spacer(modifier = Modifier.height(4.dp)) Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, + text = "Pick your favorite hobbies and interests to find matching vibes.", fontSize = 12.sp, - fontWeight = FontWeight.Medium + color = Color.Gray, + textAlign = TextAlign.Center ) } } - } -} + Spacer(modifier = Modifier.height(16.dp)) + + // 2. Chip Selection Card + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + border = BorderStroke(1.dp, LightBorderColor) + ) { + Column(modifier = Modifier.padding(16.dp)) { + + // Flow layout dynamically wraps interest chips + FlowRow( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Center, + verticalArrangement = Arrangement.spacedBy(10.dp), + maxItemsInEachRow = 3 + ) { + availableInterests.forEach { interest -> + val isSelected = selectedIds.contains(interest.id) + + Surface( + onClick = { + if (isSelected) { + selectedIds.remove(interest.id) + } else { + selectedIds.add(interest.id) + } + }, + shape = RoundedCornerShape(20.dp), + color = Color.Transparent, + modifier = Modifier + .padding(horizontal = 4.dp) + .clip(RoundedCornerShape(20.dp)) + .then( + if (isSelected) { + Modifier.background( + brush = Brush.horizontalGradient( + colors = listOf( + PinkGradientStart, + PurpleGradientEnd + ) + ) + ) + } else { + Modifier.background(Color(0xFFEFF3F8)) + } + ) + ) { + Row( + modifier = Modifier.padding(horizontal = 14.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Text( + text = "${interest.emoji} ${interest.label}", + fontSize = 13.sp, + fontWeight = FontWeight.Bold, + color = if (isSelected) Color.White else textDark + ) + Spacer(modifier = Modifier.width(6.dp)) + Icon( + imageVector = if (isSelected) Icons.Default.Close else Icons.Default.Add, + contentDescription = null, + tint = if (isSelected) Color.White.copy(alpha = 0.8f) else Color.Gray, + modifier = Modifier.size(16.dp) + ) + } + } + } + + // "Add more" pill button + Surface( + onClick = { /* Open custom interest modal */ }, + shape = RoundedCornerShape(20.dp), + color = Color(0xFFEFF3F8), + modifier = Modifier.padding(horizontal = 4.dp) + ) { + Row( + modifier = Modifier.padding(horizontal = 14.dp, vertical = 10.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Icon( + imageVector = Icons.Default.AddCircle, + contentDescription = null, + tint = PurpleGradientEnd, + modifier = Modifier.size(18.dp) + ) + Spacer(modifier = Modifier.width(6.dp)) + Text( + text = "Add more", + fontSize = 13.sp, + fontWeight = FontWeight.Bold, + color = PurpleGradientEnd + ) + } + } + } + Spacer(modifier = Modifier.height(20.dp)) + + // Selection Counter Row + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "Selected", + fontSize = 12.sp, + color = Color.Gray, + fontWeight = FontWeight.Medium + ) + Surface( + shape = RoundedCornerShape(10.dp), + color = Color(0xFFE8EAF6) + ) { + Text( + text = "${selectedIds.size} selected", + fontSize = 11.sp, + fontWeight = FontWeight.Bold, + color = PurpleGradientEnd, + modifier = Modifier.padding(horizontal = 10.dp, vertical = 4.dp) + ) + } + } + } + } + } +} +data class GenderPreferenceOption( + val id: String, + val title: String, + val subtitle: String, + val icon: ImageVector +) @Composable -fun VibeCheckWidget( - selectedVibe: String, - onVibeSelected: (String) -> Unit, - errorMessage: String? = null -) { +fun StepSixContent() { + var selectedOptionId by remember { mutableStateOf("women") } + val options = listOf( - VibeOption("Serious Relationship", "Looking for the one", Icons.Outlined.FavoriteBorder, Color(0xFFFFEAEA), Color(0xFFE91E63)), - VibeOption("Casual Dates", "Drinks & good times", Icons.Outlined.LocalBar, Color(0xFFEDE7F6), Color(0xFF673AB7)), - VibeOption("New Friends", "Expand my circle", Icons.Outlined.Group, Color(0xFFFFECB3), Color(0xFFFF8F00)), - VibeOption("Marraige", "Seeing where things go", Icons.Outlined.Explore, Color(0xFFE0F2F1), Color(0xFF00897B)) + GenderPreferenceOption( + id = "women", + title = "Women", + subtitle = "Female profiles", + icon = Icons.Default.Female + ), + GenderPreferenceOption( + id = "men", + title = "Men", + subtitle = "Male profiles", + icon = Icons.Default.Male + ), + GenderPreferenceOption( + id = "everyone", + title = "Everyone", + subtitle = "Open to all matches", + icon = Icons.Default.Groups + ) ) Column( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 8.dp), + modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) { - // App Title / Brand Header - Text( - text = "VibeCheck", - fontSize = 24.sp, - fontWeight = FontWeight.Bold, - color = Color(0xFFC2185B) - ) - - Spacer(modifier = Modifier.height(8.dp)) - - // Three Indicator Bars - Row( - horizontalArrangement = Arrangement.spacedBy(6.dp), - verticalAlignment = Alignment.CenterVertically + // 1. Title Header Card + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(20.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + border = BorderStroke(1.dp, LightBorderColor) ) { - Box(modifier = Modifier.width(20.dp).height(4.dp).background(Color(0xFFE0E0E0), CircleShape)) - Box(modifier = Modifier.width(20.dp).height(4.dp).background(Color(0xFFC2185B), CircleShape)) - Box(modifier = Modifier.width(20.dp).height(4.dp).background(Color(0xFFE0E0E0), CircleShape)) + Column( + modifier = Modifier.padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "Who are you interested in?", + style = MaterialTheme.typography.titleMedium.copy( + fontWeight = FontWeight.Bold, + fontSize = 18.sp, + color = textDark + ) + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = "Select the gender you'd like to match with.", + fontSize = 12.sp, + color = Color.Gray, + textAlign = TextAlign.Center + ) + } } - Spacer(modifier = Modifier.height(20.dp)) - - // Title & Description - Text( - text = "What's your vibe?", - fontSize = 20.sp, - fontWeight = FontWeight.Bold, - color = Color.Black - ) - - Spacer(modifier = Modifier.height(8.dp)) - - Text( - text = "Select your main goal right now to help us find the right connections.", - fontSize = 13.sp, - color = Color.Gray, - textAlign = TextAlign.Center, - modifier = Modifier.padding(horizontal = 16.dp) - ) - - Spacer(modifier = Modifier.height(24.dp)) + Spacer(modifier = Modifier.height(16.dp)) - // Vibe Cards List + // 2. Selection Option Cards Column( - verticalArrangement = Arrangement.spacedBy(12.dp), - modifier = Modifier.fillMaxWidth() + verticalArrangement = Arrangement.spacedBy(12.dp) ) { options.forEach { option -> - val isSelected = selectedVibe == option.title + val isSelected = selectedOptionId == option.id - Card( - shape = RoundedCornerShape(16.dp), - colors = CardDefaults.cardColors(containerColor = Color.White), + Surface( + onClick = { selectedOptionId = option.id }, + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(20.dp), + color = Color.White, border = BorderStroke( width = if (isSelected) 2.dp else 1.dp, - color = if (isSelected) Color(0xFFC2185B) else Color(0xFFEEEEEE) - ), - modifier = Modifier - .fillMaxWidth() - .clickable { onVibeSelected(option.title) } + color = if (isSelected) textPink else LightBorderColor + ) ) { Row( modifier = Modifier @@ -868,70 +1523,117 @@ fun VibeCheckWidget( .padding(16.dp), verticalAlignment = Alignment.CenterVertically ) { - // Round Icon Container + // Icon Box Box( modifier = Modifier - .size(42.dp) - .background(option.iconBgColor, CircleShape), + .size(48.dp) + .background( + color = Color(0xFFEFF3F8), + shape = RoundedCornerShape(12.dp) + ), contentAlignment = Alignment.Center ) { Icon( imageVector = option.icon, - contentDescription = option.title, - tint = option.iconTintColor, - modifier = Modifier.size(22.dp) + contentDescription = null, + tint = Color(0xFF1976D2), + modifier = Modifier.size(24.dp) ) } Spacer(modifier = Modifier.width(16.dp)) - // Text Labels - Column { + // Labels + Column(modifier = Modifier.weight(1f)) { Text( text = option.title, - fontSize = 15.sp, + fontSize = 16.sp, fontWeight = FontWeight.Bold, - color = Color.Black + color = textDark ) + Spacer(modifier = Modifier.height(2.dp)) Text( text = option.subtitle, fontSize = 12.sp, color = Color.Gray ) } + + // Selection Indicator (Radio / Check) + Box( + modifier = Modifier + .size(24.dp) + .background( + color = if (isSelected) Color.Black else Color(0xFFE8EEF5), + shape = CircleShape + ), + contentAlignment = Alignment.Center + ) { + if (isSelected) { + Icon( + imageVector = Icons.Default.Check, + contentDescription = "Selected", + tint = Color.White, + modifier = Modifier.size(16.dp) + ) + } + } } } } } + } +} - if (errorMessage != null) { - Spacer(modifier = Modifier.height(12.dp)) - Text( - text = errorMessage, - color = MaterialTheme.colorScheme.error, - fontSize = 12.sp, - fontWeight = FontWeight.Medium - ) - } +@Preview(showBackground = true, showSystemUi = true) +@Composable +fun RegistrationFlowContainerPreview() { + RegistrationFlowContainer() +} - Spacer(modifier = Modifier.height(16.dp)) +@Preview(showBackground = true, showSystemUi = true) +@Composable +fun RegistrationFlowContainerPreviewss() { + StepFiveContent() +} - Text( - text = "You can always change this later in your profile.", - fontSize = 11.sp, - color = Color.Gray +// 1. Email Tab Preview (Normal State) +@Preview(showBackground = true, name = "Step Two - Email Tab") +@Composable +fun StepTwoContentEmailPreview() { + MaterialTheme { + StepTwoContent( + selectedTab = 0, + onTabSelected = {}, + emailText = "john.doe@example.com", + onEmailChange = {}, + phoneText = "", + onPhoneChange = {}, + countryCode = "+1", + emailError = null, + phoneError = null, + isEmailValid = true, + isPhoneValid = false ) } } -@Preview( - name = "Light Mode - Full Wizard", - showBackground = true, - showSystemUi = true -) +@Preview(showBackground = true, name = "Step Two - Phone Tab with Error") @Composable -fun FullscreenRegistrationWizardPreview() { - Surface(modifier = Modifier.fillMaxSize()) { - FullscreenRegistrationWizard() +fun StepTwoContentPhonePreview() { + MaterialTheme { + StepTwoContent( + selectedTab = 1, + onTabSelected = {}, + emailText = "", + onEmailChange = {}, + phoneText = "12345", + onPhoneChange = {}, + countryCode = "+1", + emailError = null, + phoneError = "Enter a valid phone number (10-15 digits)", + isEmailValid = false, + isPhoneValid = false + ) } } \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt b/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt index c6425a2..f3a64bf 100644 --- a/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt +++ b/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt @@ -25,7 +25,6 @@ val BrandMagenta = Color(0xFFB5005A) val BackgroundLight = Color(0xFFFAFAFA) val CardBackground = Color(0xFFF2ECEF) val GuidelinesBackground = Color(0xFFFFFBFD) -val TextDark = Color(0xFF1E1E1E) val TextMuted = Color(0xFF6E6E6E) val OverlayDark = Color(0x66000000) val BrandPink = Color(0xFFE91E63) @@ -72,4 +71,9 @@ val PrimaryBlue = Color(0xFF0066FF) val LightBlueIconBg = Color(0xFF8CAEF8) val DarkText = Color(0xFF1E232A) val MutedText = Color(0xFF757575) - +val textDark = Color(0xFF1E1B18) +val textPink = Color(0xFFE91E63) +val PinkGradientStart = Color(0xFFE91E63) +val PurpleGradientEnd = Color(0xFF8E24AA) +val LightBorderColor = Color(0xFFFFD1DC) +val InputBg = Color(0xFFF9FAFB) \ No newline at end of file diff --git a/app/src/main/res/drawable/vibeai.png b/app/src/main/res/drawable/vibeai.png new file mode 100644 index 0000000..5183fc0 Binary files /dev/null and b/app/src/main/res/drawable/vibeai.png differ diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d7053ab..1fc311d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,6 +17,7 @@ media3UiVersion = "1.5.1" navigationFragmentKtx = "2.6.0" navigationUiKtx = "2.6.0" material3 = "1.4.0" +ui = "1.12.0" [libraries] androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } @@ -42,6 +43,7 @@ androidx-navigation-ui-ktx = { group = "androidx.navigation", name = "navigation androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3UiVersion" } media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3ExoplayerVersion" } +androidx-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" } [plugins] android-application = { id = "com.android.application", version.ref = "agp" }