AI:remove more boilerplate

This commit is contained in:
androidlover5842
2026-02-03 10:20:48 +05:30
parent 52a6d379b0
commit dcaaba92dd
13 changed files with 295 additions and 569 deletions

View File

@@ -14,10 +14,8 @@ import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
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.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.CalendarMonth import androidx.compose.material.icons.filled.CalendarMonth
import androidx.compose.material.icons.filled.Schedule import androidx.compose.material.icons.filled.Schedule
import androidx.compose.material.icons.filled.Done
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuBox
@@ -27,11 +25,8 @@ import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -46,6 +41,7 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.trisolarispms.data.api.model.BookingBillingMode import com.android.trisolarispms.data.api.model.BookingBillingMode
import com.android.trisolarispms.ui.common.SaveTopBarScaffold
import java.time.LocalDate import java.time.LocalDate
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@@ -94,23 +90,10 @@ fun BookingCreateScreen(
viewModel.onExpectedCheckOutAtChange(formatBookingIso(defaultCheckoutDate, checkOutTime.value)) viewModel.onExpectedCheckOutAtChange(formatBookingIso(defaultCheckoutDate, checkOutTime.value))
} }
Scaffold( SaveTopBarScaffold(
topBar = { title = "Create Booking",
TopAppBar( onBack = onBack,
title = { Text("Create Booking") }, onSave = { viewModel.submit(propertyId, onCreated) }
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.AutoMirrored.Default.ArrowBack, contentDescription = "Back")
}
},
actions = {
IconButton(onClick = { viewModel.submit(propertyId, onCreated) }) {
Icon(Icons.Default.Done, contentDescription = "Save")
}
},
colors = TopAppBarDefaults.topAppBarColors()
)
}
) { padding -> ) { padding ->
Column( Column(
modifier = Modifier modifier = Modifier

View File

@@ -3,24 +3,16 @@ package com.android.trisolarispms.ui.booking
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.CalendarMonth import androidx.compose.material.icons.filled.CalendarMonth
import androidx.compose.material.icons.filled.Done
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
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
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -30,6 +22,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.android.trisolarispms.data.api.core.ApiClient import com.android.trisolarispms.data.api.core.ApiClient
import com.android.trisolarispms.data.api.model.BookingExpectedDatesRequest import com.android.trisolarispms.data.api.model.BookingExpectedDatesRequest
import com.android.trisolarispms.ui.common.PaddedScreenColumn
import com.android.trisolarispms.ui.common.SaveTopBarScaffold
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.time.LocalDate import java.time.LocalDate
import java.time.OffsetDateTime import java.time.OffsetDateTime
@@ -37,7 +31,6 @@ import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class)
fun BookingExpectedDatesScreen( fun BookingExpectedDatesScreen(
propertyId: String, propertyId: String,
bookingId: String, bookingId: String,
@@ -84,65 +77,44 @@ fun BookingExpectedDatesScreen(
} }
} }
Scaffold( SaveTopBarScaffold(
topBar = { title = "Update Expected Dates",
TopAppBar( onBack = onBack,
title = { Text("Update Expected Dates") }, saveEnabled = !isLoading.value,
navigationIcon = { onSave = {
IconButton(onClick = onBack) { isLoading.value = true
Icon(Icons.AutoMirrored.Default.ArrowBack, contentDescription = "Back") error.value = null
val inAt = if (editableCheckIn) {
checkInDate.value?.let { formatBookingIso(it, checkInTime.value) }
} else {
null
}
val outAt = checkOutDate.value?.let { formatBookingIso(it, checkOutTime.value) }
scope.launch {
try {
val api = ApiClient.create()
val response = api.updateExpectedDates(
propertyId = propertyId,
bookingId = bookingId,
body = BookingExpectedDatesRequest(
expectedCheckInAt = inAt,
expectedCheckOutAt = outAt
)
)
if (response.isSuccessful) {
onDone()
} else {
error.value = "Update failed: ${response.code()}"
} }
}, } catch (e: Exception) {
actions = { error.value = e.localizedMessage ?: "Update failed"
IconButton( } finally {
onClick = { isLoading.value = false
isLoading.value = true }
error.value = null }
val inAt = if (editableCheckIn) {
checkInDate.value?.let { formatBookingIso(it, checkInTime.value) }
} else {
null
}
val outAt = checkOutDate.value?.let { formatBookingIso(it, checkOutTime.value) }
scope.launch {
try {
val api = ApiClient.create()
val response = api.updateExpectedDates(
propertyId = propertyId,
bookingId = bookingId,
body = BookingExpectedDatesRequest(
expectedCheckInAt = inAt,
expectedCheckOutAt = outAt
)
)
if (response.isSuccessful) {
onDone()
} else {
error.value = "Update failed: ${response.code()}"
}
} catch (e: Exception) {
error.value = e.localizedMessage ?: "Update failed"
} finally {
isLoading.value = false
}
}
},
enabled = !isLoading.value
) {
Icon(Icons.Default.Done, contentDescription = "Save")
}
},
colors = TopAppBarDefaults.topAppBarColors()
)
} }
) { padding -> ) { padding ->
Column( PaddedScreenColumn(padding = padding) {
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(24.dp),
verticalArrangement = Arrangement.Top
) {
if (editableCheckIn) { if (editableCheckIn) {
OutlinedTextField( OutlinedTextField(
value = checkInDate.value?.let { value = checkInDate.value?.let {

View File

@@ -3,23 +3,12 @@ package com.android.trisolarispms.ui.guest
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Done
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -27,9 +16,10 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.trisolarispms.ui.common.PaddedScreenColumn
import com.android.trisolarispms.ui.common.SaveTopBarScaffold
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class)
fun GuestInfoScreen( fun GuestInfoScreen(
propertyId: String, propertyId: String,
guestId: String, guestId: String,
@@ -47,31 +37,12 @@ fun GuestInfoScreen(
viewModel.loadGuest(propertyId, guestId, initialPhone) viewModel.loadGuest(propertyId, guestId, initialPhone)
} }
Scaffold( SaveTopBarScaffold(
topBar = { title = "Guest Info",
TopAppBar( onBack = onBack,
title = { Text("Guest Info") }, onSave = { viewModel.submit(propertyId, guestId, onSave) }
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.AutoMirrored.Default.ArrowBack, contentDescription = "Back")
}
},
actions = {
IconButton(onClick = { viewModel.submit(propertyId, guestId, onSave) }) {
Icon(Icons.Default.Done, contentDescription = "Save")
}
},
colors = TopAppBarDefaults.topAppBarColors()
)
}
) { padding -> ) { padding ->
Column( PaddedScreenColumn(padding = padding) {
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(24.dp),
verticalArrangement = Arrangement.Top
) {
OutlinedTextField( OutlinedTextField(
value = state.phoneE164, value = state.phoneE164,
onValueChange = viewModel::onPhoneChange, onValueChange = viewModel::onPhoneChange,

View File

@@ -8,24 +8,17 @@ import androidx.compose.foundation.gestures.drag
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Done import androidx.compose.material.icons.filled.Done
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
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
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -48,9 +41,10 @@ import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import java.util.Locale import java.util.Locale
import com.android.trisolarispms.ui.common.BackTopBarScaffold
import com.android.trisolarispms.ui.common.PaddedScreenColumn
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class)
fun GuestSignatureScreen( fun GuestSignatureScreen(
propertyId: String, propertyId: String,
guestId: String, guestId: String,
@@ -66,43 +60,28 @@ fun GuestSignatureScreen(
viewModel.reset() viewModel.reset()
} }
Scaffold( BackTopBarScaffold(
topBar = { title = "Guest Signature",
TopAppBar( onBack = onBack,
title = { Text("Guest Signature") }, actions = {
navigationIcon = { IconButton(
IconButton(onClick = onBack) { onClick = {
Icon(Icons.AutoMirrored.Default.ArrowBack, contentDescription = "Back") val svg = buildSignatureSvg(strokes, canvasSize.value)
if (!svg.isNullOrBlank()) {
viewModel.uploadSignature(propertyId, guestId, svg, onDone)
} }
}, },
actions = { enabled = strokes.isNotEmpty() && !state.isLoading
IconButton( ) {
onClick = { if (state.isLoading) {
val svg = buildSignatureSvg(strokes, canvasSize.value) CircularProgressIndicator(modifier = Modifier.size(18.dp), strokeWidth = 2.dp)
if (!svg.isNullOrBlank()) { } else {
viewModel.uploadSignature(propertyId, guestId, svg, onDone) Icon(Icons.Default.Done, contentDescription = "Upload")
} }
}, }
enabled = strokes.isNotEmpty() && !state.isLoading
) {
if (state.isLoading) {
CircularProgressIndicator(modifier = Modifier.size(18.dp), strokeWidth = 2.dp)
} else {
Icon(Icons.Default.Done, contentDescription = "Upload")
}
}
},
colors = TopAppBarDefaults.topAppBarColors()
)
} }
) { padding -> ) { padding ->
Column( PaddedScreenColumn(padding = padding) {
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(24.dp),
verticalArrangement = Arrangement.Top
) {
Text( Text(
text = "Please draw the guest signature below.", text = "Please draw the guest signature below.",
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium

View File

@@ -4,7 +4,6 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -14,14 +13,10 @@ import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
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
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -36,9 +31,10 @@ import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.trisolarispms.core.auth.Role import com.android.trisolarispms.core.auth.Role
import com.android.trisolarispms.ui.property.PropertyListViewModel import com.android.trisolarispms.ui.property.PropertyListViewModel
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import com.android.trisolarispms.ui.common.BackTopBarScaffold
import com.android.trisolarispms.ui.common.PaddedScreenColumn
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class)
fun HomeScreen( fun HomeScreen(
userId: String?, userId: String?,
userName: String?, userName: String?,
@@ -78,66 +74,57 @@ fun HomeScreen(
} }
} }
Scaffold( BackTopBarScaffold(
topBar = { title = "Trisolaris PMS",
TopAppBar( onBack = {},
title = { Text("Trisolaris PMS") }, showBack = false,
colors = TopAppBarDefaults.topAppBarColors(), actions = {
actions = { if (isSuperAdmin) {
if (isSuperAdmin) { IconButton(onClick = onUserDirectory) {
IconButton(onClick = onUserDirectory) { Icon(Icons.Default.People, contentDescription = "User Directory")
Icon(Icons.Default.People, contentDescription = "User Directory")
}
}
IconButton(onClick = { menuExpanded = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "Menu")
}
DropdownMenu(
expanded = menuExpanded,
onDismissRequest = { menuExpanded = false }
) {
DropdownMenuItem(
text = { Text("Add Property") },
onClick = {
menuExpanded = false
onAddProperty()
}
)
DropdownMenuItem(
text = { Text("Logout") },
onClick = {
menuExpanded = false
onLogout()
}
)
if (isSuperAdmin) {
DropdownMenuItem(
text = { Text("Update Tags") },
onClick = {
menuExpanded = false
onImageTags()
}
)
DropdownMenuItem(
text = { Text("Modify Amenities") },
onClick = {
menuExpanded = false
onAmenities()
}
)
}
}
} }
) }
IconButton(onClick = { menuExpanded = true }) {
Icon(Icons.Default.MoreVert, contentDescription = "Menu")
}
DropdownMenu(
expanded = menuExpanded,
onDismissRequest = { menuExpanded = false }
) {
DropdownMenuItem(
text = { Text("Add Property") },
onClick = {
menuExpanded = false
onAddProperty()
}
)
DropdownMenuItem(
text = { Text("Logout") },
onClick = {
menuExpanded = false
onLogout()
}
)
if (isSuperAdmin) {
DropdownMenuItem(
text = { Text("Update Tags") },
onClick = {
menuExpanded = false
onImageTags()
}
)
DropdownMenuItem(
text = { Text("Modify Amenities") },
onClick = {
menuExpanded = false
onAmenities()
}
)
}
}
} }
) { padding -> ) { padding ->
Column( PaddedScreenColumn(padding = padding) {
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(24.dp),
verticalArrangement = Arrangement.Top
) {
val title = if (!userName.isNullOrBlank()) "Welcome, $userName" else "Welcome" val title = if (!userName.isNullOrBlank()) "Welcome, $userName" else "Welcome"
Text(text = title, style = MaterialTheme.typography.headlineMedium) Text(text = title, style = MaterialTheme.typography.headlineMedium)
if (isSuperAdmin) { if (isSuperAdmin) {

View File

@@ -3,27 +3,18 @@ package com.android.trisolarispms.ui.property
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenuItem import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExposedDropdownMenuBox import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -44,6 +35,8 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.UUID import java.util.UUID
import com.android.trisolarispms.ui.common.BackTopBarScaffold
import com.android.trisolarispms.ui.common.PaddedScreenColumn
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -70,120 +63,105 @@ fun AddPropertyScreen(
} }
} }
Scaffold( BackTopBarScaffold(
topBar = { title = "Add Property",
TopAppBar( onBack = onBack
title = { Text("Add Property") },
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
}
},
colors = TopAppBarDefaults.topAppBarColors()
)
}
) { padding -> ) { padding ->
Column( PaddedScreenColumn(padding = padding) {
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(24.dp),
verticalArrangement = Arrangement.Top
) {
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
ExposedDropdownMenuBox( ExposedDropdownMenuBox(
expanded = expanded,
onExpandedChange = { expanded = it }
) {
OutlinedTextField(
value = state.name,
onValueChange = { value ->
viewModel.onNameChange(value)
val query = value.trim()
searchJob?.cancel()
placesError = null
if (query.length >= 4) {
searchJob = coroutineScope.launch {
delay(300)
isSearching = true
try {
predictions = PlacesRestClient.autocomplete(
apiKey = apiKey,
input = query,
sessionToken = sessionToken,
regionCode = "IN"
)
expanded = predictions.isNotEmpty()
} catch (e: Exception) {
predictions = emptyList()
expanded = false
placesError = e.localizedMessage ?: "Places search failed"
} finally {
isSearching = false
}
}
} else {
predictions = emptyList()
expanded = false
isSearching = false
}
},
label = { Text("Name") },
isError = state.nameError != null,
modifier = Modifier
.fillMaxWidth()
.menuAnchor(),
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }
)
ExposedDropdownMenu(
expanded = expanded, expanded = expanded,
onDismissRequest = { expanded = false } onExpandedChange = { expanded = it }
) { ) {
predictions.forEach { prediction -> OutlinedTextField(
DropdownMenuItem( value = state.name,
text = { Text(prediction.text) }, onValueChange = { value ->
onClick = { viewModel.onNameChange(value)
expanded = false val query = value.trim()
predictions = emptyList() searchJob?.cancel()
coroutineScope.launch { placesError = null
if (query.length >= 4) {
searchJob = coroutineScope.launch {
delay(300)
isSearching = true
try { try {
val details = PlacesRestClient.placeDetails( predictions = PlacesRestClient.autocomplete(
apiKey = apiKey, apiKey = apiKey,
placeId = prediction.placeId, input = query,
sessionToken = sessionToken sessionToken = sessionToken,
regionCode = "IN"
) )
viewModel.applyPlace(details.name, details.address) expanded = predictions.isNotEmpty()
} catch (e: Exception) { } catch (e: Exception) {
placesError = e.localizedMessage ?: "Place lookup failed" predictions = emptyList()
expanded = false
placesError = e.localizedMessage ?: "Places search failed"
} finally {
isSearching = false
} }
} }
} else {
predictions = emptyList()
expanded = false
isSearching = false
} }
) },
label = { Text("Name") },
isError = state.nameError != null,
modifier = Modifier
.fillMaxWidth()
.menuAnchor(),
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) }
)
ExposedDropdownMenu(
expanded = expanded,
onDismissRequest = { expanded = false }
) {
predictions.forEach { prediction ->
DropdownMenuItem(
text = { Text(prediction.text) },
onClick = {
expanded = false
predictions = emptyList()
coroutineScope.launch {
try {
val details = PlacesRestClient.placeDetails(
apiKey = apiKey,
placeId = prediction.placeId,
sessionToken = sessionToken
)
viewModel.applyPlace(details.name, details.address)
} catch (e: Exception) {
placesError = e.localizedMessage ?: "Place lookup failed"
}
}
}
)
}
} }
} }
} state.nameError?.let {
state.nameError?.let { Spacer(modifier = Modifier.height(4.dp))
Spacer(modifier = Modifier.height(4.dp)) Text(text = it, color = MaterialTheme.colorScheme.error)
Text(text = it, color = MaterialTheme.colorScheme.error) }
} if (isSearching) {
if (isSearching) { Spacer(modifier = Modifier.height(4.dp))
Spacer(modifier = Modifier.height(4.dp)) Text(text = "Searching…", style = MaterialTheme.typography.bodySmall)
Text(text = "Searching…", style = MaterialTheme.typography.bodySmall) }
} placesError?.let {
placesError?.let { Spacer(modifier = Modifier.height(4.dp))
Spacer(modifier = Modifier.height(4.dp)) Text(text = it, color = MaterialTheme.colorScheme.error)
Text(text = it, color = MaterialTheme.colorScheme.error) }
}
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
OutlinedTextField( OutlinedTextField(
value = state.addressText, value = state.addressText,
onValueChange = viewModel::onAddressChange, onValueChange = viewModel::onAddressChange,
label = { Text("Address") }, label = { Text("Address") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))

View File

@@ -4,27 +4,19 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.android.trisolarispms.ui.common.BackTopBarScaffold
import com.android.trisolarispms.ui.common.PaddedScreenColumn
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class)
fun PropertyHomeScreen( fun PropertyHomeScreen(
propertyId: String, propertyId: String,
onBack: () -> Unit, onBack: () -> Unit,
@@ -33,26 +25,11 @@ fun PropertyHomeScreen(
onRoomTypes: () -> Unit, onRoomTypes: () -> Unit,
canManageRooms: Boolean canManageRooms: Boolean
) { ) {
Scaffold( BackTopBarScaffold(
topBar = { title = "Property",
TopAppBar( onBack = onBack
title = { Text("Property") },
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
}
},
colors = TopAppBarDefaults.topAppBarColors()
)
}
) { padding -> ) { padding ->
Column( PaddedScreenColumn(padding = padding) {
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(24.dp),
verticalArrangement = Arrangement.Top
) {
PropertyTile(title = "Checked-in Rooms", subtitle = "Active stays", onClick = onActiveStays) PropertyTile(title = "Checked-in Rooms", subtitle = "Active stays", onClick = onActiveStays)
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
PropertyTile(title = "Available Rooms", subtitle = "Room list", onClick = onRooms) PropertyTile(title = "Available Rooms", subtitle = "Room list", onClick = onRooms)

View File

@@ -16,19 +16,14 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
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
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -49,9 +44,9 @@ import com.android.trisolarispms.BuildConfig
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
import com.android.trisolarispms.ui.common.BackTopBarScaffold
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class)
fun RazorpayQrScreen( fun RazorpayQrScreen(
propertyId: String, propertyId: String,
bookingId: String, bookingId: String,
@@ -93,23 +88,14 @@ fun RazorpayQrScreen(
exitAndRefresh() exitAndRefresh()
} }
Scaffold( BackTopBarScaffold(
topBar = { title = "Generate Payment Links | QR",
TopAppBar( onBack = {
title = { Text("Generate Payment Links | QR") }, if (isViewingQr) {
navigationIcon = { exitAndRefresh()
IconButton(onClick = { } else {
if (isViewingQr) { onBack()
exitAndRefresh() }
} else {
onBack()
}
}) {
Icon(Icons.AutoMirrored.Default.ArrowBack, contentDescription = "Back")
}
},
colors = TopAppBarDefaults.topAppBarColors()
)
} }
) { padding -> ) { padding ->
if (state.isCredited) { if (state.isCredited) {

View File

@@ -3,7 +3,6 @@ package com.android.trisolarispms.ui.roomstay
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -13,7 +12,6 @@ import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.People import androidx.compose.material.icons.filled.People
import androidx.compose.material.icons.filled.MeetingRoom import androidx.compose.material.icons.filled.MeetingRoom
import androidx.compose.material.icons.filled.Payment import androidx.compose.material.icons.filled.Payment
@@ -23,15 +21,11 @@ import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
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
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.LinearProgressIndicator
@@ -43,11 +37,12 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.trisolarispms.data.api.model.BookingListItem import com.android.trisolarispms.data.api.model.BookingListItem
import com.android.trisolarispms.ui.common.BackTopBarScaffold
import com.android.trisolarispms.ui.common.PaddedScreenColumn
import java.time.Duration import java.time.Duration
import java.time.OffsetDateTime import java.time.OffsetDateTime
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class)
fun ActiveRoomStaysScreen( fun ActiveRoomStaysScreen(
propertyId: String, propertyId: String,
propertyName: String, propertyName: String,
@@ -73,37 +68,27 @@ fun ActiveRoomStaysScreen(
viewModel.load(propertyId) viewModel.load(propertyId)
} }
Scaffold( BackTopBarScaffold(
topBar = { title = propertyName,
TopAppBar( onBack = onBack,
title = { Text(propertyName) }, showBack = showBack,
navigationIcon = { actions = {
if (showBack) { IconButton(onClick = onViewRooms) {
IconButton(onClick = onBack) { Icon(Icons.Default.MeetingRoom, contentDescription = "Available Rooms")
Icon(Icons.Default.ArrowBack, contentDescription = "Back") }
} IconButton(onClick = onOpenSettings) {
} Icon(Icons.Default.Settings, contentDescription = "Settings")
}, }
actions = { if (showRazorpaySettings) {
IconButton(onClick = onViewRooms) { IconButton(onClick = onRazorpaySettings) {
Icon(Icons.Default.MeetingRoom, contentDescription = "Available Rooms") Icon(Icons.Default.Payment, contentDescription = "Razorpay Settings")
} }
IconButton(onClick = onOpenSettings) { }
Icon(Icons.Default.Settings, contentDescription = "Settings") if (showUserAdmin) {
} IconButton(onClick = onUserAdmin) {
if (showRazorpaySettings) { Icon(Icons.Default.People, contentDescription = "Property Users")
IconButton(onClick = onRazorpaySettings) { }
Icon(Icons.Default.Payment, contentDescription = "Razorpay Settings") }
}
}
if (showUserAdmin) {
IconButton(onClick = onUserAdmin) {
Icon(Icons.Default.People, contentDescription = "Property Users")
}
}
},
colors = TopAppBarDefaults.topAppBarColors()
)
}, },
floatingActionButton = { floatingActionButton = {
if (canCreateBooking) { if (canCreateBooking) {
@@ -113,13 +98,7 @@ fun ActiveRoomStaysScreen(
} }
} }
) { padding -> ) { padding ->
Column( PaddedScreenColumn(padding = padding) {
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(24.dp),
verticalArrangement = Arrangement.Top
) {
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
if (state.isLoading) { if (state.isLoading) {

View File

@@ -5,25 +5,16 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -36,9 +27,10 @@ import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import com.android.trisolarispms.ui.common.BackTopBarScaffold
import com.android.trisolarispms.ui.common.PaddedScreenColumn
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class)
fun ManageRoomStayRatesScreen( fun ManageRoomStayRatesScreen(
propertyId: String, propertyId: String,
bookingId: String, bookingId: String,
@@ -57,18 +49,9 @@ fun ManageRoomStayRatesScreen(
viewModel.setItems(selectedRooms) viewModel.setItems(selectedRooms)
} }
Scaffold( BackTopBarScaffold(
topBar = { title = "Selected Rooms",
TopAppBar( onBack = onBack,
title = { Text("Selected Rooms") },
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.AutoMirrored.Default.ArrowBack, contentDescription = "Back")
}
},
colors = TopAppBarDefaults.topAppBarColors()
)
},
bottomBar = { bottomBar = {
Column( Column(
modifier = Modifier modifier = Modifier
@@ -102,16 +85,13 @@ fun ManageRoomStayRatesScreen(
} }
} }
) { padding -> ) { padding ->
Column( PaddedScreenColumn(
modifier = Modifier padding = padding,
.fillMaxSize() contentPadding = 16.dp
.padding(padding)
.padding(16.dp),
verticalArrangement = Arrangement.Top
) { ) {
if (state.items.isEmpty()) { if (state.items.isEmpty()) {
Text(text = "No rooms selected.") Text(text = "No rooms selected.")
return@Column return@PaddedScreenColumn
} }
state.items.forEach { item -> state.items.forEach { item ->
Row( Row(

View File

@@ -17,18 +17,10 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -41,11 +33,13 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.android.trisolarispms.data.api.model.RoomAvailableRateResponse import com.android.trisolarispms.data.api.model.RoomAvailableRateResponse
import com.android.trisolarispms.ui.common.BackTopBarScaffold
import com.android.trisolarispms.ui.common.LoadingAndError
import com.android.trisolarispms.ui.common.PaddedScreenColumn
import java.time.OffsetDateTime import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class)
fun ManageRoomStaySelectScreen( fun ManageRoomStaySelectScreen(
propertyId: String, propertyId: String,
bookingFromAt: String, bookingFromAt: String,
@@ -76,18 +70,9 @@ fun ManageRoomStaySelectScreen(
} }
} }
Scaffold( BackTopBarScaffold(
topBar = { title = "Select Rooms",
TopAppBar( onBack = onBack,
title = { Text("Select Rooms") },
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.AutoMirrored.Default.ArrowBack, contentDescription = "Back")
}
},
colors = TopAppBarDefaults.topAppBarColors()
)
},
bottomBar = { bottomBar = {
Column( Column(
modifier = Modifier modifier = Modifier
@@ -112,23 +97,14 @@ fun ManageRoomStaySelectScreen(
} }
} }
) { padding -> ) { padding ->
Column( PaddedScreenColumn(
modifier = Modifier padding = padding,
.fillMaxSize() contentPadding = 16.dp
.padding(padding)
.padding(16.dp)
) { ) {
if (state.isLoading) { LoadingAndError(isLoading = state.isLoading, error = state.error)
CircularProgressIndicator()
Spacer(modifier = Modifier.height(8.dp))
}
state.error?.let {
Text(text = it, color = MaterialTheme.colorScheme.error)
Spacer(modifier = Modifier.height(8.dp))
}
if (fromDate == null || fallbackToDate == null) { if (fromDate == null || fallbackToDate == null) {
Text(text = "Booking dates not available.", color = MaterialTheme.colorScheme.error) Text(text = "Booking dates not available.", color = MaterialTheme.colorScheme.error)
return@Column return@PaddedScreenColumn
} }
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(2), columns = GridCells.Fixed(2),

View File

@@ -16,22 +16,16 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Done
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
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
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
@@ -45,9 +39,9 @@ import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import coil.compose.AsyncImage import coil.compose.AsyncImage
import com.android.trisolarispms.data.api.core.ApiConstants import com.android.trisolarispms.data.api.core.ApiConstants
import com.android.trisolarispms.ui.common.SaveTopBarScaffold
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class)
fun RoomTypeFormScreen( fun RoomTypeFormScreen(
title: String, title: String,
propertyId: String, propertyId: String,
@@ -68,27 +62,16 @@ fun RoomTypeFormScreen(
amenityViewModel.load() amenityViewModel.load()
} }
Scaffold( SaveTopBarScaffold(
topBar = { title = title,
TopAppBar( onBack = onBack,
title = { Text(title) }, onSave = onSave,
navigationIcon = { actions = {
IconButton(onClick = onBack) { if (onDelete != null) {
Icon(Icons.Default.ArrowBack, contentDescription = "Back") IconButton(onClick = onDelete) {
} Icon(Icons.Default.Delete, contentDescription = "Delete Room Type")
}, }
actions = { }
IconButton(onClick = onSave) {
Icon(Icons.Default.Done, contentDescription = "Save")
}
if (onDelete != null) {
IconButton(onClick = onDelete) {
Icon(Icons.Default.Delete, contentDescription = "Delete Room Type")
}
}
},
colors = TopAppBarDefaults.topAppBarColors()
)
} }
) { padding -> ) { padding ->
Column( Column(

View File

@@ -3,36 +3,30 @@ package com.android.trisolarispms.ui.users
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Search
import androidx.compose.material.icons.filled.VpnKey import androidx.compose.material.icons.filled.VpnKey
import androidx.compose.material3.AlertDialog import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.CircularProgressIndicator
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
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.android.trisolarispms.ui.common.BackTopBarScaffold
import com.android.trisolarispms.ui.common.LoadingAndError
import com.android.trisolarispms.ui.common.PaddedScreenColumn
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class)
fun UserDirectoryScaffold( fun UserDirectoryScaffold(
title: String, title: String,
onBack: () -> Unit, onBack: () -> Unit,
@@ -47,48 +41,29 @@ fun UserDirectoryScaffold(
beforeListContent: @Composable () -> Unit = {}, beforeListContent: @Composable () -> Unit = {},
itemContent: @Composable (PropertyUserUi) -> Unit itemContent: @Composable (PropertyUserUi) -> Unit
) { ) {
Scaffold( BackTopBarScaffold(
topBar = { title = title,
TopAppBar( onBack = onBack,
title = { Text(title) }, actions = {
navigationIcon = { if (showAccessCodeIcon) {
IconButton(onClick = onBack) { IconButton(onClick = onAccessCodeClick) {
Icon(Icons.AutoMirrored.Default.ArrowBack, contentDescription = "Back") Icon(Icons.Default.VpnKey, contentDescription = "Generate access code")
} }
}, }
actions = { if (showSearchIcon) {
if (showAccessCodeIcon) { IconButton(onClick = onSearchClick) {
IconButton(onClick = onAccessCodeClick) { Icon(Icons.Default.Search, contentDescription = "Search users")
Icon(Icons.Default.VpnKey, contentDescription = "Generate access code") }
} }
}
if (showSearchIcon) {
IconButton(onClick = onSearchClick) {
Icon(Icons.Default.Search, contentDescription = "Search users")
}
}
},
colors = TopAppBarDefaults.topAppBarColors()
)
} }
) { padding -> ) { padding ->
Column( PaddedScreenColumn(
modifier = Modifier padding = padding,
.fillMaxSize() contentPadding = 16.dp
.padding(padding)
.padding(16.dp),
verticalArrangement = Arrangement.Top
) { ) {
beforeListContent() beforeListContent()
if (isLoading) { LoadingAndError(isLoading = isLoading, error = error)
Spacer(modifier = Modifier.height(12.dp))
CircularProgressIndicator()
}
error?.let {
Spacer(modifier = Modifier.height(12.dp))
Text(text = it, color = MaterialTheme.colorScheme.error)
}
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
LazyColumn(verticalArrangement = Arrangement.spacedBy(12.dp)) { LazyColumn(verticalArrangement = Arrangement.spacedBy(12.dp)) {