bookingCreate: improve input phone number ui
This commit is contained in:
@@ -58,6 +58,7 @@ dependencies {
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.lottie.compose)
|
||||
implementation(libs.calendar.compose)
|
||||
implementation(libs.libphonenumber)
|
||||
implementation(platform(libs.firebase.bom))
|
||||
implementation(libs.firebase.auth.ktx)
|
||||
implementation(libs.kotlinx.coroutines.play.services)
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.android.trisolarispms.ui.booking
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
@@ -47,7 +46,6 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.kizitonwose.calendar.compose.HorizontalCalendar
|
||||
import com.kizitonwose.calendar.compose.rememberCalendarState
|
||||
import com.android.trisolarispms.data.api.model.GuestDto
|
||||
import com.kizitonwose.calendar.core.CalendarDay
|
||||
import com.kizitonwose.calendar.core.CalendarMonth
|
||||
import com.kizitonwose.calendar.core.DayPosition
|
||||
@@ -77,7 +75,12 @@ fun BookingCreateScreen(
|
||||
val checkInNow = remember { mutableStateOf(true) }
|
||||
val sourceMenuExpanded = remember { mutableStateOf(false) }
|
||||
val sourceOptions = listOf("WALKIN", "OTA", "AGENT")
|
||||
val transportMenuExpanded = remember { mutableStateOf(false) }
|
||||
val transportOptions = listOf("CAR", "BIKE", "TRAIN", "PLANE", "BUS", "FOOT", "CYCLE", "OTHER")
|
||||
val displayFormatter = remember { DateTimeFormatter.ofPattern("dd-MM-yy HH:mm") }
|
||||
val phoneCountryMenuExpanded = remember { mutableStateOf(false) }
|
||||
val phoneCountries = remember { phoneCountryOptions() }
|
||||
val phoneCountrySearch = remember { mutableStateOf("") }
|
||||
|
||||
LaunchedEffect(propertyId) {
|
||||
viewModel.reset()
|
||||
@@ -171,12 +174,75 @@ fun BookingCreateScreen(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
OutlinedTextField(
|
||||
value = state.phoneE164,
|
||||
onValueChange = viewModel::onPhoneChange,
|
||||
label = { Text("Guest Phone E164 (optional)") },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Text(text = "Guest Phone (optional)", style = MaterialTheme.typography.titleSmall)
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
val selectedCountry = findPhoneCountryOption(state.phoneCountryCode)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.Top
|
||||
) {
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = phoneCountryMenuExpanded.value,
|
||||
onExpandedChange = { phoneCountryMenuExpanded.value = !phoneCountryMenuExpanded.value },
|
||||
modifier = Modifier.weight(0.3f)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = "${selectedCountry.code} +${selectedCountry.dialCode}",
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
label = { Text("Country") },
|
||||
trailingIcon = {
|
||||
ExposedDropdownMenuDefaults.TrailingIcon(expanded = phoneCountryMenuExpanded.value)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.menuAnchor()
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
expanded = phoneCountryMenuExpanded.value,
|
||||
onDismissRequest = { phoneCountryMenuExpanded.value = false }
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = phoneCountrySearch.value,
|
||||
onValueChange = { phoneCountrySearch.value = it },
|
||||
label = { Text("Search") },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(8.dp)
|
||||
)
|
||||
val filteredCountries = phoneCountries.filter { option ->
|
||||
val query = phoneCountrySearch.value.trim()
|
||||
if (query.isBlank()) {
|
||||
true
|
||||
} else {
|
||||
option.name.contains(query, ignoreCase = true) ||
|
||||
option.code.contains(query, ignoreCase = true) ||
|
||||
option.dialCode.contains(query)
|
||||
}
|
||||
}
|
||||
filteredCountries.forEach { option ->
|
||||
DropdownMenuItem(
|
||||
text = { Text("${option.name} (+${option.dialCode})") },
|
||||
onClick = {
|
||||
phoneCountryMenuExpanded.value = false
|
||||
phoneCountrySearch.value = ""
|
||||
viewModel.onPhoneCountryChange(option.code)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
OutlinedTextField(
|
||||
value = state.phoneNationalNumber,
|
||||
onValueChange = viewModel::onPhoneNationalNumberChange,
|
||||
label = { Text("Number") },
|
||||
prefix = { Text("+${selectedCountry.dialCode}") },
|
||||
supportingText = { Text("Max ${selectedCountry.maxLength} digits") },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Phone),
|
||||
modifier = Modifier.weight(0.7f)
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = sourceMenuExpanded.value,
|
||||
@@ -208,12 +274,37 @@ fun BookingCreateScreen(
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
OutlinedTextField(
|
||||
value = state.transportMode,
|
||||
onValueChange = viewModel::onTransportModeChange,
|
||||
label = { Text("Transport Mode (optional)") },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = transportMenuExpanded.value,
|
||||
onExpandedChange = { transportMenuExpanded.value = !transportMenuExpanded.value }
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = state.transportMode,
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
label = { Text("Transport Mode") },
|
||||
trailingIcon = {
|
||||
ExposedDropdownMenuDefaults.TrailingIcon(expanded = transportMenuExpanded.value)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.menuAnchor()
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
expanded = transportMenuExpanded.value,
|
||||
onDismissRequest = { transportMenuExpanded.value = false }
|
||||
) {
|
||||
transportOptions.forEach { option ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(option) },
|
||||
onClick = {
|
||||
transportMenuExpanded.value = false
|
||||
viewModel.onTransportModeChange(option)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
OutlinedTextField(
|
||||
value = state.adultCount,
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
package com.android.trisolarispms.ui.booking
|
||||
|
||||
data class BookingCreateState(
|
||||
val phoneE164: String = "",
|
||||
val phoneCountryCode: String = "IN",
|
||||
val phoneNationalNumber: String = "",
|
||||
val expectedCheckInAt: String = "",
|
||||
val expectedCheckOutAt: String = "",
|
||||
val source: String = "WALKIN",
|
||||
val transportMode: String = "",
|
||||
val transportMode: String = "CAR",
|
||||
val adultCount: String = "",
|
||||
val totalGuestCount: String = "",
|
||||
val notes: String = "",
|
||||
|
||||
@@ -28,8 +28,18 @@ class BookingCreateViewModel : ViewModel() {
|
||||
_state.update { it.copy(expectedCheckOutAt = value, error = null) }
|
||||
}
|
||||
|
||||
fun onPhoneChange(value: String) {
|
||||
_state.update { it.copy(phoneE164 = value, error = null) }
|
||||
fun onPhoneCountryChange(value: String) {
|
||||
val option = findPhoneCountryOption(value)
|
||||
_state.update { current ->
|
||||
val trimmed = current.phoneNationalNumber.filter { it.isDigit() }.take(option.maxLength)
|
||||
current.copy(phoneCountryCode = value, phoneNationalNumber = trimmed, error = null)
|
||||
}
|
||||
}
|
||||
|
||||
fun onPhoneNationalNumberChange(value: String) {
|
||||
val option = findPhoneCountryOption(_state.value.phoneCountryCode)
|
||||
val trimmed = value.filter { it.isDigit() }.take(option.maxLength)
|
||||
_state.update { it.copy(phoneNationalNumber = trimmed, error = null) }
|
||||
}
|
||||
|
||||
fun onSourceChange(value: String) {
|
||||
@@ -66,7 +76,13 @@ class BookingCreateViewModel : ViewModel() {
|
||||
_state.update { it.copy(isLoading = true, error = null) }
|
||||
try {
|
||||
val api = ApiClient.create()
|
||||
val phone = current.phoneE164.trim().ifBlank { null }
|
||||
val phoneCountry = findPhoneCountryOption(current.phoneCountryCode)
|
||||
val phoneDigits = current.phoneNationalNumber.trim()
|
||||
val phone = if (phoneDigits.isNotBlank()) {
|
||||
"+${phoneCountry.dialCode}$phoneDigits"
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val existingGuest = if (!phone.isNullOrBlank()) {
|
||||
val guestResponse = api.searchGuests(propertyId, phone = phone)
|
||||
if (guestResponse.isSuccessful) {
|
||||
|
||||
Reference in New Issue
Block a user