diff --git a/app/src/main/java/com/android/trisolarispms/MainActivity.kt b/app/src/main/java/com/android/trisolarispms/MainActivity.kt index d2a9a95..0a0c1f8 100644 --- a/app/src/main/java/com/android/trisolarispms/MainActivity.kt +++ b/app/src/main/java/com/android/trisolarispms/MainActivity.kt @@ -3,56 +3,14 @@ package com.android.trisolarispms import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent -import androidx.activity.compose.BackHandler import androidx.activity.enableEdgeToEdge import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.LaunchedEffect import androidx.lifecycle.viewmodel.compose.viewModel -import com.android.trisolarispms.auth.AuthzPolicy -import com.android.trisolarispms.auth.Role -import com.android.trisolarispms.auth.toRoleNameList -import com.android.trisolarispms.auth.toRoleSet -import com.android.trisolarispms.ui.AppRoute import com.android.trisolarispms.ui.auth.AuthScreen import com.android.trisolarispms.ui.auth.AuthViewModel import com.android.trisolarispms.ui.auth.NameScreen import com.android.trisolarispms.ui.auth.UnauthorizedScreen -import com.android.trisolarispms.ui.booking.BookingCreateScreen -import com.android.trisolarispms.ui.guest.GuestInfoScreen -import com.android.trisolarispms.ui.guest.GuestSignatureScreen -import com.android.trisolarispms.ui.booking.BookingExpectedDatesScreen -import com.android.trisolarispms.ui.roomstay.ManageRoomStayRatesScreen -import com.android.trisolarispms.ui.roomstay.ManageRoomStaySelectScreen -import com.android.trisolarispms.ui.roomstay.ManageRoomStaySelection -import com.android.trisolarispms.ui.roomstay.BookingRoomStaysScreen -import com.android.trisolarispms.ui.roomstay.BookingDetailsTabsScreen -import com.android.trisolarispms.ui.home.HomeScreen -import com.android.trisolarispms.ui.payment.BookingPaymentsScreen -import com.android.trisolarispms.ui.property.AddPropertyScreen -import com.android.trisolarispms.ui.room.RoomFormScreen -import com.android.trisolarispms.ui.room.RoomsScreen -import com.android.trisolarispms.ui.card.IssueTemporaryCardScreen -import com.android.trisolarispms.ui.card.CardInfoScreen -import com.android.trisolarispms.ui.roomimage.RoomImagesScreen -import com.android.trisolarispms.ui.roomimage.ImageTagsScreen -import com.android.trisolarispms.ui.roomimage.AddImageTagScreen -import com.android.trisolarispms.ui.roomimage.EditImageTagScreen -import com.android.trisolarispms.ui.roomstay.ActiveRoomStaysScreen -import com.android.trisolarispms.ui.razorpay.RazorpaySettingsScreen -import com.android.trisolarispms.ui.razorpay.RazorpayQrScreen -import com.android.trisolarispms.ui.users.PropertyUsersScreen -import com.android.trisolarispms.ui.users.PropertyAccessCodeScreen -import com.android.trisolarispms.ui.users.SuperAdminUserDirectoryScreen -import com.android.trisolarispms.ui.roomtype.AddAmenityScreen -import com.android.trisolarispms.ui.roomtype.AddRoomTypeScreen -import com.android.trisolarispms.ui.roomtype.AmenitiesScreen -import com.android.trisolarispms.ui.roomtype.EditAmenityScreen -import com.android.trisolarispms.ui.roomtype.EditRoomTypeScreen -import com.android.trisolarispms.ui.roomtype.RatePlanCalendarScreen -import com.android.trisolarispms.ui.roomtype.RoomTypesScreen import com.android.trisolarispms.ui.theme.TrisolarisPMSTheme class MainActivity : ComponentActivity() { @@ -64,674 +22,17 @@ class MainActivity : ComponentActivity() { val authViewModel: AuthViewModel = viewModel() val state by authViewModel.state.collectAsState() - if (state.unauthorized) { - UnauthorizedScreen( + when { + state.unauthorized -> UnauthorizedScreen( message = state.error ?: "Not authorized. Contact admin.", onSignOut = authViewModel::signOut ) - } else if (state.apiVerified && state.needsName) { - NameScreen(viewModel = authViewModel) - } else if (state.apiVerified) { - val route = remember { mutableStateOf(AppRoute.Home) } - val refreshKey = remember { mutableStateOf(0) } - val selectedPropertyId = remember { mutableStateOf(null) } - val selectedPropertyName = remember { mutableStateOf(null) } - val selectedRoom = remember { mutableStateOf(null) } - val selectedRoomType = remember { mutableStateOf(null) } - val selectedAmenity = remember { mutableStateOf(null) } - val selectedGuest = remember { mutableStateOf(null) } - val selectedGuestPhone = remember { mutableStateOf(null) } - val selectedImageTag = remember { mutableStateOf(null) } - val selectedManageRooms = remember { mutableStateOf>(emptyList()) } - val roomFormKey = remember { mutableStateOf(0) } - val amenitiesReturnRoute = remember { mutableStateOf(AppRoute.Home) } - val currentRoute = route.value - val authz = remember(state.isSuperAdmin, state.propertyRoles) { - AuthzPolicy( - isSuperAdmin = state.isSuperAdmin, - propertyRoles = state.propertyRoles - ) - } - val singlePropertyId = state.propertyRoles.keys.firstOrNull() - val singlePropertyIsAdmin = singlePropertyId?.let(authz::isPropertyAdmin) ?: false - val allowedAccessCodeRoles: (String) -> List = { propertyId -> - authz.allowedAccessCodeRoles(propertyId).toRoleNameList() - } - - BackHandler(enabled = currentRoute != AppRoute.Home) { - when (currentRoute) { - AppRoute.Home -> Unit - AppRoute.AddProperty -> route.value = AppRoute.Home - AppRoute.SuperAdminUsers -> route.value = AppRoute.Home - is AppRoute.ActiveRoomStays -> { - val blockBack = authz.shouldBlockBackToHome( - propertyId = currentRoute.propertyId, - propertyCount = state.propertyRoles.size - ) - if (!blockBack) { - route.value = AppRoute.Home - } - } - is AppRoute.PropertyUsers -> route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - is AppRoute.PropertyAccessCode -> route.value = AppRoute.PropertyUsers( - currentRoute.propertyId - ) - is AppRoute.Rooms -> route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - is AppRoute.AddRoom -> route.value = AppRoute.Rooms(currentRoute.propertyId) - is AppRoute.EditRoom -> route.value = AppRoute.Rooms(currentRoute.propertyId) - is AppRoute.RoomTypes -> route.value = AppRoute.Rooms(currentRoute.propertyId) - is AppRoute.AddRoomType -> route.value = AppRoute.RoomTypes(currentRoute.propertyId) - is AppRoute.EditRoomType -> route.value = AppRoute.RoomTypes(currentRoute.propertyId) - AppRoute.Amenities -> route.value = amenitiesReturnRoute.value - AppRoute.AddAmenity -> route.value = AppRoute.Amenities - is AppRoute.EditAmenity -> route.value = AppRoute.Amenities - AppRoute.ImageTags -> route.value = AppRoute.Home - AppRoute.AddImageTag -> route.value = AppRoute.ImageTags - is AppRoute.EditImageTag -> route.value = AppRoute.ImageTags - is AppRoute.RoomImages -> route.value = AppRoute.EditRoom( - currentRoute.propertyId, - currentRoute.roomId - ) - is AppRoute.IssueTemporaryCard -> route.value = AppRoute.Rooms(currentRoute.propertyId) - is AppRoute.CardInfo -> route.value = AppRoute.Rooms(currentRoute.propertyId) - is AppRoute.RatePlanCalendar -> route.value = AppRoute.EditRoomType( - currentRoute.propertyId, - currentRoute.roomTypeId - ) - is AppRoute.RazorpaySettings -> route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - is AppRoute.RazorpayQr -> route.value = AppRoute.BookingDetailsTabs( - currentRoute.propertyId, - currentRoute.bookingId, - null - ) - is AppRoute.CreateBooking -> route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - is AppRoute.GuestInfo -> route.value = AppRoute.Home - is AppRoute.GuestSignature -> route.value = AppRoute.GuestInfo( - currentRoute.propertyId, - currentRoute.bookingId, - currentRoute.guestId - ) - is AppRoute.ManageRoomStaySelect -> route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - is AppRoute.ManageRoomStayRates -> route.value = AppRoute.ManageRoomStaySelect( - currentRoute.propertyId, - currentRoute.bookingId, - currentRoute.fromAt, - currentRoute.toAt - ) - is AppRoute.ManageRoomStaySelectFromBooking -> route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - is AppRoute.ManageRoomStayRatesFromBooking -> route.value = AppRoute.ManageRoomStaySelectFromBooking( - currentRoute.propertyId, - currentRoute.bookingId, - currentRoute.guestId, - currentRoute.fromAt, - currentRoute.toAt - ) - is AppRoute.BookingRoomStays -> route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - is AppRoute.BookingExpectedDates -> route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - is AppRoute.BookingDetailsTabs -> route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - is AppRoute.BookingPayments -> route.value = AppRoute.BookingDetailsTabs( - currentRoute.propertyId, - currentRoute.bookingId, - null - ) - } - } - - if (currentRoute == AppRoute.Home && - !state.isSuperAdmin && - !singlePropertyIsAdmin && - state.propertyRoles.size == 1 && - singlePropertyId != null - ) { - LaunchedEffect(singlePropertyId) { - selectedPropertyId.value = singlePropertyId - route.value = AppRoute.ActiveRoomStays( - singlePropertyId, - selectedPropertyName.value ?: "Property" - ) - } - } - - when (currentRoute) { - AppRoute.Home -> HomeScreen( - userId = state.userId, - userName = state.userName, - isSuperAdmin = state.isSuperAdmin, - onUserDirectory = { route.value = AppRoute.SuperAdminUsers }, - onLogout = authViewModel::signOut, - onAddProperty = { route.value = AppRoute.AddProperty }, - onAmenities = { - amenitiesReturnRoute.value = AppRoute.Home - route.value = AppRoute.Amenities - }, - onImageTags = { route.value = AppRoute.ImageTags }, - refreshKey = refreshKey.value, - selectedPropertyId = selectedPropertyId.value, - onSelectProperty = { id, name -> - selectedPropertyId.value = id - selectedPropertyName.value = name - route.value = AppRoute.ActiveRoomStays(id, name) - }, - onRefreshProfile = authViewModel::refreshMe, - showJoinProperty = state.propertyRoles.isEmpty() && !state.isSuperAdmin, - onJoinPropertySuccess = { joinedPropertyId, joinedRoles -> - refreshKey.value++ - authViewModel.refreshMe() - val isAdmin = joinedRoles.toRoleSet().contains(Role.ADMIN) - val shouldAutoOpen = !state.isSuperAdmin && !isAdmin && state.propertyRoles.size <= 1 - if (shouldAutoOpen) { - route.value = AppRoute.ActiveRoomStays( - joinedPropertyId, - selectedPropertyName.value ?: "Property" - ) - } - } - ) - AppRoute.AddProperty -> AddPropertyScreen( - onBack = { route.value = AppRoute.Home }, - onCreated = { - refreshKey.value++ - route.value = AppRoute.Home - } - ) - is AppRoute.CreateBooking -> BookingCreateScreen( - propertyId = currentRoute.propertyId, - onBack = { - route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - }, - onCreated = { response, guest, phone -> - val bookingId = response.id.orEmpty() - val guestId = (guest?.id ?: response.guestId).orEmpty() - selectedGuest.value = guest - selectedGuestPhone.value = phone - if (bookingId.isNotBlank() && guestId.isNotBlank()) { - val fromAt = response.checkInAt?.takeIf { it.isNotBlank() } - ?: response.expectedCheckInAt.orEmpty() - val toAt = response.expectedCheckOutAt?.takeIf { it.isNotBlank() } - route.value = AppRoute.ManageRoomStaySelectFromBooking( - propertyId = currentRoute.propertyId, - bookingId = bookingId, - guestId = guestId, - fromAt = fromAt, - toAt = toAt - ) - } else { - route.value = AppRoute.Home - } - } - ) - is AppRoute.GuestInfo -> GuestInfoScreen( - propertyId = currentRoute.propertyId, - guestId = currentRoute.guestId, - initialGuest = selectedGuest.value, - initialPhone = selectedGuestPhone.value, - onBack = { route.value = AppRoute.Home }, - onSave = { - route.value = AppRoute.GuestSignature( - currentRoute.propertyId, - currentRoute.bookingId, - currentRoute.guestId - ) - } - ) - is AppRoute.GuestSignature -> GuestSignatureScreen( - propertyId = currentRoute.propertyId, - guestId = currentRoute.guestId, - onBack = { - route.value = AppRoute.GuestInfo( - currentRoute.propertyId, - currentRoute.bookingId, - currentRoute.guestId - ) - }, - onDone = { - route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - } - ) - is AppRoute.ActiveRoomStays -> ActiveRoomStaysScreen( - propertyId = currentRoute.propertyId, - propertyName = currentRoute.propertyName, - onBack = { - val blockBack = authz.shouldBlockBackToHome( - propertyId = currentRoute.propertyId, - propertyCount = state.propertyRoles.size - ) - if (!blockBack) { - route.value = AppRoute.Home - } - }, - showBack = !authz.shouldBlockBackToHome( - propertyId = currentRoute.propertyId, - propertyCount = state.propertyRoles.size - ), - onViewRooms = { route.value = AppRoute.Rooms(currentRoute.propertyId) }, - onCreateBooking = { route.value = AppRoute.CreateBooking(currentRoute.propertyId) }, - canCreateBooking = authz.canCreateBookingFor(currentRoute.propertyId), - showRazorpaySettings = authz.canManageRazorpaySettings(currentRoute.propertyId), - onRazorpaySettings = { route.value = AppRoute.RazorpaySettings(currentRoute.propertyId) }, - showUserAdmin = authz.canManagePropertyUsers(currentRoute.propertyId), - onUserAdmin = { route.value = AppRoute.PropertyUsers(currentRoute.propertyId) }, - onLogout = authViewModel::signOut, - onManageRoomStay = { booking -> - val fromAt = booking.checkInAt?.takeIf { it.isNotBlank() } - ?: booking.expectedCheckInAt.orEmpty() - val toAt = booking.expectedCheckOutAt?.takeIf { it.isNotBlank() } - ?: booking.checkOutAt?.takeIf { it.isNotBlank() } - if (fromAt.isNotBlank()) { - route.value = AppRoute.ManageRoomStaySelect( - propertyId = currentRoute.propertyId, - bookingId = booking.id.orEmpty(), - fromAt = fromAt, - toAt = toAt - ) - } - }, - onViewBookingStays = { booking -> - route.value = AppRoute.BookingRoomStays( - propertyId = currentRoute.propertyId, - bookingId = booking.id.orEmpty() - ) - }, - onOpenBookingDetails = { booking -> - route.value = AppRoute.BookingDetailsTabs( - propertyId = currentRoute.propertyId, - bookingId = booking.id.orEmpty(), - guestId = booking.guestId - ) - } - ) - is AppRoute.RazorpaySettings -> RazorpaySettingsScreen( - propertyId = currentRoute.propertyId, - onBack = { - route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - } - ) - is AppRoute.RazorpayQr -> RazorpayQrScreen( - propertyId = currentRoute.propertyId, - bookingId = currentRoute.bookingId, - pendingAmount = currentRoute.pendingAmount, - guestPhone = currentRoute.guestPhone, - onBack = { - route.value = AppRoute.BookingDetailsTabs( - currentRoute.propertyId, - currentRoute.bookingId, - null - ) - } - ) - is AppRoute.ManageRoomStaySelect -> ManageRoomStaySelectScreen( - propertyId = currentRoute.propertyId, - bookingFromAt = currentRoute.fromAt, - bookingToAt = currentRoute.toAt, - onBack = { - route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - }, - onNext = { rooms -> - selectedManageRooms.value = rooms - route.value = AppRoute.ManageRoomStayRates( - propertyId = currentRoute.propertyId, - bookingId = currentRoute.bookingId, - fromAt = currentRoute.fromAt, - toAt = currentRoute.toAt - ) - } - ) - is AppRoute.ManageRoomStaySelectFromBooking -> ManageRoomStaySelectScreen( - propertyId = currentRoute.propertyId, - bookingFromAt = currentRoute.fromAt, - bookingToAt = currentRoute.toAt, - onBack = { - route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - }, - onNext = { rooms -> - selectedManageRooms.value = rooms - route.value = AppRoute.ManageRoomStayRatesFromBooking( - propertyId = currentRoute.propertyId, - bookingId = currentRoute.bookingId, - guestId = currentRoute.guestId, - fromAt = currentRoute.fromAt, - toAt = currentRoute.toAt - ) - } - ) - is AppRoute.ManageRoomStayRates -> ManageRoomStayRatesScreen( - propertyId = currentRoute.propertyId, - bookingId = currentRoute.bookingId, - checkInAt = currentRoute.fromAt, - checkOutAt = currentRoute.toAt, - selectedRooms = selectedManageRooms.value, - onBack = { - route.value = AppRoute.ManageRoomStaySelect( - currentRoute.propertyId, - currentRoute.bookingId, - currentRoute.fromAt, - currentRoute.toAt - ) - }, - onDone = { - route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - } - ) - is AppRoute.ManageRoomStayRatesFromBooking -> ManageRoomStayRatesScreen( - propertyId = currentRoute.propertyId, - bookingId = currentRoute.bookingId, - checkInAt = currentRoute.fromAt, - checkOutAt = currentRoute.toAt, - selectedRooms = selectedManageRooms.value, - onBack = { - route.value = AppRoute.ManageRoomStaySelectFromBooking( - currentRoute.propertyId, - currentRoute.bookingId, - currentRoute.guestId, - currentRoute.fromAt, - currentRoute.toAt - ) - }, - onDone = { - route.value = AppRoute.GuestInfo( - currentRoute.propertyId, - currentRoute.bookingId, - currentRoute.guestId - ) - } - ) - is AppRoute.BookingRoomStays -> BookingRoomStaysScreen( - propertyId = currentRoute.propertyId, - bookingId = currentRoute.bookingId, - onBack = { - route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - } - ) - is AppRoute.BookingExpectedDates -> BookingExpectedDatesScreen( - propertyId = currentRoute.propertyId, - bookingId = currentRoute.bookingId, - status = currentRoute.status, - expectedCheckInAt = currentRoute.expectedCheckInAt, - expectedCheckOutAt = currentRoute.expectedCheckOutAt, - onBack = { - route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - }, - onDone = { - route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - } - ) - is AppRoute.BookingDetailsTabs -> BookingDetailsTabsScreen( - propertyId = currentRoute.propertyId, - bookingId = currentRoute.bookingId, - guestId = currentRoute.guestId, - onBack = { - route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - }, - onEditCheckout = { expectedCheckInAt, expectedCheckOutAt -> - route.value = AppRoute.BookingExpectedDates( - propertyId = currentRoute.propertyId, - bookingId = currentRoute.bookingId, - status = "CHECKED_IN", - expectedCheckInAt = expectedCheckInAt, - expectedCheckOutAt = expectedCheckOutAt - ) - }, - onEditSignature = { guestId -> - route.value = AppRoute.GuestSignature( - currentRoute.propertyId, - currentRoute.bookingId, - guestId - ) - }, - onOpenRazorpayQr = { pendingAmount, guestPhone -> - route.value = AppRoute.RazorpayQr( - propertyId = currentRoute.propertyId, - bookingId = currentRoute.bookingId, - pendingAmount = pendingAmount, - guestPhone = guestPhone - ) - }, - onOpenPayments = { - route.value = AppRoute.BookingPayments( - propertyId = currentRoute.propertyId, - bookingId = currentRoute.bookingId - ) - }, - canManageDocuments = authz.canManageRazorpaySettings(currentRoute.propertyId) - ) - is AppRoute.BookingPayments -> BookingPaymentsScreen( - propertyId = currentRoute.propertyId, - bookingId = currentRoute.bookingId, - canAddCash = authz.canAddBookingPayment(currentRoute.propertyId), - canDeleteCash = authz.canDeleteCashPayment(currentRoute.propertyId), - canRefund = authz.canRefundBookingPayment(currentRoute.propertyId), - onBack = { - route.value = AppRoute.BookingDetailsTabs( - currentRoute.propertyId, - currentRoute.bookingId, - null - ) - } - ) - AppRoute.SuperAdminUsers -> SuperAdminUserDirectoryScreen( - onBack = { route.value = AppRoute.Home } - ) - is AppRoute.PropertyUsers -> PropertyUsersScreen( - propertyId = currentRoute.propertyId, - allowedRoleAssignments = authz - .allowedRoleAssignments(currentRoute.propertyId) - .toRoleNameList(), - canDisableAdmin = authz.canDisableAdmin(currentRoute.propertyId), - canDisableManager = authz.canDisableManager(currentRoute.propertyId), - onBack = { - route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - }, - onOpenAccessCode = { - route.value = AppRoute.PropertyAccessCode(currentRoute.propertyId) - } - ) - is AppRoute.PropertyAccessCode -> PropertyAccessCodeScreen( - propertyId = currentRoute.propertyId, - allowedRoles = allowedAccessCodeRoles(currentRoute.propertyId), - onBack = { route.value = AppRoute.PropertyUsers(currentRoute.propertyId) } - ) - is AppRoute.Rooms -> RoomsScreen( - propertyId = currentRoute.propertyId, - onBack = { - route.value = AppRoute.ActiveRoomStays( - currentRoute.propertyId, - selectedPropertyName.value ?: "Property" - ) - }, - onAddRoom = { - roomFormKey.value++ - route.value = AppRoute.AddRoom(currentRoute.propertyId) - }, - onViewRoomTypes = { route.value = AppRoute.RoomTypes(currentRoute.propertyId) }, - onViewCardInfo = { route.value = AppRoute.CardInfo(currentRoute.propertyId) }, - canManageRooms = authz.canManageProperty(currentRoute.propertyId), - canViewCardInfo = authz.canViewCardInfo(currentRoute.propertyId), - canIssueTemporaryCard = authz.canIssueTemporaryCard(currentRoute.propertyId), - onEditRoom = { - selectedRoom.value = it - roomFormKey.value++ - route.value = AppRoute.EditRoom(currentRoute.propertyId, it.id ?: "") - }, - onIssueTemporaryCard = { - if (it.id != null && authz.canIssueTemporaryCard(currentRoute.propertyId)) { - selectedRoom.value = it - route.value = AppRoute.IssueTemporaryCard(currentRoute.propertyId, it.id) - } - } - ) - is AppRoute.RoomTypes -> RoomTypesScreen( - propertyId = currentRoute.propertyId, - onBack = { route.value = AppRoute.Rooms(currentRoute.propertyId) }, - onAdd = { route.value = AppRoute.AddRoomType(currentRoute.propertyId) }, - canManageRoomTypes = authz.canManageProperty(currentRoute.propertyId), - onEdit = { - selectedRoomType.value = it - route.value = AppRoute.EditRoomType(currentRoute.propertyId, it.id ?: "") - } - ) - is AppRoute.AddRoomType -> AddRoomTypeScreen( - propertyId = currentRoute.propertyId, - onBack = { route.value = AppRoute.RoomTypes(currentRoute.propertyId) }, - onSave = { route.value = AppRoute.RoomTypes(currentRoute.propertyId) } - ) - is AppRoute.EditRoomType -> EditRoomTypeScreen( - propertyId = currentRoute.propertyId, - roomType = selectedRoomType.value - ?: com.android.trisolarispms.data.api.model.RoomTypeDto(id = currentRoute.roomTypeId, code = "", name = ""), - onBack = { route.value = AppRoute.RoomTypes(currentRoute.propertyId) }, - onSave = { route.value = AppRoute.RoomTypes(currentRoute.propertyId) }, - onOpenRatePlanCalendar = { ratePlanId, ratePlanCode -> - route.value = AppRoute.RatePlanCalendar( - currentRoute.propertyId, - currentRoute.roomTypeId, - ratePlanId, - ratePlanCode - ) - } - ) - AppRoute.Amenities -> AmenitiesScreen( - onBack = { route.value = amenitiesReturnRoute.value }, - onAdd = { route.value = AppRoute.AddAmenity }, - canManageAmenities = state.isSuperAdmin, - onEdit = { - selectedAmenity.value = it - route.value = AppRoute.EditAmenity(it.id ?: "") - } - ) - AppRoute.AddAmenity -> AddAmenityScreen( - onBack = { route.value = AppRoute.Amenities }, - onSave = { route.value = AppRoute.Amenities } - ) - is AppRoute.EditAmenity -> EditAmenityScreen( - amenity = selectedAmenity.value - ?: com.android.trisolarispms.data.api.model.AmenityDto(id = currentRoute.amenityId, name = ""), - onBack = { route.value = AppRoute.Amenities }, - onSave = { route.value = AppRoute.Amenities } - ) - AppRoute.ImageTags -> ImageTagsScreen( - onBack = { route.value = AppRoute.Home }, - onAdd = { route.value = AppRoute.AddImageTag }, - onEdit = { - selectedImageTag.value = it - route.value = AppRoute.EditImageTag(it.id ?: "") - } - ) - AppRoute.AddImageTag -> AddImageTagScreen( - onBack = { route.value = AppRoute.ImageTags }, - onSave = { route.value = AppRoute.ImageTags } - ) - is AppRoute.EditImageTag -> EditImageTagScreen( - tag = selectedImageTag.value - ?: com.android.trisolarispms.data.api.model.RoomImageTagDto(id = currentRoute.tagId, name = ""), - onBack = { route.value = AppRoute.ImageTags }, - onSave = { route.value = AppRoute.ImageTags } - ) - is AppRoute.AddRoom -> RoomFormScreen( - title = "Add Room", - propertyId = currentRoute.propertyId, - roomId = null, - roomData = null, - formKey = roomFormKey.value, - onBack = { route.value = AppRoute.Rooms(currentRoute.propertyId) }, - onSave = { route.value = AppRoute.Rooms(currentRoute.propertyId) }, - onViewImages = { } - ) - is AppRoute.EditRoom -> RoomFormScreen( - title = "Modify Room", - propertyId = currentRoute.propertyId, - roomId = currentRoute.roomId, - roomData = selectedRoom.value, - formKey = roomFormKey.value, - onBack = { route.value = AppRoute.Rooms(currentRoute.propertyId) }, - onSave = { route.value = AppRoute.Rooms(currentRoute.propertyId) }, - onViewImages = { roomId -> - route.value = AppRoute.RoomImages(currentRoute.propertyId, roomId) - } - ) - is AppRoute.IssueTemporaryCard -> IssueTemporaryCardScreen( - propertyId = currentRoute.propertyId, - roomId = currentRoute.roomId, - roomNumber = selectedRoom.value?.roomNumber?.toString(), - onBack = { route.value = AppRoute.Rooms(currentRoute.propertyId) } - ) - is AppRoute.CardInfo -> CardInfoScreen( - propertyId = currentRoute.propertyId, - onBack = { route.value = AppRoute.Rooms(currentRoute.propertyId) } - ) - is AppRoute.RatePlanCalendar -> RatePlanCalendarScreen( - propertyId = currentRoute.propertyId, - ratePlanId = currentRoute.ratePlanId, - ratePlanCode = currentRoute.ratePlanCode, - onBack = { route.value = AppRoute.EditRoomType(currentRoute.propertyId, currentRoute.roomTypeId) } - ) - is AppRoute.RoomImages -> RoomImagesScreen( - propertyId = currentRoute.propertyId, - roomId = currentRoute.roomId, - onBack = { route.value = AppRoute.EditRoom(currentRoute.propertyId, currentRoute.roomId) } - ) - } - } else { - AuthScreen(viewModel = authViewModel) + state.apiVerified && state.needsName -> NameScreen(viewModel = authViewModel) + state.apiVerified -> MainRouteContent( + state = state, + authViewModel = authViewModel + ) + else -> AuthScreen(viewModel = authViewModel) } } } diff --git a/app/src/main/java/com/android/trisolarispms/MainBackNavigation.kt b/app/src/main/java/com/android/trisolarispms/MainBackNavigation.kt new file mode 100644 index 0000000..675e529 --- /dev/null +++ b/app/src/main/java/com/android/trisolarispms/MainBackNavigation.kt @@ -0,0 +1,82 @@ +package com.android.trisolarispms + +import com.android.trisolarispms.auth.AuthzPolicy +import com.android.trisolarispms.ui.AppRoute + +internal fun handleBackNavigation( + refs: MainUiRefs, + authz: AuthzPolicy, + propertyCount: Int +) { + when (val currentRoute = refs.currentRoute) { + AppRoute.Home -> Unit + AppRoute.AddProperty -> refs.route.value = AppRoute.Home + AppRoute.SuperAdminUsers -> refs.route.value = AppRoute.Home + is AppRoute.ActiveRoomStays -> { + val blockBack = authz.shouldBlockBackToHome( + propertyId = currentRoute.propertyId, + propertyCount = propertyCount + ) + if (!blockBack) { + refs.route.value = AppRoute.Home + } + } + is AppRoute.PropertyUsers -> refs.openActiveRoomStays(currentRoute.propertyId) + is AppRoute.PropertyAccessCode -> refs.route.value = AppRoute.PropertyUsers(currentRoute.propertyId) + is AppRoute.Rooms -> refs.openActiveRoomStays(currentRoute.propertyId) + is AppRoute.AddRoom -> refs.route.value = AppRoute.Rooms(currentRoute.propertyId) + is AppRoute.EditRoom -> refs.route.value = AppRoute.Rooms(currentRoute.propertyId) + is AppRoute.RoomTypes -> refs.route.value = AppRoute.Rooms(currentRoute.propertyId) + is AppRoute.AddRoomType -> refs.route.value = AppRoute.RoomTypes(currentRoute.propertyId) + is AppRoute.EditRoomType -> refs.route.value = AppRoute.RoomTypes(currentRoute.propertyId) + AppRoute.Amenities -> refs.route.value = refs.amenitiesReturnRoute.value + AppRoute.AddAmenity -> refs.route.value = AppRoute.Amenities + is AppRoute.EditAmenity -> refs.route.value = AppRoute.Amenities + AppRoute.ImageTags -> refs.route.value = AppRoute.Home + AppRoute.AddImageTag -> refs.route.value = AppRoute.ImageTags + is AppRoute.EditImageTag -> refs.route.value = AppRoute.ImageTags + is AppRoute.RoomImages -> refs.route.value = AppRoute.EditRoom(currentRoute.propertyId, currentRoute.roomId) + is AppRoute.IssueTemporaryCard -> refs.route.value = AppRoute.Rooms(currentRoute.propertyId) + is AppRoute.CardInfo -> refs.route.value = AppRoute.Rooms(currentRoute.propertyId) + is AppRoute.RatePlanCalendar -> refs.route.value = AppRoute.EditRoomType( + currentRoute.propertyId, + currentRoute.roomTypeId + ) + is AppRoute.RazorpaySettings -> refs.openActiveRoomStays(currentRoute.propertyId) + is AppRoute.RazorpayQr -> refs.route.value = AppRoute.BookingDetailsTabs( + currentRoute.propertyId, + currentRoute.bookingId, + null + ) + is AppRoute.CreateBooking -> refs.openActiveRoomStays(currentRoute.propertyId) + is AppRoute.GuestInfo -> refs.route.value = AppRoute.Home + is AppRoute.GuestSignature -> refs.route.value = AppRoute.GuestInfo( + currentRoute.propertyId, + currentRoute.bookingId, + currentRoute.guestId + ) + is AppRoute.ManageRoomStaySelect -> refs.openActiveRoomStays(currentRoute.propertyId) + is AppRoute.ManageRoomStayRates -> refs.route.value = AppRoute.ManageRoomStaySelect( + currentRoute.propertyId, + currentRoute.bookingId, + currentRoute.fromAt, + currentRoute.toAt + ) + is AppRoute.ManageRoomStaySelectFromBooking -> refs.openActiveRoomStays(currentRoute.propertyId) + is AppRoute.ManageRoomStayRatesFromBooking -> refs.route.value = AppRoute.ManageRoomStaySelectFromBooking( + currentRoute.propertyId, + currentRoute.bookingId, + currentRoute.guestId, + currentRoute.fromAt, + currentRoute.toAt + ) + is AppRoute.BookingRoomStays -> refs.openActiveRoomStays(currentRoute.propertyId) + is AppRoute.BookingExpectedDates -> refs.openActiveRoomStays(currentRoute.propertyId) + is AppRoute.BookingDetailsTabs -> refs.openActiveRoomStays(currentRoute.propertyId) + is AppRoute.BookingPayments -> refs.route.value = AppRoute.BookingDetailsTabs( + currentRoute.propertyId, + currentRoute.bookingId, + null + ) + } +} diff --git a/app/src/main/java/com/android/trisolarispms/MainRouteContent.kt b/app/src/main/java/com/android/trisolarispms/MainRouteContent.kt new file mode 100644 index 0000000..c4f56db --- /dev/null +++ b/app/src/main/java/com/android/trisolarispms/MainRouteContent.kt @@ -0,0 +1,98 @@ +package com.android.trisolarispms + +import androidx.activity.compose.BackHandler +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import com.android.trisolarispms.auth.AuthzPolicy +import com.android.trisolarispms.data.api.model.AmenityDto +import com.android.trisolarispms.data.api.model.GuestDto +import com.android.trisolarispms.data.api.model.RoomDto +import com.android.trisolarispms.data.api.model.RoomImageTagDto +import com.android.trisolarispms.data.api.model.RoomTypeDto +import com.android.trisolarispms.ui.AppRoute +import com.android.trisolarispms.ui.auth.AuthUiState +import com.android.trisolarispms.ui.auth.AuthViewModel +import com.android.trisolarispms.ui.roomstay.ManageRoomStaySelection + +@Composable +internal fun MainRouteContent( + state: AuthUiState, + authViewModel: AuthViewModel +) { + val refs = remember { + MainUiRefs( + route = mutableStateOf(AppRoute.Home), + refreshKey = mutableStateOf(0), + selectedPropertyId = mutableStateOf(null), + selectedPropertyName = mutableStateOf(null), + selectedRoom = mutableStateOf(null), + selectedRoomType = mutableStateOf(null), + selectedAmenity = mutableStateOf(null), + selectedGuest = mutableStateOf(null), + selectedGuestPhone = mutableStateOf(null), + selectedImageTag = mutableStateOf(null), + selectedManageRooms = mutableStateOf>(emptyList()), + roomFormKey = mutableStateOf(0), + amenitiesReturnRoute = mutableStateOf(AppRoute.Home) + ) + } + + val authz = remember(state.isSuperAdmin, state.propertyRoles) { + AuthzPolicy( + isSuperAdmin = state.isSuperAdmin, + propertyRoles = state.propertyRoles + ) + } + + val singlePropertyId = state.propertyRoles.keys.firstOrNull() + val singlePropertyIsAdmin = singlePropertyId?.let(authz::isPropertyAdmin) ?: false + + BackHandler(enabled = refs.currentRoute != AppRoute.Home) { + handleBackNavigation( + refs = refs, + authz = authz, + propertyCount = state.propertyRoles.size + ) + } + + if ( + refs.currentRoute == AppRoute.Home && + !state.isSuperAdmin && + !singlePropertyIsAdmin && + state.propertyRoles.size == 1 && + singlePropertyId != null + ) { + LaunchedEffect(singlePropertyId) { + refs.selectedPropertyId.value = singlePropertyId + refs.openActiveRoomStays(singlePropertyId) + } + } + + when { + renderHomeGuestRoutes( + refs = refs, + state = state, + authViewModel = authViewModel + ) -> Unit + + renderStayFlowRoutes( + refs = refs, + state = state, + authViewModel = authViewModel, + authz = authz + ) -> Unit + + renderBookingRoutes( + refs = refs, + authz = authz + ) -> Unit + + renderManagementRoutes( + refs = refs, + stateIsSuperAdmin = state.isSuperAdmin, + authz = authz + ) -> Unit + } +} diff --git a/app/src/main/java/com/android/trisolarispms/MainRoutesBooking.kt b/app/src/main/java/com/android/trisolarispms/MainRoutesBooking.kt new file mode 100644 index 0000000..02134fc --- /dev/null +++ b/app/src/main/java/com/android/trisolarispms/MainRoutesBooking.kt @@ -0,0 +1,89 @@ +package com.android.trisolarispms + +import androidx.compose.runtime.Composable +import com.android.trisolarispms.auth.AuthzPolicy +import com.android.trisolarispms.ui.AppRoute +import com.android.trisolarispms.ui.booking.BookingExpectedDatesScreen +import com.android.trisolarispms.ui.payment.BookingPaymentsScreen +import com.android.trisolarispms.ui.roomstay.BookingDetailsTabsScreen +import com.android.trisolarispms.ui.roomstay.BookingRoomStaysScreen + +@Composable +internal fun renderBookingRoutes( + refs: MainUiRefs, + authz: AuthzPolicy +): Boolean { + when (val currentRoute = refs.currentRoute) { + is AppRoute.BookingRoomStays -> BookingRoomStaysScreen( + propertyId = currentRoute.propertyId, + bookingId = currentRoute.bookingId, + onBack = { refs.openActiveRoomStays(currentRoute.propertyId) } + ) + + is AppRoute.BookingExpectedDates -> BookingExpectedDatesScreen( + propertyId = currentRoute.propertyId, + bookingId = currentRoute.bookingId, + status = currentRoute.status, + expectedCheckInAt = currentRoute.expectedCheckInAt, + expectedCheckOutAt = currentRoute.expectedCheckOutAt, + onBack = { refs.openActiveRoomStays(currentRoute.propertyId) }, + onDone = { refs.openActiveRoomStays(currentRoute.propertyId) } + ) + + is AppRoute.BookingDetailsTabs -> BookingDetailsTabsScreen( + propertyId = currentRoute.propertyId, + bookingId = currentRoute.bookingId, + guestId = currentRoute.guestId, + onBack = { refs.openActiveRoomStays(currentRoute.propertyId) }, + onEditCheckout = { expectedCheckInAt, expectedCheckOutAt -> + refs.route.value = AppRoute.BookingExpectedDates( + propertyId = currentRoute.propertyId, + bookingId = currentRoute.bookingId, + status = "CHECKED_IN", + expectedCheckInAt = expectedCheckInAt, + expectedCheckOutAt = expectedCheckOutAt + ) + }, + onEditSignature = { guestId -> + refs.route.value = AppRoute.GuestSignature( + currentRoute.propertyId, + currentRoute.bookingId, + guestId + ) + }, + onOpenRazorpayQr = { pendingAmount, guestPhone -> + refs.route.value = AppRoute.RazorpayQr( + propertyId = currentRoute.propertyId, + bookingId = currentRoute.bookingId, + pendingAmount = pendingAmount, + guestPhone = guestPhone + ) + }, + onOpenPayments = { + refs.route.value = AppRoute.BookingPayments( + propertyId = currentRoute.propertyId, + bookingId = currentRoute.bookingId + ) + }, + canManageDocuments = authz.canManageRazorpaySettings(currentRoute.propertyId) + ) + + is AppRoute.BookingPayments -> BookingPaymentsScreen( + propertyId = currentRoute.propertyId, + bookingId = currentRoute.bookingId, + canAddCash = authz.canAddBookingPayment(currentRoute.propertyId), + canDeleteCash = authz.canDeleteCashPayment(currentRoute.propertyId), + canRefund = authz.canRefundBookingPayment(currentRoute.propertyId), + onBack = { + refs.route.value = AppRoute.BookingDetailsTabs( + currentRoute.propertyId, + currentRoute.bookingId, + null + ) + } + ) + + else -> return false + } + return true +} diff --git a/app/src/main/java/com/android/trisolarispms/MainRoutesHomeGuest.kt b/app/src/main/java/com/android/trisolarispms/MainRoutesHomeGuest.kt new file mode 100644 index 0000000..57cc628 --- /dev/null +++ b/app/src/main/java/com/android/trisolarispms/MainRoutesHomeGuest.kt @@ -0,0 +1,117 @@ +package com.android.trisolarispms + +import androidx.compose.runtime.Composable +import com.android.trisolarispms.auth.Role +import com.android.trisolarispms.ui.AppRoute +import com.android.trisolarispms.ui.auth.AuthUiState +import com.android.trisolarispms.ui.auth.AuthViewModel +import com.android.trisolarispms.ui.booking.BookingCreateScreen +import com.android.trisolarispms.ui.guest.GuestInfoScreen +import com.android.trisolarispms.ui.guest.GuestSignatureScreen +import com.android.trisolarispms.ui.home.HomeScreen +import com.android.trisolarispms.ui.property.AddPropertyScreen + +@Composable +internal fun renderHomeGuestRoutes( + refs: MainUiRefs, + state: AuthUiState, + authViewModel: AuthViewModel +): Boolean { + when (val currentRoute = refs.currentRoute) { + AppRoute.Home -> HomeScreen( + userId = state.userId, + userName = state.userName, + isSuperAdmin = state.isSuperAdmin, + onUserDirectory = { refs.route.value = AppRoute.SuperAdminUsers }, + onLogout = authViewModel::signOut, + onAddProperty = { refs.route.value = AppRoute.AddProperty }, + onAmenities = { + refs.amenitiesReturnRoute.value = AppRoute.Home + refs.route.value = AppRoute.Amenities + }, + onImageTags = { refs.route.value = AppRoute.ImageTags }, + refreshKey = refs.refreshKey.value, + selectedPropertyId = refs.selectedPropertyId.value, + onSelectProperty = { id, name -> + refs.selectedPropertyId.value = id + refs.selectedPropertyName.value = name + refs.route.value = AppRoute.ActiveRoomStays(id, name) + }, + onRefreshProfile = authViewModel::refreshMe, + showJoinProperty = state.propertyRoles.isEmpty() && !state.isSuperAdmin, + onJoinPropertySuccess = { joinedPropertyId, joinedRoles -> + refs.refreshKey.value++ + authViewModel.refreshMe() + val isAdmin = joinedRoles.contains(Role.ADMIN) + val shouldAutoOpen = !state.isSuperAdmin && !isAdmin && state.propertyRoles.size <= 1 + if (shouldAutoOpen) { + refs.openActiveRoomStays(joinedPropertyId) + } + } + ) + + AppRoute.AddProperty -> AddPropertyScreen( + onBack = { refs.route.value = AppRoute.Home }, + onCreated = { + refs.refreshKey.value++ + refs.route.value = AppRoute.Home + } + ) + + is AppRoute.CreateBooking -> BookingCreateScreen( + propertyId = currentRoute.propertyId, + onBack = { refs.openActiveRoomStays(currentRoute.propertyId) }, + onCreated = { response, guest, phone -> + val bookingId = response.id.orEmpty() + val guestId = (guest?.id ?: response.guestId).orEmpty() + refs.selectedGuest.value = guest + refs.selectedGuestPhone.value = phone + if (bookingId.isNotBlank() && guestId.isNotBlank()) { + val fromAt = response.checkInAt?.takeIf { it.isNotBlank() } + ?: response.expectedCheckInAt.orEmpty() + val toAt = response.expectedCheckOutAt?.takeIf { it.isNotBlank() } + refs.route.value = AppRoute.ManageRoomStaySelectFromBooking( + propertyId = currentRoute.propertyId, + bookingId = bookingId, + guestId = guestId, + fromAt = fromAt, + toAt = toAt + ) + } else { + refs.route.value = AppRoute.Home + } + } + ) + + is AppRoute.GuestInfo -> GuestInfoScreen( + propertyId = currentRoute.propertyId, + guestId = currentRoute.guestId, + initialGuest = refs.selectedGuest.value, + initialPhone = refs.selectedGuestPhone.value, + onBack = { refs.route.value = AppRoute.Home }, + onSave = { + refs.route.value = AppRoute.GuestSignature( + currentRoute.propertyId, + currentRoute.bookingId, + currentRoute.guestId + ) + } + ) + + is AppRoute.GuestSignature -> GuestSignatureScreen( + propertyId = currentRoute.propertyId, + guestId = currentRoute.guestId, + onBack = { + refs.route.value = AppRoute.GuestInfo( + currentRoute.propertyId, + currentRoute.bookingId, + currentRoute.guestId + ) + }, + onDone = { refs.openActiveRoomStays(currentRoute.propertyId) } + ) + + else -> return false + } + return true +} diff --git a/app/src/main/java/com/android/trisolarispms/MainRoutesManagement.kt b/app/src/main/java/com/android/trisolarispms/MainRoutesManagement.kt new file mode 100644 index 0000000..be197df --- /dev/null +++ b/app/src/main/java/com/android/trisolarispms/MainRoutesManagement.kt @@ -0,0 +1,210 @@ +package com.android.trisolarispms + +import androidx.compose.runtime.Composable +import com.android.trisolarispms.auth.AuthzPolicy +import com.android.trisolarispms.auth.toRoleNameList +import com.android.trisolarispms.data.api.model.AmenityDto +import com.android.trisolarispms.data.api.model.RoomImageTagDto +import com.android.trisolarispms.data.api.model.RoomTypeDto +import com.android.trisolarispms.ui.AppRoute +import com.android.trisolarispms.ui.card.CardInfoScreen +import com.android.trisolarispms.ui.card.IssueTemporaryCardScreen +import com.android.trisolarispms.ui.room.RoomFormScreen +import com.android.trisolarispms.ui.room.RoomsScreen +import com.android.trisolarispms.ui.roomimage.AddImageTagScreen +import com.android.trisolarispms.ui.roomimage.EditImageTagScreen +import com.android.trisolarispms.ui.roomimage.ImageTagsScreen +import com.android.trisolarispms.ui.roomimage.RoomImagesScreen +import com.android.trisolarispms.ui.roomtype.AddAmenityScreen +import com.android.trisolarispms.ui.roomtype.AddRoomTypeScreen +import com.android.trisolarispms.ui.roomtype.AmenitiesScreen +import com.android.trisolarispms.ui.roomtype.EditAmenityScreen +import com.android.trisolarispms.ui.roomtype.EditRoomTypeScreen +import com.android.trisolarispms.ui.roomtype.RatePlanCalendarScreen +import com.android.trisolarispms.ui.roomtype.RoomTypesScreen +import com.android.trisolarispms.ui.users.PropertyAccessCodeScreen +import com.android.trisolarispms.ui.users.PropertyUsersScreen +import com.android.trisolarispms.ui.users.SuperAdminUserDirectoryScreen + +@Composable +internal fun renderManagementRoutes( + refs: MainUiRefs, + stateIsSuperAdmin: Boolean, + authz: AuthzPolicy +): Boolean { + when (val currentRoute = refs.currentRoute) { + AppRoute.SuperAdminUsers -> SuperAdminUserDirectoryScreen( + onBack = { refs.route.value = AppRoute.Home } + ) + + is AppRoute.PropertyUsers -> PropertyUsersScreen( + propertyId = currentRoute.propertyId, + allowedRoleAssignments = authz + .allowedRoleAssignments(currentRoute.propertyId) + .toRoleNameList(), + canDisableAdmin = authz.canDisableAdmin(currentRoute.propertyId), + canDisableManager = authz.canDisableManager(currentRoute.propertyId), + onBack = { refs.openActiveRoomStays(currentRoute.propertyId) }, + onOpenAccessCode = { + refs.route.value = AppRoute.PropertyAccessCode(currentRoute.propertyId) + } + ) + + is AppRoute.PropertyAccessCode -> PropertyAccessCodeScreen( + propertyId = currentRoute.propertyId, + allowedRoles = authz.allowedAccessCodeRoles(currentRoute.propertyId).toRoleNameList(), + onBack = { refs.route.value = AppRoute.PropertyUsers(currentRoute.propertyId) } + ) + + is AppRoute.Rooms -> RoomsScreen( + propertyId = currentRoute.propertyId, + onBack = { refs.openActiveRoomStays(currentRoute.propertyId) }, + onAddRoom = { + refs.roomFormKey.value++ + refs.route.value = AppRoute.AddRoom(currentRoute.propertyId) + }, + onViewRoomTypes = { refs.route.value = AppRoute.RoomTypes(currentRoute.propertyId) }, + onViewCardInfo = { refs.route.value = AppRoute.CardInfo(currentRoute.propertyId) }, + canManageRooms = authz.canManageProperty(currentRoute.propertyId), + canViewCardInfo = authz.canViewCardInfo(currentRoute.propertyId), + canIssueTemporaryCard = authz.canIssueTemporaryCard(currentRoute.propertyId), + onEditRoom = { + refs.selectedRoom.value = it + refs.roomFormKey.value++ + refs.route.value = AppRoute.EditRoom(currentRoute.propertyId, it.id ?: "") + }, + onIssueTemporaryCard = { + if (it.id != null && authz.canIssueTemporaryCard(currentRoute.propertyId)) { + refs.selectedRoom.value = it + refs.route.value = AppRoute.IssueTemporaryCard(currentRoute.propertyId, it.id) + } + } + ) + + is AppRoute.RoomTypes -> RoomTypesScreen( + propertyId = currentRoute.propertyId, + onBack = { refs.route.value = AppRoute.Rooms(currentRoute.propertyId) }, + onAdd = { refs.route.value = AppRoute.AddRoomType(currentRoute.propertyId) }, + canManageRoomTypes = authz.canManageProperty(currentRoute.propertyId), + onEdit = { + refs.selectedRoomType.value = it + refs.route.value = AppRoute.EditRoomType(currentRoute.propertyId, it.id ?: "") + } + ) + + is AppRoute.AddRoomType -> AddRoomTypeScreen( + propertyId = currentRoute.propertyId, + onBack = { refs.route.value = AppRoute.RoomTypes(currentRoute.propertyId) }, + onSave = { refs.route.value = AppRoute.RoomTypes(currentRoute.propertyId) } + ) + + is AppRoute.EditRoomType -> EditRoomTypeScreen( + propertyId = currentRoute.propertyId, + roomType = refs.selectedRoomType.value + ?: RoomTypeDto(id = currentRoute.roomTypeId, code = "", name = ""), + onBack = { refs.route.value = AppRoute.RoomTypes(currentRoute.propertyId) }, + onSave = { refs.route.value = AppRoute.RoomTypes(currentRoute.propertyId) }, + onOpenRatePlanCalendar = { ratePlanId, ratePlanCode -> + refs.route.value = AppRoute.RatePlanCalendar( + currentRoute.propertyId, + currentRoute.roomTypeId, + ratePlanId, + ratePlanCode + ) + } + ) + + AppRoute.Amenities -> AmenitiesScreen( + onBack = { refs.route.value = refs.amenitiesReturnRoute.value }, + onAdd = { refs.route.value = AppRoute.AddAmenity }, + canManageAmenities = stateIsSuperAdmin, + onEdit = { + refs.selectedAmenity.value = it + refs.route.value = AppRoute.EditAmenity(it.id ?: "") + } + ) + + AppRoute.AddAmenity -> AddAmenityScreen( + onBack = { refs.route.value = AppRoute.Amenities }, + onSave = { refs.route.value = AppRoute.Amenities } + ) + + is AppRoute.EditAmenity -> EditAmenityScreen( + amenity = refs.selectedAmenity.value ?: AmenityDto(id = currentRoute.amenityId, name = ""), + onBack = { refs.route.value = AppRoute.Amenities }, + onSave = { refs.route.value = AppRoute.Amenities } + ) + + AppRoute.ImageTags -> ImageTagsScreen( + onBack = { refs.route.value = AppRoute.Home }, + onAdd = { refs.route.value = AppRoute.AddImageTag }, + onEdit = { + refs.selectedImageTag.value = it + refs.route.value = AppRoute.EditImageTag(it.id ?: "") + } + ) + + AppRoute.AddImageTag -> AddImageTagScreen( + onBack = { refs.route.value = AppRoute.ImageTags }, + onSave = { refs.route.value = AppRoute.ImageTags } + ) + + is AppRoute.EditImageTag -> EditImageTagScreen( + tag = refs.selectedImageTag.value ?: RoomImageTagDto(id = currentRoute.tagId, name = ""), + onBack = { refs.route.value = AppRoute.ImageTags }, + onSave = { refs.route.value = AppRoute.ImageTags } + ) + + is AppRoute.AddRoom -> RoomFormScreen( + title = "Add Room", + propertyId = currentRoute.propertyId, + roomId = null, + roomData = null, + formKey = refs.roomFormKey.value, + onBack = { refs.route.value = AppRoute.Rooms(currentRoute.propertyId) }, + onSave = { refs.route.value = AppRoute.Rooms(currentRoute.propertyId) }, + onViewImages = { } + ) + + is AppRoute.EditRoom -> RoomFormScreen( + title = "Modify Room", + propertyId = currentRoute.propertyId, + roomId = currentRoute.roomId, + roomData = refs.selectedRoom.value, + formKey = refs.roomFormKey.value, + onBack = { refs.route.value = AppRoute.Rooms(currentRoute.propertyId) }, + onSave = { refs.route.value = AppRoute.Rooms(currentRoute.propertyId) }, + onViewImages = { roomId -> + refs.route.value = AppRoute.RoomImages(currentRoute.propertyId, roomId) + } + ) + + is AppRoute.IssueTemporaryCard -> IssueTemporaryCardScreen( + propertyId = currentRoute.propertyId, + roomId = currentRoute.roomId, + roomNumber = refs.selectedRoom.value?.roomNumber?.toString(), + onBack = { refs.route.value = AppRoute.Rooms(currentRoute.propertyId) } + ) + + is AppRoute.CardInfo -> CardInfoScreen( + propertyId = currentRoute.propertyId, + onBack = { refs.route.value = AppRoute.Rooms(currentRoute.propertyId) } + ) + + is AppRoute.RatePlanCalendar -> RatePlanCalendarScreen( + propertyId = currentRoute.propertyId, + ratePlanId = currentRoute.ratePlanId, + ratePlanCode = currentRoute.ratePlanCode, + onBack = { refs.route.value = AppRoute.EditRoomType(currentRoute.propertyId, currentRoute.roomTypeId) } + ) + + is AppRoute.RoomImages -> RoomImagesScreen( + propertyId = currentRoute.propertyId, + roomId = currentRoute.roomId, + onBack = { refs.route.value = AppRoute.EditRoom(currentRoute.propertyId, currentRoute.roomId) } + ) + + else -> return false + } + return true +} diff --git a/app/src/main/java/com/android/trisolarispms/MainRoutesStayFlow.kt b/app/src/main/java/com/android/trisolarispms/MainRoutesStayFlow.kt new file mode 100644 index 0000000..7901383 --- /dev/null +++ b/app/src/main/java/com/android/trisolarispms/MainRoutesStayFlow.kt @@ -0,0 +1,171 @@ +package com.android.trisolarispms + +import androidx.compose.runtime.Composable +import com.android.trisolarispms.auth.AuthzPolicy +import com.android.trisolarispms.ui.AppRoute +import com.android.trisolarispms.ui.auth.AuthUiState +import com.android.trisolarispms.ui.auth.AuthViewModel +import com.android.trisolarispms.ui.razorpay.RazorpayQrScreen +import com.android.trisolarispms.ui.razorpay.RazorpaySettingsScreen +import com.android.trisolarispms.ui.roomstay.ActiveRoomStaysScreen +import com.android.trisolarispms.ui.roomstay.ManageRoomStayRatesScreen +import com.android.trisolarispms.ui.roomstay.ManageRoomStaySelectScreen + +@Composable +internal fun renderStayFlowRoutes( + refs: MainUiRefs, + state: AuthUiState, + authViewModel: AuthViewModel, + authz: AuthzPolicy +): Boolean { + when (val currentRoute = refs.currentRoute) { + is AppRoute.ActiveRoomStays -> ActiveRoomStaysScreen( + propertyId = currentRoute.propertyId, + propertyName = currentRoute.propertyName, + onBack = { + val blockBack = authz.shouldBlockBackToHome( + propertyId = currentRoute.propertyId, + propertyCount = state.propertyRoles.size + ) + if (!blockBack) { + refs.route.value = AppRoute.Home + } + }, + showBack = !authz.shouldBlockBackToHome( + propertyId = currentRoute.propertyId, + propertyCount = state.propertyRoles.size + ), + onViewRooms = { refs.route.value = AppRoute.Rooms(currentRoute.propertyId) }, + onCreateBooking = { refs.route.value = AppRoute.CreateBooking(currentRoute.propertyId) }, + canCreateBooking = authz.canCreateBookingFor(currentRoute.propertyId), + showRazorpaySettings = authz.canManageRazorpaySettings(currentRoute.propertyId), + onRazorpaySettings = { refs.route.value = AppRoute.RazorpaySettings(currentRoute.propertyId) }, + showUserAdmin = authz.canManagePropertyUsers(currentRoute.propertyId), + onUserAdmin = { refs.route.value = AppRoute.PropertyUsers(currentRoute.propertyId) }, + onLogout = authViewModel::signOut, + onManageRoomStay = { booking -> + val fromAt = booking.checkInAt?.takeIf { it.isNotBlank() } + ?: booking.expectedCheckInAt.orEmpty() + val toAt = booking.expectedCheckOutAt?.takeIf { it.isNotBlank() } + ?: booking.checkOutAt?.takeIf { it.isNotBlank() } + if (fromAt.isNotBlank()) { + refs.route.value = AppRoute.ManageRoomStaySelect( + propertyId = currentRoute.propertyId, + bookingId = booking.id.orEmpty(), + fromAt = fromAt, + toAt = toAt + ) + } + }, + onViewBookingStays = { booking -> + refs.route.value = AppRoute.BookingRoomStays( + propertyId = currentRoute.propertyId, + bookingId = booking.id.orEmpty() + ) + }, + onOpenBookingDetails = { booking -> + refs.route.value = AppRoute.BookingDetailsTabs( + propertyId = currentRoute.propertyId, + bookingId = booking.id.orEmpty(), + guestId = booking.guestId + ) + } + ) + + is AppRoute.RazorpaySettings -> RazorpaySettingsScreen( + propertyId = currentRoute.propertyId, + onBack = { refs.openActiveRoomStays(currentRoute.propertyId) } + ) + + is AppRoute.RazorpayQr -> RazorpayQrScreen( + propertyId = currentRoute.propertyId, + bookingId = currentRoute.bookingId, + pendingAmount = currentRoute.pendingAmount, + guestPhone = currentRoute.guestPhone, + onBack = { + refs.route.value = AppRoute.BookingDetailsTabs( + currentRoute.propertyId, + currentRoute.bookingId, + null + ) + } + ) + + is AppRoute.ManageRoomStaySelect -> ManageRoomStaySelectScreen( + propertyId = currentRoute.propertyId, + bookingFromAt = currentRoute.fromAt, + bookingToAt = currentRoute.toAt, + onBack = { refs.openActiveRoomStays(currentRoute.propertyId) }, + onNext = { rooms -> + refs.selectedManageRooms.value = rooms + refs.route.value = AppRoute.ManageRoomStayRates( + propertyId = currentRoute.propertyId, + bookingId = currentRoute.bookingId, + fromAt = currentRoute.fromAt, + toAt = currentRoute.toAt + ) + } + ) + + is AppRoute.ManageRoomStaySelectFromBooking -> ManageRoomStaySelectScreen( + propertyId = currentRoute.propertyId, + bookingFromAt = currentRoute.fromAt, + bookingToAt = currentRoute.toAt, + onBack = { refs.openActiveRoomStays(currentRoute.propertyId) }, + onNext = { rooms -> + refs.selectedManageRooms.value = rooms + refs.route.value = AppRoute.ManageRoomStayRatesFromBooking( + propertyId = currentRoute.propertyId, + bookingId = currentRoute.bookingId, + guestId = currentRoute.guestId, + fromAt = currentRoute.fromAt, + toAt = currentRoute.toAt + ) + } + ) + + is AppRoute.ManageRoomStayRates -> ManageRoomStayRatesScreen( + propertyId = currentRoute.propertyId, + bookingId = currentRoute.bookingId, + checkInAt = currentRoute.fromAt, + checkOutAt = currentRoute.toAt, + selectedRooms = refs.selectedManageRooms.value, + onBack = { + refs.route.value = AppRoute.ManageRoomStaySelect( + currentRoute.propertyId, + currentRoute.bookingId, + currentRoute.fromAt, + currentRoute.toAt + ) + }, + onDone = { refs.openActiveRoomStays(currentRoute.propertyId) } + ) + + is AppRoute.ManageRoomStayRatesFromBooking -> ManageRoomStayRatesScreen( + propertyId = currentRoute.propertyId, + bookingId = currentRoute.bookingId, + checkInAt = currentRoute.fromAt, + checkOutAt = currentRoute.toAt, + selectedRooms = refs.selectedManageRooms.value, + onBack = { + refs.route.value = AppRoute.ManageRoomStaySelectFromBooking( + currentRoute.propertyId, + currentRoute.bookingId, + currentRoute.guestId, + currentRoute.fromAt, + currentRoute.toAt + ) + }, + onDone = { + refs.route.value = AppRoute.GuestInfo( + currentRoute.propertyId, + currentRoute.bookingId, + currentRoute.guestId + ) + } + ) + + else -> return false + } + return true +} diff --git a/app/src/main/java/com/android/trisolarispms/MainUiRefs.kt b/app/src/main/java/com/android/trisolarispms/MainUiRefs.kt new file mode 100644 index 0000000..150057b --- /dev/null +++ b/app/src/main/java/com/android/trisolarispms/MainUiRefs.kt @@ -0,0 +1,33 @@ +package com.android.trisolarispms + +import androidx.compose.runtime.MutableState +import com.android.trisolarispms.data.api.model.AmenityDto +import com.android.trisolarispms.data.api.model.GuestDto +import com.android.trisolarispms.data.api.model.RoomDto +import com.android.trisolarispms.data.api.model.RoomImageTagDto +import com.android.trisolarispms.data.api.model.RoomTypeDto +import com.android.trisolarispms.ui.AppRoute +import com.android.trisolarispms.ui.roomstay.ManageRoomStaySelection + +internal data class MainUiRefs( + val route: MutableState, + val refreshKey: MutableState, + val selectedPropertyId: MutableState, + val selectedPropertyName: MutableState, + val selectedRoom: MutableState, + val selectedRoomType: MutableState, + val selectedAmenity: MutableState, + val selectedGuest: MutableState, + val selectedGuestPhone: MutableState, + val selectedImageTag: MutableState, + val selectedManageRooms: MutableState>, + val roomFormKey: MutableState, + val amenitiesReturnRoute: MutableState +) { + val currentRoute: AppRoute + get() = route.value + + fun openActiveRoomStays(propertyId: String) { + route.value = AppRoute.ActiveRoomStays(propertyId, selectedPropertyName.value ?: "Property") + } +} diff --git a/app/src/main/java/com/android/trisolarispms/ui/home/HomeJoinPropertyViewModel.kt b/app/src/main/java/com/android/trisolarispms/ui/home/HomeJoinPropertyViewModel.kt index e7288de..dac5579 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/home/HomeJoinPropertyViewModel.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/home/HomeJoinPropertyViewModel.kt @@ -2,6 +2,8 @@ package com.android.trisolarispms.ui.home import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.android.trisolarispms.auth.Role +import com.android.trisolarispms.auth.toRoles import com.android.trisolarispms.data.api.ApiClient import com.android.trisolarispms.data.api.model.PropertyAccessCodeJoinRequest import kotlinx.coroutines.flow.MutableStateFlow @@ -14,7 +16,7 @@ data class HomeJoinPropertyState( val error: String? = null, val message: String? = null, val joinedPropertyId: String? = null, - val joinedRoles: List = emptyList() + val joinedRoles: List = emptyList() ) class HomeJoinPropertyViewModel : ViewModel() { @@ -49,7 +51,7 @@ class HomeJoinPropertyViewModel : ViewModel() { message = "Joined property", error = null, joinedPropertyId = body.propertyId ?: trimmedPropertyId, - joinedRoles = body.roles + joinedRoles = body.roles.toRoles() ) } } else { diff --git a/app/src/main/java/com/android/trisolarispms/ui/home/HomeScreen.kt b/app/src/main/java/com/android/trisolarispms/ui/home/HomeScreen.kt index 821c33d..d90920b 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/home/HomeScreen.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/home/HomeScreen.kt @@ -33,6 +33,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel +import com.android.trisolarispms.auth.Role import com.android.trisolarispms.ui.property.PropertyListViewModel import androidx.compose.foundation.text.KeyboardOptions @@ -52,7 +53,7 @@ fun HomeScreen( onSelectProperty: (String, String) -> Unit, onRefreshProfile: () -> Unit, showJoinProperty: Boolean, - onJoinPropertySuccess: (String, List) -> Unit, + onJoinPropertySuccess: (String, List) -> Unit, viewModel: PropertyListViewModel = viewModel(), joinViewModel: HomeJoinPropertyViewModel = viewModel() ) { diff --git a/app/src/main/java/com/android/trisolarispms/ui/users/UserDirectoryViewModel.kt b/app/src/main/java/com/android/trisolarispms/ui/users/UserDirectoryViewModel.kt index c8aacce..d0c5f02 100644 --- a/app/src/main/java/com/android/trisolarispms/ui/users/UserDirectoryViewModel.kt +++ b/app/src/main/java/com/android/trisolarispms/ui/users/UserDirectoryViewModel.kt @@ -32,18 +32,9 @@ class UserDirectoryViewModel : ViewModel() { when (mode) { UserDirectoryMode.SuperAdmin -> { val response = api.listUsers(null) - val body = response.body() - if (response.isSuccessful && body != null) { - val mapped = body.map { - PropertyUserUi( - userId = it.id, - name = it.name, - phoneE164 = it.phoneE164, - disabled = it.disabled, - superAdmin = it.superAdmin - ) - } - _state.update { it.copy(isLoading = false, users = mapped, error = null) } + val users = response.body()?.toSuperAdminUsers() + if (response.isSuccessful && users != null) { + _state.update { it.copy(isLoading = false, users = users, error = null) } } else { _state.update { it.copy(isLoading = false, error = "Load failed: ${response.code()}") } } @@ -53,19 +44,9 @@ class UserDirectoryViewModel : ViewModel() { propertyId = mode.propertyId, phone = null ) - val body = response.body() - if (response.isSuccessful && body != null) { - val mapped = body.map { - PropertyUserUi( - userId = it.userId, - roles = it.roles, - name = it.name, - phoneE164 = it.phoneE164, - disabled = it.disabled, - superAdmin = it.superAdmin - ) - } - _state.update { it.copy(isLoading = false, users = mapped, error = null) } + val users = response.body()?.toPropertyUsers() + if (response.isSuccessful && users != null) { + _state.update { it.copy(isLoading = false, users = users, error = null) } } else { _state.update { it.copy(isLoading = false, error = "Load failed: ${response.code()}") } } @@ -90,18 +71,9 @@ class UserDirectoryViewModel : ViewModel() { when (mode) { UserDirectoryMode.SuperAdmin -> { val response = api.listUsers(digits) - val body = response.body() - if (response.isSuccessful && body != null) { - val mapped = body.map { - PropertyUserUi( - userId = it.id, - name = it.name, - phoneE164 = it.phoneE164, - disabled = it.disabled, - superAdmin = it.superAdmin - ) - } - _state.update { it.copy(isLoading = false, users = mapped, error = null) } + val users = response.body()?.toSuperAdminUsers() + if (response.isSuccessful && users != null) { + _state.update { it.copy(isLoading = false, users = users, error = null) } } else { _state.update { it.copy(isLoading = false, error = "Search failed: ${response.code()}") } } @@ -111,19 +83,9 @@ class UserDirectoryViewModel : ViewModel() { propertyId = mode.propertyId, phone = digits ) - val body = response.body() - if (response.isSuccessful && body != null) { - val mapped = body.map { - PropertyUserUi( - userId = it.userId, - roles = it.roles, - name = it.name, - phoneE164 = it.phoneE164, - disabled = it.disabled, - superAdmin = it.superAdmin - ) - } - _state.update { it.copy(isLoading = false, users = mapped, error = null) } + val users = response.body()?.toPropertyUsers() + if (response.isSuccessful && users != null) { + _state.update { it.copy(isLoading = false, users = users, error = null) } } else { _state.update { it.copy(isLoading = false, error = "Search failed: ${response.code()}") } } @@ -134,4 +96,28 @@ class UserDirectoryViewModel : ViewModel() { } } } + + private fun List.toSuperAdminUsers(): + List = + map { + PropertyUserUi( + userId = it.id, + name = it.name, + phoneE164 = it.phoneE164, + disabled = it.disabled, + superAdmin = it.superAdmin + ) + } + + private fun List.toPropertyUsers(): + List = map { + PropertyUserUi( + userId = it.userId, + roles = it.roles, + name = it.name, + phoneE164 = it.phoneE164, + disabled = it.disabled, + superAdmin = it.superAdmin + ) + } }