From 869e59aaac3384efe7d68c9c2ea26a6698d69896 Mon Sep 17 00:00:00 2001 From: androidlover5842 Date: Thu, 29 Jan 2026 09:18:35 +0530 Subject: [PATCH] improve booking section --- .../trisolarispms/data/api/GuestApi.kt | 6 ++ .../data/api/model/BookingModels.kt | 1 + .../ui/booking/BookingCreateViewModel.kt | 24 +------- .../trisolarispms/ui/booking/PhoneCountry.kt | 56 +++++++++++++++++++ .../trisolarispms/ui/guest/GuestInfoScreen.kt | 1 + .../ui/guest/GuestInfoViewModel.kt | 40 +++++++++++++ 6 files changed, 106 insertions(+), 22 deletions(-) create mode 100644 app/src/main/java/com/android/trisolarispms/ui/booking/PhoneCountry.kt diff --git a/app/src/main/java/com/android/trisolarispms/data/api/GuestApi.kt b/app/src/main/java/com/android/trisolarispms/data/api/GuestApi.kt index 52a8a09..c925e8f 100644 --- a/app/src/main/java/com/android/trisolarispms/data/api/GuestApi.kt +++ b/app/src/main/java/com/android/trisolarispms/data/api/GuestApi.kt @@ -36,6 +36,12 @@ interface GuestApi { @Query("vehicleNumber") vehicleNumber: String? = null ): Response> + @GET("properties/{propertyId}/guests/{guestId}") + suspend fun getGuest( + @Path("propertyId") propertyId: String, + @Path("guestId") guestId: String + ): Response + @POST("properties/{propertyId}/guests/{guestId}/vehicles") suspend fun addGuestVehicle( @Path("propertyId") propertyId: String, diff --git a/app/src/main/java/com/android/trisolarispms/data/api/model/BookingModels.kt b/app/src/main/java/com/android/trisolarispms/data/api/model/BookingModels.kt index b9562d5..6ee18db 100644 --- a/app/src/main/java/com/android/trisolarispms/data/api/model/BookingModels.kt +++ b/app/src/main/java/com/android/trisolarispms/data/api/model/BookingModels.kt @@ -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, diff --git a/app/src/main/java/com/android/trisolarispms/ui/booking/BookingCreateViewModel.kt b/app/src/main/java/com/android/trisolarispms/ui/booking/BookingCreateViewModel.kt index 8c77066..b2448ae 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/booking/BookingCreateViewModel.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/booking/BookingCreateViewModel.kt @@ -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()}") } } diff --git a/app/src/main/java/com/android/trisolarispms/ui/booking/PhoneCountry.kt b/app/src/main/java/com/android/trisolarispms/ui/booking/PhoneCountry.kt new file mode 100644 index 0000000..ad8cd7c --- /dev/null +++ b/app/src/main/java/com/android/trisolarispms/ui/booking/PhoneCountry.kt @@ -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 = listOf( + PhoneCountryOption(code = "IN", name = "India", dialCode = "91", maxLength = 10), + PhoneCountryOption(code = "US", name = "United States", dialCode = "1", maxLength = 10) +) + +private val phoneCountryOptions: List 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 = 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() +} diff --git a/app/src/main/java/com/android/trisolarispms/ui/guest/GuestInfoScreen.kt b/app/src/main/java/com/android/trisolarispms/ui/guest/GuestInfoScreen.kt index 4ebc618..65e9cf7 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/guest/GuestInfoScreen.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/guest/GuestInfoScreen.kt @@ -44,6 +44,7 @@ fun GuestInfoScreen( LaunchedEffect(guestId) { viewModel.reset() viewModel.setInitial(initialGuest, initialPhone) + viewModel.loadGuest(propertyId, guestId, initialPhone) } Scaffold( diff --git a/app/src/main/java/com/android/trisolarispms/ui/guest/GuestInfoViewModel.kt b/app/src/main/java/com/android/trisolarispms/ui/guest/GuestInfoViewModel.kt index 8c067dc..286c702 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/guest/GuestInfoViewModel.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/guest/GuestInfoViewModel.kt @@ -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