improve booking section
This commit is contained in:
@@ -36,6 +36,12 @@ interface GuestApi {
|
||||
@Query("vehicleNumber") vehicleNumber: String? = null
|
||||
): Response<List<GuestDto>>
|
||||
|
||||
@GET("properties/{propertyId}/guests/{guestId}")
|
||||
suspend fun getGuest(
|
||||
@Path("propertyId") propertyId: String,
|
||||
@Path("guestId") guestId: String
|
||||
): Response<GuestDto>
|
||||
|
||||
@POST("properties/{propertyId}/guests/{guestId}/vehicles")
|
||||
suspend fun addGuestVehicle(
|
||||
@Path("propertyId") propertyId: String,
|
||||
|
||||
@@ -12,6 +12,7 @@ data class BookingCreateRequest(
|
||||
val expectedCheckInAt: String,
|
||||
val expectedCheckOutAt: String,
|
||||
val source: String? = null,
|
||||
val guestPhoneE164: String? = null,
|
||||
val transportMode: String? = null,
|
||||
val adultCount: Int? = null,
|
||||
val totalGuestCount: Int? = null,
|
||||
|
||||
@@ -5,7 +5,6 @@ import androidx.lifecycle.viewModelScope
|
||||
import com.android.trisolarispms.data.api.ApiClient
|
||||
import com.android.trisolarispms.data.api.model.BookingCreateRequest
|
||||
import com.android.trisolarispms.data.api.model.BookingCreateResponse
|
||||
import com.android.trisolarispms.data.api.model.BookingLinkGuestRequest
|
||||
import com.android.trisolarispms.data.api.model.GuestDto
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -83,21 +82,13 @@ class BookingCreateViewModel : ViewModel() {
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val existingGuest = if (!phone.isNullOrBlank()) {
|
||||
val guestResponse = api.searchGuests(propertyId, phone = phone)
|
||||
if (guestResponse.isSuccessful) {
|
||||
guestResponse.body().orEmpty().firstOrNull()
|
||||
} else {
|
||||
_state.update { it.copy(isLoading = false, error = "Guest search failed: ${guestResponse.code()}") }
|
||||
return@launch
|
||||
}
|
||||
} else null
|
||||
val response = api.createBooking(
|
||||
propertyId = propertyId,
|
||||
body = BookingCreateRequest(
|
||||
expectedCheckInAt = checkIn,
|
||||
expectedCheckOutAt = checkOut,
|
||||
source = current.source.trim().ifBlank { null },
|
||||
guestPhoneE164 = phone,
|
||||
transportMode = current.transportMode.trim().ifBlank { null },
|
||||
adultCount = adultCount,
|
||||
totalGuestCount = totalGuestCount,
|
||||
@@ -106,19 +97,8 @@ class BookingCreateViewModel : ViewModel() {
|
||||
)
|
||||
val body = response.body()
|
||||
if (response.isSuccessful && body != null) {
|
||||
if (existingGuest?.id != null) {
|
||||
val linkResponse = api.linkGuest(
|
||||
propertyId = propertyId,
|
||||
bookingId = body.id.orEmpty(),
|
||||
body = BookingLinkGuestRequest(existingGuest.id)
|
||||
)
|
||||
if (!linkResponse.isSuccessful) {
|
||||
_state.update { it.copy(isLoading = false, error = "Link guest failed: ${linkResponse.code()}") }
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
_state.update { it.copy(isLoading = false, error = null) }
|
||||
onDone(body, existingGuest, phone)
|
||||
onDone(body, null, phone)
|
||||
} else {
|
||||
_state.update { it.copy(isLoading = false, error = "Create failed: ${response.code()}") }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.android.trisolarispms.ui.booking
|
||||
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil
|
||||
import com.google.i18n.phonenumbers.PhoneNumberUtil.PhoneNumberType
|
||||
import java.util.Locale
|
||||
|
||||
data class PhoneCountryOption(
|
||||
val code: String,
|
||||
val name: String,
|
||||
val dialCode: String,
|
||||
val maxLength: Int
|
||||
)
|
||||
|
||||
private val fallbackPhoneCountries: List<PhoneCountryOption> = listOf(
|
||||
PhoneCountryOption(code = "IN", name = "India", dialCode = "91", maxLength = 10),
|
||||
PhoneCountryOption(code = "US", name = "United States", dialCode = "1", maxLength = 10)
|
||||
)
|
||||
|
||||
private val phoneCountryOptions: List<PhoneCountryOption> by lazy {
|
||||
val util = PhoneNumberUtil.getInstance()
|
||||
val regions = util.supportedRegions
|
||||
val options = regions.mapNotNull { region ->
|
||||
val dialCode = util.getCountryCodeForRegion(region)
|
||||
if (dialCode == 0) return@mapNotNull null
|
||||
val maxLength = guessMaxLength(util, region) ?: 15
|
||||
val name = Locale("", region).displayCountry
|
||||
PhoneCountryOption(
|
||||
code = region,
|
||||
name = name,
|
||||
dialCode = dialCode.toString(),
|
||||
maxLength = maxLength
|
||||
)
|
||||
}.sortedWith(compareBy({ it.name.lowercase() }, { it.dialCode }))
|
||||
if (options.isNotEmpty()) options else fallbackPhoneCountries
|
||||
}
|
||||
|
||||
fun phoneCountryOptions(): List<PhoneCountryOption> = phoneCountryOptions
|
||||
|
||||
fun findPhoneCountryOption(code: String): PhoneCountryOption {
|
||||
return phoneCountryOptions.firstOrNull { it.code == code }
|
||||
?: phoneCountryOptions.first()
|
||||
}
|
||||
|
||||
private fun guessMaxLength(util: PhoneNumberUtil, region: String): Int? {
|
||||
val types = listOf(
|
||||
PhoneNumberType.MOBILE,
|
||||
PhoneNumberType.FIXED_LINE,
|
||||
PhoneNumberType.FIXED_LINE_OR_MOBILE
|
||||
)
|
||||
val lengths = types.mapNotNull { type ->
|
||||
util.getExampleNumberForType(region, type)?.let { example ->
|
||||
util.getNationalSignificantNumber(example).length
|
||||
}
|
||||
}
|
||||
return lengths.maxOrNull()
|
||||
}
|
||||
@@ -44,6 +44,7 @@ fun GuestInfoScreen(
|
||||
LaunchedEffect(guestId) {
|
||||
viewModel.reset()
|
||||
viewModel.setInitial(initialGuest, initialPhone)
|
||||
viewModel.loadGuest(propertyId, guestId, initialPhone)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
|
||||
@@ -46,6 +46,46 @@ class GuestInfoViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun loadGuest(propertyId: String, guestId: String, fallbackPhone: String?) {
|
||||
if (propertyId.isBlank() || guestId.isBlank()) return
|
||||
viewModelScope.launch {
|
||||
_state.update { it.copy(isLoading = true, error = null) }
|
||||
try {
|
||||
val api = ApiClient.create()
|
||||
val response = api.getGuest(propertyId = propertyId, guestId = guestId)
|
||||
val guest = response.body()
|
||||
if (response.isSuccessful && guest != null) {
|
||||
_state.update {
|
||||
it.copy(
|
||||
phoneE164 = guest.phoneE164 ?: fallbackPhone.orEmpty(),
|
||||
name = guest.name.orEmpty(),
|
||||
nationality = guest.nationality.orEmpty(),
|
||||
addressText = guest.addressText.orEmpty(),
|
||||
isLoading = false,
|
||||
error = null
|
||||
)
|
||||
}
|
||||
} else {
|
||||
_state.update {
|
||||
it.copy(
|
||||
phoneE164 = it.phoneE164.ifBlank { fallbackPhone.orEmpty() },
|
||||
isLoading = false,
|
||||
error = "Load failed: ${response.code()}"
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
_state.update {
|
||||
it.copy(
|
||||
phoneE164 = it.phoneE164.ifBlank { fallbackPhone.orEmpty() },
|
||||
isLoading = false,
|
||||
error = e.localizedMessage ?: "Load failed"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun submit(propertyId: String, guestId: String, onDone: () -> Unit) {
|
||||
if (propertyId.isBlank() || guestId.isBlank()) return
|
||||
val current = state.value
|
||||
|
||||
Reference in New Issue
Block a user