You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
938 lines
34 KiB
938 lines
34 KiB
package com.example.dattingapp
|
|
|
|
import android.content.Intent
|
|
import android.os.Bundle
|
|
import androidx.activity.ComponentActivity
|
|
import androidx.activity.compose.setContent
|
|
import androidx.activity.enableEdgeToEdge
|
|
import androidx.compose.animation.*
|
|
import androidx.compose.animation.core.tween
|
|
import androidx.compose.foundation.background
|
|
import androidx.compose.foundation.clickable
|
|
import androidx.compose.foundation.layout.*
|
|
import androidx.compose.foundation.text.KeyboardOptions
|
|
import androidx.compose.material3.*
|
|
import androidx.compose.runtime.*
|
|
import androidx.compose.runtime.snapshots.SnapshotStateList
|
|
import androidx.compose.ui.Alignment
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.draw.clip
|
|
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.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.registration.UploadProfileImageActivity
|
|
|
|
// 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()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun FullscreenRegistrationWizard() {
|
|
// Active step state
|
|
var currentStep by remember { mutableIntStateOf(1) }
|
|
val totalSteps = 7
|
|
|
|
// 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<String?>(null) }
|
|
|
|
var passwordInput by remember { mutableStateOf("") }
|
|
var confirmPasswordInput by remember { mutableStateOf("") }
|
|
var passwordError by remember { mutableStateOf<String?>(null) }
|
|
|
|
var nameInput by remember { mutableStateOf("") }
|
|
var nameError by remember { mutableStateOf<String?>(null) }
|
|
|
|
var dobInput by remember { mutableStateOf("") }
|
|
var dobError by remember { mutableStateOf<String?>(null) }
|
|
|
|
var genderInput by remember { mutableStateOf("") }
|
|
var genderError by remember { mutableStateOf<String?>(null) }
|
|
|
|
val selectedInterests = remember { mutableStateListOf<String>() }
|
|
var interestsError by remember { mutableStateOf<String?>(null) }
|
|
|
|
|
|
var vibeInput by remember { mutableStateOf("") }
|
|
var vibeError by remember { mutableStateOf<String?>(null) }
|
|
|
|
// Inside your FullscreenRegistrationWizard composable:
|
|
val context = LocalContext.current
|
|
|
|
// Validation & Navigation
|
|
fun validateAndProceed() {
|
|
when (currentStep) {
|
|
|
|
|
|
|
|
1 -> {
|
|
if (nameInput.trim().isBlank()) {
|
|
nameError = "Please enter your name"
|
|
} else {
|
|
nameError = null
|
|
currentStep = 2
|
|
}
|
|
|
|
}
|
|
|
|
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 = 2 }
|
|
}
|
|
} 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 = 4 }
|
|
}
|
|
}
|
|
|
|
}
|
|
3 -> {
|
|
|
|
when {
|
|
passwordInput.length < 6 -> passwordError = "Password must be at least 6 characters"
|
|
passwordInput != confirmPasswordInput -> passwordError = "Passwords do not match"
|
|
else -> { passwordError = null; currentStep = 3 }
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
6 -> {
|
|
if (selectedInterests.size < 2) {
|
|
interestsError = "Please select at least 2 interests"
|
|
} else {
|
|
interestsError = null
|
|
// Submit registration payload
|
|
currentStep = 7
|
|
}
|
|
}
|
|
|
|
|
|
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)
|
|
}
|
|
context.startActivity(intent)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Box(
|
|
modifier = Modifier
|
|
.fillMaxSize()
|
|
.background(Color(0xFFF8F9FA))
|
|
.systemBarsPadding()
|
|
) {
|
|
Column(modifier = Modifier.fillMaxSize()) {
|
|
// TOP BAR & PROGRESS
|
|
Column(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(horizontal = 20.dp, vertical = 16.dp)
|
|
) {
|
|
Row(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
verticalAlignment = Alignment.CenterVertically,
|
|
horizontalArrangement = Arrangement.SpaceBetween
|
|
) {
|
|
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,
|
|
fontSize = 16.sp,
|
|
color = Color.Black
|
|
)
|
|
|
|
Spacer(modifier = Modifier.width(48.dp))
|
|
}
|
|
|
|
Spacer(modifier = Modifier.height(12.dp))
|
|
|
|
LinearProgressIndicator(
|
|
progress = { currentStep / totalSteps.toFloat() },
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.height(6.dp),
|
|
color = Color.Black,
|
|
trackColor = Color(0xFFE0E0E0)
|
|
)
|
|
}
|
|
|
|
// SLIDING CONTENT CONTAINER
|
|
Box(
|
|
modifier = Modifier
|
|
.weight(1f)
|
|
.fillMaxWidth()
|
|
.padding(horizontal = 24.dp),
|
|
contentAlignment = Alignment.Center
|
|
) {
|
|
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
|
|
) {
|
|
when (step) {
|
|
|
|
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
|
|
)
|
|
3 -> PasswordInputWidget(
|
|
password = passwordInput,
|
|
onPasswordChange = { passwordInput = it; passwordError = null },
|
|
confirmPassword = confirmPasswordInput,
|
|
onConfirmPasswordChange = { confirmPasswordInput = it; passwordError = null },
|
|
errorMessage = passwordError
|
|
)
|
|
|
|
4 -> DobInputWidget(
|
|
dob = dobInput,
|
|
onDobChange = { dobInput = it; dobError = null },
|
|
errorMessage = dobError
|
|
)
|
|
5 -> GenderInputWidget(
|
|
selectedGender = genderInput,
|
|
onGenderSelect = { genderInput = it; genderError = null },
|
|
errorMessage = genderError
|
|
)
|
|
6 -> InterestsInputWidget(
|
|
selectedInterests = selectedInterests,
|
|
errorMessage = interestsError,
|
|
onSelectionChanged = { interestsError = null }
|
|
)
|
|
7 -> VibeCheckWidget(
|
|
selectedVibe = vibeInput,
|
|
onVibeSelected = {
|
|
vibeInput = it
|
|
vibeError = null
|
|
},
|
|
errorMessage = vibeError
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
) {
|
|
Text(
|
|
text = if (currentStep == totalSteps) "Complete Registration" else "Continue",
|
|
color = Color.White,
|
|
fontSize = 16.sp,
|
|
fontWeight = FontWeight.Bold
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ==================== STEP WIDGET COMPOSABLES ====================
|
|
|
|
@Composable
|
|
fun ContactInputWidget(
|
|
isPhoneSelected: Boolean,
|
|
onModeChange: (Boolean) -> Unit,
|
|
countryCode: String,
|
|
onCountryCodeChange: (String) -> Unit,
|
|
contactValue: String,
|
|
onContactChange: (String) -> Unit,
|
|
errorMessage: String?
|
|
) {
|
|
Column(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
) {
|
|
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
|
|
) {
|
|
Box(
|
|
modifier = Modifier
|
|
.weight(1f)
|
|
.fillMaxHeight()
|
|
.clip(RoundedCornerShape(20.dp))
|
|
.background(if (isPhoneSelected) Color.Black else Color.Transparent)
|
|
.clickable { onModeChange(true) },
|
|
contentAlignment = Alignment.Center
|
|
) {
|
|
Text(
|
|
text = "Phone",
|
|
fontWeight = FontWeight.Bold,
|
|
fontSize = 14.sp,
|
|
color = if (isPhoneSelected) Color.White else Color.DarkGray
|
|
)
|
|
}
|
|
|
|
Box(
|
|
modifier = Modifier
|
|
.weight(1f)
|
|
.fillMaxHeight()
|
|
.clip(RoundedCornerShape(20.dp))
|
|
.background(if (!isPhoneSelected) Color.Black else Color.Transparent)
|
|
.clickable { onModeChange(false) },
|
|
contentAlignment = Alignment.Center
|
|
) {
|
|
Text(
|
|
text = "Email",
|
|
fontWeight = FontWeight.Bold,
|
|
fontSize = 14.sp,
|
|
color = if (!isPhoneSelected) Color.White else Color.DarkGray
|
|
)
|
|
}
|
|
}
|
|
|
|
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
|
|
)
|
|
)
|
|
|
|
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
|
|
)
|
|
)
|
|
}
|
|
} 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?
|
|
) {
|
|
Column(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
verticalArrangement = Arrangement.spacedBy(12.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
|
|
)
|
|
)
|
|
|
|
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
|
|
)
|
|
)
|
|
|
|
if (errorMessage != null) {
|
|
Text(
|
|
text = errorMessage,
|
|
color = MaterialTheme.colorScheme.error,
|
|
fontSize = 12.sp,
|
|
fontWeight = FontWeight.Medium,
|
|
modifier = Modifier.padding(start = 4.dp)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun NameInputWidget(
|
|
name: String,
|
|
onNameChange: (String) -> Unit,
|
|
errorMessage: String?
|
|
) {
|
|
Column(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
) {
|
|
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,
|
|
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")
|
|
}
|
|
}
|
|
) {
|
|
DatePicker(state = datePickerState)
|
|
}
|
|
}
|
|
|
|
Column(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
) {
|
|
Text(
|
|
text = "YOUR DATE OF BIRTH",
|
|
fontSize = 11.sp,
|
|
fontWeight = FontWeight.Bold,
|
|
color = Color.Gray
|
|
)
|
|
|
|
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
|
|
)
|
|
)
|
|
Box(
|
|
modifier = Modifier
|
|
.matchParentSize()
|
|
.clickable { showDatePicker = true }
|
|
)
|
|
}
|
|
|
|
if (errorMessage != null) {
|
|
Text(
|
|
text = errorMessage,
|
|
color = MaterialTheme.colorScheme.error,
|
|
fontSize = 12.sp,
|
|
fontWeight = FontWeight.Medium,
|
|
modifier = Modifier.padding(start = 4.dp)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun GenderInputWidget(
|
|
selectedGender: String,
|
|
onGenderSelect: (String) -> Unit,
|
|
errorMessage: String?
|
|
) {
|
|
val options = listOf("Male", "Female", "Other")
|
|
|
|
Column(
|
|
modifier = Modifier.fillMaxWidth(),
|
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
) {
|
|
Text(
|
|
text = "SELECT YOUR GENDER",
|
|
fontSize = 11.sp,
|
|
fontWeight = FontWeight.Bold,
|
|
color = Color.Gray
|
|
)
|
|
|
|
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
|
|
) {
|
|
Text(
|
|
text = gender,
|
|
color = if (isSelected) Color.White else Color.Black,
|
|
fontWeight = FontWeight.SemiBold,
|
|
fontSize = 15.sp
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (errorMessage != null) {
|
|
Text(
|
|
text = errorMessage,
|
|
color = MaterialTheme.colorScheme.error,
|
|
fontSize = 12.sp,
|
|
fontWeight = FontWeight.Medium,
|
|
modifier = Modifier.padding(start = 4.dp)
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
@OptIn(ExperimentalLayoutApi::class)
|
|
@Composable
|
|
fun InterestsInputWidget(
|
|
selectedInterests: SnapshotStateList<String>,
|
|
errorMessage: String? = null,
|
|
onSelectionChanged: () -> Unit = {}
|
|
) {
|
|
val allInterests = listOf("Photography", "Dogs", "Travel", "Music", "Gaming", "Fitness", "Cooking")
|
|
|
|
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))
|
|
|
|
FlowRow(
|
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
|
) {
|
|
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) }
|
|
)
|
|
}
|
|
}
|
|
|
|
if (errorMessage != null) {
|
|
Spacer(modifier = Modifier.height(8.dp))
|
|
Text(
|
|
text = errorMessage,
|
|
color = MaterialTheme.colorScheme.error,
|
|
fontSize = 12.sp,
|
|
fontWeight = FontWeight.Medium
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
@Composable
|
|
fun VibeCheckWidget(
|
|
selectedVibe: String,
|
|
onVibeSelected: (String) -> Unit,
|
|
errorMessage: String? = null
|
|
) {
|
|
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))
|
|
)
|
|
|
|
Column(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(horizontal = 8.dp),
|
|
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
|
|
) {
|
|
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))
|
|
}
|
|
|
|
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))
|
|
|
|
// Vibe Cards List
|
|
Column(
|
|
verticalArrangement = Arrangement.spacedBy(12.dp),
|
|
modifier = Modifier.fillMaxWidth()
|
|
) {
|
|
options.forEach { option ->
|
|
val isSelected = selectedVibe == option.title
|
|
|
|
Card(
|
|
shape = RoundedCornerShape(16.dp),
|
|
colors = CardDefaults.cardColors(containerColor = 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) }
|
|
) {
|
|
Row(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(16.dp),
|
|
verticalAlignment = Alignment.CenterVertically
|
|
) {
|
|
// Round Icon Container
|
|
Box(
|
|
modifier = Modifier
|
|
.size(42.dp)
|
|
.background(option.iconBgColor, CircleShape),
|
|
contentAlignment = Alignment.Center
|
|
) {
|
|
Icon(
|
|
imageVector = option.icon,
|
|
contentDescription = option.title,
|
|
tint = option.iconTintColor,
|
|
modifier = Modifier.size(22.dp)
|
|
)
|
|
}
|
|
|
|
Spacer(modifier = Modifier.width(16.dp))
|
|
|
|
// Text Labels
|
|
Column {
|
|
Text(
|
|
text = option.title,
|
|
fontSize = 15.sp,
|
|
fontWeight = FontWeight.Bold,
|
|
color = Color.Black
|
|
)
|
|
Text(
|
|
text = option.subtitle,
|
|
fontSize = 12.sp,
|
|
color = Color.Gray
|
|
)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (errorMessage != null) {
|
|
Spacer(modifier = Modifier.height(12.dp))
|
|
Text(
|
|
text = errorMessage,
|
|
color = MaterialTheme.colorScheme.error,
|
|
fontSize = 12.sp,
|
|
fontWeight = FontWeight.Medium
|
|
)
|
|
}
|
|
|
|
Spacer(modifier = Modifier.height(16.dp))
|
|
|
|
Text(
|
|
text = "You can always change this later in your profile.",
|
|
fontSize = 11.sp,
|
|
color = Color.Gray
|
|
)
|
|
}
|
|
}
|
|
|
|
@Preview(
|
|
name = "Light Mode - Full Wizard",
|
|
showBackground = true,
|
|
showSystemUi = true
|
|
)
|
|
@Composable
|
|
fun FullscreenRegistrationWizardPreview() {
|
|
Surface(modifier = Modifier.fillMaxSize()) {
|
|
FullscreenRegistrationWizard()
|
|
}
|
|
}
|