wip
This commit is contained in:
@@ -15,6 +15,7 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.mycrib.android.ui.screens.AddResidenceScreen
|
||||
import com.mycrib.android.ui.screens.EditResidenceScreen
|
||||
import com.mycrib.android.ui.screens.HomeScreen
|
||||
import com.mycrib.android.ui.screens.LoginScreen
|
||||
import com.mycrib.android.ui.screens.RegisterScreen
|
||||
@@ -30,6 +31,8 @@ import androidx.navigation.compose.composable
|
||||
import androidx.navigation.toRoute
|
||||
import com.mycrib.navigation.*
|
||||
import com.mycrib.repository.LookupsRepository
|
||||
import com.mycrib.shared.models.Residence
|
||||
import com.mycrib.storage.TokenStorage
|
||||
|
||||
import mycrib.composeapp.generated.resources.Res
|
||||
import mycrib.composeapp.generated.resources.compose_multiplatform
|
||||
@@ -37,12 +40,12 @@ import mycrib.composeapp.generated.resources.compose_multiplatform
|
||||
@Composable
|
||||
@Preview
|
||||
fun App() {
|
||||
var isLoggedIn by remember { mutableStateOf(com.mycrib.storage.TokenStorage.hasToken()) }
|
||||
var isLoggedIn by remember { mutableStateOf(TokenStorage.hasToken()) }
|
||||
val navController = rememberNavController()
|
||||
|
||||
// Check for stored token on app start and initialize lookups if logged in
|
||||
LaunchedEffect(Unit) {
|
||||
isLoggedIn = com.mycrib.storage.TokenStorage.hasToken()
|
||||
isLoggedIn = TokenStorage.hasToken()
|
||||
if (isLoggedIn) {
|
||||
LookupsRepository.initialize()
|
||||
}
|
||||
@@ -98,7 +101,7 @@ fun App() {
|
||||
},
|
||||
onLogout = {
|
||||
// Clear token and lookups on logout
|
||||
com.mycrib.storage.TokenStorage.clearToken()
|
||||
TokenStorage.clearToken()
|
||||
LookupsRepository.clear()
|
||||
isLoggedIn = false
|
||||
navController.navigate(LoginRoute) {
|
||||
@@ -118,7 +121,7 @@ fun App() {
|
||||
},
|
||||
onLogout = {
|
||||
// Clear token and lookups on logout
|
||||
com.mycrib.storage.TokenStorage.clearToken()
|
||||
TokenStorage.clearToken()
|
||||
LookupsRepository.clear()
|
||||
isLoggedIn = false
|
||||
navController.navigate(LoginRoute) {
|
||||
@@ -139,6 +142,42 @@ fun App() {
|
||||
)
|
||||
}
|
||||
|
||||
composable<EditResidenceRoute> { backStackEntry ->
|
||||
val route = backStackEntry.toRoute<EditResidenceRoute>()
|
||||
EditResidenceScreen(
|
||||
residence = Residence(
|
||||
id = route.residenceId,
|
||||
name = route.name,
|
||||
propertyType = route.propertyType.toString(), // Will be fetched from lookups
|
||||
streetAddress = route.streetAddress,
|
||||
apartmentUnit = route.apartmentUnit,
|
||||
city = route.city,
|
||||
stateProvince = route.stateProvince,
|
||||
postalCode = route.postalCode,
|
||||
country = route.country,
|
||||
bedrooms = route.bedrooms,
|
||||
bathrooms = route.bathrooms,
|
||||
squareFootage = route.squareFootage,
|
||||
lotSize = route.lotSize,
|
||||
yearBuilt = route.yearBuilt,
|
||||
description = route.description,
|
||||
purchaseDate = null,
|
||||
purchasePrice = null,
|
||||
isPrimary = route.isPrimary,
|
||||
ownerUsername = route.ownerUserName,
|
||||
owner = route.owner,
|
||||
createdAt = route.createdAt,
|
||||
updatedAt = route.updatedAt
|
||||
),
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onResidenceUpdated = {
|
||||
navController.popBackStack()
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
composable<TasksRoute> {
|
||||
TasksScreen(
|
||||
onNavigateBack = {
|
||||
@@ -153,6 +192,32 @@ fun App() {
|
||||
residenceId = route.residenceId,
|
||||
onNavigateBack = {
|
||||
navController.popBackStack()
|
||||
},
|
||||
onNavigateToEditResidence = { residence ->
|
||||
navController.navigate(
|
||||
EditResidenceRoute(
|
||||
residenceId = residence.id,
|
||||
name = residence.name,
|
||||
propertyType = residence.propertyType.toInt(),
|
||||
streetAddress = residence.streetAddress,
|
||||
apartmentUnit = residence.apartmentUnit,
|
||||
city = residence.city,
|
||||
stateProvince = residence.stateProvince,
|
||||
postalCode = residence.postalCode,
|
||||
country = residence.country,
|
||||
bedrooms = residence.bedrooms,
|
||||
bathrooms = residence.bathrooms,
|
||||
squareFootage = residence.squareFootage,
|
||||
lotSize = residence.lotSize,
|
||||
yearBuilt = residence.yearBuilt,
|
||||
description = residence.description,
|
||||
isPrimary = residence.isPrimary,
|
||||
ownerUserName = residence.ownerUsername,
|
||||
createdAt = residence.createdAt,
|
||||
updatedAt = residence.updatedAt,
|
||||
owner = residence.owner
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,14 +10,14 @@ data class CustomTask (
|
||||
@SerialName("created_by") val createdBy: Int,
|
||||
@SerialName("created_by_username") val createdByUsername: String,
|
||||
val title: String,
|
||||
val description: String?,
|
||||
val description: String? = null,
|
||||
val category: String,
|
||||
val priority: String,
|
||||
val status: String,
|
||||
@SerialName("due_date") val dueDate: String,
|
||||
@SerialName("estimated_cost") val estimatedCost: String?,
|
||||
@SerialName("actual_cost") val actualCost: String?,
|
||||
val notes: String?,
|
||||
@SerialName("estimated_cost") val estimatedCost: String? = null,
|
||||
@SerialName("actual_cost") val actualCost: String? = null,
|
||||
val notes: String? = null,
|
||||
@SerialName("created_at") val createdAt: String,
|
||||
@SerialName("updated_at") val updatedAt: String,
|
||||
@SerialName("show_completed_button") val showCompletedButton: Boolean = false,
|
||||
@@ -43,7 +43,7 @@ data class TaskCreateRequest(
|
||||
val frequency: Int,
|
||||
@SerialName("interval_days") val intervalDays: Int? = null,
|
||||
val priority: Int,
|
||||
val status: Int = 9,
|
||||
val status: Int,
|
||||
@SerialName("due_date") val dueDate: String,
|
||||
@SerialName("estimated_cost") val estimatedCost: String? = null
|
||||
)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.mycrib.navigation
|
||||
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@@ -17,6 +18,30 @@ object ResidencesRoute
|
||||
@Serializable
|
||||
object AddResidenceRoute
|
||||
|
||||
@Serializable
|
||||
data class EditResidenceRoute(
|
||||
val residenceId: Int,
|
||||
val name: String,
|
||||
val propertyType: Int,
|
||||
val streetAddress: String,
|
||||
val apartmentUnit: String?,
|
||||
val city: String,
|
||||
val stateProvince: String,
|
||||
val postalCode: String,
|
||||
val country: String,
|
||||
val bedrooms: Int?,
|
||||
val bathrooms: Float?,
|
||||
val squareFootage: Int?,
|
||||
val lotSize: Float?,
|
||||
val yearBuilt: Int?,
|
||||
val description: String?,
|
||||
val isPrimary: Boolean,
|
||||
val ownerUserName: String,
|
||||
val createdAt: String,
|
||||
val updatedAt: String,
|
||||
val owner: Int?,
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class ResidenceDetailRoute(val residenceId: Int)
|
||||
|
||||
|
||||
@@ -270,7 +270,8 @@ fun AddNewTaskDialog(
|
||||
intervalDays = intervalDays.toIntOrNull(),
|
||||
priority = priority.id,
|
||||
dueDate = dueDate,
|
||||
estimatedCost = estimatedCost.ifBlank { null }
|
||||
estimatedCost = estimatedCost.ifBlank { null },
|
||||
status = 9
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,369 @@
|
||||
package com.mycrib.android.ui.screens
|
||||
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
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.mycrib.android.viewmodel.ResidenceViewModel
|
||||
import com.mycrib.repository.LookupsRepository
|
||||
import com.mycrib.shared.models.Residence
|
||||
import com.mycrib.shared.models.ResidenceCreateRequest
|
||||
import com.mycrib.shared.models.ResidenceType
|
||||
import com.mycrib.shared.network.ApiResult
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun EditResidenceScreen(
|
||||
residence: Residence,
|
||||
onNavigateBack: () -> Unit,
|
||||
onResidenceUpdated: () -> Unit,
|
||||
viewModel: ResidenceViewModel = viewModel { ResidenceViewModel() }
|
||||
) {
|
||||
var name by remember { mutableStateOf(residence.name) }
|
||||
var propertyType by remember { mutableStateOf<ResidenceType?>(null) }
|
||||
var streetAddress by remember { mutableStateOf(residence.streetAddress) }
|
||||
var apartmentUnit by remember { mutableStateOf(residence.apartmentUnit ?: "") }
|
||||
var city by remember { mutableStateOf(residence.city) }
|
||||
var stateProvince by remember { mutableStateOf(residence.stateProvince) }
|
||||
var postalCode by remember { mutableStateOf(residence.postalCode) }
|
||||
var country by remember { mutableStateOf(residence.country) }
|
||||
var bedrooms by remember { mutableStateOf(residence.bedrooms?.toString() ?: "") }
|
||||
var bathrooms by remember { mutableStateOf(residence.bathrooms?.toString() ?: "") }
|
||||
var squareFootage by remember { mutableStateOf(residence.squareFootage?.toString() ?: "") }
|
||||
var lotSize by remember { mutableStateOf(residence.lotSize?.toString() ?: "") }
|
||||
var yearBuilt by remember { mutableStateOf(residence.yearBuilt?.toString() ?: "") }
|
||||
var description by remember { mutableStateOf(residence.description ?: "") }
|
||||
var isPrimary by remember { mutableStateOf(residence.isPrimary) }
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
|
||||
val updateState by viewModel.updateResidenceState.collectAsState()
|
||||
val propertyTypes by LookupsRepository.residenceTypes.collectAsState()
|
||||
|
||||
// Validation errors
|
||||
var nameError by remember { mutableStateOf("") }
|
||||
var streetAddressError by remember { mutableStateOf("") }
|
||||
var cityError by remember { mutableStateOf("") }
|
||||
var stateProvinceError by remember { mutableStateOf("") }
|
||||
var postalCodeError by remember { mutableStateOf("") }
|
||||
|
||||
// Handle update state changes
|
||||
LaunchedEffect(updateState) {
|
||||
when (updateState) {
|
||||
is ApiResult.Success -> {
|
||||
viewModel.resetUpdateState()
|
||||
onResidenceUpdated()
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
// Set property type from residence when types are loaded
|
||||
LaunchedEffect(propertyTypes, residence) {
|
||||
if (propertyTypes.isNotEmpty() && propertyType == null) {
|
||||
propertyType = residence.propertyType.let { pt ->
|
||||
propertyTypes.find { it.id == pt.toInt() }
|
||||
} ?: propertyTypes.first()
|
||||
}
|
||||
}
|
||||
|
||||
fun validateForm(): Boolean {
|
||||
var isValid = true
|
||||
|
||||
if (name.isBlank()) {
|
||||
nameError = "Name is required"
|
||||
isValid = false
|
||||
} else {
|
||||
nameError = ""
|
||||
}
|
||||
|
||||
if (streetAddress.isBlank()) {
|
||||
streetAddressError = "Street address is required"
|
||||
isValid = false
|
||||
} else {
|
||||
streetAddressError = ""
|
||||
}
|
||||
|
||||
if (city.isBlank()) {
|
||||
cityError = "City is required"
|
||||
isValid = false
|
||||
} else {
|
||||
cityError = ""
|
||||
}
|
||||
|
||||
if (stateProvince.isBlank()) {
|
||||
stateProvinceError = "State/Province is required"
|
||||
isValid = false
|
||||
} else {
|
||||
stateProvinceError = ""
|
||||
}
|
||||
|
||||
if (postalCode.isBlank()) {
|
||||
postalCodeError = "Postal code is required"
|
||||
isValid = false
|
||||
} else {
|
||||
postalCodeError = ""
|
||||
}
|
||||
|
||||
return isValid
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Edit Residence") },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues)
|
||||
.padding(16.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
// Required fields section
|
||||
Text(
|
||||
text = "Required Information",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = name,
|
||||
onValueChange = { name = it },
|
||||
label = { Text("Property Name *") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
isError = nameError.isNotEmpty(),
|
||||
supportingText = if (nameError.isNotEmpty()) {
|
||||
{ Text(nameError) }
|
||||
} else null
|
||||
)
|
||||
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded,
|
||||
onExpandedChange = { expanded = it }
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = propertyType?.name?.replaceFirstChar { it.uppercase() } ?: "",
|
||||
onValueChange = {},
|
||||
readOnly = true,
|
||||
label = { Text("Property Type *") },
|
||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.menuAnchor(),
|
||||
enabled = propertyTypes.isNotEmpty()
|
||||
)
|
||||
ExposedDropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false }
|
||||
) {
|
||||
propertyTypes.forEach { type ->
|
||||
DropdownMenuItem(
|
||||
text = { Text(type.name.replaceFirstChar { it.uppercase() }) },
|
||||
onClick = {
|
||||
propertyType = type
|
||||
expanded = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OutlinedTextField(
|
||||
value = streetAddress,
|
||||
onValueChange = { streetAddress = it },
|
||||
label = { Text("Street Address *") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
isError = streetAddressError.isNotEmpty(),
|
||||
supportingText = if (streetAddressError.isNotEmpty()) {
|
||||
{ Text(streetAddressError) }
|
||||
} else null
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = apartmentUnit,
|
||||
onValueChange = { apartmentUnit = it },
|
||||
label = { Text("Apartment/Unit #") },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = city,
|
||||
onValueChange = { city = it },
|
||||
label = { Text("City *") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
isError = cityError.isNotEmpty(),
|
||||
supportingText = if (cityError.isNotEmpty()) {
|
||||
{ Text(cityError) }
|
||||
} else null
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = stateProvince,
|
||||
onValueChange = { stateProvince = it },
|
||||
label = { Text("State/Province *") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
isError = stateProvinceError.isNotEmpty(),
|
||||
supportingText = if (stateProvinceError.isNotEmpty()) {
|
||||
{ Text(stateProvinceError) }
|
||||
} else null
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = postalCode,
|
||||
onValueChange = { postalCode = it },
|
||||
label = { Text("Postal Code *") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
isError = postalCodeError.isNotEmpty(),
|
||||
supportingText = if (postalCodeError.isNotEmpty()) {
|
||||
{ Text(postalCodeError) }
|
||||
} else null
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = country,
|
||||
onValueChange = { country = it },
|
||||
label = { Text("Country") },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
// Optional fields section
|
||||
Divider()
|
||||
Text(
|
||||
text = "Optional Details",
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = bedrooms,
|
||||
onValueChange = { bedrooms = it.filter { char -> char.isDigit() } },
|
||||
label = { Text("Bedrooms") },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = bathrooms,
|
||||
onValueChange = { bathrooms = it },
|
||||
label = { Text("Bathrooms") },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
|
||||
OutlinedTextField(
|
||||
value = squareFootage,
|
||||
onValueChange = { squareFootage = it.filter { char -> char.isDigit() } },
|
||||
label = { Text("Square Footage") },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = lotSize,
|
||||
onValueChange = { lotSize = it },
|
||||
label = { Text("Lot Size (acres)") },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Decimal),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = yearBuilt,
|
||||
onValueChange = { yearBuilt = it.filter { char -> char.isDigit() } },
|
||||
label = { Text("Year Built") },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
OutlinedTextField(
|
||||
value = description,
|
||||
onValueChange = { description = it },
|
||||
label = { Text("Description") },
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
minLines = 3,
|
||||
maxLines = 5
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text("Primary Residence")
|
||||
Switch(
|
||||
checked = isPrimary,
|
||||
onCheckedChange = { isPrimary = it }
|
||||
)
|
||||
}
|
||||
|
||||
// Error message
|
||||
if (updateState is ApiResult.Error) {
|
||||
Text(
|
||||
text = (updateState as ApiResult.Error).message,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
|
||||
// Submit button
|
||||
Button(
|
||||
onClick = {
|
||||
if (validateForm() && propertyType != null) {
|
||||
viewModel.updateResidence(
|
||||
residenceId = residence.id,
|
||||
request = ResidenceCreateRequest(
|
||||
name = name,
|
||||
propertyType = propertyType!!.id,
|
||||
streetAddress = streetAddress,
|
||||
apartmentUnit = apartmentUnit.ifBlank { null },
|
||||
city = city,
|
||||
stateProvince = stateProvince,
|
||||
postalCode = postalCode,
|
||||
country = country,
|
||||
bedrooms = bedrooms.toIntOrNull(),
|
||||
bathrooms = bathrooms.toFloatOrNull(),
|
||||
squareFootage = squareFootage.toIntOrNull(),
|
||||
lotSize = lotSize.toFloatOrNull(),
|
||||
yearBuilt = yearBuilt.toIntOrNull(),
|
||||
description = description.ifBlank { null },
|
||||
isPrimary = isPrimary
|
||||
)
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
enabled = validateForm() && propertyType != null
|
||||
) {
|
||||
if (updateState is ApiResult.Loading) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(24.dp),
|
||||
color = MaterialTheme.colorScheme.onPrimary
|
||||
)
|
||||
} else {
|
||||
Text("Update Residence")
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ import com.mycrib.shared.network.ApiResult
|
||||
fun ResidenceDetailScreen(
|
||||
residenceId: Int,
|
||||
onNavigateBack: () -> Unit,
|
||||
onNavigateToEditResidence: (Residence) -> Unit,
|
||||
residenceViewModel: ResidenceViewModel = viewModel { ResidenceViewModel() },
|
||||
taskCompletionViewModel: TaskCompletionViewModel = viewModel { TaskCompletionViewModel() },
|
||||
taskViewModel: TaskViewModel = viewModel { TaskViewModel() }
|
||||
@@ -63,9 +64,8 @@ fun ResidenceDetailScreen(
|
||||
LaunchedEffect(taskAddNewTaskState) {
|
||||
when (taskAddNewTaskState) {
|
||||
is ApiResult.Success -> {
|
||||
showCompleteDialog = false
|
||||
selectedTask = null
|
||||
taskCompletionViewModel.resetCreateState()
|
||||
showNewTaskDialog = false
|
||||
taskViewModel.resetAddTaskState()
|
||||
residenceViewModel.loadResidenceTasks(residenceId)
|
||||
}
|
||||
else -> {}
|
||||
@@ -113,6 +113,17 @@ fun ResidenceDetailScreen(
|
||||
Icon(Icons.Default.ArrowBack, contentDescription = "Back")
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
// Edit button - only show when residence is loaded
|
||||
if (residenceState is ApiResult.Success) {
|
||||
IconButton(onClick = {
|
||||
val residence = (residenceState as ApiResult.Success<Residence>).data
|
||||
onNavigateToEditResidence(residence)
|
||||
}) {
|
||||
Icon(Icons.Default.Edit, contentDescription = "Edit Residence")
|
||||
}
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.surface
|
||||
)
|
||||
@@ -264,7 +275,7 @@ fun ResidenceDetailScreen(
|
||||
}
|
||||
|
||||
// Description Card
|
||||
if (residence.description != null) {
|
||||
if (residence.description != null && !residence.description.isEmpty()) {
|
||||
item {
|
||||
InfoCard(
|
||||
icon = Icons.Default.Description,
|
||||
|
||||
@@ -28,6 +28,9 @@ class ResidenceViewModel : ViewModel() {
|
||||
private val _createResidenceState = MutableStateFlow<ApiResult<Residence>>(ApiResult.Loading)
|
||||
val createResidenceState: StateFlow<ApiResult<Residence>> = _createResidenceState
|
||||
|
||||
private val _updateResidenceState = MutableStateFlow<ApiResult<Residence>>(ApiResult.Loading)
|
||||
val updateResidenceState: StateFlow<ApiResult<Residence>> = _updateResidenceState
|
||||
|
||||
private val _residenceTasksState = MutableStateFlow<ApiResult<TasksByResidenceResponse>>(ApiResult.Loading)
|
||||
val residenceTasksState: StateFlow<ApiResult<TasksByResidenceResponse>> = _residenceTasksState
|
||||
|
||||
@@ -98,10 +101,26 @@ class ResidenceViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateResidence(residenceId: Int, request: ResidenceCreateRequest) {
|
||||
viewModelScope.launch {
|
||||
_updateResidenceState.value = ApiResult.Loading
|
||||
val token = TokenStorage.getToken()
|
||||
if (token != null) {
|
||||
_updateResidenceState.value = residenceApi.updateResidence(token, residenceId, request)
|
||||
} else {
|
||||
_updateResidenceState.value = ApiResult.Error("Not authenticated", 401)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun resetCreateState() {
|
||||
_createResidenceState.value = ApiResult.Loading
|
||||
}
|
||||
|
||||
fun resetUpdateState() {
|
||||
_updateResidenceState.value = ApiResult.Loading
|
||||
}
|
||||
|
||||
fun loadMyResidences() {
|
||||
viewModelScope.launch {
|
||||
_myResidencesState.value = ApiResult.Loading
|
||||
|
||||
Reference in New Issue
Block a user