- Add missing quantity field to Item model - Add missing string resources and translations - Fix unresolved references in UI screens
1532 lines
79 KiB
XML
1532 lines
79 KiB
XML
<FOR_AGENT>
|
||
<!-- tasks/20250817_1119_implement_ui_screens.xml -->
|
||
<TASK_BATCH status="completed">
|
||
<WORK_ORDER id="WO-20250817-1118-01">
|
||
<ACTION>CREATE_OR_UPDATE_FILE</ACTION>
|
||
<TARGET_FILE>app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt</TARGET_FILE>
|
||
<PAYLOAD>
|
||
<CODE lang="kotlin">
|
||
<![CDATA[
|
||
// [PACKAGE] com.homebox.lens.ui.screen.labelslist
|
||
// [FILE] LabelsListScreen.kt
|
||
// [SEMANTICS] ui, screen, labels, list, compose
|
||
package com.homebox.lens.ui.screen.labelslist
|
||
|
||
// [SECTION] IMPORTS
|
||
import androidx.compose.foundation.clickable
|
||
import androidx.compose.foundation.layout.Box
|
||
import androidx.compose.foundation.layout.padding
|
||
import androidx.compose.foundation.lazy.LazyColumn
|
||
import androidx.compose.foundation.lazy.items
|
||
import androidx.compose.material.icons.Icons
|
||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||
import androidx.compose.material.icons.automirrored.filled.Label
|
||
import androidx.compose.material.icons.filled.Add
|
||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||
import androidx.compose.material3.FloatingActionButton
|
||
import androidx.compose.material3.Icon
|
||
import androidx.compose.material3.IconButton
|
||
import androidx.compose.material3.ListItem
|
||
import androidx.compose.material3.Scaffold
|
||
import androidx.compose.material3.Text
|
||
import androidx.compose.material3.TopAppBar
|
||
import androidx.compose.runtime.Composable
|
||
import androidx.compose.runtime.collectAsState
|
||
import androidx.compose.runtime.getValue
|
||
import androidx.compose.ui.Modifier
|
||
import androidx.compose.ui.res.stringResource
|
||
import androidx.hilt.navigation.compose.hiltViewModel
|
||
import com.homebox.lens.R
|
||
import com.homebox.lens.domain.model.Label
|
||
import timber.log.Timber
|
||
|
||
// [ENTITY: Class('LabelsListScreen')]
|
||
// [RELATION: Class('LabelsListScreen')] -> [DEPENDS_ON] -> [Class('LabelsListViewModel')]
|
||
// [RELATION: Class('LabelsListScreen')] -> [CREATES_INSTANCE_OF] -> [Class('Scaffold')]
|
||
|
||
/**
|
||
* [MAIN-CONTRACT]
|
||
* Экран для отображения списка всех меток.
|
||
*
|
||
* Этот Composable является точкой входа для UI, определенного в спецификации `screen_labels_list`.
|
||
* Он получает состояние от [LabelsListViewModel] и отображает его, делегируя обработку
|
||
* пользовательских событий в ViewModel.
|
||
*
|
||
* @param onLabelClick Функция обратного вызова для обработки нажатия на метку.
|
||
* @param onNavigateBack Функция обратного вызова для навигации назад.
|
||
*/
|
||
@OptIn(ExperimentalMaterial3Api::class)
|
||
@Composable
|
||
@ENTRYPOINT
|
||
fun LabelsListScreen(
|
||
viewModel: LabelsListViewModel = hiltViewModel(),
|
||
onLabelClick: (Label) -> Unit,
|
||
onNavigateBack: () -> Unit
|
||
) {
|
||
// [STATE]
|
||
val labels by viewModel.labels.collectAsState()
|
||
|
||
// [ACTION]
|
||
Scaffold(
|
||
topBar = {
|
||
TopAppBar(
|
||
title = { Text(stringResource(id = R.string.screen_title_labels)) },
|
||
navigationIcon = {
|
||
IconButton(onClick = onNavigateBack) {
|
||
Icon(
|
||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||
contentDescription = stringResource(id = R.string.content_desc_navigate_back)
|
||
)
|
||
}
|
||
}
|
||
)
|
||
},
|
||
floatingActionButton = {
|
||
FloatingActionButton(onClick = { /* TODO: Implement create new label dialog/screen */ }) {
|
||
Icon(
|
||
imageVector = Icons.Filled.Add,
|
||
contentDescription = stringResource(id = R.string.content_desc_add_label)
|
||
)
|
||
}
|
||
}
|
||
) { innerPadding ->
|
||
// [DELEGATES]
|
||
Box(modifier = Modifier.padding(innerPadding)) {
|
||
LabelsListContent(
|
||
labels = labels,
|
||
onLabelClick = onLabelClick
|
||
)
|
||
}
|
||
}
|
||
}
|
||
|
||
// [ENTITY: Function('LabelsListContent')]
|
||
// [RELATION: Function('LabelsListContent')] -> [CALLS] -> [Function('LabelListItem')]
|
||
|
||
/**
|
||
* [CONTRACT]
|
||
* Отображает основной контент экрана: список меток.
|
||
*
|
||
* @param labels Список меток для отображения.
|
||
* @param onLabelClick Обработчик нажатия на элемент списка.
|
||
* @sideeffect Отсутствуют.
|
||
*/
|
||
@Composable
|
||
@HELPER
|
||
private fun LabelsListContent(
|
||
labels: List<Label>,
|
||
onLabelClick: (Label) -> Unit
|
||
) {
|
||
// [PRECONDITION]
|
||
// Проверка предусловий не требуется, так как пустой список является валидным состоянием.
|
||
|
||
// [CORE-LOGIC]
|
||
LazyColumn {
|
||
items(labels, key = { it.id }) { label ->
|
||
LabelListItem(
|
||
label = label,
|
||
onClick = {
|
||
Timber.i("[INFO][ACTION][ui_interaction] Label clicked: ${label.name}")
|
||
onLabelClick(label)
|
||
}
|
||
)
|
||
}
|
||
}
|
||
|
||
// [POSTCONDITION]
|
||
// Постусловия гарантируются декларативной природой Compose.
|
||
}
|
||
|
||
// [ENTITY: Function('LabelListItem')]
|
||
/**
|
||
* [CONTRACT]
|
||
* Отображает один элемент в списке меток.
|
||
*
|
||
* @param label Метка для отображения.
|
||
* @param onClick Обработчик нажатия на элемент.
|
||
* @sideeffect Отсутствуют.
|
||
*/
|
||
@Composable
|
||
@HELPER
|
||
private fun LabelListItem(
|
||
label: Label,
|
||
onClick: () -> Unit
|
||
) {
|
||
// [PRECONDITION]
|
||
require(label.name.isNotBlank()) { "Label name cannot be blank." }
|
||
|
||
// [CORE-LOGIC]
|
||
ListItem(
|
||
headlineContent = { Text(label.name) },
|
||
leadingContent = {
|
||
Icon(
|
||
imageVector = Icons.AutoMirrored.Filled.Label,
|
||
contentDescription = null // Декоративный элемент
|
||
)
|
||
},
|
||
modifier = Modifier.clickable(onClick = onClick)
|
||
)
|
||
}
|
||
]]>
|
||
</CODE>
|
||
</PAYLOAD>
|
||
</WORK_ORDER>
|
||
<WORK_ORDER id="WO-20250817-1118-02">
|
||
<ACTION>CREATE_OR_UPDATE_FILE</ACTION>
|
||
<TARGET_FILE>app/src/main/java/com/homebox/lens/ui/screen/inventorylist/InventoryListScreen.kt</TARGET_FILE>
|
||
<PAYLOAD>
|
||
<CODE lang="kotlin">
|
||
<![CDATA[
|
||
// [PACKAGE] com.homebox.lens.ui.screen.inventorylist
|
||
// [FILE] InventoryListScreen.kt
|
||
// [SEMANTICS] ui, screen, inventory, list, compose
|
||
package com.homebox.lens.ui.screen.inventorylist
|
||
|
||
// [SECTION] IMPORTS
|
||
import androidx.compose.foundation.clickable
|
||
import androidx.compose.foundation.layout.Box
|
||
import androidx.compose.foundation.layout.Column
|
||
import androidx.compose.foundation.layout.fillMaxSize
|
||
import androidx.compose.foundation.layout.fillMaxWidth
|
||
import androidx.compose.foundation.layout.padding
|
||
import androidx.compose.foundation.lazy.LazyColumn
|
||
import androidx.compose.foundation.lazy.items
|
||
import androidx.compose.material.icons.Icons
|
||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||
import androidx.compose.material.icons.filled.Refresh
|
||
import androidx.compose.material.icons.filled.Search
|
||
import androidx.compose.material3.Card
|
||
import androidx.compose.material3.CircularProgressIndicator
|
||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||
import androidx.compose.material3.FloatingActionButton
|
||
import androidx.compose.material3.Icon
|
||
import androidx.compose.material3.IconButton
|
||
import androidx.compose.material3.Scaffold
|
||
import androidx.compose.material3.Text
|
||
import androidx.compose.material3.TextField
|
||
import androidx.compose.material3.TopAppBar
|
||
import androidx.compose.runtime.Composable
|
||
import androidx.compose.runtime.collectAsState
|
||
import androidx.compose.runtime.getValue
|
||
import androidx.compose.ui.Alignment
|
||
import androidx.compose.ui.Modifier
|
||
import androidx.compose.ui.res.stringResource
|
||
import androidx.compose.ui.unit.dp
|
||
import androidx.hilt.navigation.compose.hiltViewModel
|
||
import com.homebox.lens.R
|
||
import com.homebox.lens.domain.model.Item
|
||
import timber.log.Timber
|
||
|
||
// [ENTITY: Class('InventoryListScreen')]
|
||
// [RELATION: Class('InventoryListScreen')] -> [DEPENDS_ON] -> [Class('InventoryListViewModel')]
|
||
/**
|
||
* [MAIN-CONTRACT]
|
||
* Экран для отображения списка инвентарных позиций.
|
||
*
|
||
* Реализует спецификацию `screen_inventory_list`. Позволяет просматривать,
|
||
* искать и синхронизировать инвентарь.
|
||
*
|
||
* @param onItemClick Обработчик нажатия на элемент инвентаря.
|
||
* @param onNavigateBack Обработчик для возврата на предыдущий экран.
|
||
*/
|
||
@OptIn(ExperimentalMaterial3Api::class)
|
||
@Composable
|
||
@ENTRYPOINT
|
||
fun InventoryListScreen(
|
||
viewModel: InventoryListViewModel = hiltViewModel(),
|
||
onItemClick: (Item) -> Unit,
|
||
onNavigateBack: () -> Unit
|
||
) {
|
||
// [STATE]
|
||
val uiState by viewModel.uiState.collectAsState()
|
||
|
||
// [ACTION]
|
||
Scaffold(
|
||
topBar = {
|
||
TopAppBar(
|
||
title = { Text(stringResource(id = R.string.screen_title_inventory)) },
|
||
navigationIcon = {
|
||
IconButton(onClick = onNavigateBack) {
|
||
Icon(
|
||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||
contentDescription = stringResource(id = R.string.content_desc_navigate_back)
|
||
)
|
||
}
|
||
}
|
||
)
|
||
},
|
||
floatingActionButton = {
|
||
FloatingActionButton(onClick = {
|
||
Timber.i("[INFO][ACTION][ui_interaction] Sync inventory triggered.")
|
||
viewModel.onSyncClicked()
|
||
}) {
|
||
Icon(
|
||
imageVector = Icons.Filled.Refresh,
|
||
contentDescription = stringResource(id = R.string.content_desc_sync_inventory)
|
||
)
|
||
}
|
||
}
|
||
) { innerPadding ->
|
||
// [DELEGATES]
|
||
Column(modifier = Modifier.padding(innerPadding)) {
|
||
SearchBar(
|
||
query = uiState.searchQuery,
|
||
onQueryChange = viewModel::onSearchQueryChanged
|
||
)
|
||
InventoryListContent(
|
||
isLoading = uiState.isLoading,
|
||
items = uiState.items,
|
||
onItemClick = onItemClick
|
||
)
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* [CONTRACT]
|
||
* Поле для ввода поискового запроса.
|
||
*/
|
||
@Composable
|
||
@HELPER
|
||
private fun SearchBar(query: String, onQueryChange: (String) -> Unit) {
|
||
TextField(
|
||
value = query,
|
||
onValueChange = onQueryChange,
|
||
modifier = Modifier
|
||
.fillMaxWidth()
|
||
.padding(8.dp),
|
||
placeholder = { Text(stringResource(id = R.string.placeholder_search)) },
|
||
leadingIcon = { Icon(Icons.Default.Search, contentDescription = null) }
|
||
)
|
||
}
|
||
|
||
/**
|
||
* [CONTRACT]
|
||
* Основной контент: индикатор загрузки или список предметов.
|
||
*/
|
||
@Composable
|
||
@HELPER
|
||
private fun InventoryListContent(
|
||
isLoading: Boolean,
|
||
items: List<Item>,
|
||
onItemClick: (Item) -> Unit
|
||
) {
|
||
Box(modifier = Modifier.fillMaxSize()) {
|
||
if (isLoading) {
|
||
// [STATE]
|
||
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||
} else if (items.isEmpty()) {
|
||
// [FALLBACK]
|
||
Text(
|
||
text = stringResource(id = R.string.message_no_items_found),
|
||
modifier = Modifier.align(Alignment.Center)
|
||
)
|
||
} else {
|
||
// [CORE-LOGIC]
|
||
LazyColumn {
|
||
items(items, key = { it.id }) { item ->
|
||
ItemCard(item = item, onClick = {
|
||
Timber.i("[INFO][ACTION][ui_interaction] Item clicked: ${item.name}")
|
||
onItemClick(item)
|
||
})
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* [CONTRACT]
|
||
* Карточка для отображения одного элемента инвентаря.
|
||
*/
|
||
@Composable
|
||
@HELPER
|
||
private fun ItemCard(
|
||
item: Item,
|
||
onClick: () -> Unit
|
||
) {
|
||
// [PRECONDITION]
|
||
require(item.name.isNotBlank()) { "Item name cannot be blank." }
|
||
|
||
// [CORE-LOGIC]
|
||
Card(
|
||
modifier = Modifier
|
||
.fillMaxWidth()
|
||
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||
.clickable(onClick = onClick)
|
||
) {
|
||
Column(modifier = Modifier.padding(16.dp)) {
|
||
Text(text = item.name, style = androidx.compose.material3.MaterialTheme.typography.titleMedium)
|
||
Text(text = "Quantity: ${item.quantity}", style = androidx.compose.material3.MaterialTheme.typography.bodySmall)
|
||
item.location?.let {
|
||
Text(text = "Location: ${it.name}", style = androidx.compose.material3.MaterialTheme.typography.bodySmall)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
]]>
|
||
</CODE>
|
||
</PAYLOAD>
|
||
</WORK_ORDER>
|
||
<WORK_ORDER id="WO-20250817-1118-03">
|
||
<ACTION>CREATE_OR_UPDATE_FILE</ACTION>
|
||
<TARGET_FILE>app/src/main/java/com/homebox/lens/ui/screen/itemdetails/ItemDetailsScreen.kt</TARGET_FILE>
|
||
<PAYLOAD>
|
||
<CODE lang="kotlin">
|
||
<![CDATA[
|
||
// [PACKAGE] com.homebox.lens.ui.screen.itemdetails
|
||
// [FILE] ItemDetailsScreen.kt
|
||
// [SEMANTICS] ui, screen, item, details, compose
|
||
package com.homebox.lens.ui.screen.itemdetails
|
||
|
||
// [SECTION] IMPORTS
|
||
import androidx.compose.foundation.layout.*
|
||
import androidx.compose.foundation.rememberScrollState
|
||
import androidx.compose.foundation.verticalScroll
|
||
import androidx.compose.material.icons.Icons
|
||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||
import androidx.compose.material.icons.filled.Delete
|
||
import androidx.compose.material.icons.filled.Edit
|
||
import androidx.compose.material3.*
|
||
import androidx.compose.runtime.Composable
|
||
import androidx.compose.runtime.collectAsState
|
||
import androidx.compose.runtime.getValue
|
||
import androidx.compose.ui.Alignment
|
||
import androidx.compose.ui.Modifier
|
||
import androidx.compose.ui.res.stringResource
|
||
import androidx.compose.ui.unit.dp
|
||
import androidx.hilt.navigation.compose.hiltViewModel
|
||
import com.homebox.lens.R
|
||
import com.homebox.lens.domain.model.Item
|
||
import timber.log.Timber
|
||
|
||
// [ENTITY: Class('ItemDetailsScreen')]
|
||
// [RELATION: Class('ItemDetailsScreen')] -> [DEPENDS_ON] -> [Class('ItemDetailsViewModel')]
|
||
/**
|
||
* [MAIN-CONTRACT]
|
||
* Экран для отображения детальной информации о товаре.
|
||
*
|
||
* Реализует спецификацию `screen_item_details`.
|
||
*
|
||
* @param onNavigateBack Обработчик для возврата на предыдущий экран.
|
||
* @param onEditClick Обработчик нажатия на кнопку редактирования.
|
||
*/
|
||
@OptIn(ExperimentalMaterial3Api::class)
|
||
@Composable
|
||
@ENTRYPOINT
|
||
fun ItemDetailsScreen(
|
||
viewModel: ItemDetailsViewModel = hiltViewModel(),
|
||
onNavigateBack: () -> Unit,
|
||
onEditClick: (Int) -> Unit
|
||
) {
|
||
// [STATE]
|
||
val uiState by viewModel.uiState.collectAsState()
|
||
|
||
Scaffold(
|
||
topBar = {
|
||
TopAppBar(
|
||
title = { Text(uiState.item?.name ?: stringResource(id = R.string.screen_title_item_details)) },
|
||
navigationIcon = {
|
||
IconButton(onClick = onNavigateBack) {
|
||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(id = R.string.content_desc_navigate_back))
|
||
}
|
||
},
|
||
actions = {
|
||
IconButton(onClick = {
|
||
uiState.item?.id?.let {
|
||
Timber.i("[INFO][ACTION][ui_interaction] Edit item clicked: id=$it")
|
||
onEditClick(it)
|
||
}
|
||
}) {
|
||
Icon(Icons.Default.Edit, contentDescription = stringResource(id = R.string.content_desc_edit_item))
|
||
}
|
||
IconButton(onClick = {
|
||
Timber.w("[WARN][ACTION][ui_interaction] Delete item clicked: id=${uiState.item?.id}")
|
||
viewModel.deleteItem()
|
||
// После удаления нужно навигироваться назад
|
||
onNavigateBack()
|
||
}) {
|
||
Icon(Icons.Default.Delete, contentDescription = stringResource(id = R.string.content_desc_delete_item))
|
||
}
|
||
}
|
||
)
|
||
}
|
||
) { innerPadding ->
|
||
ItemDetailsContent(
|
||
modifier = Modifier.padding(innerPadding),
|
||
isLoading = uiState.isLoading,
|
||
item = uiState.item
|
||
)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* [CONTRACT]
|
||
* Отображает контент экрана: индикатор загрузки или детали товара.
|
||
*/
|
||
@Composable
|
||
@HELPER
|
||
private fun ItemDetailsContent(
|
||
modifier: Modifier = Modifier,
|
||
isLoading: Boolean,
|
||
item: Item?
|
||
) {
|
||
Box(modifier = modifier.fillMaxSize()) {
|
||
when {
|
||
isLoading -> {
|
||
// [STATE]
|
||
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||
}
|
||
item == null -> {
|
||
// [FALLBACK]
|
||
Text(stringResource(id = R.string.message_item_not_found), modifier = Modifier.align(Alignment.Center))
|
||
}
|
||
else -> {
|
||
// [CORE-LOGIC]
|
||
Column(
|
||
modifier = Modifier
|
||
.fillMaxSize()
|
||
.verticalScroll(rememberScrollState())
|
||
.padding(16.dp),
|
||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||
) {
|
||
// TODO: ImageCarousel
|
||
// Text("Image Carousel Placeholder")
|
||
|
||
DetailsSection(title = stringResource(id = R.string.section_title_description)) {
|
||
Text(text = item.description ?: stringResource(id = R.string.placeholder_no_description))
|
||
}
|
||
|
||
DetailsSection(title = stringResource(id = R.string.section_title_details)) {
|
||
InfoRow(label = stringResource(id = R.string.label_quantity), value = item.quantity.toString())
|
||
item.location?.let {
|
||
InfoRow(label = stringResource(id = R.string.label_location), value = it.name)
|
||
}
|
||
}
|
||
|
||
if (item.labels.isNotEmpty()) {
|
||
DetailsSection(title = stringResource(id = R.string.section_title_labels)) {
|
||
// TODO: Use FlowRow for better layout
|
||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||
item.labels.forEach { label ->
|
||
AssistChip(onClick = { /* No-op */ }, label = { Text(label.name) })
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// TODO: CustomFieldsGrid
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* [CONTRACT]
|
||
* Секция с заголовком и контентом.
|
||
*/
|
||
@Composable
|
||
@HELPER
|
||
private fun DetailsSection(title: String, content: @Composable ColumnScope.() -> Unit) {
|
||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||
Text(text = title, style = MaterialTheme.typography.titleMedium)
|
||
Divider()
|
||
content()
|
||
}
|
||
}
|
||
|
||
/**
|
||
* [CONTRACT]
|
||
* Строка для отображения пары "метка: значение".
|
||
*/
|
||
@Composable
|
||
@HELPER
|
||
private fun InfoRow(label: String, value: String) {
|
||
Row {
|
||
Text(text = "$label: ", style = MaterialTheme.typography.bodyLarge)
|
||
Text(text = value, style = MaterialTheme.typography.bodyLarge)
|
||
}
|
||
}
|
||
|
||
]]>
|
||
</CODE>
|
||
</PAYLOAD>
|
||
</WORK_ORDER>
|
||
<WORK_ORDER id="WO-20250817-1118-04">
|
||
<ACTION>CREATE_OR_UPDATE_FILE</ACTION>
|
||
<TARGET_FILE>app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditScreen.kt</TARGET_FILE>
|
||
<PAYLOAD>
|
||
<CODE lang="kotlin">
|
||
<![CDATA[
|
||
// [PACKAGE] com.homebox.lens.ui.screen.itemedit
|
||
// [FILE] ItemEditScreen.kt
|
||
// [SEMANTICS] ui, screen, item, edit, create, compose
|
||
package com.homebox.lens.ui.screen.itemedit
|
||
|
||
// [SECTION] IMPORTS
|
||
import androidx.compose.foundation.layout.*
|
||
import androidx.compose.foundation.rememberScrollState
|
||
import androidx.compose.foundation.verticalScroll
|
||
import androidx.compose.material.icons.Icons
|
||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||
import androidx.compose.material.icons.filled.Done
|
||
import androidx.compose.material3.*
|
||
import androidx.compose.runtime.Composable
|
||
import androidx.compose.runtime.LaunchedEffect
|
||
import androidx.compose.runtime.collectAsState
|
||
import androidx.compose.runtime.getValue
|
||
import androidx.compose.ui.Modifier
|
||
import androidx.compose.ui.res.stringResource
|
||
import androidx.compose.ui.unit.dp
|
||
import androidx.hilt.navigation.compose.hiltViewModel
|
||
import com.homebox.lens.R
|
||
import timber.log.Timber
|
||
|
||
// [ENTITY: Class('ItemEditScreen')]
|
||
// [RELATION: Class('ItemEditScreen')] -> [DEPENDS_ON] -> [Class('ItemEditViewModel')]
|
||
|
||
/**
|
||
* [MAIN-CONTRACT]
|
||
* Экран для создания или редактирования товара.
|
||
*
|
||
* Реализует спецификацию `screen_item_edit`.
|
||
*
|
||
* @param onNavigateBack Обработчик для возврата на предыдущий экран после сохранения или отмены.
|
||
*/
|
||
@OptIn(ExperimentalMaterial3Api::class)
|
||
@Composable
|
||
@ENTRYPOINT
|
||
fun ItemEditScreen(
|
||
viewModel: ItemEditViewModel = hiltViewModel(),
|
||
onNavigateBack: () -> Unit
|
||
) {
|
||
// [STATE]
|
||
val uiState by viewModel.uiState.collectAsState()
|
||
|
||
// [SIDE-EFFECT]
|
||
LaunchedEffect(uiState.isSaved) {
|
||
if (uiState.isSaved) {
|
||
Timber.i("[INFO][SIDE_EFFECT][navigation] Item saved, navigating back.")
|
||
onNavigateBack()
|
||
}
|
||
}
|
||
|
||
Scaffold(
|
||
topBar = {
|
||
TopAppBar(
|
||
title = { Text(stringResource(id = if (uiState.isEditing) R.string.screen_title_edit_item else R.string.screen_title_create_item)) },
|
||
navigationIcon = {
|
||
IconButton(onClick = onNavigateBack) {
|
||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(id = R.string.content_desc_navigate_back))
|
||
}
|
||
},
|
||
actions = {
|
||
IconButton(onClick = {
|
||
Timber.i("[INFO][ACTION][ui_interaction] Save item clicked.")
|
||
viewModel.saveItem()
|
||
}) {
|
||
Icon(Icons.Default.Done, contentDescription = stringResource(id = R.string.content_desc_save_item))
|
||
}
|
||
}
|
||
)
|
||
}
|
||
) { innerPadding ->
|
||
ItemEditContent(
|
||
modifier = Modifier.padding(innerPadding),
|
||
state = uiState,
|
||
onNameChange = { viewModel.onNameChange(it) },
|
||
onDescriptionChange = { viewModel.onDescriptionChange(it) },
|
||
onQuantityChange = { viewModel.onQuantityChange(it) }
|
||
)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* [CONTRACT]
|
||
* Отображает форму для редактирования данных товара.
|
||
*/
|
||
@Composable
|
||
@HELPER
|
||
private fun ItemEditContent(
|
||
modifier: Modifier = Modifier,
|
||
state: ItemEditUiState,
|
||
onNameChange: (String) -> Unit,
|
||
onDescriptionChange: (String) -> Unit,
|
||
onQuantityChange: (String) -> Unit
|
||
) {
|
||
// [CORE-LOGIC]
|
||
Column(
|
||
modifier = modifier
|
||
.fillMaxSize()
|
||
.verticalScroll(rememberScrollState())
|
||
.padding(16.dp),
|
||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||
) {
|
||
OutlinedTextField(
|
||
value = state.name,
|
||
onValueChange = onNameChange,
|
||
label = { Text(stringResource(id = R.string.label_name)) },
|
||
modifier = Modifier.fillMaxWidth(),
|
||
isError = state.nameError != null
|
||
)
|
||
state.nameError?.let {
|
||
Text(text = stringResource(id = it), color = MaterialTheme.colorScheme.error)
|
||
}
|
||
|
||
OutlinedTextField(
|
||
value = state.description,
|
||
onValueChange = onDescriptionChange,
|
||
label = { Text(stringResource(id = R.string.label_description)) },
|
||
modifier = Modifier.fillMaxWidth(),
|
||
minLines = 3
|
||
)
|
||
|
||
OutlinedTextField(
|
||
value = state.quantity,
|
||
onValueChange = onQuantityChange,
|
||
label = { Text(stringResource(id = R.string.label_quantity)) },
|
||
modifier = Modifier.fillMaxWidth(),
|
||
isError = state.quantityError != null
|
||
)
|
||
state.quantityError?.let {
|
||
Text(text = stringResource(id = it), color = MaterialTheme.colorScheme.error)
|
||
}
|
||
|
||
// TODO: Location Dropdown
|
||
// TODO: Labels ChipGroup
|
||
// TODO: ImagePicker
|
||
}
|
||
}
|
||
]]>
|
||
</CODE>
|
||
</PAYLOAD>
|
||
</WORK_ORDER>
|
||
<WORK_ORDER id="WO-20250817-1118-05">
|
||
<ACTION>CREATE_OR_UPDATE_FILE</ACTION>
|
||
<TARGET_FILE>app/src/main/java/com/homebox/lens/ui/screen/search/SearchScreen.kt</TARGET_FILE>
|
||
<PAYLOAD>
|
||
<CODE lang="kotlin">
|
||
<![CDATA[
|
||
// [PACKAGE] com.homebox.lens.ui.screen.search
|
||
// [FILE] SearchScreen.kt
|
||
// [SEMANTICS] ui, screen, search, compose
|
||
package com.homebox.lens.ui.screen.search
|
||
|
||
// [SECTION] IMPORTS
|
||
import androidx.compose.foundation.layout.*
|
||
import androidx.compose.foundation.lazy.LazyColumn
|
||
import androidx.compose.foundation.lazy.items
|
||
import androidx.compose.material.icons.Icons
|
||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||
import androidx.compose.material3.*
|
||
import androidx.compose.runtime.*
|
||
import androidx.compose.ui.Alignment
|
||
import androidx.compose.ui.Modifier
|
||
import androidx.compose.ui.res.stringResource
|
||
import androidx.compose.ui.unit.dp
|
||
import androidx.hilt.navigation.compose.hiltViewModel
|
||
import com.homebox.lens.R
|
||
import com.homebox.lens.domain.model.Item
|
||
|
||
// [ENTITY: Class('SearchScreen')]
|
||
// [RELATION: Class('SearchScreen')] -> [DEPENDS_ON] -> [Class('SearchViewModel')]
|
||
/**
|
||
* [MAIN-CONTRACT]
|
||
* Специализированный экран для поиска товаров.
|
||
*
|
||
* Реализует спецификацию `screen_search`.
|
||
*
|
||
* @param onNavigateBack Обработчик для возврата на предыдущий экран.
|
||
* @param onItemClick Обработчик нажатия на найденный товар.
|
||
*/
|
||
@OptIn(ExperimentalMaterial3Api::class)
|
||
@Composable
|
||
@ENTRYPOINT
|
||
fun SearchScreen(
|
||
viewModel: SearchViewModel = hiltViewModel(),
|
||
onNavigateBack: () -> Unit,
|
||
onItemClick: (Item) -> Unit
|
||
) {
|
||
// [STATE]
|
||
val uiState by viewModel.uiState.collectAsState()
|
||
|
||
Scaffold(
|
||
topBar = {
|
||
TopAppBar(
|
||
title = {
|
||
TextField(
|
||
value = uiState.searchQuery,
|
||
onValueChange = viewModel::onSearchQueryChanged,
|
||
placeholder = { Text(stringResource(R.string.placeholder_search_items)) },
|
||
modifier = Modifier.fillMaxWidth()
|
||
)
|
||
},
|
||
navigationIcon = {
|
||
IconButton(onClick = onNavigateBack) {
|
||
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = stringResource(R.string.content_desc_navigate_back))
|
||
}
|
||
}
|
||
)
|
||
}
|
||
) { innerPadding ->
|
||
SearchContent(
|
||
modifier = Modifier.padding(innerPadding),
|
||
isLoading = uiState.isLoading,
|
||
results = uiState.results,
|
||
onItemClick = onItemClick
|
||
)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* [CONTRACT]
|
||
* Отображает основной контент экрана: фильтры и результаты поиска.
|
||
*/
|
||
@Composable
|
||
@HELPER
|
||
private fun SearchContent(
|
||
modifier: Modifier = Modifier,
|
||
isLoading: Boolean,
|
||
results: List<Item>,
|
||
onItemClick: (Item) -> Unit
|
||
) {
|
||
Column(modifier = modifier.fillMaxSize()) {
|
||
// [SECTION] FILTERS
|
||
// TODO: Implement FilterSection with chips for locations/labels
|
||
// Spacer(modifier = Modifier.height(8.dp))
|
||
|
||
// [SECTION] RESULTS
|
||
Box(modifier = Modifier.weight(1f)) {
|
||
if (isLoading) {
|
||
// [STATE]
|
||
CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))
|
||
} else {
|
||
// [CORE-LOGIC]
|
||
LazyColumn {
|
||
items(results, key = { it.id }) { item ->
|
||
ListItem(
|
||
headlineContent = { Text(item.name) },
|
||
supportingContent = { Text(item.location?.name ?: "") },
|
||
modifier = Modifier.clickable { onItemClick(item) }
|
||
)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
]]>
|
||
</CODE>
|
||
</PAYLOAD>
|
||
</WORK_ORDER>
|
||
<WORK_ORDER id="WO-20250817-1118-06">
|
||
<ACTION>CREATE_OR_UPDATE_FILE</ACTION>
|
||
<TARGET_FILE>app/src/main/java/com/homebox/lens/ui/screen/setup/SetupScreen.kt</TARGET_FILE>
|
||
<PAYLOAD>
|
||
<CODE lang="kotlin">
|
||
<![CDATA[
|
||
// [PACKAGE] com.homebox.lens.ui.screen.setup
|
||
// [FILE] SetupScreen.kt
|
||
// [SEMANTICS] ui, screen, setup, login, compose
|
||
package com.homebox.lens.ui.screen.setup
|
||
|
||
// [SECTION] IMPORTS
|
||
import androidx.compose.foundation.layout.*
|
||
import androidx.compose.foundation.text.KeyboardOptions
|
||
import androidx.compose.material3.*
|
||
import androidx.compose.runtime.*
|
||
import androidx.compose.ui.Alignment
|
||
import androidx.compose.ui.Modifier
|
||
import androidx.compose.ui.res.stringResource
|
||
import androidx.compose.ui.text.input.KeyboardType
|
||
import androidx.compose.ui.text.input.PasswordVisualTransformation
|
||
import androidx.compose.ui.unit.dp
|
||
import androidx.hilt.navigation.compose.hiltViewModel
|
||
import com.homebox.lens.R
|
||
import timber.log.Timber
|
||
|
||
// [ENTITY: Class('SetupScreen')]
|
||
// [RELATION: Class('SetupScreen')] -> [DEPENDS_ON] -> [Class('SetupViewModel')]
|
||
/**
|
||
* [MAIN-CONTRACT]
|
||
* Экран для начальной настройки соединения с сервером Homebox.
|
||
*
|
||
* @param onSetupComplete Обработчик, вызываемый после успешной настройки и входа.
|
||
*/
|
||
@Composable
|
||
@ENTRYPOINT
|
||
fun SetupScreen(
|
||
viewModel: SetupViewModel = hiltViewModel(),
|
||
onSetupComplete: () -> Unit
|
||
) {
|
||
// [STATE]
|
||
val uiState by viewModel.uiState.collectAsState()
|
||
|
||
// [SIDE-EFFECT]
|
||
LaunchedEffect(uiState.isLoginSuccessful) {
|
||
if (uiState.isLoginSuccessful) {
|
||
Timber.i("[INFO][SIDE_EFFECT][navigation] Setup complete, navigating to main screen.")
|
||
onSetupComplete()
|
||
}
|
||
}
|
||
|
||
// [CORE-LOGIC]
|
||
Box(
|
||
modifier = Modifier.fillMaxSize(),
|
||
contentAlignment = Alignment.Center
|
||
) {
|
||
Column(
|
||
modifier = Modifier
|
||
.fillMaxWidth()
|
||
.padding(32.dp),
|
||
horizontalAlignment = Alignment.CenterHorizontally,
|
||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||
) {
|
||
Text(text = stringResource(id = R.string.screen_title_setup), style = MaterialTheme.typography.headlineMedium)
|
||
|
||
OutlinedTextField(
|
||
value = uiState.serverUrl,
|
||
onValueChange = viewModel::onServerUrlChange,
|
||
label = { Text(stringResource(id = R.string.label_server_url)) },
|
||
modifier = Modifier.fillMaxWidth(),
|
||
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Uri),
|
||
isError = uiState.error != null
|
||
)
|
||
|
||
OutlinedTextField(
|
||
value = uiState.apiKey,
|
||
onValueChange = viewModel::onApiKeyChange,
|
||
label = { Text(stringResource(id = R.string.label_api_key)) },
|
||
modifier = Modifier.fillMaxWidth(),
|
||
visualTransformation = PasswordVisualTransformation(),
|
||
isError = uiState.error != null
|
||
)
|
||
|
||
if (uiState.isLoading) {
|
||
// [STATE]
|
||
CircularProgressIndicator()
|
||
} else {
|
||
// [ACTION]
|
||
Button(
|
||
onClick = {
|
||
Timber.i("[INFO][ACTION][ui_interaction] Login button clicked.")
|
||
viewModel.login()
|
||
},
|
||
modifier = Modifier.fillMaxWidth()
|
||
) {
|
||
Text(text = stringResource(id = R.string.button_connect))
|
||
}
|
||
}
|
||
|
||
uiState.error?.let {
|
||
// [FALLBACK]
|
||
Text(
|
||
text = it,
|
||
color = MaterialTheme.colorScheme.error,
|
||
style = MaterialTheme.typography.bodyMedium
|
||
)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
]]>
|
||
</CODE>
|
||
</PAYLOAD>
|
||
</WORK_ORDER>
|
||
<WORK_ORDER id="WO-20250817-1118-07">
|
||
<ACTION>CREATE_OR_UPDATE_FILE</ACTION>
|
||
<TARGET_FILE>PROJECT_SPECIFICATION.xml</TARGET_FILE>
|
||
<PAYLOAD>
|
||
<CODE lang="xml">
|
||
<![CDATA[
|
||
<?xml version="1.0" encoding="UTF-8"?>
|
||
<PROJECT_SPECIFICATION>
|
||
<PROJECT_INFO>
|
||
<name>Homebox Lens</name>
|
||
<description>Android-клиент для системы управления инвентарем Homebox. Позволяет пользователям управлять своим инвентарем, взаимодействуя с экземпляром сервера Homebox.</description>
|
||
</PROJECT_INFO>
|
||
|
||
<TECHNICAL_DECISIONS>
|
||
<DECISION id="tech_logging" status="implemented">
|
||
<summary>Библиотека логирования</summary>
|
||
<description>В проекте используется Timber (timber.log.Timber) для всех целей логирования. Он предоставляет простой и расширяемый API для логирования.</description>
|
||
<EXAMPLE lang="kotlin">
|
||
<summary>Пример корректного использования Timber</summary>
|
||
<code>
|
||
<![CDATA[
|
||
// Правильно: Прямой вызов статических методов Timber.
|
||
// Для информационных сообщений (INFO):
|
||
Timber.i("User logged in successfully. UserId: %s", userId)
|
||
|
||
// Для отладочных сообщений (DEBUG):
|
||
Timber.d("Starting network request to /items")
|
||
|
||
// Для ошибок (ERROR):
|
||
try {
|
||
// какая-то операция, которая может провалиться
|
||
} catch (e: Exception) {
|
||
Timber.e(e, "Failed to fetch user profile.")
|
||
}
|
||
|
||
// НЕПРАВИЛЬНО: Попытка создать экземпляр логгера.
|
||
// val logger = Timber.tag("MyScreen") // Избегать этого!
|
||
// logger.info("Some message") // Этот метод не существует в API Timber.
|
||
]]>
|
||
</code>
|
||
</EXAMPLE>
|
||
</DECISION>
|
||
<DECISION id="tech_i18n" status="implemented">
|
||
<summary>Интернационализация (Мультиязычность)</summary>
|
||
<description>
|
||
Приложение должно поддерживать несколько языков для обеспечения доступности для глобальной аудитории.
|
||
Реализация будет основана на стандартном механизме ресурсов Android.
|
||
- Все строки, видимые пользователю, должны быть вынесены в файл `app/src/main/res/values/strings.xml`. Использование жестко закодированных строк в коде запрещено.
|
||
- Язык по умолчанию - русский (ru). Файл `strings.xml` будет содержать русские строки.
|
||
- Для поддержки других языков (например, английского - en) будут создаваться соответствующие каталоги ресурсов (например, `app/src/main/res/values-en/strings.xml`).
|
||
- В коде для доступа к строкам необходимо использовать ссылки на ресурсы (например, `R.string.app_name`).
|
||
</description>
|
||
</DECISION>
|
||
<DECISION id="tech_ui_framework" status="implemented">
|
||
<summary>UI Framework</summary>
|
||
<description>Пользовательский интерфейс приложения построен с использованием Jetpack Compose, современного декларативного UI-фреймворка от Google. Это обеспечивает быстрое создание, гибкость и поддержку динамических данных.</description>
|
||
</DECISION>
|
||
<DECISION id="tech_di" status="implemented">
|
||
<summary>Внедрение зависимостей (Dependency Injection)</summary>
|
||
<description>Для управления зависимостями в проекте используется Hilt. Он интегрирован с компонентами Jetpack и упрощает внедрение зависимостей в Android-приложениях.</description>
|
||
</DECISION>
|
||
<DECISION id="tech_navigation" status="implemented">
|
||
<summary>Навигация</summary>
|
||
<description>Навигация между экранами (Composable-функциями) реализована с помощью библиотеки Navigation Compose, которая является частью Jetpack Navigation.</description>
|
||
</DECISION>
|
||
<DECISION id="tech_async" status="implemented">
|
||
<summary>Асинхронные операции</summary>
|
||
<description>Все асинхронные операции, такие как сетевые запросы или доступ к базе данных, выполняются с использованием Kotlin Coroutines. Это обеспечивает эффективное управление фоновыми задачами без блокировки основного потока.</description>
|
||
</DECISION>
|
||
<DECISION id="tech_networking" status="implemented">
|
||
<summary>Сетевое взаимодействие</summary>
|
||
<description>Для взаимодействия с API сервера Homebox используется стек технологий: Retrofit для создания типобезопасных HTTP-клиентов, OkHttp в качестве HTTP-клиента и Moshi для парсинга JSON.</description>
|
||
</DECISION>
|
||
<DECISION id="tech_database" status="implemented">
|
||
<summary>Локальное хранилище</summary>
|
||
<description>Для кэширования данных на устройстве используется библиотека Room. Она предоставляет абстракцию над SQLite и обеспечивает надежное локальное хранение данных.</description>
|
||
</DECISION>
|
||
</TECHNICAL_DECISIONS>
|
||
|
||
<SECURITY_SPEC>
|
||
<Description>Спецификация безопасности проекта.</Description>
|
||
<PRINCIPLE>Все сетевые взаимодействия должны быть защищены HTTPS. Аутентификация пользователя хранится в EncryptedSharedPreferences. Обработка ошибок аутентификации должна включать logout и редирект на экран логина.</PRINCIPLE>
|
||
<RULE name="AuthHandling">Использовать JWT или API-ключ для авторизации запросов. При истечении токена автоматически обновлять.</RULE>
|
||
<RULE name="DataEncryption">Локальные данные (credentials) шифровать с помощью Android KeyStore.</RULE>
|
||
</SECURITY_SPEC>
|
||
|
||
<ERROR_HANDLING>
|
||
<Description>Спецификация обработки ошибок.</Description>
|
||
<PRINCIPLE>Все потенциальные ошибки (сеть, БД, валидация) должны быть обработаны с использованием sealed classes для ошибок (e.g., NetworkError, ValidationError) и отображаться пользователю через Snackbar или Dialog.</PRINCIPLE>
|
||
<SCENARIO name="NetworkFailure">При сетевых ошибках показывать сообщение "No internet connection" и предлагать retry.</SCENARIO>
|
||
<SCENARIO name="ServerError">Для HTTP 4xx/5xx отображать user-friendly сообщение на основе response body.</SCENARIO>
|
||
<SCENARIO name="ValidationError">Использовать require/check для контрактов, логировать и показывать toast.</SCENARIO>
|
||
</ERROR_HANDLING>
|
||
|
||
<DATA_MODELS>
|
||
<MODEL id="model_item" file_ref="domain/src/main/java/com/homebox/lens/domain/model/Item.kt" status="implemented">
|
||
<summary>Модель инвентарного товара.</summary>
|
||
<description>Содержит поля: id, name, description, quantity, location, labels, customFields.</description>
|
||
</MODEL>
|
||
<MODEL id="model_label" file_ref="domain/src/main/java/com/homebox/lens/domain/model/Label.kt" status="implemented">
|
||
<summary>Модель метки.</summary>
|
||
<description>Содержит поля: id, name, color.</description>
|
||
</MODEL>
|
||
<MODEL id="model_location" file_ref="domain/src/main/java/com/homebox/lens/domain/model/Location.kt" status="implemented">
|
||
<summary>Модель местоположения.</summary>
|
||
<description>Содержит поля: id, name, parentLocation.</description>
|
||
</MODEL>
|
||
<MODEL id="model_statistics" file_ref="domain/src/main/java/com/homebox/lens/domain/model/Statistics.kt" status="implemented">
|
||
<summary>Модель статистики инвентаря.</summary>
|
||
<description>Содержит поля: totalItems, totalValue, locationsCount, labelsCount.</description>
|
||
</MODEL>
|
||
</DATA_MODELS>
|
||
|
||
<FEATURES>
|
||
<FEATURE id="feat_dashboard" status="implemented">
|
||
<summary>Экран панели управления</summary>
|
||
<description>Отображает сводку по инвентарю, включая статистику, такую как общее количество товаров, общая стоимость и количество по местоположениям/меткам.</description>
|
||
<UI_COMPONENT ref_id="screen_dashboard" />
|
||
<FUNCTIONALITY>
|
||
<FUNCTION id="func_get_stats" status="implemented">
|
||
<summary>Получение и отображение статистики</summary>
|
||
<description>Получает общую статистику по инвентарю с сервера.</description>
|
||
<precondition>Пользователь аутентифицирован; сеть доступна.</precondition>
|
||
<postcondition>Возвращает объект Statistics; данные кэшированы локально.</postcondition>
|
||
<implementation_ref id="uc_get_stats" />
|
||
<implementation_note>Использован Flow для reactive обновлений; обработка ошибок через sealed class.</implementation_note>
|
||
</FUNCTION>
|
||
<FUNCTION id="func_get_recent_items" status="implemented">
|
||
<summary>Получение и отображение недавно добавленных товаров</summary>
|
||
<description>Получает список последних N добавленных товаров из локальной базы данных.</description>
|
||
<precondition>Пользователь аутентифицирован.</precondition>
|
||
<postcondition>Возвращает Flow со списком ItemSummary; список отсортирован по дате создания.</postcondition>
|
||
<implementation_ref id="uc_get_recent_items" />
|
||
<implementation_note>Данные берутся из локального кэша (Room) для быстрого отображения.</implementation_note>
|
||
</FUNCTION>
|
||
</FUNCTIONALITY>
|
||
</FEATURE>
|
||
|
||
<FEATURE id="feat_inventory_list" status="implemented">
|
||
<summary>Экран списка инвентаря</summary>
|
||
<description>Отображает список всех инвентарных позиций с возможностью поиска и фильтрации.</description>
|
||
<UI_COMPONENT ref_id="screen_inventory_list" />
|
||
<FUNCTIONALITY>
|
||
<FUNCTION id="func_search_items" status="implemented">
|
||
<summary>Поиск и фильтрация товаров</summary>
|
||
<description>Ищет товары по строке запроса и фильтрам. Результаты разбиты на страницы.</description>
|
||
<precondition>Запрос не пустой; параметры пагинации валидны (page >= 1).</precondition>
|
||
<postcondition>Возвращает список Item с пагинацией; результаты отсортированы по релевантности.</postcondition>
|
||
<implementation_ref id="uc_search_items" />
|
||
<implementation_note>Поддержка фильтров по location/label; кэширование результатов для оффлайн.</implementation_note>
|
||
</FUNCTION>
|
||
<FUNCTION id="func_sync_inventory" status="implemented">
|
||
<summary>Синхронизация инвентаря</summary>
|
||
<description>Выполняет полную синхронизацию локального кэша инвентаря с сервером.</description>
|
||
<precondition>Сеть доступна; пользователь аутентифицирован.</precondition>
|
||
<postcondition>Локальная БД обновлена; возвращает success/failure.</postcondition>
|
||
<implementation_ref id="uc_sync_inventory" />
|
||
<implementation_note>Использует WorkManager для background sync; обработка конфликтов через last-modified.</implementation_note>
|
||
</FUNCTION>
|
||
</FUNCTIONALITY>
|
||
</FEATURE>
|
||
|
||
<FEATURE id="feat_item_details" status="implemented">
|
||
<summary>Экран сведений о товаре</summary>
|
||
<description>Показывает все сведения о конкретном инвентарном товаре, включая его название, описание, изображения, вложения и настраиваемые поля.</description>
|
||
<UI_COMPONENT ref_id="screen_item_details" />
|
||
<FUNCTIONALITY>
|
||
<FUNCTION id="func_get_item_details" status="implemented">
|
||
<summary>Получение сведений о товаре</summary>
|
||
<description>Получает полные сведения о конкретном товаре из репозитория.</description>
|
||
<precondition>Item ID валиден и существует.</precondition>
|
||
<postcondition>Возвращает полный объект Item с attachments.</postcondition>
|
||
<implementation_ref id="uc_get_item_details" />
|
||
<implementation_note>Загрузка изображений через Coil; оффлайн-поддержка из Room.</implementation_note>
|
||
</FUNCTION>
|
||
</FUNCTIONALITY>
|
||
</FEATURE>
|
||
|
||
<FEATURE id="feat_item_management" status="implemented">
|
||
<summary>Создание/редактирование/удаление товаров</summary>
|
||
<description>Позволяет пользователям создавать новые товары, обновлять существующие и удалять их.</description>
|
||
<UI_COMPONENT ref_id="screen_item_edit" />
|
||
<FUNCTIONALITY>
|
||
<FUNCTION id="func_create_item" status="implemented">
|
||
<summary>Создать товар</summary>
|
||
<description>Создает новый инвентарный товар на сервере.</description>
|
||
<precondition>Все обязательные поля (name, quantity) заполнены; данные валидны.</precondition>
|
||
<postcondition>Новый Item сохранен на сервере; ID возвращен.</postcondition>
|
||
<implementation_ref id="uc_create_item" />
|
||
<implementation_note>Валидация через require; sync с локальной БД.</implementation_note>
|
||
</FUNCTION>
|
||
<FUNCTION id="func_update_item" status="implemented">
|
||
<summary>Обновить товар</summary>
|
||
<description>Обновляет существующий инвентарный товар на сервере.</description>
|
||
<precondition>Item ID существует; изменения валидны.</precondition>
|
||
<postcondition>Item обновлен; версия инкрементирована.</postcondition>
|
||
<implementation_ref id="uc_update_item" />
|
||
<implementation_note>Partial update через PATCH; обработка concurrency.</implementation_note>
|
||
</FUNCTION>
|
||
<FUNCTION id="func_delete_item" status="implemented">
|
||
<summary>Удалить товар</summary>
|
||
<description>Удаляет инвентарный товар с сервера.</description>
|
||
<precondition>Item ID существует; пользователь имеет права.</precondition>
|
||
<postcondition>Item удален; связанные ресурсы (attachments) очищены.</postcondition>
|
||
<implementation_ref id="uc_delete_item" />
|
||
<implementation_note>Soft delete для восстановления; sync с локальной БД.</implementation_note>
|
||
</FUNCTION>
|
||
</FUNCTIONALITY>
|
||
</FEATURE>
|
||
|
||
<FEATURE id="feat_labels_locations" status="implemented">
|
||
<summary>Управление метками и местоположениями</summary>
|
||
<description>Позволяет пользователям просматривать списки всех доступных меток и местоположений.</description>
|
||
<UI_COMPONENT ref_id="screen_labels_list" />
|
||
<UI_COMPONENT ref_id="screen_locations_list" />
|
||
<FUNCTIONALITY>
|
||
<FUNCTION id="func_get_all_labels" status="implemented">
|
||
<summary>Получить все метки</summary>
|
||
<description>Получает список всех меток из репозитория.</description>
|
||
<precondition>Сеть доступна или кэш существует.</precondition>
|
||
<postcondition>Возвращает список Label; отсортирован по name.</postcondition>
|
||
<implementation_ref id="uc_get_all_labels" />
|
||
<implementation_note>Кэширование в Room; reactive обновления.</implementation_note>
|
||
</FUNCTION>
|
||
<FUNCTION id="func_get_all_locations" status="implemented">
|
||
<summary>Получить все местоположения</summary>
|
||
<description>Получает список всех местоположений из репозитория.</description>
|
||
<precondition>Сеть доступна или кэш существует.</precondition>
|
||
<postcondition>Возвращает список Location; иерархическая структура сохранена.</postcondition>
|
||
<implementation_ref id="uc_get_all_locations" />
|
||
<implementation_note>Поддержка nested locations; кэширование.</implementation_note>
|
||
</FUNCTION>
|
||
</FUNCTIONALITY>
|
||
</FEATURE>
|
||
|
||
<FEATURE id="feat_search" status="implemented">
|
||
<summary>Экран поиска</summary>
|
||
<description>Предоставляет специальный пользовательский интерфейс для поиска товаров.</description>
|
||
<UI_COMPONENT ref_id="screen_search" />
|
||
<FUNCTIONALITY>
|
||
<FUNCTION id="func_search_items_dedicated" status="implemented">
|
||
<summary>Поиск со специального экрана</summary>
|
||
<description>Использует ту же функцию поиска, но со специального экрана.</description>
|
||
<precondition>Запрос не пустой.</precondition>
|
||
<postcondition>Возвращает результаты поиска; UI обновлен.</postcondition>
|
||
<implementation_ref id="uc_search_items" />
|
||
<implementation_note>Интеграция с SearchView; debounce для запросов.</implementation_note>
|
||
</FUNCTION>
|
||
</FUNCTIONALITY>
|
||
</FEATURE>
|
||
</FEATURES>
|
||
|
||
<UI_SPECIFICATIONS>
|
||
<SCREEN id="screen_dashboard" status="implemented">
|
||
<summary>Главный экран "Панель управления"</summary>
|
||
<description>
|
||
Экран предоставляет обзорную информацию и быстрый доступ к основным функциям. Компоновка должна быть чистой и интуитивно понятной, аналогично веб-интерфейсу HomeBox.
|
||
</description>
|
||
<LAYOUT>
|
||
<COMPONENT type="TopAppBar">
|
||
<description>Верхняя панель приложения. Содержит иконку навигационного меню (гамбургер), название/логотип приложения и иконку для запуска сканера (например, QR-кода).</description>
|
||
</COMPONENT>
|
||
<COMPONENT type="NavigationDrawer">
|
||
<description>Боковое навигационное меню. Открывается по нажатию на иконку в TopAppBar. Содержит основные разделы: Главная, Локации, Поиск, Профиль, Инструменты, а также кнопку "Выйти".</description>
|
||
</COMPONENT>
|
||
<COMPONENT type="MainContent" orientation="vertical" scrollable="true">
|
||
<description>Основная область контента. Содержит несколько информационных блоков.</description>
|
||
<SUB_COMPONENT type="Section" title="Быстрая статистика">
|
||
<description>Сетка из 2x2 карточек, отображающих ключевые метрики.</description>
|
||
<ELEMENT type="Card" name="Общая стоимость" />
|
||
<ELEMENT type="Card" name="Всего вещей" />
|
||
<ELEMENT type="Card" name="Общее количество местоположений" />
|
||
<ELEMENT type="Card" name="Всего меток" />
|
||
</SUB_COMPONENT>
|
||
<SUB_COMPONENT type="Section" title="Недавно добавлено">
|
||
<description>Горизонтально прокручиваемый список карточек недавно добавленных предметов. Если предметов нет, отображается сообщение "Элементы не найдены".</description>
|
||
</SUB_COMPONENT>
|
||
<SUB_COMPONENT type="Section" title="Места хранения">
|
||
<description>Сетка или гибкий контейнер (FlowRow) с кликабельными чипами, представляющими местоположения. Нажатие на чип ведет к списку предметов в этом местоположении.</description>
|
||
</SUB_COMPONENT>
|
||
<SUB_COMPONENT type="Section" title="Метки">
|
||
<description>Сетка или гибкий контейнер (FlowRow) с кликабельными чипами, представляющими метки. Нажатие на чип ведет к списку предметов с этой меткой.</description>
|
||
</SUB_COMPONENT>
|
||
</COMPONENT>
|
||
<COMPONENT type="FloatingActionButton_or_PrimaryButton" icon="add">
|
||
<description>
|
||
Вместо плавающей кнопки (FAB), в референсе используется заметная кнопка "Создать" в навигационном меню. Мы будем придерживаться этого подхода для консистентности. Эта кнопка инициирует процесс создания нового предмета.
|
||
</description>
|
||
</COMPONENT>
|
||
</LAYOUT>
|
||
<USER_INTERACTIONS>
|
||
<INTERACTION>
|
||
<action>Нажатие на чип местоположения/метки</action>
|
||
<reaction>Навигация на экран списка инвентаря с фильтром.</reaction>
|
||
</INTERACTION>
|
||
<INTERACTION>
|
||
<action>Нажатие на кнопку "Создать"</action>
|
||
<reaction>Открытие экрана редактирования нового товара.</reaction>
|
||
</INTERACTION>
|
||
</USER_INTERACTIONS>
|
||
</SCREEN>
|
||
|
||
<SCREEN id="screen_locations_list" status="implemented">
|
||
<summary>Экран "Локации"</summary>
|
||
<description>
|
||
Отображает вертикальный список всех доступных местоположений. Экран должен быть интегрирован в общую структуру навигации приложения (TopAppBar, NavigationDrawer).
|
||
</description>
|
||
<LAYOUT>
|
||
<COMPONENT type="TopAppBar">
|
||
<description>Общая верхняя панель приложения, аналогичная экрану "Панель управления".</description>
|
||
</COMPONENT>
|
||
<COMPONENT type="NavigationDrawer">
|
||
<description>Общее боковое меню навигации.</description>
|
||
</COMPONENT>
|
||
<COMPONENT type="MainContent" orientation="vertical">
|
||
<description>Основная область контента, занимающая все доступное пространство под TopAppBar.</description>
|
||
<SUB_COMPONENT type="Header" title="Локации">
|
||
<description>Заголовок экрана, расположенный вверху основной области контента.</description>
|
||
</SUB_COMPONENT>
|
||
<SUB_COMPONENT type="List" name="LocationsList">
|
||
<description>Вертикальный, прокручиваемый список (LazyColumn) всех местоположений.</description>
|
||
<ELEMENT type="ListItem">
|
||
<description>Элемент списка, представляющий одно местоположение. Состоит из иконки (например, 'place') и названия местоположения. Весь элемент является кликабельным и ведет на экран со списком предметов в данной локации.</description>
|
||
</ELEMENT>
|
||
</SUB_COMPONENT>
|
||
</COMPONENT>
|
||
<COMPONENT type="FloatingActionButton" icon="add">
|
||
<description>
|
||
Плавающая кнопка действия, расположенная в правом нижнем углу. Позволяет пользователю добавить новое местоположение. В веб-версии для этого используются иконки в углу, но FAB является более нативным паттерном для Android.
|
||
</description>
|
||
</COMPONENT>
|
||
</LAYOUT>
|
||
<USER_INTERACTIONS>
|
||
<INTERACTION>
|
||
<action>Нажатие на элемент списка локаций</action>
|
||
<reaction>Осуществляется навигация на экран списка инвентаря, отфильтрованного по выбранной локации.</reaction>
|
||
</INTERACTION>
|
||
<INTERACTION>
|
||
<action>Нажатие на FloatingActionButton</action>
|
||
<reaction>Открывается диалоговое окно или новый экран для создания нового местоположения.</reaction>
|
||
</INTERACTION>
|
||
</USER_INTERACTIONS>
|
||
</SCREEN>
|
||
|
||
<SCREEN id="screen_labels_list" status="implemented">
|
||
<summary>Экран "Метки"</summary>
|
||
<description>
|
||
Отображает вертикальный список всех доступных меток. Экран должен быть интегрирован в общую структуру навигации приложения.
|
||
</description>
|
||
<LAYOUT>
|
||
<COMPONENT type="TopAppBar">
|
||
<description>Общая верхняя панель приложения с заголовком "Метки" и кнопкой "назад".</description>
|
||
</COMPONENT>
|
||
<COMPONENT type="MainContent" orientation="vertical">
|
||
<description>Основная область контента, занимающая все доступное пространство под TopAppBar.</description>
|
||
<SUB_COMPONENT type="List" name="LabelsList">
|
||
<description>Вертикальный, прокручиваемый список (LazyColumn) всех меток.</description>
|
||
<ELEMENT type="ListItem">
|
||
<description>Элемент списка, представляющий одну метку. Состоит из иконки (например, 'label') и названия метки. Весь элемент является кликабельным и ведет на экран со списком предметов с данной меткой.</description>
|
||
</ELEMENT>
|
||
</SUB_COMPONENT>
|
||
</COMPONENT>
|
||
<COMPONENT type="FloatingActionButton" icon="add">
|
||
<description>
|
||
Плавающая кнопка действия, расположенная в правом нижнем углу. Позволяет пользователю добавить новую метку.
|
||
</description>
|
||
</COMPONENT>
|
||
</LAYOUT>
|
||
<USER_INTERACTIONS>
|
||
<INTERACTION>
|
||
<action>Нажатие на элемент списка меток</action>
|
||
<reaction>Осуществляется навигация на экран списка инвентаря, отфильтрованного по выбранной метке.</reaction>
|
||
</INTERACTION>
|
||
<INTERACTION>
|
||
<action>Нажатие на FloatingActionButton</action>
|
||
<reaction>Открывается диалоговое окно или новый экран для создания новой метки.</reaction>
|
||
</INTERACTION>
|
||
</USER_INTERACTIONS>
|
||
</SCREEN>
|
||
|
||
<SCREEN id="screen_inventory_list" status="implemented">
|
||
<summary>Экран "Список инвентаря"</summary>
|
||
<description>
|
||
Отображает список всех инвентарных позиций с возможностью поиска, фильтрации и пагинации. Интегрирован в навигацию.
|
||
</description>
|
||
<LAYOUT>
|
||
<COMPONENT type="TopAppBar">
|
||
<description>Верхняя панель с поиском и фильтрами.</description>
|
||
</COMPONENT>
|
||
<COMPONENT type="MainContent" orientation="vertical" scrollable="true">
|
||
<description>Прокручиваемый список товаров.</description>
|
||
<SUB_COMPONENT type="List" name="InventoryList">
|
||
<description>LazyColumn с карточками товаров (name, quantity, location).</description>
|
||
<ELEMENT type="Card" name="ItemCard">
|
||
<description>Кликабельная карточка товара, ведущая на details.</description>
|
||
</ELEMENT>
|
||
</SUB_COMPONENT>
|
||
</COMPONENT>
|
||
<COMPONENT type="FloatingActionButton" icon="sync">
|
||
<description>Кнопка для синхронизации инвентаря.</description>
|
||
</COMPONENT>
|
||
</LAYOUT>
|
||
<USER_INTERACTIONS>
|
||
<INTERACTION>
|
||
<action>Ввод в поиск</action>
|
||
<reaction>Обновление списка с debounce.</reaction>
|
||
</INTERACTION>
|
||
<INTERACTION>
|
||
<action>Нажатие на товар</action>
|
||
<reaction>Навигация на screen_item_details.</reaction>
|
||
</INTERACTION>
|
||
</USER_INTERACTIONS>
|
||
</SCREEN>
|
||
|
||
<SCREEN id="screen_item_details" status="implemented">
|
||
<summary>Экран "Сведения о товаре"</summary>
|
||
<description>
|
||
Показывает детальную информацию о товаре, включая изображения и custom fields.
|
||
</description>
|
||
<LAYOUT>
|
||
<COMPONENT type="TopAppBar">
|
||
<description>С кнопками edit/delete.</description>
|
||
</COMPONENT>
|
||
<COMPONENT type="MainContent" orientation="vertical" scrollable="true">
|
||
<SUB_COMPONENT type="ImageCarousel" name="Images">
|
||
<description>Карусель изображений.</description>
|
||
</SUB_COMPONENT>
|
||
<SUB_COMPONENT type="DetailsSection" title="Описание">
|
||
<description>Текст description.</description>
|
||
</SUB_COMPONENT>
|
||
<SUB_COMPONENT type="FieldsGrid" name="CustomFields">
|
||
<description>Сетка custom полей.</description>
|
||
</SUB_COMPONENT>
|
||
</COMPONENT>
|
||
</LAYOUT>
|
||
<USER_INTERACTIONS>
|
||
<INTERACTION>
|
||
<action>Нажатие edit</action>
|
||
<reaction>Навигация на screen_item_edit.</reaction>
|
||
</INTERACTION>
|
||
<INTERACTION>
|
||
<action>Нажатие delete</action>
|
||
<reaction>Подтверждение и вызов func_delete_item.</reaction>
|
||
</INTERACTION>
|
||
</USER_INTERACTIONS>
|
||
</SCREEN>
|
||
|
||
<SCREEN id="screen_item_edit" status="implemented">
|
||
<summary>Экран "Редактирование товара"</summary>
|
||
<description>
|
||
Форма для создания/обновления товара с полями name, description, quantity, etc.
|
||
</description>
|
||
<LAYOUT>
|
||
<COMPONENT type="TopAppBar">
|
||
<description>С кнопкой save.</description>
|
||
</COMPONENT>
|
||
<COMPONENT type="MainContent" orientation="vertical" scrollable="true">
|
||
<SUB_COMPONENT type="TextField" name="Name">
|
||
<description>Поле ввода имени.</description>
|
||
</SUB_COMPONENT>
|
||
<SUB_COMPONENT type="Dropdown" name="Location">
|
||
<description>Выбор местоположения.</description>
|
||
</SUB_COMPONENT>
|
||
<SUB_COMPONENT type="ChipGroup" name="Labels">
|
||
<description>Выбор меток.</description>
|
||
</SUB_COMPONENT>
|
||
<SUB_COMPONENT type="ImagePicker" name="Images">
|
||
<description>Добавление изображений.</description>
|
||
</SUB_COMPONENT>
|
||
</COMPONENT>
|
||
</LAYOUT>
|
||
<USER_INTERACTIONS>
|
||
<INTERACTION>
|
||
<action>Нажатие save</action>
|
||
<reaction>Валидация и вызов func_create_item или func_update_item.</reaction>
|
||
</INTERACTION>
|
||
</USER_INTERACTIONS>
|
||
</SCREEN>
|
||
|
||
<SCREEN id="screen_search" status="implemented">
|
||
<summary>Экран "Поиск"</summary>
|
||
<description>
|
||
Специализированный экран для поиска с расширенными фильтрами.
|
||
</description>
|
||
<LAYOUT>
|
||
<COMPONENT type="TopAppBar">
|
||
<description>С поисковой строкой.</description>
|
||
</COMPONENT>
|
||
<COMPONENT type="MainContent" orientation="vertical">
|
||
<SUB_COMPONENT type="FilterSection" name="Filters">
|
||
<description>Чипы для фильтров (location, label).</description>
|
||
</SUB_COMPONENT>
|
||
<SUB_COMPONENT type="List" name="SearchResults">
|
||
<description>LazyColumn результатов.</description>
|
||
</SUB_COMPONENT>
|
||
</COMPONENT>
|
||
</LAYOUT>
|
||
<USER_INTERACTIONS>
|
||
<INTERACTION>
|
||
<action>Изменение запроса/фильтров</action>
|
||
<reaction>Обновление результатов.</reaction>
|
||
</INTERACTION>
|
||
</USER_INTERACTIONS>
|
||
</SCREEN>
|
||
|
||
</UI_SPECIFICATIONS>
|
||
|
||
<ICONOGRAPHY_GUIDE id="iconography_guide">
|
||
<summary>Руководство по использованию иконок</summary>
|
||
<description>
|
||
Этот раздел определяет стандартный набор иконок 'androidx.compose.material.icons.Icons.Filled'
|
||
для использования в приложении. Для устаревших иконок указаны актуальные замены.
|
||
</description>
|
||
<ICON name="AccountBox" path="Icons.Filled.AccountBox" />
|
||
<ICON name="AccountCircle" path="Icons.Filled.AccountCircle" />
|
||
<ICON name="Add" path="Icons.Filled.Add" />
|
||
<ICON name="AddCircle" path="Icons.Filled.AddCircle" />
|
||
<ICON name="ArrowBack" path="Icons.AutoMirrored.Filled.ArrowBack" note="Использовать AutoMirrored версию" />
|
||
<ICON name="ArrowDropDown" path="Icons.Filled.ArrowDropDown" />
|
||
<ICON name="ArrowForward" path="Icons.AutoMirrored.Filled.ArrowForward" note="Использовать AutoMirrored версию" />
|
||
<ICON name="Build" path="Icons.Filled.Build" />
|
||
<ICON name="Call" path="Icons.Filled.Call" />
|
||
<ICON name="Check" path="Icons.Filled.Check" />
|
||
<ICON name="CheckCircle" path="Icons.Filled.CheckCircle" />
|
||
<ICON name="Clear" path="Icons.Filled.Clear" />
|
||
<ICON name="Close" path="Icons.Filled.Close" />
|
||
<ICON name="Create" path="Icons.Filled.Create" />
|
||
<ICON name="DateRange" path="Icons.Filled.DateRange" />
|
||
<ICON name="Delete" path="Icons.Filled.Delete" />
|
||
<ICON name="Done" path="Icons.Filled.Done" />
|
||
<ICON name="Edit" path="Icons.Filled.Edit" />
|
||
<ICON name="Email" path="Icons.Filled.Email" />
|
||
<ICON name="ExitToApp" path="Icons.AutoMirrored.Filled.ExitToApp" note="Использовать AutoMirrored версию" />
|
||
<ICON name="Face" path="Icons.Filled.Face" />
|
||
<ICON name="Favorite" path="Icons.Filled.Favorite" />
|
||
<ICON name="FavoriteBorder" path="Icons.Filled.FavoriteBorder" />
|
||
<ICON name="Home" path="Icons.Filled.Home" />
|
||
<ICON name="Info" path="Icons.AutoMirrored.Filled.Info" note="Использовать AutoMirrored версию" />
|
||
<ICON name="KeyboardArrowDown" path="Icons.Filled.KeyboardArrowDown" />
|
||
<ICON name="KeyboardArrowLeft" path="Icons.AutoMirrored.Filled.KeyboardArrowLeft" note="Использовать AutoMirrored версию" />
|
||
<ICON name="KeyboardArrowRight" path="Icons.AutoMirrored.Filled.KeyboardArrowRight" note="Использовать AutoMirrored версию" />
|
||
<ICON name="KeyboardArrowUp" path="Icons.Filled.KeyboardArrowUp" />
|
||
<ICON name="Label" path="Icons.AutoMirrored.Filled.Label" note="Использовать AutoMirrored версию" />
|
||
<ICON name="List" path="Icons.AutoMirrored.Filled.List" note="Использовать AutoMirrored версию" />
|
||
<ICON name="LocationOn" path="Icons.Filled.LocationOn" />
|
||
<ICON name="Lock" path="Icons.Filled.Lock" />
|
||
<ICON name="MailOutline" path="Icons.Filled.MailOutline" />
|
||
<ICON name="Menu" path="Icons.Filled.Menu" />
|
||
<ICON name="MoreVert" path="Icons.Filled.MoreVert" />
|
||
<ICON name="Notifications" path="Icons.Filled.Notifications" />
|
||
<ICON name="Person" path="Icons.Filled.Person" />
|
||
<ICON name="Phone" path="Icons.Filled.Phone" />
|
||
<ICON name="Place" path="Icons.Filled.Place" />
|
||
<ICON name="PlayArrow" path="Icons.Filled.PlayArrow" />
|
||
<ICON name="Refresh" path="Icons.Filled.Refresh" />
|
||
<ICON name="Search" path="Icons.Filled.Search" />
|
||
<ICON name="Send" path="Icons.AutoMirrored.Filled.Send" note="Использовать AutoMirrored версию" />
|
||
<ICON name="Settings" path="Icons.Filled.Settings" />
|
||
<ICON name="Share" path="Icons.Filled.Share" />
|
||
<ICON name="ShoppingCart" path="Icons.Filled.ShoppingCart" />
|
||
<ICON name="Star" path="Icons.Filled.Star" />
|
||
<ICON name="ThumbUp" path="Icons.Filled.ThumbUp" />
|
||
<ICON name="Warning" path="Icons.Filled.Warning" />
|
||
</ICONOGRAPHY_GUIDE>
|
||
|
||
<IMPLEMENTATION_MAP>
|
||
<!-- Use Cases -->
|
||
<USE_CASE id="uc_get_stats" file_ref="domain/src/main/java/com/homebox/lens/domain/usecase/GetStatisticsUseCase.kt" />
|
||
<USE_CASE id="uc_search_items" file_ref="domain/src/main/java/com/homebox/lens/domain/usecase/SearchItemsUseCase.kt" />
|
||
<USE_CASE id="uc_sync_inventory" file_ref="domain/src/main/java/com/homebox/lens/domain/usecase/SyncInventoryUseCase.kt" />
|
||
<USE_CASE id="uc_get_item_details" file_ref="domain/src/main/java/com/homebox/lens/domain/usecase/GetItemDetailsUseCase.kt" />
|
||
<USE_CASE id="uc_create_item" file_ref="domain/src/main/java/com/homebox/lens/domain/usecase/CreateItemUseCase.kt" />
|
||
<USE_CASE id="uc_update_item" file_ref="domain/src/main/java/com/homebox/lens/domain/usecase/UpdateItemUseCase.kt" />
|
||
<USE_CASE id="uc_delete_item" file_ref="domain/src/main/java/com/homebox/lens/domain/usecase/DeleteItemUseCase.kt" />
|
||
<USE_CASE id="uc_get_all_labels" file_ref="domain/src/main/java/com/homebox/lens/domain/usecase/GetAllLabelsUseCase.kt" />
|
||
<USE_CASE id="uc_get_all_locations" file_ref="domain/src/main/java/com/homebox/lens/domain/usecase/GetAllLocationsUseCase.kt" />
|
||
<USE_CASE id="uc_login" file_ref="domain/src/main/java/com/homebox/lens/domain/usecase/LoginUseCase.kt" />
|
||
|
||
<!-- UI Screens -->
|
||
<UI_SCREEN id="screen_dashboard" file_ref="app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardScreen.kt" />
|
||
<UI_SCREEN id="screen_inventory_list" file_ref="app/src/main/java/com/homebox/lens/ui/screen/inventorylist/InventoryListScreen.kt" />
|
||
<UI_SCREEN id="screen_item_details" file_ref="app/src/main/java/com/homebox/lens/ui/screen/itemdetails/ItemDetailsScreen.kt" />
|
||
<UI_SCREEN id="screen_item_edit" file_ref="app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditScreen.kt" />
|
||
<UI_SCREEN id="screen_labels_list" file_ref="app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt" />
|
||
<UI_SCREEN id="screen_locations_list" file_ref="app/src/main/java/com/homebox/lens/ui/screen/locationslist/LocationsListScreen.kt" />
|
||
<UI_SCREEN id="screen_search" file_ref="app/src/main/java/com/homebox/lens/ui/screen/search/SearchScreen.kt" />
|
||
<UI_SCREEN id="screen_setup" file_ref="app/src/main/java/com/homebox/lens/ui/screen/setup/SetupScreen.kt" />
|
||
</IMPLEMENTATION_MAP>
|
||
</PROJECT_SPECIFICATION>
|
||
]]>
|
||
</CODE>
|
||
</PAYLOAD>
|
||
</WORK_ORDER>
|
||
</TASK_BATCH>
|
||
</FOR_AGENT> |