diff --git a/app/src/main/java/com/android/trisolarispms/ui/roomstay/BookingDetailsTabsScreen.kt b/app/src/main/java/com/android/trisolarispms/ui/roomstay/BookingDetailsTabsScreen.kt index 6ece699..e642bb2 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/roomstay/BookingDetailsTabsScreen.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/roomstay/BookingDetailsTabsScreen.kt @@ -18,11 +18,15 @@ import androidx.compose.foundation.pager.rememberPagerState import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll 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.ReceiptLong import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton @@ -31,6 +35,7 @@ import androidx.compose.material3.Tab import androidx.compose.material3.TabRow import androidx.compose.material3.Text import androidx.compose.material3.TextButton +import androidx.compose.material3.AlertDialog import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect 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.FirebaseAuthTokenProvider 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.BookingExpectedDatesRequest import com.android.trisolarispms.ui.booking.BookingDatePickerDialog @@ -91,10 +97,15 @@ fun BookingDetailsTabsScreen( val scope = rememberCoroutineScope() val staysState by staysViewModel.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(null) } val canModifyDocuments = canManageDocuments && when (detailsState.details?.status) { "OPEN", "CHECKED_IN" -> true else -> false } + val canCancelBooking = detailsState.details?.status?.uppercase() == "OPEN" LaunchedEffect(propertyId, bookingId, guestId) { staysViewModel.load(propertyId, bookingId) @@ -107,7 +118,34 @@ fun BookingDetailsTabsScreen( BackTopBarScaffold( 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 -> Column( 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