Labels
This commit is contained in:
211
tasks/completed/003_implement_labels_screen_ui.xml
Normal file
211
tasks/completed/003_implement_labels_screen_ui.xml
Normal file
@@ -0,0 +1,211 @@
|
||||
<!-- tasks/003_implement_labels_screen_ui.xml -->
|
||||
<TASK status="completed">
|
||||
<WORK_ORDER id="task-20250812-115003">
|
||||
<ACTION>MODIFY_CODE</ACTION>
|
||||
|
||||
<TARGET_FILE>app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt</TARGET_FILE>
|
||||
|
||||
<GOAL>
|
||||
Реализовать UI для экрана "Метки" (LabelsListScreen), заменив заглушку.
|
||||
Экран должен получать данные от LabelsListViewModel, отображать список меток в LazyColumn,
|
||||
а также содержать TopAppBar с кнопкой "назад" и FloatingActionButton для добавления новой метки,
|
||||
в полном соответствии со спецификацией screen_labels_list.
|
||||
</GOAL>
|
||||
|
||||
<CONTEXT_FILES>
|
||||
<FILE>tech_spec.txt</FILE>
|
||||
<FILE>project_structure.txt</FILE>
|
||||
<FILE>app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListViewModel.kt</FILE>
|
||||
</CONTEXT_FILES>
|
||||
|
||||
<CONTRACT>
|
||||
<CONSTRAINTS>
|
||||
<CONSTRAINT>Полностью заменить содержимое файла `LabelsListScreen.kt`.</CONSTRAINT>
|
||||
<CONSTRAINT>Главная функция `LabelsListScreen` должна получать `LabelsListViewModel` через `hiltViewModel()`.</CONSTRAINT>
|
||||
<CONSTRAINT>Состояние UI должно собираться из `viewModel.uiState` с использованием `collectAsStateWithLifecycle`.</CONSTRAINT>
|
||||
<CONSTRAINT>Для отображения списка должен использоваться `LazyColumn`.</CONSTRAINT>
|
||||
<CONSTRAINT>Каждый элемент списка должен быть реализован в отдельном Composable `LabelListItem`.</CONSTRAINT>
|
||||
<CONSTRAINT>`TopAppBar` должен содержать `IconButton` для навигации назад.</CONSTRAINT>
|
||||
<CONSTRAINT>`Scaffold` должен содержать `FloatingActionButton`.</CONSTRAINT>
|
||||
</CONSTRAINTS>
|
||||
</CONTRACT>
|
||||
|
||||
<PAYLOAD mode="FULL_CONTENT">
|
||||
<![CDATA[
|
||||
// [FILE] app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt
|
||||
// [PACKAGE]
|
||||
package com.homebox.lens.ui.screen.labelslist
|
||||
|
||||
// [IMPORTS]
|
||||
import androidx.compose.foundation.clickable
|
||||
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.filled.Add
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Label
|
||||
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.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.homebox.lens.domain.model.Label
|
||||
import com.homebox.lens.ui.theme.HomeboxLensTheme
|
||||
|
||||
// [COMPOSABLE_FUNCTION] LabelsListScreen (Stateful)
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Контейнерный Composable для экрана "Метки", управляющий состоянием.
|
||||
* Он получает данные от ViewModel и передает их в state-less Composable `LabelsListContent`.
|
||||
*
|
||||
* @param viewModel ViewModel, предоставляемая Hilt, для доступа к бизнес-логике и состоянию UI.
|
||||
* @param onNavigateBack Лямбда для обработки действия "назад".
|
||||
* @param onLabelClick Лямбда для обработки нажатия на метку, передает ID метки.
|
||||
* @param onAddLabelClick Лямбда для обработки нажатия на FAB.
|
||||
* @sideeffect Получает состояние UI (`uiState`) из `viewModel`.
|
||||
* @sideeffect Вызывает навигационные лямбды в ответ на действия пользователя.
|
||||
*/
|
||||
@Composable
|
||||
fun LabelsListScreen(
|
||||
viewModel: LabelsListViewModel = hiltViewModel(),
|
||||
onNavigateBack: () -> Unit,
|
||||
onLabelClick: (String) -> Unit,
|
||||
onAddLabelClick: () -> Unit
|
||||
) {
|
||||
// [ACTION] Сбор состояния из ViewModel
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
LabelsListContent(
|
||||
labels = uiState.labels,
|
||||
onNavigateBack = onNavigateBack,
|
||||
onLabelClick = onLabelClick,
|
||||
onAddLabelClick = onAddLabelClick
|
||||
)
|
||||
// [COHERENCE_CHECK_PASSED] Состояние передается в stateless composable.
|
||||
}
|
||||
// [END_FUNCTION]
|
||||
|
||||
// [COMPOSABLE_FUNCTION] LabelsListContent (Stateless)
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Отображает UI для экрана "Метки". Этот Composable не имеет своего состояния (stateless).
|
||||
*
|
||||
* @param labels Список объектов `Label` для отображения.
|
||||
* @param onNavigateBack Лямбда для обработки действия "назад".
|
||||
* @param onLabelClick Лямбда для обработки нажатия на метку.
|
||||
* @param onAddLabelClick Лямбда для обработки нажатия на FAB.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun LabelsListContent(
|
||||
labels: List<Label>,
|
||||
onNavigateBack: () -> Unit,
|
||||
onLabelClick: (String) -> Unit,
|
||||
onAddLabelClick: () -> Unit
|
||||
) {
|
||||
// [CORE-LOGIC] Основная разметка экрана
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Метки") },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = "Назад" // TODO: Заменить на stringResource
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(onClick = onAddLabelClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Add,
|
||||
contentDescription = "Добавить метку" // TODO: Заменить на stringResource
|
||||
)
|
||||
}
|
||||
}
|
||||
) { innerPadding ->
|
||||
// [CORE-LOGIC] Список меток
|
||||
LazyColumn(modifier = Modifier.padding(innerPadding)) {
|
||||
items(items = labels, key = { it.id }) { label ->
|
||||
LabelListItem(
|
||||
label = label,
|
||||
onClick = { onLabelClick(label.id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_FUNCTION]
|
||||
|
||||
// [HELPER] LabelListItem
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Отображает один элемент списка для метки.
|
||||
*
|
||||
* @param label Объект `Label`, данные которого нужно отобразить.
|
||||
* @param onClick Лямбда, вызываемая при нажатии на элемент.
|
||||
*/
|
||||
@Composable
|
||||
private fun LabelListItem(
|
||||
label: Label,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = { Text(label.name) },
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Label,
|
||||
contentDescription = null
|
||||
)
|
||||
},
|
||||
modifier = modifier.clickable(onClick = onClick)
|
||||
)
|
||||
}
|
||||
// [END_FUNCTION]
|
||||
|
||||
// [PREVIEW]
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun LabelsListContentPreview() {
|
||||
val sampleLabels = listOf(
|
||||
Label(id = "1", name = "Электроника", color = "#FF0000"),
|
||||
Label(id = "2", name = "Книги", color = "#00FF00"),
|
||||
Label(id = "3", name = "Инструменты", color = "#0000FF")
|
||||
)
|
||||
HomeboxLensTheme {
|
||||
LabelsListContent(
|
||||
labels = sampleLabels,
|
||||
onNavigateBack = {},
|
||||
onLabelClick = {},
|
||||
onAddLabelClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
// [END_PREVIEW]
|
||||
// [END_FILE]
|
||||
]]>
|
||||
</PAYLOAD>
|
||||
|
||||
<IMPLEMENTATION_HINTS>
|
||||
<HINT>Это задание заменяет весь контент файла `app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt`.</HINT>
|
||||
<HINT>Основная логика разделена на два Composable: `LabelsListScreen` (stateful) и `LabelsListContent` (stateless), что является хорошей практикой.</HINT>
|
||||
<HINT>Функция `LabelsListScreen` отвечает за взаимодействие с ViewModel.</HINT>
|
||||
<HINT>Функция `LabelsListContent` отвечает исключительно за отображение UI на основе переданных данных.</HINT>
|
||||
<HINT>Убедись, что все импорты, указанные в секции [IMPORTS], добавлены корректно. Особенно важны `hiltViewModel` и `collectAsStateWithLifecycle`.</HINT>
|
||||
</IMPLEMENTATION_HINTS>
|
||||
</WORK_ORDER>
|
||||
</TASK>
|
||||
207
tasks/completed/004_fix_labels_screen_compilation_errors.xml
Normal file
207
tasks/completed/004_fix_labels_screen_compilation_errors.xml
Normal file
@@ -0,0 +1,207 @@
|
||||
<!-- tasks/004_refactor_labels_screen_with_dbc.xml -->
|
||||
<TASK status="completed">
|
||||
<WORK_ORDER id="task-20250812-121505">
|
||||
<ACTION>MODIFY_CODE</ACTION>
|
||||
|
||||
<TARGET_FILE>app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt</TARGET_FILE>
|
||||
|
||||
<GOAL>
|
||||
Исправить ошибки компиляции в файле LabelsListScreen.kt и полностью отрефакторить его в соответствии с принципами Design by Contract (DbC) и семантической разметки. Код должен быть не только рабочим, но и формально корректным и легко читаемым для AI.
|
||||
</GOAL>
|
||||
|
||||
<CONTEXT_FILES>
|
||||
<FILE>app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt</FILE>
|
||||
<FILE>domain/src/main/java/com/homebox/lens/domain/model/Label.kt</FILE>
|
||||
</CONTEXT_FILES>
|
||||
|
||||
<CONTRACT>
|
||||
<CONSTRAINTS>
|
||||
<CONSTRAINT>Код должен успешно компилироваться.</CONSTRAINT>
|
||||
<CONSTRAINT>Обязательно добавить импорты для `com.homebox.lens.domain.model.Label` и `androidx.lifecycle.compose.collectAsStateWithLifecycle`.</CONSTRAINT>
|
||||
<CONSTRAINT>Каждая Composable-функция должна иметь исчерпывающий KDoc-комментарий с тегом `[CONTRACT]`.</CONSTRAINT>
|
||||
<CONSTRAINT>В коде должны использоваться семантические якоря ([ACTION], [CORE-LOGIC], [HELPER], [PREVIEW] и т.д.) для структурирования.</CONSTRAINT>
|
||||
<CONSTRAINT>В коде Preview-функции должна быть устранена ошибка создания `Label` и добавлен `[COHERENCE_NOTE]` с объяснением исправления.</CONSTRAINT>
|
||||
</CONSTRAINTS>
|
||||
</CONTRACT>
|
||||
|
||||
<PAYLOAD mode="FULL_CONTENT">
|
||||
<![CDATA[
|
||||
// [FILE] app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt
|
||||
// [PACKAGE]
|
||||
package com.homebox.lens.ui.screen.labelslist
|
||||
|
||||
// [IMPORTS]
|
||||
import androidx.compose.foundation.clickable
|
||||
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.filled.Add
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.Label
|
||||
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.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.homebox.lens.domain.model.Label
|
||||
import com.homebox.lens.ui.theme.HomeboxLensTheme
|
||||
|
||||
// [COMPOSABLE_FUNCTION] LabelsListScreen (Stateful)
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Контейнерный Composable для экрана "Метки", управляющий состоянием (stateful).
|
||||
* Его единственная ответственность — получение состояния от ViewModel и передача его в презентационный компонент.
|
||||
*
|
||||
* @param viewModel ViewModel, предоставляемая Hilt, для доступа к бизнес-логике и состоянию UI.
|
||||
* @param onNavigateBack Лямбда для обработки действия "назад".
|
||||
* @param onLabelClick Лямбда для обработки нажатия на метку, передает ID метки.
|
||||
* @param onAddLabelClick Лямбда для обработки нажатия на FloatingActionButton.
|
||||
* @sideeffect Получает `uiState` из `viewModel` и подписывается на его обновления.
|
||||
* @sideeffect Вызывает навигационные лямбды (`onNavigateBack`, `onLabelClick`) в ответ на действия пользователя.
|
||||
*/
|
||||
@Composable
|
||||
fun LabelsListScreen(
|
||||
viewModel: LabelsListViewModel = hiltViewModel(),
|
||||
onNavigateBack: () -> Unit,
|
||||
onLabelClick: (String) -> Unit,
|
||||
onAddLabelClick: () -> Unit
|
||||
) {
|
||||
// [ACTION] Сбор актуального состояния из ViewModel.
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
// [ACTION] Делегирование отрисовки компоненту без состояния.
|
||||
LabelsListContent(
|
||||
labels = uiState.labels,
|
||||
onNavigateBack = onNavigateBack,
|
||||
onLabelClick = onLabelClick,
|
||||
onAddLabelClick = onAddLabelClick
|
||||
)
|
||||
// [COHERENCE_CHECK_PASSED] Разделение ответственности между stateful и stateless компонентами соблюдено.
|
||||
}
|
||||
// [END_FUNCTION]
|
||||
|
||||
// [COMPOSABLE_FUNCTION] LabelsListContent (Stateless)
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Презентационный Composable (stateless), отвечающий исключительно за отображение UI экрана "Метки".
|
||||
* Он не содержит бизнес-логики и полностью управляется извне через параметры.
|
||||
*
|
||||
* @param labels Список объектов `Label` для отображения.
|
||||
* @param onNavigateBack Лямбда для обработки действия "назад".
|
||||
* @param onLabelClick Лямбда для обработки нажатия на метку.
|
||||
* @param onAddLabelClick Лямбда для обработки нажатия на FAB.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
private fun LabelsListContent(
|
||||
labels: List<Label>,
|
||||
onNavigateBack: () -> Unit,
|
||||
onLabelClick: (String) -> Unit,
|
||||
onAddLabelClick: () -> Unit
|
||||
) {
|
||||
// [CORE-LOGIC] Основная разметка экрана, определенная в UI_SPECIFICATIONS.
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text("Метки") }, // TODO: Заменить на stringResource(R.string.labels_screen_title)
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onNavigateBack) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
contentDescription = "Назад" // TODO: Заменить на stringResource
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(onClick = onAddLabelClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Add,
|
||||
contentDescription = "Добавить метку" // TODO: Заменить на stringResource
|
||||
)
|
||||
}
|
||||
}
|
||||
) { innerPadding ->
|
||||
// [CORE-LOGIC] Отображение списка меток.
|
||||
LazyColumn(modifier = Modifier.padding(innerPadding)) {
|
||||
items(items = labels, key = { it.id }) { label ->
|
||||
LabelListItem(
|
||||
label = label,
|
||||
onClick = { onLabelClick(label.id) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_FUNCTION]
|
||||
|
||||
// [HELPER] LabelListItem
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Вспомогательный Composable для отображения одного элемента в списке меток.
|
||||
*
|
||||
* @param label Объект `Label`, данные которого нужно отобразить.
|
||||
* @param onClick Лямбда, вызываемая при нажатии на элемент.
|
||||
*/
|
||||
@Composable
|
||||
private fun LabelListItem(
|
||||
label: Label,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
ListItem(
|
||||
headlineContent = { Text(label.name) },
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Label,
|
||||
contentDescription = null // Декоративная иконка
|
||||
)
|
||||
},
|
||||
modifier = modifier.clickable(onClick = onClick)
|
||||
)
|
||||
}
|
||||
// [END_FUNCTION]
|
||||
|
||||
// [PREVIEW]
|
||||
@Preview(showBackground = true, name = "Экран меток")
|
||||
@Composable
|
||||
private fun LabelsListContentPreview() {
|
||||
// [COHERENCE_NOTE] Исправлено создание тестовых данных. Поле 'color' отсутствует в реальной
|
||||
// доменной модели 'Label.kt', поэтому оно было убрано из preview для устранения ошибки компиляции.
|
||||
val sampleLabels = listOf(
|
||||
Label(id = "1", name = "Электроника"),
|
||||
Label(id = "2", name = "Книги"),
|
||||
Label(id = "3", name = "Инструменты")
|
||||
)
|
||||
HomeboxLensTheme {
|
||||
LabelsListContent(
|
||||
labels = sampleLabels,
|
||||
onNavigateBack = {},
|
||||
onLabelClick = {},
|
||||
onAddLabelClick = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
// [END_PREVIEW]
|
||||
// [END_FILE]
|
||||
]]>
|
||||
</PAYLOAD>
|
||||
|
||||
<IMPLEMENTATION_HINTS>
|
||||
<HINT>Это задание полностью заменяет содержимое файла, исправляя ошибки и приводя код в соответствие с архитектурными принципами проекта.</HINT>
|
||||
<HINT>Ключевое изменение — добавление исчерпывающих KDoc-комментариев с блоками [CONTRACT] для каждой функции.</HINT>
|
||||
<HINT>Убедись, что все импорты, включая `com.homebox.lens.domain.model.Label`, на месте.</HINT>
|
||||
</IMPLEMENTATION_HINTS>
|
||||
</WORK_ORDER>
|
||||
</TASK>
|
||||
86
tasks/completed/005_add_iconography_to_spec.xml
Normal file
86
tasks/completed/005_add_iconography_to_spec.xml
Normal file
@@ -0,0 +1,86 @@
|
||||
<!-- tasks/005_add_iconography_to_spec.xml -->
|
||||
<TASK status="completed">
|
||||
<WORK_ORDER id="task-20250812-121002">
|
||||
<ACTION>MODIFY_SPECIFICATION</ACTION>
|
||||
|
||||
<TARGET_FILE>tech_spec.txt</TARGET_FILE>
|
||||
|
||||
<GOAL>
|
||||
Добавить в техническую спецификацию новый раздел ICONOGRAPHY_GUIDE, содержащий список
|
||||
рекомендованных к использованию иконок из 'androidx.compose.material.icons.Icons'.
|
||||
Это создаст единый стандарт для иконок в приложении.
|
||||
</GOAL>
|
||||
|
||||
<CONTEXT_FILES>
|
||||
<FILE>tech_spec.txt</FILE>
|
||||
</CONTEXT_FILES>
|
||||
|
||||
<PAYLOAD mode="UPSERT_NODE" target_node_id="iconography_guide">
|
||||
<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>
|
||||
</PAYLOAD>
|
||||
|
||||
<IMPLEMENTATION_HINTS>
|
||||
<HINT>Найди корневой узел PROJECT_SPECIFICATION в tech_spec.txt.</HINT>
|
||||
<HINT>Добавь новый узел ICONOGRAPHY_GUIDE в конец, после UI_SPECIFICATIONS, но перед IMPLEMENTATION_MAP.</HINT>
|
||||
<HINT>Я уже обработал устаревшие иконки и указал правильные AutoMirrored версии, просто вставь этот блок.</HINT>
|
||||
</IMPLEMENTATION_HINTS>
|
||||
</WORK_ORDER>
|
||||
</TASK>```
|
||||
|
||||
Пожалуйста, выполните эти задания последовательно, начиная с исправления ошибки. Жду вашего сигнала о результатах.
|
||||
59
tasks/completed/01_update_label_screen_spec_status.xml
Normal file
59
tasks/completed/01_update_label_screen_spec_status.xml
Normal file
@@ -0,0 +1,59 @@
|
||||
<!-- tasks/001_update_label_screen_spec_status.xml -->
|
||||
<TASK status="completed">
|
||||
<WORK_ORDER id="task-20250812-114001">
|
||||
<ACTION>MODIFY_SPECIFICATION</ACTION>
|
||||
|
||||
<TARGET_FILE>tech_spec.txt</TARGET_FILE>
|
||||
<GOAL>
|
||||
Изменить статус UI-экрана 'screen_labels_list' на 'in_progress', чтобы отразить начало работ по его реализации.
|
||||
</GOAL>
|
||||
|
||||
<CONTEXT_FILES>
|
||||
<FILE>tech_spec.txt</FILE>
|
||||
</CONTEXT_FILES>
|
||||
|
||||
<PAYLOAD mode="UPSERT_NODE" target_node_id="screen_labels_list">
|
||||
<SCREEN id="screen_labels_list" status="in_progress">
|
||||
<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>
|
||||
</PAYLOAD>
|
||||
|
||||
<IMPLEMENTATION_HINTS>
|
||||
<HINT>Найди узел SCREEN с id="screen_labels_list" в файле tech_spec.txt.</HINT>
|
||||
<HINT>Замени атрибут status="implemented" на status="in_progress".</HINT>
|
||||
<HINT>Не изменяй остальное содержимое узла. Просто обнови атрибут.</HINT>
|
||||
</IMPLEMENTATION_HINTS>
|
||||
</WORK_ORDER>
|
||||
</TASK>
|
||||
92
tasks/completed/02_create_labels_screen_file.xml
Normal file
92
tasks/completed/02_create_labels_screen_file.xml
Normal file
@@ -0,0 +1,92 @@
|
||||
<!-- tasks/02_create_labels_screen_file.xml -->
|
||||
<TASK status="completed">
|
||||
<WORK_ORDER id="task-20250812-114502">
|
||||
<ACTION>CREATE_FILE</ACTION>
|
||||
|
||||
<TARGET_FILE>app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt</TARGET_FILE>
|
||||
|
||||
<GOAL>
|
||||
Создать базовую структуру (stub) для экрана "Метки" (LabelsListScreen) с использованием Jetpack Compose.
|
||||
Этот файл будет служить основой для дальнейшей реализации полноценного UI.
|
||||
</GOAL>
|
||||
|
||||
<CONTEXT_FILES>
|
||||
<FILE>tech_spec.txt</FILE>
|
||||
</CONTEXT_FILES>
|
||||
|
||||
<CONTRACT>
|
||||
<CONSTRAINTS>
|
||||
<CONSTRAINT>Имя файла должно быть 'LabelsListScreen.kt'.</CONSTRAINT>
|
||||
<CONSTRAINT>Функция должна называться 'LabelsListScreen'.</CONSTRAINT>
|
||||
<CONSTRAINT>Функция должна быть аннотирована как @Composable.</CONSTRAINT>
|
||||
<CONSTRAINT>Основная разметка должна использовать Scaffold.</CONSTRAINT>
|
||||
<CONSTRAINT>Должен быть TopAppBar с заголовком "Метки".</CONSTRAINT>
|
||||
<CONSTRAINT>В качестве временного контента для Scaffold должен использоваться Text-компонент с текстом "Hello, Labels Screen!".</CONSTRAINT>
|
||||
</CONSTRAINTS>
|
||||
</CONTRACT>
|
||||
|
||||
<PAYLOAD mode="FULL_CONTENT">
|
||||
<![CDATA[
|
||||
[FILE:app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt]
|
||||
[PACKAGE]
|
||||
package com.homebox.lens.ui.screen.labelslist
|
||||
[/PACKAGE]
|
||||
|
||||
[IMPORTS]
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
[/IMPORTS]
|
||||
|
||||
[COMPOSABLE_FUNCTION]
|
||||
/**
|
||||
* Заглушка для экрана, отображающего список меток.
|
||||
* В соответствии со спецификацией 'screen_labels_list'.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun LabelsListScreen(
|
||||
// В будущем здесь будут параметры: navController для навигации, viewModel для получения данных.
|
||||
) {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = {
|
||||
Text(text = "Метки") // В будущем будет заменено на stringResource
|
||||
}
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
// Временный контент-заглушка.
|
||||
// В будущем здесь будет LazyColumn для отображения списка меток.
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text("Hello, Labels Screen!")
|
||||
}
|
||||
}
|
||||
}
|
||||
[/COMPOSABLE_FUNCTION]
|
||||
[END_FILE]
|
||||
]]>
|
||||
</PAYLOAD>
|
||||
|
||||
<IMPLEMENTATION_HINTS>
|
||||
<HINT>Создай новый файл по пути 'app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt'.</HINT>
|
||||
<HINT>Скопируй предоставленный код из секции PAYLOAD в этот файл.</HINT>
|
||||
<HINT>Убедись, что используется правильный package: com.homebox.lens.ui.screen.labelslist.</HINT>
|
||||
<HINT>Добавь все необходимые импорты для Jetpack Compose (Scaffold, TopAppBar, Text, Composable и т.д.), как указано в PAYLOAD.</HINT>
|
||||
<HINT>Следуй структуре, заданной семантическими якорями.</HINT>
|
||||
</IMPLEMENTATION_HINTS>
|
||||
</WORK_ORDER>
|
||||
</TASK>
|
||||
237
tasks/completed/20250813_080300_implement_labels_screen.xml
Normal file
237
tasks/completed/20250813_080300_implement_labels_screen.xml
Normal file
@@ -0,0 +1,237 @@
|
||||
<!-- tasks/20250813_080300_implement_labels_screen.xml -->
|
||||
<TASK status="completed">
|
||||
<WORK_ORDER id="task-20250813080300-001">
|
||||
<ACTION>MODIFY_CODE</ACTION>
|
||||
|
||||
<TARGET_FILE>app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt</TARGET_FILE>
|
||||
|
||||
<GOAL>
|
||||
Реализовать UI для экрана "Метки" (`LabelsListScreen`) в соответствии со спецификацией `screen_labels_list`.
|
||||
Это включает в себя создание Composable-функции, которая:
|
||||
1. Использует `Scaffold` с `TopAppBar` и `FloatingActionButton`.
|
||||
2. Получает состояние (список меток, статус загрузки, ошибки) от `LabelsListViewModel`.
|
||||
3. Отображает список меток с помощью `LazyColumn`.
|
||||
4. Обрабатывает клики по элементам списка для навигации на экран инвентаря с фильтром по метке.
|
||||
5. Обрабатывает нажатие на FAB для создания новой метки (пока что через лог).
|
||||
6. Отображает индикатор загрузки и сообщения об ошибках или пустом списке.
|
||||
7. Строго следует принципам Design by Contract, использует иконки из гайда и строки из ресурсов.
|
||||
</GOAL>
|
||||
|
||||
<CONTEXT_FILES>
|
||||
<FILE>PROJECT_SPECIFICATION.xml</FILE>
|
||||
<FILE>PROJECT_STRUCTURE.xml</FILE>
|
||||
<FILE>app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListViewModel.kt</FILE>
|
||||
<FILE>domain/src/main/java/com/homebox/lens/domain/model/Label.kt</FILE>
|
||||
<FILE>app/src/main/java/com/homebox/lens/navigation/Screen.kt</FILE>
|
||||
</CONTEXT_FILES>
|
||||
|
||||
<CONTRACT>
|
||||
<![CDATA[
|
||||
// [PACKAGE] com.homebox.lens.ui.screen.labelslist
|
||||
// [FILE] app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt
|
||||
|
||||
// [IMPORTS]
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
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.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.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.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 androidx.navigation.NavController
|
||||
import com.homebox.lens.R
|
||||
import com.homebox.lens.domain.model.Label
|
||||
import timber.log.Timber
|
||||
|
||||
// [CONTRACT]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Отображает экран со списком всех меток.
|
||||
*
|
||||
* @param navController Контроллер навигации для перемещения между экранами.
|
||||
* @param viewModel ViewModel, предоставляющая состояние UI для экрана меток.
|
||||
*
|
||||
* @precondition `navController` должен быть корректно инициализирован и способен обрабатывать навигационные события.
|
||||
* @precondition `viewModel` должен быть доступен через Hilt.
|
||||
* @postcondition Экран отображает список меток или соответствующее состояние (загрузка, ошибка, пустой список).
|
||||
* @sideeffect Пользовательские действия (клики) инициируют навигационные команды через `navController` или логируются.
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun LabelsListScreen(
|
||||
navController: NavController,
|
||||
viewModel: LabelsListViewModel = hiltViewModel()
|
||||
) {
|
||||
// [ACTION]
|
||||
val uiState by viewModel.uiState.collectAsState()
|
||||
val logger = Timber.tag("LabelsListScreen")
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
title = { Text(text = stringResource(id = R.string.screen_title_labels)) },
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
logger.info { "[ACTION] Navigate up initiated." }
|
||||
navController.navigateUp()
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
contentDescription = stringResource(id = R.string.content_desc_navigate_back)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
floatingActionButton = {
|
||||
FloatingActionButton(onClick = {
|
||||
// [ACTION]
|
||||
// TODO: Открыть диалог или экран создания метки
|
||||
logger.info { "[ACTION] FAB clicked: Initiate create new label flow." }
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Add,
|
||||
contentDescription = stringResource(id = R.string.content_desc_create_label)
|
||||
)
|
||||
}
|
||||
}
|
||||
) { paddingValues ->
|
||||
// [CORE-LOGIC]
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(paddingValues),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
when {
|
||||
uiState.isLoading -> {
|
||||
// [STATE_BRANCH] Loading
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
uiState.error != null -> {
|
||||
// [STATE_BRANCH] Error
|
||||
Text(text = uiState.error ?: stringResource(id = R.string.error_unknown))
|
||||
}
|
||||
uiState.labels.isEmpty() -> {
|
||||
// [STATE_BRANCH] Empty
|
||||
Text(text = stringResource(id = R.string.labels_list_empty))
|
||||
}
|
||||
else -> {
|
||||
// [STATE_BRANCH] Success
|
||||
LabelsList(
|
||||
labels = uiState.labels,
|
||||
onLabelClick = { label ->
|
||||
// [ACTION]
|
||||
// TODO: Реализовать навигацию на экран инвентаря с фильтром
|
||||
logger.info { "[ACTION] Label clicked: ${label.id}. Navigating to inventory list." }
|
||||
// navController.navigate(Screen.InventoryList.withFilter("label", label.id))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// [COHERENCE_CHECK_PASSED]
|
||||
}
|
||||
// [END_FUNCTION] LabelsListScreen
|
||||
|
||||
// [HELPER]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Composable-функция для отображения списка меток.
|
||||
*
|
||||
* @param labels Список объектов `Label` для отображения.
|
||||
* @param onLabelClick Лямбда-функция, вызываемая при нажатии на элемент списка.
|
||||
*
|
||||
* @precondition `labels` не должен быть null.
|
||||
* @postcondition Отображается вертикальный прокручиваемый список.
|
||||
*/
|
||||
@Composable
|
||||
private fun LabelsList(
|
||||
labels: List<Label>,
|
||||
onLabelClick: (Label) -> Unit,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
// [CORE-LOGIC]
|
||||
LazyColumn(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
items(labels) { label ->
|
||||
LabelListItem(
|
||||
label = label,
|
||||
onClick = { onLabelClick(label) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_FUNCTION] LabelsList
|
||||
|
||||
// [HELPER]
|
||||
/**
|
||||
* [CONTRACT]
|
||||
* Composable-функция для отображения одного элемента в списке меток.
|
||||
*
|
||||
* @param label Объект `Label`, который нужно отобразить.
|
||||
* @param onClick Лямбда-функция, вызываемая при нажатии на элемент.
|
||||
*
|
||||
* @precondition `label` не должен быть null.
|
||||
* @postcondition Отображается кликабельный элемент списка с иконкой и названием метки.
|
||||
*/
|
||||
@Composable
|
||||
private fun LabelListItem(
|
||||
label: Label,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
// [CORE-LOGIC]
|
||||
ListItem(
|
||||
headlineContent = { Text(text = label.name) },
|
||||
leadingContent = {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.Label,
|
||||
contentDescription = stringResource(id = R.string.content_desc_label_icon)
|
||||
)
|
||||
},
|
||||
modifier = Modifier.clickable(onClick = onClick)
|
||||
)
|
||||
}
|
||||
// [END_FUNCTION] LabelListItem
|
||||
|
||||
// [END_FILE] LabelsListScreen.kt
|
||||
]]>
|
||||
</CONTRACT>
|
||||
|
||||
<IMPLEMENTATION_HINTS>
|
||||
<HINT>Используйте `@HiltViewModel` для получения экземпляра `LabelsListViewModel`.</HINT>
|
||||
<HINT>Собирайте `uiState` из ViewModel с помощью `collectAsState()` для автоматического обновления UI при изменении состояния.</HINT>
|
||||
<HINT>Используйте `Scaffold` для базовой структуры экрана (TopAppBar, FAB, основное содержимое).</HINT>
|
||||
<HINT>Для навигации назад используйте `navController.navigateUp()`.</HINT>
|
||||
<HINT>Используйте `LazyColumn` для эффективного отображения потенциально длинных списков меток.</HINT>
|
||||
<HINT>Обязательно добавьте новые строковые ресурсы (`screen_title_labels`, `content_desc_navigate_back`, `content_desc_create_label`, `labels_list_empty`, `content_desc_label_icon`) в `strings.xml`.</HINT>
|
||||
<HINT>Иконки должны браться из `androidx.compose.material.icons` в соответствии с `ICONOGRAPHY_GUIDE`.</HINT>
|
||||
</IMPLEMENTATION_HINTS>
|
||||
</WORK_ORDER>
|
||||
</TASK>
|
||||
Reference in New Issue
Block a user