feat: Refactor Item Edit Screen with all API fields and user-friendly UI
This commit is contained in:
@@ -5,28 +5,48 @@
|
||||
package com.homebox.lens.ui.screen.itemedit
|
||||
|
||||
// [IMPORTS]
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
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.ArrowDropDown
|
||||
import androidx.compose.material.icons.filled.DateRange
|
||||
import androidx.compose.material.icons.filled.Save
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Checkbox
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.DatePicker
|
||||
import androidx.compose.material3.DatePickerDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.FloatingActionButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarHost
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
@@ -36,13 +56,16 @@ import com.homebox.lens.R
|
||||
import com.homebox.lens.navigation.NavigationActions
|
||||
import com.homebox.lens.ui.common.MainScaffold
|
||||
import timber.log.Timber
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Date
|
||||
import java.util.Locale
|
||||
// [END_IMPORTS]
|
||||
|
||||
// [ENTITY: Function('ItemEditScreen')]
|
||||
// [RELATION: Function('ItemEditScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
|
||||
// [RELATION: Function('ItemEditScreen')] -> [DEPENDS_ON] -> [ViewModel('ItemEditViewModel')]
|
||||
// [RELATION: Function('ItemEditScreen')] -> [CONSUMES_STATE] -> [DataClass('ItemEditUiState')]
|
||||
// [RELATION: Function('ItemEditScreen')] -> [CALLS] -> [Function('MainScaffold')]
|
||||
// [ENTITY: Composable('ItemEditScreen')]
|
||||
// [RELATION: Composable('ItemEditScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
|
||||
// [RELATION: Composable('ItemEditScreen')] -> [DEPENDS_ON] -> [ViewModel('ItemEditViewModel')]
|
||||
// [RELATION: Composable('ItemEditScreen')] -> [CONSUMES_STATE] -> [DataClass('ItemEditUiState')]
|
||||
// [RELATION: Composable('ItemEditScreen')] -> [CALLS] -> [Composable('MainScaffold')]
|
||||
/**
|
||||
* @summary Composable-функция для экрана "Редактирование элемента".
|
||||
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer.
|
||||
@@ -51,6 +74,7 @@ import timber.log.Timber
|
||||
* @param viewModel ViewModel для управления состоянием экрана.
|
||||
* @param onSaveSuccess Callback, вызываемый после успешного сохранения товара.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ItemEditScreen(
|
||||
currentRoute: String?,
|
||||
@@ -75,7 +99,7 @@ fun ItemEditScreen(
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.saveCompleted.collect {
|
||||
viewModel.saveCompleted.collect {
|
||||
Timber.i("[INFO][ACTION][save_completed_callback] Item save completed. Triggering onSaveSuccess.")
|
||||
onSaveSuccess()
|
||||
}
|
||||
@@ -85,7 +109,7 @@ fun ItemEditScreen(
|
||||
topBarTitle = stringResource(id = R.string.item_edit_title),
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions
|
||||
) {
|
||||
) { paddingValues ->
|
||||
Scaffold(
|
||||
snackbarHost = { SnackbarHost(snackbarHostState) },
|
||||
floatingActionButton = {
|
||||
@@ -100,40 +124,389 @@ fun ItemEditScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(it)
|
||||
.padding(paddingValues)
|
||||
.padding(16.dp)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
if (uiState.isLoading) {
|
||||
CircularProgressIndicator(modifier = Modifier.fillMaxWidth())
|
||||
} else {
|
||||
uiState.item?.let { item ->
|
||||
OutlinedTextField(
|
||||
value = item.name,
|
||||
onValueChange = { viewModel.updateName(it) },
|
||||
label = { Text(stringResource(R.string.item_name)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = item.description ?: "",
|
||||
onValueChange = { viewModel.updateDescription(it) },
|
||||
label = { Text(stringResource(R.string.item_description)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = item.quantity.toString(),
|
||||
onValueChange = { viewModel.updateQuantity(it.toIntOrNull() ?: 0) },
|
||||
label = { Text(stringResource(R.string.item_quantity)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
// Add more fields as needed
|
||||
// [AI_NOTE]: General Information section for basic item details.
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
elevation = CardDefaults.cardElevation(4.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.item_general_information),
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
OutlinedTextField(
|
||||
value = item.name,
|
||||
onValueChange = { viewModel.updateName(it) },
|
||||
label = { Text(stringResource(R.string.item_name)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = item.description ?: "",
|
||||
onValueChange = { viewModel.updateDescription(it) },
|
||||
label = { Text(stringResource(R.string.item_description)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = item.quantity.toString(),
|
||||
onValueChange = { viewModel.updateQuantity(it.toIntOrNull() ?: 0) },
|
||||
label = { Text(stringResource(R.string.item_quantity)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
// [AI_NOTE]: Location selection will require a separate component or screen.
|
||||
OutlinedTextField(
|
||||
value = item.location?.name ?: "",
|
||||
onValueChange = { /* TODO: Implement location selection */ },
|
||||
label = { Text(stringResource(R.string.item_location)) },
|
||||
readOnly = true,
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { /* TODO: Implement location selection */ }) {
|
||||
Icon(Icons.Filled.ArrowDropDown, contentDescription = stringResource(R.string.select_location))
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
// [AI_NOTE]: Label selection will require a separate component or screen.
|
||||
OutlinedTextField(
|
||||
value = item.labels.joinToString { it.name },
|
||||
onValueChange = { /* TODO: Implement label selection */ },
|
||||
label = { Text(stringResource(R.string.item_labels)) },
|
||||
readOnly = true,
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { /* TODO: Implement label selection */ }) {
|
||||
Icon(Icons.Filled.ArrowDropDown, contentDescription = stringResource(R.string.select_labels))
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// [AI_NOTE]: Purchase Information section.
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
elevation = CardDefaults.cardElevation(4.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.item_purchase_information),
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
OutlinedTextField(
|
||||
value = item.purchasePrice?.toString() ?: "",
|
||||
onValueChange = { viewModel.updatePurchasePrice(it.toBigDecimalOrNull()) },
|
||||
label = { Text(stringResource(R.string.item_purchase_price)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = item.purchaseFrom ?: "",
|
||||
onValueChange = { viewModel.updatePurchaseFrom(it) },
|
||||
label = { Text(stringResource(R.string.item_purchase_from)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
// [AI_NOTE]: Date picker for purchase time.
|
||||
var showPurchaseDatePicker by remember { mutableStateOf(false) }
|
||||
val purchaseDateState = rememberDatePickerState()
|
||||
OutlinedTextField(
|
||||
value = item.purchaseTime ?: "",
|
||||
onValueChange = { }, // Read-only, handled by date picker
|
||||
label = { Text(stringResource(R.string.item_purchase_time)) },
|
||||
readOnly = true,
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { showPurchaseDatePicker = true }) {
|
||||
Icon(Icons.Filled.DateRange, contentDescription = stringResource(R.string.select_date))
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { showPurchaseDatePicker = true }
|
||||
)
|
||||
if (showPurchaseDatePicker) {
|
||||
DatePickerDialog(
|
||||
onDismissRequest = { showPurchaseDatePicker = false },
|
||||
confirmButton = {
|
||||
Text(stringResource(R.string.ok), modifier = Modifier.clickable {
|
||||
val selectedDate = purchaseDateState.selectedDateMillis?.let {
|
||||
SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date(it))
|
||||
}
|
||||
if (selectedDate != null) {
|
||||
viewModel.updatePurchaseTime(selectedDate)
|
||||
}
|
||||
showPurchaseDatePicker = false
|
||||
})
|
||||
},
|
||||
dismissButton = {
|
||||
Text(stringResource(R.string.cancel), modifier = Modifier.clickable { showPurchaseDatePicker = false })
|
||||
}
|
||||
) {
|
||||
DatePicker(state = purchaseDateState)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// [AI_NOTE]: Warranty Information section.
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
elevation = CardDefaults.cardElevation(4.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.item_warranty_information),
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(stringResource(R.string.item_lifetime_warranty))
|
||||
Switch(
|
||||
checked = item.lifetimeWarranty,
|
||||
onCheckedChange = { viewModel.updateLifetimeWarranty(it) }
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = item.warrantyDetails ?: "",
|
||||
onValueChange = { viewModel.updateWarrantyDetails(it) },
|
||||
label = { Text(stringResource(R.string.item_warranty_details)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
// [AI_NOTE]: Date picker for warranty expiration.
|
||||
var showWarrantyDatePicker by remember { mutableStateOf(false) }
|
||||
val warrantyDateState = rememberDatePickerState()
|
||||
OutlinedTextField(
|
||||
value = item.warrantyExpires ?: "",
|
||||
onValueChange = { }, // Read-only, handled by date picker
|
||||
label = { Text(stringResource(R.string.item_warranty_expires)) },
|
||||
readOnly = true,
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { showWarrantyDatePicker = true }) {
|
||||
Icon(Icons.Filled.DateRange, contentDescription = stringResource(R.string.select_date))
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { showWarrantyDatePicker = true }
|
||||
)
|
||||
if (showWarrantyDatePicker) {
|
||||
DatePickerDialog(
|
||||
onDismissRequest = { showWarrantyDatePicker = false },
|
||||
confirmButton = {
|
||||
Text(stringResource(R.string.ok), modifier = Modifier.clickable {
|
||||
val selectedDate = warrantyDateState.selectedDateMillis?.let {
|
||||
SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date(it))
|
||||
}
|
||||
if (selectedDate != null) {
|
||||
viewModel.updateWarrantyExpires(selectedDate)
|
||||
}
|
||||
showWarrantyDatePicker = false
|
||||
})
|
||||
},
|
||||
dismissButton = {
|
||||
Text(stringResource(R.string.cancel), modifier = Modifier.clickable { showWarrantyDatePicker = false })
|
||||
}
|
||||
) {
|
||||
DatePicker(state = warrantyDateState)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// [AI_NOTE]: Identification section.
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
elevation = CardDefaults.cardElevation(4.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.item_identification),
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
OutlinedTextField(
|
||||
value = item.assetId ?: "",
|
||||
onValueChange = { viewModel.updateAssetId(it) },
|
||||
label = { Text(stringResource(R.string.item_asset_id)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = item.serialNumber ?: "",
|
||||
onValueChange = { viewModel.updateSerialNumber(it) },
|
||||
label = { Text(stringResource(R.string.item_serial_number)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = item.manufacturer ?: "",
|
||||
onValueChange = { viewModel.updateManufacturer(it) },
|
||||
label = { Text(stringResource(R.string.item_manufacturer)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = item.modelNumber ?: "",
|
||||
onValueChange = { viewModel.updateModelNumber(it) },
|
||||
label = { Text(stringResource(R.string.item_model_number)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// [AI_NOTE]: Status & Notes section.
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
elevation = CardDefaults.cardElevation(4.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.item_status_notes),
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(stringResource(R.string.item_archived))
|
||||
Switch(
|
||||
checked = item.archived,
|
||||
onCheckedChange = { viewModel.updateArchived(it) }
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(stringResource(R.string.item_insured))
|
||||
Switch(
|
||||
checked = item.insured,
|
||||
onCheckedChange = { viewModel.updateInsured(it) }
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = item.notes ?: "",
|
||||
onValueChange = { viewModel.updateNotes(it) },
|
||||
label = { Text(stringResource(R.string.item_notes)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// [AI_NOTE]: Sold Information section (conditionally displayed).
|
||||
if (item.soldTime != null || item.soldPrice != null || item.soldTo != null || item.soldNotes != null) {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
elevation = CardDefaults.cardElevation(4.dp)
|
||||
) {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Text(
|
||||
text = stringResource(R.string.item_sold_information),
|
||||
style = MaterialTheme.typography.headlineSmall
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
OutlinedTextField(
|
||||
value = item.soldPrice?.toString() ?: "",
|
||||
onValueChange = { viewModel.updateSoldPrice(it.toBigDecimalOrNull()) },
|
||||
label = { Text(stringResource(R.string.item_sold_price)) },
|
||||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = item.soldTo ?: "",
|
||||
onValueChange = { viewModel.updateSoldTo(it) },
|
||||
label = { Text(stringResource(R.string.item_sold_to)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
OutlinedTextField(
|
||||
value = item.soldNotes ?: "",
|
||||
onValueChange = { viewModel.updateSoldNotes(it) },
|
||||
label = { Text(stringResource(R.string.item_sold_notes)) },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
// [AI_NOTE]: Date picker for sold time.
|
||||
var showSoldDatePicker by remember { mutableStateOf(false) }
|
||||
val soldDateState = rememberDatePickerState()
|
||||
OutlinedTextField(
|
||||
value = item.soldTime ?: "",
|
||||
onValueChange = { }, // Read-only, handled by date picker
|
||||
label = { Text(stringResource(R.string.item_sold_time)) },
|
||||
readOnly = true,
|
||||
trailingIcon = {
|
||||
IconButton(onClick = { showSoldDatePicker = true }) {
|
||||
Icon(Icons.Filled.DateRange, contentDescription = stringResource(R.string.select_date))
|
||||
}
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { showSoldDatePicker = true }
|
||||
)
|
||||
if (showSoldDatePicker) {
|
||||
DatePickerDialog(
|
||||
onDismissRequest = { showSoldDatePicker = false },
|
||||
confirmButton = {
|
||||
Text(stringResource(R.string.ok), modifier = Modifier.clickable {
|
||||
val selectedDate = soldDateState.selectedDateMillis?.let {
|
||||
SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date(it))
|
||||
}
|
||||
if (selectedDate != null) {
|
||||
viewModel.updateSoldTime(selectedDate)
|
||||
}
|
||||
showSoldDatePicker = false
|
||||
})
|
||||
},
|
||||
dismissButton = {
|
||||
Text(stringResource(R.string.cancel), modifier = Modifier.clickable { showSoldDatePicker = false })
|
||||
}
|
||||
) {
|
||||
DatePicker(state = soldDateState)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('ItemEditScreen')]
|
||||
// [END_ENTITY: Composable('ItemEditScreen')]
|
||||
// [END_FILE_ItemEditScreen.kt]
|
||||
|
||||
@@ -7,8 +7,10 @@ package com.homebox.lens.ui.screen.itemedit
|
||||
// [IMPORTS]
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.homebox.lens.data.db.entity.toDomainItem
|
||||
import com.homebox.lens.domain.model.Item
|
||||
import com.homebox.lens.domain.model.ItemCreate
|
||||
import com.homebox.lens.domain.model.ItemUpdate
|
||||
import com.homebox.lens.domain.model.Label
|
||||
import com.homebox.lens.domain.model.Location
|
||||
import com.homebox.lens.domain.usecase.CreateItemUseCase
|
||||
@@ -23,6 +25,7 @@ import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
// [END_IMPORTS]
|
||||
|
||||
@@ -73,24 +76,45 @@ class ItemEditViewModel @Inject constructor(
|
||||
_uiState.value = _uiState.value.copy(isLoading = true, error = null)
|
||||
if (itemId == null) {
|
||||
Timber.i("[INFO][ACTION][new_item_preparation] Preparing for new item creation.")
|
||||
_uiState.value = _uiState.value.copy(isLoading = false, item = Item(id = "", name = "", description = null, quantity = 0, image = null, location = null, labels = emptyList(), value = null, createdAt = null))
|
||||
_uiState.value = _uiState.value.copy(
|
||||
isLoading = false,
|
||||
item = Item(
|
||||
id = "",
|
||||
name = "",
|
||||
description = null,
|
||||
quantity = 1,
|
||||
image = null,
|
||||
location = null,
|
||||
labels = emptyList(),
|
||||
purchasePrice = null,
|
||||
createdAt = null,
|
||||
archived = false,
|
||||
assetId = null,
|
||||
fields = emptyList(),
|
||||
insured = false,
|
||||
lifetimeWarranty = false,
|
||||
manufacturer = null,
|
||||
modelNumber = null,
|
||||
notes = null,
|
||||
parentId = null,
|
||||
purchaseFrom = null,
|
||||
purchaseTime = null,
|
||||
serialNumber = null,
|
||||
soldNotes = null,
|
||||
soldPrice = null,
|
||||
soldTime = null,
|
||||
soldTo = null,
|
||||
syncChildItemsLocations = false,
|
||||
warrantyDetails = null,
|
||||
warrantyExpires = null
|
||||
)
|
||||
)
|
||||
} else {
|
||||
try {
|
||||
Timber.i("[INFO][ACTION][fetching_item_details] Fetching details for item ID: %s", itemId)
|
||||
val itemOut = getItemDetailsUseCase(itemId)
|
||||
Timber.d("[DEBUG][ACTION][mapping_item_out_to_item] Mapping ItemOut to Item for UI state.")
|
||||
val item = Item(
|
||||
id = itemOut.id,
|
||||
name = itemOut.name,
|
||||
description = itemOut.description,
|
||||
quantity = itemOut.quantity,
|
||||
image = itemOut.images.firstOrNull()?.path, // Assuming first image is the main one
|
||||
location = itemOut.location?.let { Location(it.id, it.name) }, // Simplified mapping
|
||||
labels = itemOut.labels.map { Label(it.id, it.name) }, // Simplified mapping
|
||||
value = itemOut.value?.toBigDecimal(),
|
||||
createdAt = itemOut.createdAt
|
||||
)
|
||||
_uiState.value = _uiState.value.copy(isLoading = false, item = item)
|
||||
_uiState.value = _uiState.value.copy(isLoading = false, item = itemOut.toDomainItem())
|
||||
Timber.i("[INFO][ACTION][item_details_fetched] Successfully fetched item details for ID: %s", itemId)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "[ERROR][FALLBACK][item_load_failed] Failed to load item details for ID: %s", itemId)
|
||||
@@ -117,53 +141,72 @@ class ItemEditViewModel @Inject constructor(
|
||||
try {
|
||||
if (currentItem.id.isBlank()) {
|
||||
Timber.i("[INFO][ACTION][creating_new_item] Creating new item: %s", currentItem.name)
|
||||
val createdItemSummary = createItemUseCase(ItemCreate(
|
||||
name = currentItem.name,
|
||||
description = currentItem.description,
|
||||
quantity = currentItem.quantity,
|
||||
assetId = null, // Item does not have assetId
|
||||
notes = null, // Item does not have notes
|
||||
serialNumber = null, // Item does not have serialNumber
|
||||
value = currentItem.value?.toDouble(), // Convert BigDecimal to Double
|
||||
purchasePrice = null, // Item does not have purchasePrice
|
||||
purchaseDate = null, // Item does not have purchaseDate
|
||||
warrantyUntil = null, // Item does not have warrantyUntil
|
||||
locationId = currentItem.location?.id,
|
||||
parentId = null, // Item does not have parentId
|
||||
labelIds = currentItem.labels.map { it.id }
|
||||
))
|
||||
Timber.d("[DEBUG][ACTION][mapping_item_summary_to_item] Mapping ItemSummary to Item for UI state.")
|
||||
val createdItem = Item(
|
||||
id = createdItemSummary.id,
|
||||
name = createdItemSummary.name,
|
||||
description = null, // ItemSummary does not have description
|
||||
quantity = 0, // ItemSummary does not have quantity
|
||||
image = null, // ItemSummary does not have image
|
||||
location = null, // ItemSummary does not have location
|
||||
labels = emptyList(), // ItemSummary does not have labels
|
||||
value = null, // ItemSummary does not have value
|
||||
createdAt = null // ItemSummary does not have createdAt
|
||||
val createdItemOut = createItemUseCase(
|
||||
ItemCreate(
|
||||
name = currentItem.name,
|
||||
description = currentItem.description,
|
||||
quantity = currentItem.quantity,
|
||||
archived = currentItem.archived,
|
||||
assetId = currentItem.assetId,
|
||||
insured = currentItem.insured,
|
||||
lifetimeWarranty = currentItem.lifetimeWarranty,
|
||||
manufacturer = currentItem.manufacturer,
|
||||
modelNumber = currentItem.modelNumber,
|
||||
notes = currentItem.notes,
|
||||
parentId = currentItem.parentId,
|
||||
purchaseFrom = currentItem.purchaseFrom,
|
||||
purchasePrice = currentItem.purchasePrice,
|
||||
purchaseTime = currentItem.purchaseTime,
|
||||
serialNumber = currentItem.serialNumber,
|
||||
soldNotes = currentItem.soldNotes,
|
||||
soldPrice = currentItem.soldPrice,
|
||||
soldTime = currentItem.soldTime,
|
||||
soldTo = currentItem.soldTo,
|
||||
syncChildItemsLocations = currentItem.syncChildItemsLocations,
|
||||
warrantyDetails = currentItem.warrantyDetails,
|
||||
warrantyExpires = currentItem.warrantyExpires,
|
||||
locationId = currentItem.location?.id,
|
||||
labelIds = currentItem.labels.map { it.id }
|
||||
)
|
||||
)
|
||||
_uiState.value = _uiState.value.copy(isLoading = false, item = createdItem)
|
||||
Timber.i("[INFO][ACTION][new_item_created] Successfully created new item with ID: %s", createdItem.id)
|
||||
Timber.d("[DEBUG][ACTION][mapping_item_out_to_item] Mapping ItemOut to Item for UI state.")
|
||||
_uiState.value = _uiState.value.copy(isLoading = false, item = createdItemOut.toDomainItem())
|
||||
Timber.i("[INFO][ACTION][new_item_created] Successfully created new item with ID: %s", createdItemOut.id)
|
||||
_saveCompleted.emit(Unit)
|
||||
} else {
|
||||
Timber.i("[INFO][ACTION][updating_existing_item] Updating existing item with ID: %s", currentItem.id)
|
||||
val updatedItemOut = updateItemUseCase(currentItem)
|
||||
Timber.d("[DEBUG][ACTION][mapping_item_out_to_item] Mapping ItemOut to Item for UI state.")
|
||||
val updatedItem = Item(
|
||||
id = updatedItemOut.id,
|
||||
name = updatedItemOut.name,
|
||||
description = updatedItemOut.description,
|
||||
quantity = updatedItemOut.quantity,
|
||||
image = updatedItemOut.images.firstOrNull()?.path,
|
||||
location = updatedItemOut.location?.let { Location(it.id, it.name) },
|
||||
labels = updatedItemOut.labels.map { Label(it.id, it.name) },
|
||||
value = updatedItemOut.value.toBigDecimal(),
|
||||
createdAt = updatedItemOut.createdAt
|
||||
val updatedItemOut = updateItemUseCase(
|
||||
ItemUpdate(
|
||||
id = currentItem.id,
|
||||
name = currentItem.name,
|
||||
description = currentItem.description,
|
||||
quantity = currentItem.quantity,
|
||||
archived = currentItem.archived,
|
||||
assetId = currentItem.assetId,
|
||||
insured = currentItem.insured,
|
||||
lifetimeWarranty = currentItem.lifetimeWarranty,
|
||||
manufacturer = currentItem.manufacturer,
|
||||
modelNumber = currentItem.modelNumber,
|
||||
notes = currentItem.notes,
|
||||
parentId = currentItem.parentId,
|
||||
purchaseFrom = currentItem.purchaseFrom,
|
||||
purchasePrice = currentItem.purchasePrice,
|
||||
purchaseTime = currentItem.purchaseTime,
|
||||
serialNumber = currentItem.serialNumber,
|
||||
soldNotes = currentItem.soldNotes,
|
||||
soldPrice = currentItem.soldPrice,
|
||||
soldTime = currentItem.soldTime,
|
||||
soldTo = currentItem.soldTo,
|
||||
syncChildItemsLocations = currentItem.syncChildItemsLocations,
|
||||
warrantyDetails = currentItem.warrantyDetails,
|
||||
warrantyExpires = currentItem.warrantyExpires,
|
||||
locationId = currentItem.location?.id,
|
||||
labelIds = currentItem.labels.map { it.id }
|
||||
)
|
||||
)
|
||||
_uiState.value = _uiState.value.copy(isLoading = false, item = updatedItem)
|
||||
Timber.i("[INFO][ACTION][item_updated] Successfully updated item with ID: %s", updatedItem.id)
|
||||
Timber.d("[DEBUG][ACTION][mapping_item_out_to_item] Mapping ItemOut to Item for UI state.")
|
||||
_uiState.value = _uiState.value.copy(isLoading = false, item = updatedItemOut.toDomainItem())
|
||||
Timber.i("[INFO][ACTION][item_updated] Successfully updated item with ID: %s", updatedItemOut.id)
|
||||
_saveCompleted.emit(Unit)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
@@ -209,6 +252,234 @@ class ItemEditViewModel @Inject constructor(
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(quantity = newQuantity))
|
||||
}
|
||||
// [END_ENTITY: Function('updateQuantity')]
|
||||
}
|
||||
// [END_ENTITY: ViewModel('ItemEditViewModel')]
|
||||
// [END_FILE_ItemEditViewModel.kt]
|
||||
|
||||
// [ENTITY: Function('updateArchived')]
|
||||
/**
|
||||
* @summary Updates the archived status of the item in the UI state.
|
||||
* @param newArchived The new archived status for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateArchived(newArchived: Boolean) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_archived] Updating item archived status to: %s", newArchived)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(archived = newArchived))
|
||||
}
|
||||
// [END_ENTITY: Function('updateArchived')]
|
||||
|
||||
// [ENTITY: Function('updateAssetId')]
|
||||
/**
|
||||
* @summary Updates the asset ID of the item in the UI state.
|
||||
* @param newAssetId The new asset ID for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateAssetId(newAssetId: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_assetId] Updating item asset ID to: %s", newAssetId)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(assetId = newAssetId))
|
||||
}
|
||||
// [END_ENTITY: Function('updateAssetId')]
|
||||
|
||||
// [ENTITY: Function('updateInsured')]
|
||||
/**
|
||||
* @summary Updates the insured status of the item in the UI state.
|
||||
* @param newInsured The new insured status for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateInsured(newInsured: Boolean) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_insured] Updating item insured status to: %s", newInsured)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(insured = newInsured))
|
||||
}
|
||||
// [END_ENTITY: Function('updateInsured')]
|
||||
|
||||
// [ENTITY: Function('updateLifetimeWarranty')]
|
||||
/**
|
||||
* @summary Updates the lifetime warranty status of the item in the UI state.
|
||||
* @param newLifetimeWarranty The new lifetime warranty status for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateLifetimeWarranty(newLifetimeWarranty: Boolean) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_lifetime_warranty] Updating item lifetime warranty status to: %s", newLifetimeWarranty)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(lifetimeWarranty = newLifetimeWarranty))
|
||||
}
|
||||
// [END_ENTITY: Function('updateLifetimeWarranty')]
|
||||
|
||||
// [ENTITY: Function('updateManufacturer')]
|
||||
/**
|
||||
* @summary Updates the manufacturer of the item in the UI state.
|
||||
* @param newManufacturer The new manufacturer for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateManufacturer(newManufacturer: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_manufacturer] Updating item manufacturer to: %s", newManufacturer)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(manufacturer = newManufacturer))
|
||||
}
|
||||
// [END_ENTITY: Function('updateManufacturer')]
|
||||
|
||||
// [ENTITY: Function('updateModelNumber')]
|
||||
/**
|
||||
* @summary Updates the model number of the item in the UI state.
|
||||
* @param newModelNumber The new model number for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateModelNumber(newModelNumber: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_model_number] Updating item model number to: %s", newModelNumber)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(modelNumber = newModelNumber))
|
||||
}
|
||||
// [END_ENTITY: Function('updateModelNumber')]
|
||||
|
||||
// [ENTITY: Function('updateNotes')]
|
||||
/**
|
||||
* @summary Updates the notes of the item in the UI state.
|
||||
* @param newNotes The new notes for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateNotes(newNotes: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_notes] Updating item notes to: %s", newNotes)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(notes = newNotes))
|
||||
}
|
||||
// [END_ENTITY: Function('updateNotes')]
|
||||
|
||||
// [ENTITY: Function('updateParentId')]
|
||||
/**
|
||||
* @summary Updates the parent ID of the item in the UI state.
|
||||
* @param newParentId The new parent ID for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateParentId(newParentId: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_parent_id] Updating item parent ID to: %s", newParentId)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(parentId = newParentId))
|
||||
}
|
||||
// [END_ENTITY: Function('updateParentId')]
|
||||
|
||||
// [ENTITY: Function('updatePurchaseFrom')]
|
||||
/**
|
||||
* @summary Updates the purchase source of the item in the UI state.
|
||||
* @param newPurchaseFrom The new purchase source for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updatePurchaseFrom(newPurchaseFrom: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_purchase_from] Updating item purchase from to: %s", newPurchaseFrom)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(purchaseFrom = newPurchaseFrom))
|
||||
}
|
||||
// [END_ENTITY: Function('updatePurchaseFrom')]
|
||||
|
||||
// [ENTITY: Function('updatePurchasePrice')]
|
||||
/**
|
||||
* @summary Updates the purchase price of the item in the UI state.
|
||||
* @param newPurchasePrice The new purchase price for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updatePurchasePrice(newPurchasePrice: BigDecimal?) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_purchase_price] Updating item purchase price to: %s", newPurchasePrice)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(purchasePrice = newPurchasePrice))
|
||||
}
|
||||
// [END_ENTITY: Function('updatePurchasePrice')]
|
||||
|
||||
// [ENTITY: Function('updatePurchaseTime')]
|
||||
/**
|
||||
* @summary Updates the purchase time of the item in the UI state.
|
||||
* @param newPurchaseTime The new purchase time for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updatePurchaseTime(newPurchaseTime: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_purchase_time] Updating item purchase time to: %s", newPurchaseTime)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(purchaseTime = newPurchaseTime))
|
||||
}
|
||||
// [END_ENTITY: Function('updatePurchaseTime')]
|
||||
|
||||
// [ENTITY: Function('updateSerialNumber')]
|
||||
/**
|
||||
* @summary Updates the serial number of the item in the UI state.
|
||||
* @param newSerialNumber The new serial number for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateSerialNumber(newSerialNumber: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_serial_number] Updating item serial number to: %s", newSerialNumber)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(serialNumber = newSerialNumber))
|
||||
}
|
||||
// [END_ENTITY: Function('updateSerialNumber')]
|
||||
|
||||
// [ENTITY: Function('updateSoldNotes')]
|
||||
/**
|
||||
* @summary Updates the sold notes of the item in the UI state.
|
||||
* @param newSoldNotes The new sold notes for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateSoldNotes(newSoldNotes: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_sold_notes] Updating item sold notes to: %s", newSoldNotes)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(soldNotes = newSoldNotes))
|
||||
}
|
||||
// [END_ENTITY: Function('updateSoldNotes')]
|
||||
|
||||
// [ENTITY: Function('updateSoldPrice')]
|
||||
/**
|
||||
* @summary Updates the sold price of the item in the UI state.
|
||||
* @param newSoldPrice The new sold price for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateSoldPrice(newSoldPrice: BigDecimal?) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_sold_price] Updating item sold price to: %s", newSoldPrice)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(soldPrice = newSoldPrice))
|
||||
}
|
||||
// [END_ENTITY: Function('updateSoldPrice')]
|
||||
|
||||
// [ENTITY: Function('updateSoldTime')]
|
||||
/**
|
||||
* @summary Updates the sold time of the item in the UI state.
|
||||
* @param newSoldTime The new sold time for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateSoldTime(newSoldTime: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_sold_time] Updating item sold time to: %s", newSoldTime)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(soldTime = newSoldTime))
|
||||
}
|
||||
// [END_ENTITY: Function('updateSoldTime')]
|
||||
|
||||
// [ENTITY: Function('updateSoldTo')]
|
||||
/**
|
||||
* @summary Updates the sold to field of the item in the UI state.
|
||||
* @param newSoldTo The new sold to for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateSoldTo(newSoldTo: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_sold_to] Updating item sold to to: %s", newSoldTo)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(soldTo = newSoldTo))
|
||||
}
|
||||
// [END_ENTITY: Function('updateSoldTo')]
|
||||
|
||||
// [ENTITY: Function('updateSyncChildItemsLocations')]
|
||||
/**
|
||||
* @summary Updates the sync child items locations status of the item in the UI state.
|
||||
* @param newSyncChildItemsLocations The new sync child items locations status for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateSyncChildItemsLocations(newSyncChildItemsLocations: Boolean) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_sync_child_items_locations] Updating item sync child items locations status to: %s", newSyncChildItemsLocations)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(syncChildItemsLocations = newSyncChildItemsLocations))
|
||||
}
|
||||
// [END_ENTITY: Function('updateSyncChildItemsLocations')]
|
||||
|
||||
// [ENTITY: Function('updateWarrantyDetails')]
|
||||
/**
|
||||
* @summary Updates the warranty details of the item in the UI state.
|
||||
* @param newWarrantyDetails The new warranty details for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateWarrantyDetails(newWarrantyDetails: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_warranty_details] Updating item warranty details to: %s", newWarrantyDetails)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(warrantyDetails = newWarrantyDetails))
|
||||
}
|
||||
// [END_ENTITY: Function('updateWarrantyDetails')]
|
||||
|
||||
// [ENTITY: Function('updateWarrantyExpires')]
|
||||
/**
|
||||
* @summary Updates the warranty expires date of the item in the UI state.
|
||||
* @param newWarrantyExpires The new warranty expires date for the item.
|
||||
* @sideeffect Updates the `item` in `_uiState`.
|
||||
*/
|
||||
fun updateWarrantyExpires(newWarrantyExpires: String) {
|
||||
Timber.d("[DEBUG][ACTION][updating_item_warranty_expires] Updating item warranty expires date to: %s", newWarrantyExpires)
|
||||
_uiState.value = _uiState.value.copy(item = _uiState.value.item?.copy(warrantyExpires = newWarrantyExpires))
|
||||
}
|
||||
// [END_ENTITY: Function('updateWarrantyExpires')]
|
||||
}
|
||||
// [END_ENTITY: ViewModel('ItemEditViewModel')]
|
||||
// [END_FILE_ItemEditViewModel.kt]
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
plugins {
|
||||
// [PLUGIN] Android Application plugin
|
||||
id("com.android.application") version "8.12.2" apply false
|
||||
id("com.android.application") version "8.13.0" apply false
|
||||
// [PLUGIN] Kotlin Android plugin
|
||||
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
|
||||
// [PLUGIN] Hilt Android plugin
|
||||
|
||||
@@ -17,17 +17,28 @@ import com.homebox.lens.domain.model.ItemCreate
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ItemCreateDto(
|
||||
@Json(name = "name") val name: String,
|
||||
@Json(name = "assetId") val assetId: String?,
|
||||
@Json(name = "description") val description: String?,
|
||||
@Json(name = "notes") val notes: String?,
|
||||
@Json(name = "serialNumber") val serialNumber: String?,
|
||||
@Json(name = "quantity") val quantity: Int?,
|
||||
@Json(name = "value") val value: Double?,
|
||||
@Json(name = "purchasePrice") val purchasePrice: Double?,
|
||||
@Json(name = "purchaseDate") val purchaseDate: String?,
|
||||
@Json(name = "warrantyUntil") val warrantyUntil: String?,
|
||||
@Json(name = "locationId") val locationId: String?,
|
||||
@Json(name = "archived") val archived: Boolean?,
|
||||
@Json(name = "assetId") val assetId: String?,
|
||||
@Json(name = "insured") val insured: Boolean?,
|
||||
@Json(name = "lifetimeWarranty") val lifetimeWarranty: Boolean?,
|
||||
@Json(name = "manufacturer") val manufacturer: String?,
|
||||
@Json(name = "modelNumber") val modelNumber: String?,
|
||||
@Json(name = "notes") val notes: String?,
|
||||
@Json(name = "parentId") val parentId: String?,
|
||||
@Json(name = "purchaseFrom") val purchaseFrom: String?,
|
||||
@Json(name = "purchasePrice") val purchasePrice: Double?,
|
||||
@Json(name = "purchaseTime") val purchaseTime: String?,
|
||||
@Json(name = "serialNumber") val serialNumber: String?,
|
||||
@Json(name = "soldNotes") val soldNotes: String?,
|
||||
@Json(name = "soldPrice") val soldPrice: Double?,
|
||||
@Json(name = "soldTime") val soldTime: String?,
|
||||
@Json(name = "soldTo") val soldTo: String?,
|
||||
@Json(name = "syncChildItemsLocations") val syncChildItemsLocations: Boolean?,
|
||||
@Json(name = "warrantyDetails") val warrantyDetails: String?,
|
||||
@Json(name = "warrantyExpires") val warrantyExpires: String?,
|
||||
@Json(name = "locationId") val locationId: String?,
|
||||
@Json(name = "labelIds") val labelIds: List<String>?
|
||||
)
|
||||
// [END_ENTITY: DataClass('ItemCreateDto')]
|
||||
@@ -40,17 +51,28 @@ data class ItemCreateDto(
|
||||
fun ItemCreate.toDto(): ItemCreateDto {
|
||||
return ItemCreateDto(
|
||||
name = this.name,
|
||||
assetId = this.assetId,
|
||||
description = this.description,
|
||||
notes = this.notes,
|
||||
serialNumber = this.serialNumber,
|
||||
quantity = this.quantity,
|
||||
value = this.value,
|
||||
purchasePrice = this.purchasePrice,
|
||||
purchaseDate = this.purchaseDate,
|
||||
warrantyUntil = this.warrantyUntil,
|
||||
locationId = this.locationId,
|
||||
archived = this.archived,
|
||||
assetId = this.assetId,
|
||||
insured = this.insured,
|
||||
lifetimeWarranty = this.lifetimeWarranty,
|
||||
manufacturer = this.manufacturer,
|
||||
modelNumber = this.modelNumber,
|
||||
notes = this.notes,
|
||||
parentId = this.parentId,
|
||||
purchaseFrom = this.purchaseFrom,
|
||||
purchasePrice = this.purchasePrice,
|
||||
purchaseTime = this.purchaseTime,
|
||||
serialNumber = this.serialNumber,
|
||||
soldNotes = this.soldNotes,
|
||||
soldPrice = this.soldPrice,
|
||||
soldTime = this.soldTime,
|
||||
soldTo = this.soldTo,
|
||||
syncChildItemsLocations = this.syncChildItemsLocations,
|
||||
warrantyDetails = this.warrantyDetails,
|
||||
warrantyExpires = this.warrantyExpires,
|
||||
locationId = this.locationId,
|
||||
labelIds = this.labelIds
|
||||
)
|
||||
}
|
||||
|
||||
@@ -24,10 +24,20 @@ data class ItemOutDto(
|
||||
@Json(name = "serialNumber") val serialNumber: String?,
|
||||
@Json(name = "quantity") val quantity: Int,
|
||||
@Json(name = "isArchived") val isArchived: Boolean,
|
||||
@Json(name = "value") val value: Double,
|
||||
@Json(name = "purchasePrice") val purchasePrice: Double?,
|
||||
@Json(name = "purchaseDate") val purchaseDate: String?,
|
||||
@Json(name = "warrantyUntil") val warrantyUntil: String?,
|
||||
@Json(name = "purchaseTime") val purchaseTime: String?,
|
||||
@Json(name = "purchaseFrom") val purchaseFrom: String?,
|
||||
@Json(name = "warrantyExpires") val warrantyExpires: String?,
|
||||
@Json(name = "warrantyDetails") val warrantyDetails: String?,
|
||||
@Json(name = "lifetimeWarranty") val lifetimeWarranty: Boolean?,
|
||||
@Json(name = "insured") val insured: Boolean?,
|
||||
@Json(name = "manufacturer") val manufacturer: String?,
|
||||
@Json(name = "modelNumber") val modelNumber: String?,
|
||||
@Json(name = "soldPrice") val soldPrice: Double?,
|
||||
@Json(name = "soldTime") val soldTime: String?,
|
||||
@Json(name = "soldTo") val soldTo: String?,
|
||||
@Json(name = "soldNotes") val soldNotes: String?,
|
||||
@Json(name = "syncChildItemsLocations") val syncChildItemsLocations: Boolean?,
|
||||
@Json(name = "location") val location: LocationOutDto?,
|
||||
@Json(name = "parent") val parent: ItemSummaryDto?,
|
||||
@Json(name = "children") val children: List<ItemSummaryDto>,
|
||||
@@ -56,10 +66,20 @@ fun ItemOutDto.toDomain(): ItemOut {
|
||||
serialNumber = this.serialNumber,
|
||||
quantity = this.quantity,
|
||||
isArchived = this.isArchived,
|
||||
value = this.value,
|
||||
purchasePrice = this.purchasePrice,
|
||||
purchaseDate = this.purchaseDate,
|
||||
warrantyUntil = this.warrantyUntil,
|
||||
purchaseTime = this.purchaseTime,
|
||||
purchaseFrom = this.purchaseFrom,
|
||||
warrantyExpires = this.warrantyExpires,
|
||||
warrantyDetails = this.warrantyDetails,
|
||||
lifetimeWarranty = this.lifetimeWarranty,
|
||||
insured = this.insured,
|
||||
manufacturer = this.manufacturer,
|
||||
modelNumber = this.modelNumber,
|
||||
soldPrice = this.soldPrice,
|
||||
soldTime = this.soldTime,
|
||||
soldTo = this.soldTo,
|
||||
soldNotes = this.soldNotes,
|
||||
syncChildItemsLocations = this.syncChildItemsLocations,
|
||||
location = this.location?.toDomain(),
|
||||
parent = this.parent?.toDomain(),
|
||||
children = this.children.map { it.toDomain() },
|
||||
|
||||
@@ -17,18 +17,28 @@ import com.homebox.lens.domain.model.ItemUpdate
|
||||
@JsonClass(generateAdapter = true)
|
||||
data class ItemUpdateDto(
|
||||
@Json(name = "name") val name: String?,
|
||||
@Json(name = "assetId") val assetId: String?,
|
||||
@Json(name = "description") val description: String?,
|
||||
@Json(name = "notes") val notes: String?,
|
||||
@Json(name = "serialNumber") val serialNumber: String?,
|
||||
@Json(name = "quantity") val quantity: Int?,
|
||||
@Json(name = "isArchived") val isArchived: Boolean?,
|
||||
@Json(name = "value") val value: Double?,
|
||||
@Json(name = "purchasePrice") val purchasePrice: Double?,
|
||||
@Json(name = "purchaseDate") val purchaseDate: String?,
|
||||
@Json(name = "warrantyUntil") val warrantyUntil: String?,
|
||||
@Json(name = "locationId") val locationId: String?,
|
||||
@Json(name = "archived") val archived: Boolean?,
|
||||
@Json(name = "assetId") val assetId: String?,
|
||||
@Json(name = "insured") val insured: Boolean?,
|
||||
@Json(name = "lifetimeWarranty") val lifetimeWarranty: Boolean?,
|
||||
@Json(name = "manufacturer") val manufacturer: String?,
|
||||
@Json(name = "modelNumber") val modelNumber: String?,
|
||||
@Json(name = "notes") val notes: String?,
|
||||
@Json(name = "parentId") val parentId: String?,
|
||||
@Json(name = "purchaseFrom") val purchaseFrom: String?,
|
||||
@Json(name = "purchasePrice") val purchasePrice: Double?,
|
||||
@Json(name = "purchaseTime") val purchaseTime: String?,
|
||||
@Json(name = "serialNumber") val serialNumber: String?,
|
||||
@Json(name = "soldNotes") val soldNotes: String?,
|
||||
@Json(name = "soldPrice") val soldPrice: Double?,
|
||||
@Json(name = "soldTime") val soldTime: String?,
|
||||
@Json(name = "soldTo") val soldTo: String?,
|
||||
@Json(name = "syncChildItemsLocations") val syncChildItemsLocations: Boolean?,
|
||||
@Json(name = "warrantyDetails") val warrantyDetails: String?,
|
||||
@Json(name = "warrantyExpires") val warrantyExpires: String?,
|
||||
@Json(name = "locationId") val locationId: String?,
|
||||
@Json(name = "labelIds") val labelIds: List<String>?
|
||||
)
|
||||
// [END_ENTITY: DataClass('ItemUpdateDto')]
|
||||
@@ -41,18 +51,28 @@ data class ItemUpdateDto(
|
||||
fun ItemUpdate.toDto(): ItemUpdateDto {
|
||||
return ItemUpdateDto(
|
||||
name = this.name,
|
||||
assetId = this.assetId,
|
||||
description = this.description,
|
||||
notes = this.notes,
|
||||
serialNumber = this.serialNumber,
|
||||
quantity = this.quantity,
|
||||
isArchived = this.isArchived,
|
||||
value = this.value,
|
||||
purchasePrice = this.purchasePrice,
|
||||
purchaseDate = this.purchaseDate,
|
||||
warrantyUntil = this.warrantyUntil,
|
||||
locationId = this.locationId,
|
||||
archived = this.archived,
|
||||
assetId = this.assetId,
|
||||
insured = this.insured,
|
||||
lifetimeWarranty = this.lifetimeWarranty,
|
||||
manufacturer = this.manufacturer,
|
||||
modelNumber = this.modelNumber,
|
||||
notes = this.notes,
|
||||
parentId = this.parentId,
|
||||
purchaseFrom = this.purchaseFrom,
|
||||
purchasePrice = this.purchasePrice,
|
||||
purchaseTime = this.purchaseTime,
|
||||
serialNumber = this.serialNumber,
|
||||
soldNotes = this.soldNotes,
|
||||
soldPrice = this.soldPrice,
|
||||
soldTime = this.soldTime,
|
||||
soldTo = this.soldTo,
|
||||
syncChildItemsLocations = this.syncChildItemsLocations,
|
||||
warrantyDetails = this.warrantyDetails,
|
||||
warrantyExpires = this.warrantyExpires,
|
||||
locationId = this.locationId,
|
||||
labelIds = this.labelIds
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,10 +18,29 @@ data class ItemEntity(
|
||||
@PrimaryKey val id: String,
|
||||
val name: String,
|
||||
val description: String?,
|
||||
val quantity: Int,
|
||||
val image: String?,
|
||||
val locationId: String?,
|
||||
val value: BigDecimal?,
|
||||
val createdAt: String?
|
||||
val purchasePrice: BigDecimal?,
|
||||
val createdAt: String?,
|
||||
val archived: Boolean,
|
||||
val assetId: String?,
|
||||
val insured: Boolean,
|
||||
val lifetimeWarranty: Boolean,
|
||||
val manufacturer: String?,
|
||||
val modelNumber: String?,
|
||||
val notes: String?,
|
||||
val parentId: String?,
|
||||
val purchaseFrom: String?,
|
||||
val purchaseTime: String?,
|
||||
val serialNumber: String?,
|
||||
val soldNotes: String?,
|
||||
val soldPrice: BigDecimal?,
|
||||
val soldTime: String?,
|
||||
val soldTo: String?,
|
||||
val syncChildItemsLocations: Boolean,
|
||||
val warrantyDetails: String?,
|
||||
val warrantyExpires: String?
|
||||
)
|
||||
// [END_ENTITY: DatabaseTable('ItemEntity')]
|
||||
|
||||
|
||||
@@ -4,39 +4,236 @@
|
||||
package com.homebox.lens.data.db.entity
|
||||
|
||||
// [IMPORTS]
|
||||
import com.homebox.lens.data.api.dto.CustomFieldDto
|
||||
import com.homebox.lens.data.api.dto.ItemCreateDto
|
||||
import com.homebox.lens.data.api.dto.ItemOutDto
|
||||
import com.homebox.lens.data.api.dto.ItemUpdateDto
|
||||
import com.homebox.lens.domain.model.CustomField
|
||||
import com.homebox.lens.domain.model.Image
|
||||
import com.homebox.lens.domain.model.Item
|
||||
import com.homebox.lens.domain.model.ItemCreate
|
||||
import com.homebox.lens.domain.model.ItemOut
|
||||
import com.homebox.lens.domain.model.ItemSummary
|
||||
import com.homebox.lens.domain.model.ItemUpdate
|
||||
import com.homebox.lens.domain.model.Label
|
||||
import com.homebox.lens.domain.model.LabelOut
|
||||
import com.homebox.lens.domain.model.Location
|
||||
import com.homebox.lens.domain.model.LocationOut
|
||||
import java.math.BigDecimal
|
||||
// [END_IMPORTS]
|
||||
|
||||
// [ENTITY: Function('toDomain')]
|
||||
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('ItemSummary')]
|
||||
// [ENTITY: Function('ItemWithLabels.toDomainItemSummary')]
|
||||
// [RELATION: Function('ItemWithLabels.toDomainItemSummary')] -> [RETURNS] -> [DataClass('ItemSummary')]
|
||||
/**
|
||||
* @summary Преобразует [ItemWithLabels] (сущность БД) в [ItemSummary] (доменную модель).
|
||||
*/
|
||||
fun ItemWithLabels.toDomain(): ItemSummary {
|
||||
fun ItemWithLabels.toDomainItemSummary(): ItemSummary {
|
||||
return ItemSummary(
|
||||
id = this.item.id,
|
||||
name = this.item.name,
|
||||
image = this.item.image?.let { Image(id = "", path = it, isPrimary = true) },
|
||||
location = this.item.locationId?.let { LocationOut(id = it, name = "", color = "", isArchived = false, createdAt = "", updatedAt = "") },
|
||||
labels = this.labels.map { it.toDomain() },
|
||||
assetId = null,
|
||||
isArchived = false,
|
||||
value = this.item.value?.toDouble() ?: 0.0,
|
||||
labels = this.labels.map { it.toDomainLabelOut() },
|
||||
assetId = this.item.assetId,
|
||||
isArchived = this.item.archived,
|
||||
value = this.item.purchasePrice?.toDouble() ?: 0.0, // Assuming value maps to purchasePrice
|
||||
createdAt = this.item.createdAt ?: "",
|
||||
updatedAt = ""
|
||||
updatedAt = "" // ItemEntity does not have updatedAt
|
||||
)
|
||||
}
|
||||
// [END_ENTITY: Function('toDomain')]
|
||||
// [END_ENTITY: Function('ItemWithLabels.toDomainItemSummary')]
|
||||
|
||||
// [ENTITY: Function('toDomain')]
|
||||
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('LabelOut')]
|
||||
// [ENTITY: Function('ItemEntity.toDomainItem')]
|
||||
// [RELATION: Function('ItemEntity.toDomainItem')] -> [RETURNS] -> [DataClass('Item')]
|
||||
/**
|
||||
* @summary Преобразует [ItemEntity] (сущность БД) в [Item] (доменную модель).
|
||||
*/
|
||||
fun ItemEntity.toDomainItem(): Item {
|
||||
return Item(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
description = this.description,
|
||||
quantity = this.quantity,
|
||||
image = this.image,
|
||||
location = this.locationId?.let { Location(it, "") }, // Simplified, name is not in ItemEntity
|
||||
labels = emptyList(), // Labels are handled via ItemWithLabels
|
||||
purchasePrice = this.purchasePrice,
|
||||
createdAt = this.createdAt,
|
||||
archived = this.archived,
|
||||
assetId = this.assetId,
|
||||
fields = emptyList(), // Custom fields are not stored in ItemEntity
|
||||
insured = this.insured,
|
||||
lifetimeWarranty = this.lifetimeWarranty,
|
||||
manufacturer = this.manufacturer,
|
||||
modelNumber = this.modelNumber,
|
||||
notes = this.notes,
|
||||
parentId = this.parentId,
|
||||
purchaseFrom = this.purchaseFrom,
|
||||
purchaseTime = this.purchaseTime,
|
||||
serialNumber = this.serialNumber,
|
||||
soldNotes = this.soldNotes,
|
||||
soldPrice = this.soldPrice,
|
||||
soldTime = this.soldTime,
|
||||
soldTo = this.soldTo,
|
||||
syncChildItemsLocations = this.syncChildItemsLocations,
|
||||
warrantyDetails = this.warrantyDetails,
|
||||
warrantyExpires = this.warrantyExpires
|
||||
)
|
||||
}
|
||||
// [END_ENTITY: Function('ItemEntity.toDomainItem')]
|
||||
|
||||
// [ENTITY: Function('Item.toItemEntity')]
|
||||
// [RELATION: Function('Item.toItemEntity')] -> [RETURNS] -> [DataClass('ItemEntity')]
|
||||
/**
|
||||
* @summary Преобразует [Item] (доменную модель) в [ItemEntity] (сущность БД).
|
||||
*/
|
||||
fun Item.toItemEntity(): ItemEntity {
|
||||
return ItemEntity(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
description = this.description,
|
||||
quantity = this.quantity,
|
||||
image = this.image,
|
||||
locationId = this.location?.id,
|
||||
purchasePrice = this.purchasePrice,
|
||||
createdAt = this.createdAt,
|
||||
archived = this.archived,
|
||||
assetId = this.assetId,
|
||||
insured = this.insured,
|
||||
lifetimeWarranty = this.lifetimeWarranty,
|
||||
manufacturer = this.manufacturer,
|
||||
modelNumber = this.modelNumber,
|
||||
notes = this.notes,
|
||||
parentId = this.parentId,
|
||||
purchaseFrom = this.purchaseFrom,
|
||||
purchaseTime = this.purchaseTime,
|
||||
serialNumber = this.serialNumber,
|
||||
soldNotes = this.soldNotes,
|
||||
soldPrice = this.soldPrice,
|
||||
soldTime = this.soldTime,
|
||||
soldTo = this.soldTo,
|
||||
syncChildItemsLocations = this.syncChildItemsLocations,
|
||||
warrantyDetails = this.warrantyDetails,
|
||||
warrantyExpires = this.warrantyExpires
|
||||
)
|
||||
}
|
||||
// [END_ENTITY: Function('Item.toItemEntity')]
|
||||
|
||||
// [ENTITY: Function('ItemOutDto.toDomainItem')]
|
||||
// [RELATION: Function('ItemOutDto.toDomainItem')] -> [RETURNS] -> [DataClass('Item')]
|
||||
/**
|
||||
* @summary Маппер из ItemOutDto в доменную модель Item.
|
||||
*/
|
||||
fun ItemOutDto.toDomainItem(): Item {
|
||||
return Item(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
description = this.description,
|
||||
quantity = this.quantity,
|
||||
image = this.images.firstOrNull()?.path,
|
||||
location = this.location?.toDomain(),
|
||||
labels = this.labels.map { Label(it.id, it.name) },
|
||||
purchasePrice = this.purchasePrice?.toBigDecimal(),
|
||||
createdAt = this.createdAt,
|
||||
archived = this.isArchived,
|
||||
assetId = this.assetId,
|
||||
fields = this.fields.map { it.toDomainCustomField() },
|
||||
insured = this.insured ?: false,
|
||||
lifetimeWarranty = this.lifetimeWarranty ?: false,
|
||||
manufacturer = this.manufacturer,
|
||||
modelNumber = this.modelNumber,
|
||||
notes = this.notes,
|
||||
parentId = this.parent?.id,
|
||||
purchaseFrom = this.purchaseFrom,
|
||||
purchaseTime = this.purchaseTime,
|
||||
serialNumber = this.serialNumber,
|
||||
soldNotes = this.soldNotes,
|
||||
soldPrice = this.soldPrice?.toBigDecimal(),
|
||||
soldTime = this.soldTime,
|
||||
soldTo = this.soldTo,
|
||||
syncChildItemsLocations = this.syncChildItemsLocations ?: false,
|
||||
warrantyDetails = this.warrantyDetails,
|
||||
warrantyExpires = this.warrantyExpires
|
||||
)
|
||||
}
|
||||
// [END_ENTITY: Function('ItemOutDto.toDomainItem')]
|
||||
|
||||
// [ENTITY: Function('ItemCreate.toItemCreateDto')]
|
||||
// [RELATION: Function('ItemCreate.toItemCreateDto')] -> [RETURNS] -> [DataClass('ItemCreateDto')]
|
||||
/**
|
||||
* @summary Маппер из доменной модели ItemCreate в ItemCreateDto.
|
||||
*/
|
||||
fun ItemCreate.toItemCreateDto(): ItemCreateDto {
|
||||
return ItemCreateDto(
|
||||
name = this.name,
|
||||
description = this.description,
|
||||
quantity = this.quantity,
|
||||
archived = null, // Not applicable for creation
|
||||
assetId = this.assetId,
|
||||
insured = null, // Not applicable for creation
|
||||
lifetimeWarranty = null, // Not applicable for creation
|
||||
manufacturer = this.manufacturer,
|
||||
modelNumber = this.modelNumber,
|
||||
notes = this.notes,
|
||||
parentId = this.parentId,
|
||||
purchaseFrom = this.purchaseFrom,
|
||||
purchasePrice = this.purchasePrice?.toDouble(),
|
||||
purchaseTime = this.purchaseTime,
|
||||
serialNumber = this.serialNumber,
|
||||
soldNotes = null, // Not applicable for creation
|
||||
soldPrice = null, // Not applicable for creation
|
||||
soldTime = null, // Not applicable for creation
|
||||
soldTo = null, // Not applicable for creation
|
||||
syncChildItemsLocations = null, // Not applicable for creation
|
||||
warrantyDetails = this.warrantyDetails,
|
||||
warrantyExpires = this.warrantyExpires,
|
||||
locationId = this.locationId,
|
||||
labelIds = this.labelIds
|
||||
)
|
||||
}
|
||||
// [END_ENTITY: Function('ItemCreate.toItemCreateDto')]
|
||||
|
||||
// [ENTITY: Function('ItemUpdate.toItemUpdateDto')]
|
||||
// [RELATION: Function('ItemUpdate.toItemUpdateDto')] -> [RETURNS] -> [DataClass('ItemUpdateDto')]
|
||||
/**
|
||||
* @summary Маппер из доменной модели ItemUpdate в ItemUpdateDto.
|
||||
*/
|
||||
fun ItemUpdate.toItemUpdateDto(): ItemUpdateDto {
|
||||
return ItemUpdateDto(
|
||||
name = this.name,
|
||||
description = this.description,
|
||||
quantity = this.quantity,
|
||||
archived = this.archived,
|
||||
assetId = this.assetId,
|
||||
insured = this.insured,
|
||||
lifetimeWarranty = this.lifetimeWarranty,
|
||||
manufacturer = this.manufacturer,
|
||||
modelNumber = this.modelNumber,
|
||||
notes = this.notes,
|
||||
parentId = this.parentId,
|
||||
purchaseFrom = this.purchaseFrom,
|
||||
purchasePrice = this.purchasePrice?.toDouble(),
|
||||
purchaseTime = this.purchaseTime,
|
||||
serialNumber = this.serialNumber,
|
||||
soldNotes = this.soldNotes,
|
||||
soldPrice = this.soldPrice?.toDouble(),
|
||||
soldTime = this.soldTime,
|
||||
soldTo = this.soldTo,
|
||||
syncChildItemsLocations = this.syncChildItemsLocations,
|
||||
warrantyDetails = this.warrantyDetails,
|
||||
warrantyExpires = this.warrantyExpires,
|
||||
locationId = this.locationId,
|
||||
labelIds = this.labelIds
|
||||
)
|
||||
}
|
||||
// [END_ENTITY: Function('ItemUpdate.toItemUpdateDto')]
|
||||
|
||||
// [ENTITY: Function('LabelEntity.toDomainLabelOut')]
|
||||
// [RELATION: Function('LabelEntity.toDomainLabelOut')] -> [RETURNS] -> [DataClass('LabelOut')]
|
||||
/**
|
||||
* @summary Преобразует [LabelEntity] (сущность БД) в [LabelOut] (доменную модель).
|
||||
*/
|
||||
fun LabelEntity.toDomain(): LabelOut {
|
||||
fun LabelEntity.toDomainLabelOut(): LabelOut {
|
||||
return LabelOut(
|
||||
id = this.id,
|
||||
name = this.name,
|
||||
@@ -46,4 +243,17 @@ fun LabelEntity.toDomain(): LabelOut {
|
||||
updatedAt = ""
|
||||
)
|
||||
}
|
||||
// [END_ENTITY: Function('toDomain')]
|
||||
// [END_ENTITY: Function('LabelEntity.toDomainLabelOut')]
|
||||
|
||||
// [ENTITY: Function('CustomFieldDto.toDomainCustomField')]
|
||||
// [RELATION: Function('CustomFieldDto.toDomainCustomField')] -> [RETURNS] -> [DataClass('CustomField')]
|
||||
/**
|
||||
* @summary Преобразует [CustomFieldDto] (DTO API) в [CustomField] (доменную модель).
|
||||
*/
|
||||
fun CustomFieldDto.toDomainCustomField(): CustomField {
|
||||
return CustomField(
|
||||
name = this.name,
|
||||
value = this.value
|
||||
)
|
||||
}
|
||||
// [END_ENTITY: Function('CustomFieldDto.toDomainCustomField')]
|
||||
@@ -5,6 +5,8 @@ package com.homebox.lens.domain.model
|
||||
|
||||
// [IMPORTS]
|
||||
import java.math.BigDecimal
|
||||
import com.homebox.lens.domain.model.CustomField
|
||||
import com.homebox.lens.domain.model.Image
|
||||
// [END_IMPORTS]
|
||||
|
||||
// [ENTITY: DataClass('Item')]
|
||||
@@ -18,8 +20,27 @@ import java.math.BigDecimal
|
||||
* @param image Url изображения.
|
||||
* @param location Местоположение вещи.
|
||||
* @param labels Список меток, присвоенных вещи.
|
||||
* @param value Стоимость вещи.
|
||||
* @param purchasePrice Цена покупки вещи.
|
||||
* @param createdAt Дата создания.
|
||||
* @param archived Архивирована ли вещь.
|
||||
* @param assetId Идентификатор актива.
|
||||
* @param fields Пользовательские поля.
|
||||
* @param insured Застрахована ли вещь.
|
||||
* @param lifetimeWarranty Пожизненная гарантия.
|
||||
* @param manufacturer Производитель.
|
||||
* @param modelNumber Номер модели.
|
||||
* @param notes Дополнительные заметки.
|
||||
* @param parentId ID родительского элемента.
|
||||
* @param purchaseFrom Место покупки.
|
||||
* @param purchaseTime Время покупки.
|
||||
* @param serialNumber Серийный номер.
|
||||
* @param soldNotes Заметки о продаже.
|
||||
* @param soldPrice Цена продажи.
|
||||
* @param soldTime Время продажи.
|
||||
* @param soldTo Кому продано.
|
||||
* @param syncChildItemsLocations Синхронизировать местоположения дочерних элементов.
|
||||
* @param warrantyDetails Детали гарантии.
|
||||
* @param warrantyExpires Дата окончания гарантии.
|
||||
*/
|
||||
data class Item(
|
||||
val id: String,
|
||||
@@ -29,8 +50,27 @@ data class Item(
|
||||
val image: String?,
|
||||
val location: Location?,
|
||||
val labels: List<Label>,
|
||||
val value: BigDecimal?,
|
||||
val createdAt: String?
|
||||
val purchasePrice: BigDecimal?,
|
||||
val createdAt: String?,
|
||||
val archived: Boolean = false,
|
||||
val assetId: String? = null,
|
||||
val fields: List<CustomField> = emptyList(),
|
||||
val insured: Boolean = false,
|
||||
val lifetimeWarranty: Boolean = false,
|
||||
val manufacturer: String? = null,
|
||||
val modelNumber: String? = null,
|
||||
val notes: String? = null,
|
||||
val parentId: String? = null,
|
||||
val purchaseFrom: String? = null,
|
||||
val purchaseTime: String? = null,
|
||||
val serialNumber: String? = null,
|
||||
val soldNotes: String? = null,
|
||||
val soldPrice: BigDecimal? = null,
|
||||
val soldTime: String? = null,
|
||||
val soldTo: String? = null,
|
||||
val syncChildItemsLocations: Boolean = false,
|
||||
val warrantyDetails: String? = null,
|
||||
val warrantyExpires: String? = null
|
||||
)
|
||||
// [END_ENTITY: DataClass('Item')]
|
||||
|
||||
|
||||
@@ -18,7 +18,6 @@ distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
org.gradle.java.home=/snap/android-studio/197/jbr
|
||||
|
||||
android.useAndroidX=true
|
||||
|
||||
|
||||
98
tasks/work_order_item_edit_screen.xml
Normal file
98
tasks/work_order_item_edit_screen.xml
Normal file
@@ -0,0 +1,98 @@
|
||||
<![CDATA[
|
||||
<WORK_ORDER>
|
||||
<META>
|
||||
<VERSION>1.0</VERSION>
|
||||
<CREATED_AT>{timestamp}</CREATED_AT>
|
||||
<TITLE>[ARCHITECT -> DEV] Refactor Item Edit Screen</TITLE>
|
||||
<DESCRIPTION>
|
||||
This work order instructs the developer agent to refactor the Item Edit screen to include all available API fields and implement a user-friendly, grouped layout.
|
||||
</DESCRIPTION>
|
||||
<ASSIGNED_TO>agent-developer</ASSIGNED_TO>
|
||||
<LABELS>type::refactoring,feature::item-edit,status::pending</LABELS>
|
||||
</META>
|
||||
|
||||
<SPECIFICATION>
|
||||
<GOAL>
|
||||
The primary goal is to refactor the `ItemEditScreen` to include all available fields from the Homebox API for creating and updating items, and to present them in a user-friendly manner.
|
||||
</GOAL>
|
||||
|
||||
<CONTEXT>
|
||||
<FILE_LIST>
|
||||
<FILE path="domain/src/main/java/com/homebox/lens/domain/model/Item.kt" />
|
||||
<FILE path="data/src/main/java/com/homebox/lens/data/db/entity/ItemEntity.kt" />
|
||||
<FILE path="data/src/main/java/com/homebox/lens/data/api/dto/ItemCreateDto.kt" />
|
||||
<FILE path="data/src/main/java/com/homebox/lens/data/api/dto/ItemUpdateDto.kt" />
|
||||
<FILE path="data/src/main/java/com/homebox/lens/data/api/dto/ItemOutDto.kt" />
|
||||
<FILE path="data/src/main/java/com/homebox/lens/data/db/entity/Mapper.kt" />
|
||||
<FILE path="app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditViewModel.kt" />
|
||||
<FILE path="app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditScreen.kt" />
|
||||
</FILE_LIST>
|
||||
</CONTEXT>
|
||||
</SPECIFICATION>
|
||||
|
||||
<EXECUTION_PLAN>
|
||||
<STEP id="1" name="Update Domain Model">
|
||||
<ACTION>Modify `domain/src/main/java/com/homebox/lens/domain/model/Item.kt`.</ACTION>
|
||||
<DETAILS>
|
||||
- Add the following fields to the `Item` data class:
|
||||
- `archived: Boolean`
|
||||
- `assetId: String?`
|
||||
- `fields: List<CustomField>`
|
||||
- `insured: Boolean`
|
||||
- `lifetimeWarranty: Boolean`
|
||||
- `manufacturer: String?`
|
||||
- `modelNumber: String?`
|
||||
- `notes: String?`
|
||||
- `parentId: String?`
|
||||
- `purchaseFrom: String?`
|
||||
- `purchaseTime: String?`
|
||||
- `serialNumber: String?`
|
||||
- `soldNotes: String?`
|
||||
- `soldPrice: Double?`
|
||||
- `soldTime: String?`
|
||||
- `soldTo: String?`
|
||||
- `syncChildItemsLocations: Boolean`
|
||||
- `warrantyDetails: String?`
|
||||
- `warrantyExpires: String?`
|
||||
- Rename the existing `value: BigDecimal?` field to `purchasePrice: Double?`.
|
||||
</DETAILS>
|
||||
</STEP>
|
||||
|
||||
<STEP id="2" name="Update Data Layer">
|
||||
<ACTION>Update database entity, DTOs, and mappers.</ACTION>
|
||||
<DETAILS>
|
||||
- Modify `data/src/main/java/com/homebox/lens/data/db/entity/ItemEntity.kt` to reflect the new `Item` model.
|
||||
- Ensure `ItemCreateDto`, `ItemUpdateDto`, and `ItemOutDto` contain all necessary fields according to the API spec.
|
||||
- Update the mapping functions in `data/src/main/java/com/homebox/lens/data/db/entity/Mapper.kt` to handle all new fields correctly across all layers.
|
||||
</DETAILS>
|
||||
</STEP>
|
||||
|
||||
<STEP id="3" name="Update ViewModel">
|
||||
<ACTION>Modify `app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditViewModel.kt`.</ACTION>
|
||||
<DETAILS>
|
||||
- Update `loadItem` and `saveItem` to handle the state for all new fields.
|
||||
- Add public functions to update the state for each new field (e.g., `updateManufacturer(String)`, `toggleInsured(Boolean)`).
|
||||
</DETAILS>
|
||||
</STEP>
|
||||
|
||||
<STEP id="4" name="Implement UI">
|
||||
<ACTION>Refactor `app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditScreen.kt`.</ACTION>
|
||||
<DETAILS>
|
||||
- Implement a user-friendly layout using tabs or expandable cards for the following groups: General, Purchase, Warranty, Identification, Status & Notes.
|
||||
- Use appropriate controls for each field type (e.g., `Switch` for booleans, Date Picker for dates).
|
||||
- Connect all UI controls to the ViewModel.
|
||||
- Implement basic input validation.
|
||||
</DETAILS>
|
||||
</STEP>
|
||||
</EXECUTION_PLAN>
|
||||
|
||||
<VERIFICATION>
|
||||
<CHECK>The application compiles successfully.</CHECK>
|
||||
<CHECK>The Item Edit screen displays all new fields, grouped logically.</CHECK>
|
||||
<CHECK>Creating a new item with all fields populated works correctly.</CHECK>
|
||||
<CHECK>Editing an existing item and modifying the new fields works correctly.</CHECK>
|
||||
<CHECK>Data is persisted correctly and is visible in the Item Details screen after saving.</CHECK>
|
||||
</VERIFICATION>
|
||||
|
||||
</WORK_ORDER>
|
||||
]]>
|
||||
Reference in New Issue
Block a user