ability to cancel future bookings

This commit is contained in:
androidlover5842
2026-02-04 16:45:15 +05:30
parent e1250a0f32
commit f9b09e2376

View File

@@ -18,11 +18,15 @@ import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Error
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.QrCode import androidx.compose.material.icons.filled.QrCode
import androidx.compose.material.icons.filled.ReceiptLong import androidx.compose.material.icons.filled.ReceiptLong
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@@ -31,6 +35,7 @@ import androidx.compose.material3.Tab
import androidx.compose.material3.TabRow import androidx.compose.material3.TabRow
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.AlertDialog
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -53,6 +58,7 @@ import com.android.trisolarispms.data.api.core.ApiClient
import com.android.trisolarispms.data.api.core.ApiConstants import com.android.trisolarispms.data.api.core.ApiConstants
import com.android.trisolarispms.data.api.core.FirebaseAuthTokenProvider import com.android.trisolarispms.data.api.core.FirebaseAuthTokenProvider
import com.android.trisolarispms.data.api.model.BookingBillingMode import com.android.trisolarispms.data.api.model.BookingBillingMode
import com.android.trisolarispms.data.api.model.BookingCancelRequest
import com.android.trisolarispms.data.api.model.BookingDetailsResponse import com.android.trisolarispms.data.api.model.BookingDetailsResponse
import com.android.trisolarispms.data.api.model.BookingExpectedDatesRequest import com.android.trisolarispms.data.api.model.BookingExpectedDatesRequest
import com.android.trisolarispms.ui.booking.BookingDatePickerDialog import com.android.trisolarispms.ui.booking.BookingDatePickerDialog
@@ -91,10 +97,15 @@ fun BookingDetailsTabsScreen(
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val staysState by staysViewModel.state.collectAsState() val staysState by staysViewModel.state.collectAsState()
val detailsState by detailsViewModel.state.collectAsState() val detailsState by detailsViewModel.state.collectAsState()
val actionsMenuExpanded = remember { mutableStateOf(false) }
val showCancelConfirm = remember { mutableStateOf(false) }
val cancelLoading = remember { mutableStateOf(false) }
val cancelError = remember { mutableStateOf<String?>(null) }
val canModifyDocuments = canManageDocuments && when (detailsState.details?.status) { val canModifyDocuments = canManageDocuments && when (detailsState.details?.status) {
"OPEN", "CHECKED_IN" -> true "OPEN", "CHECKED_IN" -> true
else -> false else -> false
} }
val canCancelBooking = detailsState.details?.status?.uppercase() == "OPEN"
LaunchedEffect(propertyId, bookingId, guestId) { LaunchedEffect(propertyId, bookingId, guestId) {
staysViewModel.load(propertyId, bookingId) staysViewModel.load(propertyId, bookingId)
@@ -107,7 +118,34 @@ fun BookingDetailsTabsScreen(
BackTopBarScaffold( BackTopBarScaffold(
title = "Details", title = "Details",
onBack = onBack onBack = onBack,
actions = {
if (canCancelBooking) {
IconButton(onClick = { actionsMenuExpanded.value = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "More options")
}
DropdownMenu(
expanded = actionsMenuExpanded.value,
onDismissRequest = { actionsMenuExpanded.value = false }
) {
DropdownMenuItem(
text = { Text("Cancel Booking") },
leadingIcon = {
Icon(
imageVector = Icons.Default.Error,
contentDescription = null,
tint = MaterialTheme.colorScheme.error
)
},
onClick = {
actionsMenuExpanded.value = false
cancelError.value = null
showCancelConfirm.value = true
}
)
}
}
}
) { padding -> ) { padding ->
Column( Column(
modifier = Modifier modifier = Modifier
@@ -179,6 +217,74 @@ fun BookingDetailsTabsScreen(
} }
} }
} }
if (showCancelConfirm.value && canCancelBooking) {
AlertDialog(
onDismissRequest = {
if (!cancelLoading.value) {
showCancelConfirm.value = false
}
},
icon = {
Icon(
imageVector = Icons.Default.Error,
contentDescription = null,
tint = MaterialTheme.colorScheme.error
)
},
title = { Text("Cancel booking?") },
text = {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
Text("This will cancel the OPEN booking.")
cancelError.value?.let { message ->
Text(
text = message,
color = MaterialTheme.colorScheme.error,
style = MaterialTheme.typography.bodySmall
)
}
}
},
confirmButton = {
TextButton(
enabled = !cancelLoading.value,
onClick = {
scope.launch {
cancelLoading.value = true
cancelError.value = null
try {
val response = ApiClient.create().cancelBooking(
propertyId = propertyId,
bookingId = bookingId,
body = BookingCancelRequest()
)
if (response.isSuccessful) {
showCancelConfirm.value = false
onBack()
} else {
cancelError.value = "Cancel failed: ${response.code()}"
}
} catch (e: Exception) {
cancelError.value = e.localizedMessage ?: "Cancel failed"
} finally {
cancelLoading.value = false
}
}
}
) {
Text(if (cancelLoading.value) "Cancelling..." else "Cancel Booking")
}
},
dismissButton = {
TextButton(
enabled = !cancelLoading.value,
onClick = { showCancelConfirm.value = false }
) {
Text("Keep Booking")
}
}
)
}
} }
@Composable @Composable