Compare commits

..

3 Commits

Author SHA1 Message Date
androidlover5842
4642102ff5 Update amenities API and category suggestions 2026-01-27 04:55:28 +05:30
androidlover5842
94eb4f9be4 Fix amenities back navigation 2026-01-27 04:29:05 +05:30
androidlover5842
2c296a2cb3 Move amenities management to home menu 2026-01-27 04:27:11 +05:30
14 changed files with 234 additions and 63 deletions

View File

@@ -52,6 +52,7 @@ class MainActivity : ComponentActivity() {
val selectedRoomType = remember { mutableStateOf<com.android.trisolarispms.data.api.model.RoomTypeDto?>(null) } val selectedRoomType = remember { mutableStateOf<com.android.trisolarispms.data.api.model.RoomTypeDto?>(null) }
val selectedAmenity = remember { mutableStateOf<com.android.trisolarispms.data.api.model.AmenityDto?>(null) } val selectedAmenity = remember { mutableStateOf<com.android.trisolarispms.data.api.model.AmenityDto?>(null) }
val roomFormKey = remember { mutableStateOf(0) } val roomFormKey = remember { mutableStateOf(0) }
val amenitiesReturnRoute = remember { mutableStateOf<AppRoute>(AppRoute.Home) }
val currentRoute = route.value val currentRoute = route.value
val canManageProperty: (String) -> Boolean = { propertyId -> val canManageProperty: (String) -> Boolean = { propertyId ->
state.isSuperAdmin || (state.propertyRoles[propertyId]?.contains("ADMIN") == true) state.isSuperAdmin || (state.propertyRoles[propertyId]?.contains("ADMIN") == true)
@@ -63,6 +64,10 @@ class MainActivity : ComponentActivity() {
userName = state.userName, userName = state.userName,
isSuperAdmin = state.isSuperAdmin, isSuperAdmin = state.isSuperAdmin,
onAddProperty = { route.value = AppRoute.AddProperty }, onAddProperty = { route.value = AppRoute.AddProperty },
onAmenities = {
amenitiesReturnRoute.value = AppRoute.Home
route.value = AppRoute.Amenities
},
refreshKey = refreshKey.value, refreshKey = refreshKey.value,
selectedPropertyId = selectedPropertyId.value, selectedPropertyId = selectedPropertyId.value,
onSelectProperty = { id, name -> onSelectProperty = { id, name ->
@@ -109,7 +114,6 @@ class MainActivity : ComponentActivity() {
propertyId = currentRoute.propertyId, propertyId = currentRoute.propertyId,
onBack = { route.value = AppRoute.Rooms(currentRoute.propertyId) }, onBack = { route.value = AppRoute.Rooms(currentRoute.propertyId) },
onAdd = { route.value = AppRoute.AddRoomType(currentRoute.propertyId) }, onAdd = { route.value = AppRoute.AddRoomType(currentRoute.propertyId) },
onAmenities = { route.value = AppRoute.Amenities(currentRoute.propertyId) },
canManageRoomTypes = canManageProperty(currentRoute.propertyId), canManageRoomTypes = canManageProperty(currentRoute.propertyId),
onEdit = { onEdit = {
selectedRoomType.value = it selectedRoomType.value = it
@@ -128,26 +132,24 @@ class MainActivity : ComponentActivity() {
onBack = { route.value = AppRoute.RoomTypes(currentRoute.propertyId) }, onBack = { route.value = AppRoute.RoomTypes(currentRoute.propertyId) },
onSave = { route.value = AppRoute.RoomTypes(currentRoute.propertyId) } onSave = { route.value = AppRoute.RoomTypes(currentRoute.propertyId) }
) )
is AppRoute.Amenities -> AmenitiesScreen( AppRoute.Amenities -> AmenitiesScreen(
propertyId = currentRoute.propertyId, onBack = { route.value = amenitiesReturnRoute.value },
onBack = { route.value = AppRoute.RoomTypes(currentRoute.propertyId) }, onAdd = { route.value = AppRoute.AddAmenity },
onAdd = { route.value = AppRoute.AddAmenity(currentRoute.propertyId) }, canManageAmenities = state.isSuperAdmin,
onEdit = { onEdit = {
selectedAmenity.value = it selectedAmenity.value = it
route.value = AppRoute.EditAmenity(currentRoute.propertyId, it.id ?: "") route.value = AppRoute.EditAmenity(it.id ?: "")
} }
) )
is AppRoute.AddAmenity -> AddAmenityScreen( AppRoute.AddAmenity -> AddAmenityScreen(
propertyId = currentRoute.propertyId, onBack = { route.value = AppRoute.Amenities },
onBack = { route.value = AppRoute.Amenities(currentRoute.propertyId) }, onSave = { route.value = AppRoute.Amenities }
onSave = { route.value = AppRoute.Amenities(currentRoute.propertyId) }
) )
is AppRoute.EditAmenity -> EditAmenityScreen( is AppRoute.EditAmenity -> EditAmenityScreen(
propertyId = currentRoute.propertyId,
amenity = selectedAmenity.value amenity = selectedAmenity.value
?: com.android.trisolarispms.data.api.model.AmenityDto(id = currentRoute.amenityId, name = ""), ?: com.android.trisolarispms.data.api.model.AmenityDto(id = currentRoute.amenityId, name = ""),
onBack = { route.value = AppRoute.Amenities(currentRoute.propertyId) }, onBack = { route.value = AppRoute.Amenities },
onSave = { route.value = AppRoute.Amenities(currentRoute.propertyId) } onSave = { route.value = AppRoute.Amenities }
) )
is AppRoute.AddRoom -> RoomFormScreen( is AppRoute.AddRoom -> RoomFormScreen(
title = "Add Room", title = "Add Room",

View File

@@ -12,25 +12,22 @@ import retrofit2.http.PUT
import retrofit2.http.Path import retrofit2.http.Path
interface AmenityApi { interface AmenityApi {
@GET("properties/{propertyId}/amenities") @GET("amenities")
suspend fun listAmenities(@Path("propertyId") propertyId: String): Response<List<AmenityDto>> suspend fun listAmenities(): Response<List<AmenityDto>>
@POST("properties/{propertyId}/amenities") @POST("amenities")
suspend fun createAmenity( suspend fun createAmenity(
@Path("propertyId") propertyId: String,
@Body body: AmenityCreateRequest @Body body: AmenityCreateRequest
): Response<AmenityDto> ): Response<AmenityDto>
@PUT("properties/{propertyId}/amenities/{amenityId}") @PUT("amenities/{amenityId}")
suspend fun updateAmenity( suspend fun updateAmenity(
@Path("propertyId") propertyId: String,
@Path("amenityId") amenityId: String, @Path("amenityId") amenityId: String,
@Body body: AmenityUpdateRequest @Body body: AmenityUpdateRequest
): Response<AmenityDto> ): Response<AmenityDto>
@DELETE("properties/{propertyId}/amenities/{amenityId}") @DELETE("amenities/{amenityId}")
suspend fun deleteAmenity( suspend fun deleteAmenity(
@Path("propertyId") propertyId: String,
@Path("amenityId") amenityId: String @Path("amenityId") amenityId: String
): Response<Unit> ): Response<Unit>
} }

View File

@@ -2,13 +2,19 @@ package com.android.trisolarispms.data.api.model
data class AmenityDto( data class AmenityDto(
val id: String? = null, val id: String? = null,
val name: String? = null val name: String? = null,
val category: String? = null,
val iconKey: String? = null
) )
data class AmenityCreateRequest( data class AmenityCreateRequest(
val name: String val name: String,
val category: String? = null,
val iconKey: String? = null
) )
data class AmenityUpdateRequest( data class AmenityUpdateRequest(
val name: String val name: String,
val category: String? = null,
val iconKey: String? = null
) )

View File

@@ -10,7 +10,7 @@ sealed interface AppRoute {
data class RoomTypes(val propertyId: String) : AppRoute data class RoomTypes(val propertyId: String) : AppRoute
data class AddRoomType(val propertyId: String) : AppRoute data class AddRoomType(val propertyId: String) : AppRoute
data class EditRoomType(val propertyId: String, val roomTypeId: String) : AppRoute data class EditRoomType(val propertyId: String, val roomTypeId: String) : AppRoute
data class Amenities(val propertyId: String) : AppRoute data object Amenities : AppRoute
data class AddAmenity(val propertyId: String) : AppRoute data object AddAmenity : AppRoute
data class EditAmenity(val propertyId: String, val amenityId: String) : AppRoute data class EditAmenity(val amenityId: String) : AppRoute
} }

View File

@@ -40,6 +40,7 @@ fun HomeScreen(
userName: String?, userName: String?,
isSuperAdmin: Boolean, isSuperAdmin: Boolean,
onAddProperty: () -> Unit, onAddProperty: () -> Unit,
onAmenities: () -> Unit,
refreshKey: Int, refreshKey: Int,
selectedPropertyId: String?, selectedPropertyId: String?,
onSelectProperty: (String, String) -> Unit, onSelectProperty: (String, String) -> Unit,
@@ -77,6 +78,15 @@ fun HomeScreen(
onAddProperty() onAddProperty()
} }
) )
if (isSuperAdmin) {
DropdownMenuItem(
text = { Text("Modify Amenities") },
onClick = {
menuExpanded = false
onAmenities()
}
)
}
} }
} }
) )

View File

@@ -1,5 +1,6 @@
package com.android.trisolarispms.ui.roomtype package com.android.trisolarispms.ui.roomtype
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
@@ -20,8 +21,10 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
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 androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@@ -29,12 +32,26 @@ import androidx.lifecycle.viewmodel.compose.viewModel
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
fun AddAmenityScreen( fun AddAmenityScreen(
propertyId: String,
onBack: () -> Unit, onBack: () -> Unit,
onSave: () -> Unit, onSave: () -> Unit,
viewModel: AmenityFormViewModel = viewModel() viewModel: AmenityFormViewModel = viewModel(),
amenityListViewModel: AmenityListViewModel = viewModel()
) { ) {
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
val amenityState by amenityListViewModel.state.collectAsState()
LaunchedEffect(Unit) {
amenityListViewModel.load()
}
val categorySuggestions = remember(state.category, amenityState.items) {
val all = amenityState.items.mapNotNull { it.category?.trim() }
.filter { it.isNotBlank() }
.distinct()
.sorted()
if (state.category.isBlank()) all
else all.filter { it.contains(state.category, ignoreCase = true) }
}
Scaffold( Scaffold(
topBar = { topBar = {
@@ -46,7 +63,7 @@ fun AddAmenityScreen(
} }
}, },
actions = { actions = {
IconButton(onClick = { viewModel.submitCreate(propertyId, onSave) }) { IconButton(onClick = { viewModel.submitCreate(onSave) }) {
Icon(Icons.Default.Done, contentDescription = "Save") Icon(Icons.Default.Done, contentDescription = "Save")
} }
}, },
@@ -67,6 +84,35 @@ fun AddAmenityScreen(
label = { Text("Name") }, label = { Text("Name") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(modifier = Modifier.height(12.dp))
OutlinedTextField(
value = state.category,
onValueChange = viewModel::onCategoryChange,
label = { Text("Category") },
modifier = Modifier.fillMaxWidth()
)
if (categorySuggestions.isNotEmpty()) {
Spacer(modifier = Modifier.height(6.dp))
categorySuggestions.take(5).forEach { suggestion ->
Text(
text = suggestion,
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier
.padding(vertical = 2.dp)
.clickable { viewModel.onCategoryChange(suggestion) }
)
}
}
Spacer(modifier = Modifier.height(12.dp))
OutlinedTextField(
value = state.iconKey,
onValueChange = viewModel::onIconKeyChange,
label = { Text("Icon Key") },
modifier = Modifier.fillMaxWidth()
)
state.error?.let { state.error?.let {
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
Text(text = it, color = MaterialTheme.colorScheme.error) Text(text = it, color = MaterialTheme.colorScheme.error)

View File

@@ -44,8 +44,8 @@ fun AddRoomTypeScreen(
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
val amenityState by amenityViewModel.state.collectAsState() val amenityState by amenityViewModel.state.collectAsState()
LaunchedEffect(propertyId) { LaunchedEffect(Unit) {
amenityViewModel.load(propertyId) amenityViewModel.load()
} }
Scaffold( Scaffold(

View File

@@ -11,6 +11,7 @@ import androidx.compose.foundation.layout.padding
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.ArrowBack
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
@@ -32,16 +33,16 @@ import com.android.trisolarispms.data.api.model.AmenityDto
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
fun AmenitiesScreen( fun AmenitiesScreen(
propertyId: String,
onBack: () -> Unit, onBack: () -> Unit,
onAdd: () -> Unit, onAdd: () -> Unit,
canManageAmenities: Boolean,
onEdit: (AmenityDto) -> Unit, onEdit: (AmenityDto) -> Unit,
viewModel: AmenityListViewModel = viewModel() viewModel: AmenityListViewModel = viewModel()
) { ) {
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
LaunchedEffect(propertyId) { LaunchedEffect(Unit) {
viewModel.load(propertyId) viewModel.load()
} }
Scaffold( Scaffold(
@@ -54,8 +55,10 @@ fun AmenitiesScreen(
} }
}, },
actions = { actions = {
IconButton(onClick = onAdd) { if (canManageAmenities) {
Icon(Icons.Default.Add, contentDescription = "Add Amenity") IconButton(onClick = onAdd) {
Icon(Icons.Default.Add, contentDescription = "Add Amenity")
}
} }
}, },
colors = TopAppBarDefaults.topAppBarColors() colors = TopAppBarDefaults.topAppBarColors()
@@ -82,14 +85,29 @@ fun AmenitiesScreen(
Text(text = "No amenities") Text(text = "No amenities")
} else { } else {
state.items.forEach { item -> state.items.forEach { item ->
Text( androidx.compose.foundation.layout.Row(
text = item.name ?: "",
style = MaterialTheme.typography.titleMedium,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clickable(enabled = item.id != null) { onEdit(item) } .padding(vertical = 8.dp),
.padding(vertical = 8.dp) horizontalArrangement = Arrangement.SpaceBetween
) ) {
Text(
text = item.name ?: "",
style = MaterialTheme.typography.titleMedium,
modifier = Modifier
.weight(1f)
.clickable(enabled = canManageAmenities && item.id != null) { onEdit(item) }
)
if (canManageAmenities && item.id != null) {
IconButton(onClick = { viewModel.deleteAmenity(item.id) }) {
Icon(Icons.Default.Delete, contentDescription = "Delete Amenity")
}
}
}
val meta = listOfNotNull(item.category, item.iconKey).joinToString("")
if (meta.isNotBlank()) {
Text(text = meta, style = MaterialTheme.typography.bodySmall)
}
} }
} }
} }

View File

@@ -2,6 +2,8 @@ package com.android.trisolarispms.ui.roomtype
data class AmenityFormState( data class AmenityFormState(
val name: String = "", val name: String = "",
val category: String = "",
val iconKey: String = "",
val isLoading: Boolean = false, val isLoading: Boolean = false,
val error: String? = null, val error: String? = null,
val success: Boolean = false val success: Boolean = false

View File

@@ -14,15 +14,30 @@ class AmenityFormViewModel : ViewModel() {
private val _state = MutableStateFlow(AmenityFormState()) private val _state = MutableStateFlow(AmenityFormState())
val state: StateFlow<AmenityFormState> = _state val state: StateFlow<AmenityFormState> = _state
fun setAmenityName(name: String) { fun setAmenity(amenity: com.android.trisolarispms.data.api.model.AmenityDto) {
_state.update { it.copy(name = name, error = null) } _state.update {
it.copy(
name = amenity.name.orEmpty(),
category = amenity.category.orEmpty(),
iconKey = amenity.iconKey.orEmpty(),
error = null
)
}
} }
fun onNameChange(value: String) { fun onNameChange(value: String) {
_state.update { it.copy(name = value, error = null) } _state.update { it.copy(name = value, error = null) }
} }
fun submitCreate(propertyId: String, onDone: () -> Unit) { fun onCategoryChange(value: String) {
_state.update { it.copy(category = value, error = null) }
}
fun onIconKeyChange(value: String) {
_state.update { it.copy(iconKey = value, error = null) }
}
fun submitCreate(onDone: () -> Unit) {
val name = state.value.name.trim() val name = state.value.name.trim()
if (name.isBlank()) { if (name.isBlank()) {
_state.update { it.copy(error = "Name is required") } _state.update { it.copy(error = "Name is required") }
@@ -32,7 +47,13 @@ class AmenityFormViewModel : ViewModel() {
_state.update { it.copy(isLoading = true, error = null) } _state.update { it.copy(isLoading = true, error = null) }
try { try {
val api = ApiClient.create() val api = ApiClient.create()
val response = api.createAmenity(propertyId, AmenityCreateRequest(name)) val response = api.createAmenity(
AmenityCreateRequest(
name = name,
category = state.value.category.trim().ifBlank { null },
iconKey = state.value.iconKey.trim().ifBlank { null }
)
)
if (response.isSuccessful) { if (response.isSuccessful) {
_state.update { it.copy(isLoading = false, success = true) } _state.update { it.copy(isLoading = false, success = true) }
onDone() onDone()
@@ -45,7 +66,7 @@ class AmenityFormViewModel : ViewModel() {
} }
} }
fun submitUpdate(propertyId: String, amenityId: String, onDone: () -> Unit) { fun submitUpdate(amenityId: String, onDone: () -> Unit) {
val name = state.value.name.trim() val name = state.value.name.trim()
if (name.isBlank()) { if (name.isBlank()) {
_state.update { it.copy(error = "Name is required") } _state.update { it.copy(error = "Name is required") }
@@ -55,7 +76,14 @@ class AmenityFormViewModel : ViewModel() {
_state.update { it.copy(isLoading = true, error = null) } _state.update { it.copy(isLoading = true, error = null) }
try { try {
val api = ApiClient.create() val api = ApiClient.create()
val response = api.updateAmenity(propertyId, amenityId, AmenityUpdateRequest(name)) val response = api.updateAmenity(
amenityId,
AmenityUpdateRequest(
name = name,
category = state.value.category.trim().ifBlank { null },
iconKey = state.value.iconKey.trim().ifBlank { null }
)
)
if (response.isSuccessful) { if (response.isSuccessful) {
_state.update { it.copy(isLoading = false, success = true) } _state.update { it.copy(isLoading = false, success = true) }
onDone() onDone()

View File

@@ -12,13 +12,12 @@ class AmenityListViewModel : ViewModel() {
private val _state = MutableStateFlow(AmenityListState()) private val _state = MutableStateFlow(AmenityListState())
val state: StateFlow<AmenityListState> = _state val state: StateFlow<AmenityListState> = _state
fun load(propertyId: String) { fun load() {
if (propertyId.isBlank()) return
viewModelScope.launch { viewModelScope.launch {
_state.update { it.copy(isLoading = true, error = null) } _state.update { it.copy(isLoading = true, error = null) }
try { try {
val api = ApiClient.create() val api = ApiClient.create()
val response = api.listAmenities(propertyId) val response = api.listAmenities()
if (response.isSuccessful) { if (response.isSuccessful) {
_state.update { _state.update {
it.copy( it.copy(
@@ -35,4 +34,28 @@ class AmenityListViewModel : ViewModel() {
} }
} }
} }
fun deleteAmenity(amenityId: String) {
if (amenityId.isBlank()) return
viewModelScope.launch {
_state.update { it.copy(isLoading = true, error = null) }
try {
val api = ApiClient.create()
val response = api.deleteAmenity(amenityId)
if (response.isSuccessful) {
_state.update { current ->
current.copy(
isLoading = false,
items = current.items.filterNot { it.id == amenityId },
error = null
)
}
} else {
_state.update { it.copy(isLoading = false, error = "Delete failed: ${response.code()}") }
}
} catch (e: Exception) {
_state.update { it.copy(isLoading = false, error = e.localizedMessage ?: "Delete failed") }
}
}
}
} }

View File

@@ -1,5 +1,6 @@
package com.android.trisolarispms.ui.roomtype package com.android.trisolarispms.ui.roomtype
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
@@ -23,6 +24,7 @@ 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
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
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 androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
@@ -31,16 +33,29 @@ import com.android.trisolarispms.data.api.model.AmenityDto
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
fun EditAmenityScreen( fun EditAmenityScreen(
propertyId: String,
amenity: AmenityDto, amenity: AmenityDto,
onBack: () -> Unit, onBack: () -> Unit,
onSave: () -> Unit, onSave: () -> Unit,
viewModel: AmenityFormViewModel = viewModel() viewModel: AmenityFormViewModel = viewModel(),
amenityListViewModel: AmenityListViewModel = viewModel()
) { ) {
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
val amenityState by amenityListViewModel.state.collectAsState()
LaunchedEffect(amenity.id) { LaunchedEffect(amenity.id) {
viewModel.setAmenityName(amenity.name.orEmpty()) viewModel.setAmenity(amenity)
}
LaunchedEffect(Unit) {
amenityListViewModel.load()
}
val categorySuggestions = remember(state.category, amenityState.items) {
val all = amenityState.items.mapNotNull { it.category?.trim() }
.filter { it.isNotBlank() }
.distinct()
.sorted()
if (state.category.isBlank()) all
else all.filter { it.contains(state.category, ignoreCase = true) }
} }
Scaffold( Scaffold(
@@ -55,7 +70,7 @@ fun EditAmenityScreen(
actions = { actions = {
IconButton(onClick = { IconButton(onClick = {
val id = amenity.id.orEmpty() val id = amenity.id.orEmpty()
viewModel.submitUpdate(propertyId, id, onSave) viewModel.submitUpdate(id, onSave)
}) { }) {
Icon(Icons.Default.Done, contentDescription = "Save") Icon(Icons.Default.Done, contentDescription = "Save")
} }
@@ -77,6 +92,35 @@ fun EditAmenityScreen(
label = { Text("Name") }, label = { Text("Name") },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(modifier = Modifier.height(12.dp))
OutlinedTextField(
value = state.category,
onValueChange = viewModel::onCategoryChange,
label = { Text("Category") },
modifier = Modifier.fillMaxWidth()
)
if (categorySuggestions.isNotEmpty()) {
Spacer(modifier = Modifier.height(6.dp))
categorySuggestions.take(5).forEach { suggestion ->
Text(
text = suggestion,
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.primary,
modifier = Modifier
.padding(vertical = 2.dp)
.clickable { viewModel.onCategoryChange(suggestion) }
)
}
}
Spacer(modifier = Modifier.height(12.dp))
OutlinedTextField(
value = state.iconKey,
onValueChange = viewModel::onIconKeyChange,
label = { Text("Icon Key") },
modifier = Modifier.fillMaxWidth()
)
state.error?.let { state.error?.let {
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
Text(text = it, color = MaterialTheme.colorScheme.error) Text(text = it, color = MaterialTheme.colorScheme.error)

View File

@@ -49,8 +49,8 @@ fun EditRoomTypeScreen(
LaunchedEffect(roomType.id) { LaunchedEffect(roomType.id) {
viewModel.setRoomType(roomType) viewModel.setRoomType(roomType)
} }
LaunchedEffect(propertyId) { LaunchedEffect(Unit) {
amenityViewModel.load(propertyId) amenityViewModel.load()
} }
Scaffold( Scaffold(

View File

@@ -27,7 +27,6 @@ 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 androidx.compose.material.icons.filled.Settings
@Composable @Composable
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -35,7 +34,6 @@ fun RoomTypesScreen(
propertyId: String, propertyId: String,
onBack: () -> Unit, onBack: () -> Unit,
onAdd: () -> Unit, onAdd: () -> Unit,
onAmenities: () -> Unit,
canManageRoomTypes: Boolean, canManageRoomTypes: Boolean,
onEdit: (com.android.trisolarispms.data.api.model.RoomTypeDto) -> Unit, onEdit: (com.android.trisolarispms.data.api.model.RoomTypeDto) -> Unit,
viewModel: RoomTypeListViewModel = viewModel() viewModel: RoomTypeListViewModel = viewModel()
@@ -57,9 +55,6 @@ fun RoomTypesScreen(
}, },
actions = { actions = {
if (canManageRoomTypes) { if (canManageRoomTypes) {
IconButton(onClick = onAmenities) {
Icon(Icons.Default.Settings, contentDescription = "Amenities")
}
IconButton(onClick = onAdd) { IconButton(onClick = onAdd) {
Icon(Icons.Default.Add, contentDescription = "Add Room Type") Icon(Icons.Default.Add, contentDescription = "Add Room Type")
} }