feat(enrichment): apply semantic markup

This commit is contained in:
2025-10-04 09:53:10 +03:00
parent eccc7ee970
commit 556b7f7c7d
133 changed files with 1220 additions and 943 deletions

View File

@@ -0,0 +1,105 @@
<![CDATA[
<AI_AGENT_SEMANTIC_ENRICHMENT_PROTOCOL>
<EXTENDS from="base_role.xml"/>
<META>
<PURPOSE>Этот документ определяет операционный протокол для **исполнения роли 'Агента Семантического Обогащения'**. Главная задача — обогащение кодовой базы семантической информацией согласно `SEMANTIC_ENRICHMENT_PROTOCOL`.</PURPOSE>
<VERSION>1.0</VERSION>
<METRICS_TO_COLLECT>
<COLLECTS group_id="core_metrics"/>
<COLLECTS group_id="enrichment_specific"/>
</METRICS_TO_COLLECT>
<DEPENDS_ON>
- ..agent_promts/interfaces/task_channel_interface.xml
- ..agent_promts/protocols/semantic_enrichment_protocol.xml
</DEPENDS_ON>
</META>
<ROLE_DEFINITION>
<SPECIALIZATION>При исполнении этой роли, я действую как агент семантического обогащения. Моя задача - находить и размечать код, добавляя ему семантическую ценность в соответствии с протоколом.</SPECIALIZATION>
<CORE_GOAL>Проактивно обогащать кодовую базу семантической разметкой для улучшения машиночитаемости и анализа.</CORE_GOAL>
</ROLE_DEFINITION>
<CORE_PHILOSOPHY>
<PHILOSOPHY_PRINCIPLE name="Enrich_Dont_Change_Logic">
<DESCRIPTION>Моя работа заключается в добавлении семантических комментариев и аннотаций, не изменяя логику существующего кода.</DESCRIPTION>
</PHILOSOPHY_PRINCIPLE>
<PHILOSOPHY_PRINCIPLE name="Traceable_And_Reviewable">
<DESCRIPTION>Все изменения должны быть доступны для просмотра, например, через Pull Request.</DESCRIPTION>
</PHILOSOPHY_PRINCIPLE>
</CORE_PHILOSOPHY>
<BOOTSTRAP_PROTOCOL name="Initialization">
<ACTION>Загрузить и полностью проанализировать `agent_promts/protocols/semantic_enrichment_protocol.xml`, включая все вложенные `INCLUDE` файлы, для построения полного набора правил в памяти.</ACTION>
</BOOTSTRAP_PROTOCOL>
<TASK_SPECIFICATION name="Enrichment_Task">
<DESCRIPTION>Задачи для этой роли определяют, какие части кодовой базы нужно обогатить.</DESCRIPTION>
<STRUCTURE>
<![CDATA[
<ENRICHMENT_TASK>
<SCOPE>full_project | directory | file_list</SCOPE>
<TARGET>
<!-- Для directory: path/to/dir -->
<!-- Для file_list: список файлов -->
</TARGET>
</ENRICHMENT_TASK>
]]>
</STRUCTURE>
</TASK_SPECIFICATION>
<MASTER_WORKFLOW name="Enrich_Code_And_Create_PR">
<WORKFLOW_STEP id="1" name="Acknowledge_Task">
<LET name="WorkOrder" value="CALL MyTaskChannel.FindNextTask(RoleName='agent-enrichment', TaskType='type::enrichment')"/>
<IF condition="WorkOrder IS NULL">
<TERMINATE/>
</IF>
<ACTION>CALL MyTaskChannel.UpdateTaskStatus(IssueID={WorkOrder.ID}, NewStatus='status::in-progress')</ACTION>
</WORKFLOW_STEP>
<WORKFLOW_STEP id="2" name="Execute_Enrichment">
<ACTION>Извлечь `<ENRICHMENT_TASK>` из `WorkOrder`.</ACTION>
<LET name="BranchName">feature/{WorkOrder.ID}/semantic-enrichment</LET>
<ACTION>CALL MyTaskChannel.CreateBranch(BranchName={BranchName})</ACTION>
<ACTION>Определить `files_to_process` на основе `SCOPE` и `TARGET`.</ACTION>
<ACTION>Для каждого файла в `files_to_process` применить правила из `SEMANTIC_ENRICHMENT_PROTOCOL`.</ACTION>
</WORKFLOW_STEP>
<WORKFLOW_STEP id="3" name="Commit_And_PR">
<IF condition="есть_изменения">
<ACTION>Сделать коммит с сообщением: `feat(enrichment): apply semantic markup`.</ACTION>
<ACTION>CALL MyTaskChannel.CommitChanges(...)</ACTION>
<LET name="PrID" value="CALL MyTaskChannel.CreatePullRequest(Title='feat(enrichment): Semantic Markup', Body='Closes #{WorkOrder.ID}', HeadBranch={BranchName}, BaseBranch='main')"/>
<ACTION>CALL MyTaskChannel.AddComment(IssueID={WorkOrder.ID}, CommentBody='Enrichment complete. PR #{PrID} is ready for review.')</ACTION>
</IF>
<ELSE>
<ACTION>CALL MyTaskChannel.AddComment(IssueID={WorkOrder.ID}, CommentBody='Enrichment complete. No new semantic markup was added.')</ACTION>
</ELSE>
</WORKFLOW_STEP>
<WORKFLOW_STEP id="4" name="Finalize">
<ACTION>CALL MyTaskChannel.UpdateTaskStatus(IssueID={WorkOrder.ID}, NewStatus='status::completed')</ACTION>
</WORKFLOW_STEP>
<WORKFLOW_STEP id="5" name="Log_Metrics">
<ACTION>Отправить метрики через `MyMetricsSink`.</ACTION>
</WORKFLOW_STEP>
<WORKFLOW_STEP id="6" name="Log_Completion">
<REQUIRES_CHANNEL type="LogSink" as="MyLogSink"/>
<LET name="EnrichmentMetrics" value="CALL MyMetricsSink.GetMetrics(group_id='enrichment_specific')"/>
<LET name="LogMessage">
`WorkOrder {WorkOrder.ID} completed.
- Files Processed: {EnrichmentMetrics.files_processed}
- Entities Enriched: {EnrichmentMetrics.entities_enriched}
- Relations Added: {EnrichmentMetrics.relations_added}
- Contracts Added: {EnrichmentMetrics.contracts_added}
- Logs Added: {EnrichmentMetrics.logs_added}`
</LET>
<ACTION>CALL MyLogSink.Log(FileName="logs/enrichment_agent_log.txt", Content={LogMessage})</ACTION>
</WORKFLOW_STEP>
</MASTER_WORKFLOW>
</AI_AGENT_SEMANTIC_ENRICHMENT_PROTOCOL>
]]>

View File

@@ -11,13 +11,13 @@
</METRICS_TO_COLLECT> </METRICS_TO_COLLECT>
<DEPENDS_ON> <DEPENDS_ON>
- ../interfaces/task_channel_interface.xml - ..agent_promts/interfaces/task_channel_interface.xml
- ../protocols/semantic_enrichment_protocol.xml - ..agent_promts/protocols/semantic_enrichment_protocol.xml
</DEPENDS_ON> </DEPENDS_ON>
</META> </META>
<ROLE_DEFINITION> <ROLE_DEFINITION>
<SPECIALIZATION>При исполнении этой роли, я, Gemini, действую как автоматизированный хранитель чистоты кода. Моя единственная задача — обеспечить, чтобы каждый файл в указанной области соответствовал `SEMANTIC_ENRICHMENT_PROTOCOL`.</SPECIALIZATION> <SPECIALIZATION>При исполнении этой роли, я, действую как автоматизированный хранитель чистоты кода. Моя единственная задача — обеспечить, чтобы каждый файл в указанной области соответствовал `SEMANTIC_ENRICHMENT_PROTOCOL`.</SPECIALIZATION>
<CORE_GOAL>Поддерживать 100% семантическую чистоту и машиночитаемость кодовой базы, делая все изменения отслеживаемыми через систему контроля версий.</CORE_GOAL> <CORE_GOAL>Поддерживать 100% семантическую чистоту и машиночитаемость кодовой базы, делая все изменения отслеживаемыми через систему контроля версий.</CORE_GOAL>
</ROLE_DEFINITION> </ROLE_DEFINITION>
@@ -30,18 +30,6 @@
</PHILOSOPHY_PRINCIPLE> </PHILOSOPHY_PRINCIPLE>
</CORE_PHILOSOPHY> </CORE_PHILOSOPHY>
<TOOLS_FOR_ROLE>
<TOOL name="CodeEditor">
<COMMANDS><COMMAND name="ReadFile"/><COMMAND name="WriteFile"/></COMMANDS>
</TOOL>
<TOOL name="Shell">
<ALLOWED_COMMANDS>
<COMMAND>find . -name "*.kt"</COMMAND>
<COMMAND>git diff --name-only {commit_range}</COMMAND>
</ALLOWED_COMMANDS>
</TOOL>
</TOOLS_FOR_ROLE>
<ISSUE_BODY_FORMAT name="Linting_Task_Specification"> <ISSUE_BODY_FORMAT name="Linting_Task_Specification">
<DESCRIPTION>Задачи для этой роли должны содержать XML-блок, определяющий режим работы.</DESCRIPTION> <DESCRIPTION>Задачи для этой роли должны содержать XML-блок, определяющий режим работы.</DESCRIPTION>
<STRUCTURE> <STRUCTURE>

View File

@@ -44,4 +44,12 @@
<METRIC id="manual_verification_time_min" type="integer" description="Время, затраченное на ручную проверку, в минутах."/> <METRIC id="manual_verification_time_min" type="integer" description="Время, затраченное на ручную проверку, в минутах."/>
</METRIC_GROUP> </METRIC_GROUP>
<METRIC_GROUP id="enrichment_specific">
<METRIC name="files_processed" type="integer" unit="files">Количество обработанных файлов.</METRIC>
<METRIC name="entities_enriched" type="integer" unit="entities">Количество обогащенных сущностей (добавлены якоря ENTITY).</METRIC>
<METRIC name="relations_added" type="integer" unit="relations">Количество добавленных семантических связей (якоря RELATION).</METRIC>
<METRIC name="contracts_added" type="integer" unit="contracts">Количество добавленных KDoc-контрактов.</METRIC>
<METRIC name="logs_added" type="integer" unit="logs">Количество добавленных структурированных логов.</METRIC>
</METRIC_GROUP>
</METRICS_CATALOG> </METRICS_CATALOG>

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens // [PACKAGE] com.homebox.lens
// [FILE] MainActivity.kt // [FILE] MainActivity.kt
// [SEMANTICS] ui, activity, entrypoint // [SEMANTICS] app, ui, activity, entrypoint
package com.homebox.lens package com.homebox.lens
// [IMPORTS] // [IMPORTS]
@@ -22,7 +22,7 @@ import timber.log.Timber
// [ENTITY: Activity('MainActivity')] // [ENTITY: Activity('MainActivity')]
/** /**
* @summary Главная и единственная Activity в приложении. * @summary The main and only Activity in the application.
*/ */
@AndroidEntryPoint @AndroidEntryPoint
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens // [PACKAGE] com.homebox.lens
// [FILE] MainApplication.kt // [FILE] MainApplication.kt
// [SEMANTICS] application, hilt, timber // [SEMANTICS] app, hilt, timber, entrypoint
package com.homebox.lens package com.homebox.lens
// [IMPORTS] // [IMPORTS]
@@ -11,7 +11,7 @@ import timber.log.Timber
// [ENTITY: Application('MainApplication')] // [ENTITY: Application('MainApplication')]
/** /**
* @summary Точка входа в приложение. Инициализирует Hilt и Timber. * @summary The entry point of the application. Initializes Hilt and Timber.
*/ */
@HiltAndroidApp @HiltAndroidApp
class MainApplication : Application() { class MainApplication : Application() {

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.navigation // [PACKAGE] com.homebox.lens.navigation
// [FILE] NavGraph.kt // [FILE] NavGraph.kt
// [SEMANTICS] navigation, compose, nav_host // [SEMANTICS] app, ui, navigation
package com.homebox.lens.navigation package com.homebox.lens.navigation
@@ -34,11 +34,11 @@ import com.homebox.lens.ui.screen.splash.SplashScreen
// [RELATION: Function('NavGraph')] -> [CREATES_INSTANCE_OF] -> [Class('NavigationActions')] // [RELATION: Function('NavGraph')] -> [CREATES_INSTANCE_OF] -> [Class('NavigationActions')]
// [RELATION: Function('NavGraph')] -> [USES] -> [Screen('SplashScreen')] // [RELATION: Function('NavGraph')] -> [USES] -> [Screen('SplashScreen')]
/** /**
* @summary Определяет граф навигации для всего приложения с использованием Jetpack Compose Navigation. * @summary Defines the navigation graph for the entire application using Jetpack Compose Navigation.
* @param navController Контроллер навигации. * @param navController The navigation controller.
* @see Screen * @see Screen
* @sideeffect Регистрирует все экраны и управляет состоянием навигации. * @sideeffect Registers all screens and manages the navigation state.
* @invariant Стартовый экран - `Screen.Setup`. * @invariant The start screen is `Screen.Splash`.
*/ */
@Composable @Composable
fun NavGraph( fun NavGraph(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.navigation // [PACKAGE] com.homebox.lens.navigation
// [FILE] NavigationActions.kt // [FILE] NavigationActions.kt
// [SEMANTICS] navigation, controller, actions // [SEMANTICS] app, ui, navigation, actions
package com.homebox.lens.navigation package com.homebox.lens.navigation
// [IMPORTS] // [IMPORTS]
@@ -11,16 +11,16 @@ import timber.log.Timber
// [ENTITY: Class('NavigationActions')] // [ENTITY: Class('NavigationActions')]
// [RELATION: Class('NavigationActions')] -> [DEPENDS_ON] -> [Framework('NavHostController')] // [RELATION: Class('NavigationActions')] -> [DEPENDS_ON] -> [Framework('NavHostController')]
/** /**
* @summary Класс-обертка над NavHostController для предоставления типизированных навигационных действий. * @summary Wrapper class over NavHostController to provide typed navigation actions.
* @param navController Контроллер Jetpack Navigation. * @param navController The Jetpack Navigation controller.
* @invariant Все навигационные действия должны использовать предоставленный navController. * @invariant All navigation actions must use the provided navController.
*/ */
class NavigationActions(private val navController: NavHostController) { class NavigationActions(private val navController: NavHostController) {
// [ENTITY: Function('navigateToDashboard')] // [ENTITY: Function('navigateToDashboard')]
/** /**
* @summary Навигация на главный экран. * @summary Navigation to the main screen.
* @sideeffect Очищает back stack до главного экрана, чтобы избежать циклов. * @sideeffect Clears the back stack up to the main screen to avoid cycles.
*/ */
fun navigateToDashboard() { fun navigateToDashboard() {
Timber.i("[INFO][ACTION][navigate_to_dashboard] Navigating to Dashboard.") Timber.i("[INFO][ACTION][navigate_to_dashboard] Navigating to Dashboard.")

View File

@@ -1,13 +1,13 @@
// [PACKAGE] com.homebox.lens.navigation // [PACKAGE] com.homebox.lens.navigation
// [FILE] Screen.kt // [FILE] Screen.kt
// [SEMANTICS] navigation, routes, sealed_class // [SEMANTICS] app, ui, navigation, routes
package com.homebox.lens.navigation package com.homebox.lens.navigation
// [ENTITY: SealedClass('Screen')] // [ENTITY: SealedClass('Screen')]
/** /**
* @summary Запечатанный класс для определения маршрутов навигации в приложении. * @summary Sealed class for defining navigation routes in the application.
* @description Обеспечивает типобезопасность при навигации. * @description Provides type safety during navigation.
* @param route Строковый идентификатор маршрута. * @param route The string identifier of the route.
*/ */
sealed class Screen(val route: String) { sealed class Screen(val route: String) {
// [ENTITY: Object('Splash')] // [ENTITY: Object('Splash')]
@@ -26,11 +26,11 @@ sealed class Screen(val route: String) {
data object InventoryList : Screen("inventory_list_screen") { data object InventoryList : Screen("inventory_list_screen") {
// [ENTITY: Function('withFilter')] // [ENTITY: Function('withFilter')]
/** /**
* @summary Создает маршрут для экрана списка инвентаря с параметром фильтра. * @summary Creates a route for the inventory list screen with a filter parameter.
* @param key Ключ фильтра (например, "label" или "location"). * @param key The filter key (e.g., "label" or "location").
* @param value Значение фильтра (например, ID метки или местоположения). * @param value The filter value (e.g., the ID of the label or location).
* @return Строку полного маршрута с query-параметром. * @return A string of the full route with a query parameter.
* @throws IllegalArgumentException если ключ или значение пустые. * @throws IllegalArgumentException if the key or value is blank.
*/ */
fun withFilter(key: String, value: String): String { fun withFilter(key: String, value: String): String {
require(key.isNotBlank()) { "Filter key cannot be blank." } require(key.isNotBlank()) { "Filter key cannot be blank." }
@@ -47,10 +47,10 @@ sealed class Screen(val route: String) {
data object ItemDetails : Screen("item_details_screen/{itemId}") { data object ItemDetails : Screen("item_details_screen/{itemId}") {
// [ENTITY: Function('createRoute')] // [ENTITY: Function('createRoute')]
/** /**
* @summary Создает маршрут для экрана деталей элемента с указанным ID. * @summary Creates a route for the item details screen with the specified ID.
* @param itemId ID элемента для отображения. * @param itemId The ID of the item to display.
* @return Строку полного маршрута. * @return A string of the full route.
* @throws IllegalArgumentException если itemId пустой. * @throws IllegalArgumentException if itemId is blank.
*/ */
fun createRoute(itemId: String): String { fun createRoute(itemId: String): String {
require(itemId.isNotBlank()) { "itemId не может быть пустым." } require(itemId.isNotBlank()) { "itemId не может быть пустым." }
@@ -66,9 +66,9 @@ sealed class Screen(val route: String) {
data object ItemEdit : Screen("item_edit_screen?itemId={itemId}") { data object ItemEdit : Screen("item_edit_screen?itemId={itemId}") {
// [ENTITY: Function('createRoute')] // [ENTITY: Function('createRoute')]
/** /**
* @summary Создает маршрут для экрана редактирования элемента с указанным ID. * @summary Creates a route for the item edit screen with the specified ID.
* @param itemId ID элемента для редактирования. Null, если создается новый элемент. * @param itemId The ID of the item to edit. Null if a new item is being created.
* @return Строку полного маршрута. * @return A string of the full route.
*/ */
fun createRoute(itemId: String? = null): String { fun createRoute(itemId: String? = null): String {
return itemId?.let { "item_edit_screen?itemId=$it" } ?: "item_edit_screen" return itemId?.let { "item_edit_screen?itemId=$it" } ?: "item_edit_screen"
@@ -85,9 +85,9 @@ sealed class Screen(val route: String) {
data object LabelEdit : Screen("label_edit_screen?labelId={labelId}") { data object LabelEdit : Screen("label_edit_screen?labelId={labelId}") {
// [ENTITY: Function('createRoute')] // [ENTITY: Function('createRoute')]
/** /**
* @summary Создает маршрут для экрана редактирования метки с указанным ID. * @summary Creates a route for the label edit screen with the specified ID.
* @param labelId ID метки для редактирования. Null, если создается новая метка. * @param labelId The ID of the label to edit. Null if a new label is being created.
* @return Строку полного маршрута. * @return A string of the full route.
*/ */
fun createRoute(labelId: String? = null): String { fun createRoute(labelId: String? = null): String {
return labelId?.let { "label_edit_screen?labelId=$it" } ?: "label_edit_screen" return labelId?.let { "label_edit_screen?labelId=$it" } ?: "label_edit_screen"
@@ -104,10 +104,10 @@ sealed class Screen(val route: String) {
data object LocationEdit : Screen("location_edit_screen/{locationId}") { data object LocationEdit : Screen("location_edit_screen/{locationId}") {
// [ENTITY: Function('createRoute')] // [ENTITY: Function('createRoute')]
/** /**
* @summary Создает маршрут для экрана редактирования местоположения с указанным ID. * @summary Creates a route for the location edit screen with the specified ID.
* @param locationId ID местоположения для редактирования. * @param locationId The ID of the location to edit.
* @return Строку полного маршрута. * @return A string of the full route.
* @throws IllegalArgumentException если locationId пустой. * @throws IllegalArgumentException if locationId is blank.
*/ */
fun createRoute(locationId: String): String { fun createRoute(locationId: String): String {
require(locationId.isNotBlank()) { "locationId не может быть пустым." } require(locationId.isNotBlank()) { "locationId не может быть пустым." }

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.common // [PACKAGE] com.homebox.lens.ui.common
// [FILE] AppDrawer.kt // [FILE] AppDrawer.kt
// [SEMANTICS] ui, common, navigation_drawer // [SEMANTICS] app, ui, common, navigation
package com.homebox.lens.ui.common package com.homebox.lens.ui.common
// [IMPORTS] // [IMPORTS]
@@ -30,10 +30,10 @@ import com.homebox.lens.navigation.Screen
// [ENTITY: Function('AppDrawerContent')] // [ENTITY: Function('AppDrawerContent')]
// [RELATION: Function('AppDrawerContent')] -> [DEPENDS_ON] -> [Class('NavigationActions')] // [RELATION: Function('AppDrawerContent')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
/** /**
* @summary Контент для бокового навигационного меню (Drawer). * @summary Content for the side navigation menu (Drawer).
* @param currentRoute Текущий маршрут для подсветки активного элемента. * @param currentRoute The current route to highlight the active item.
* @param navigationActions Объект с навигационными действиями. * @param navigationActions The object with navigation actions.
* @param onCloseDrawer Лямбда для закрытия бокового меню. * @param onCloseDrawer Lambda to close the side menu.
*/ */
@Composable @Composable
internal fun AppDrawerContent( internal fun AppDrawerContent(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.common // [PACKAGE] com.homebox.lens.ui.common
// [FILE] MainScaffold.kt // [FILE] MainScaffold.kt
// [SEMANTICS] ui, common, scaffold, navigation_drawer // [SEMANTICS] app, ui, common, scaffold
package com.homebox.lens.ui.common package com.homebox.lens.ui.common
@@ -21,14 +21,14 @@ import kotlinx.coroutines.launch
// [RELATION: Function('MainScaffold')] -> [DEPENDS_ON] -> [Class('NavigationActions')] // [RELATION: Function('MainScaffold')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
// [RELATION: Function('MainScaffold')] -> [CALLS] -> [Function('AppDrawerContent')] // [RELATION: Function('MainScaffold')] -> [CALLS] -> [Function('AppDrawerContent')]
/** /**
* @summary Общая обертка для экранов, включающая Scaffold и Navigation Drawer. * @summary A common wrapper for screens that includes a Scaffold and Navigation Drawer.
* @param topBarTitle Заголовок для TopAppBar. * @param topBarTitle The title for the TopAppBar.
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. * @param currentRoute The current route to highlight the active item in the Drawer.
* @param navigationActions Объект с навигационными действиями. * @param navigationActions The object with navigation actions.
* @param topBarActions Composable-функция для отображения действий (иконок) в TopAppBar. * @param topBarActions A Composable function to display actions (icons) in the TopAppBar.
* @param content Основное содержимое экрана, которое будет отображено внутри Scaffold. * @param content The main content of the screen to be displayed inside the Scaffold.
* @sideeffect Управляет состоянием (открыто/закрыто) бокового меню (ModalNavigationDrawer). * @sideeffect Manages the state (open/closed) of the side menu (ModalNavigationDrawer).
* @invariant TopAppBar всегда отображается с иконкой меню. * @invariant The TopAppBar is always displayed with a menu icon.
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.components // [PACKAGE] com.homebox.lens.ui.components
// [FILE] ColorPicker.kt // [FILE] ColorPicker.kt
// [SEMANTICS] ui, component, color_selection // [SEMANTICS] app, ui, component, color
package com.homebox.lens.ui.components package com.homebox.lens.ui.components
@@ -25,10 +25,10 @@ import com.homebox.lens.R
// [ENTITY: Function('ColorPicker')] // [ENTITY: Function('ColorPicker')]
/** /**
* @summary Компонент для выбора цвета. * @summary A component for color selection.
* @param selectedColor Текущий выбранный цвет в формате HEX строки (например, "#FFFFFF"). * @param selectedColor The currently selected color in HEX string format (e.g., "#FFFFFF").
* @param onColorSelected Лямбда-функция, вызываемая при выборе нового цвета. * @param onColorSelected A lambda function called when a new color is selected.
* @param modifier Модификатор для настройки внешнего вида. * @param modifier A modifier for customizing the appearance.
*/ */
@Composable @Composable
fun ColorPicker( fun ColorPicker(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.components // [PACKAGE] com.homebox.lens.ui.components
// [FILE] LoadingOverlay.kt // [FILE] LoadingOverlay.kt
// [SEMANTICS] ui, component, loading // [SEMANTICS] app, ui, component, loading
package com.homebox.lens.ui.components package com.homebox.lens.ui.components
@@ -18,7 +18,7 @@ import androidx.compose.ui.graphics.Color
// [ENTITY: Function('LoadingOverlay')] // [ENTITY: Function('LoadingOverlay')]
/** /**
* @summary Полноэкранный оверлей с индикатором загрузки. * @summary A full-screen overlay with a loading indicator.
*/ */
@Composable @Composable
fun LoadingOverlay() { fun LoadingOverlay() {

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.mapper // [PACKAGE] com.homebox.lens.ui.mapper
// [FILE] ItemMapper.kt // [FILE] ItemMapper.kt
// [SEMANTICS] ui, mapper, item // [SEMANTICS] app, ui, mapper, item
package com.homebox.lens.ui.mapper package com.homebox.lens.ui.mapper
import com.homebox.lens.domain.model.Item import com.homebox.lens.domain.model.Item

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.dashboard // [PACKAGE] com.homebox.lens.ui.screen.dashboard
// [FILE] DashboardScreen.kt // [FILE] DashboardScreen.kt
// [SEMANTICS] ui, screen, dashboard, compose, navigation // [SEMANTICS] app, ui, screen, dashboard
package com.homebox.lens.ui.screen.dashboard package com.homebox.lens.ui.screen.dashboard
// [IMPORTS] // [IMPORTS]
@@ -37,11 +37,11 @@ import timber.log.Timber
// [RELATION: Function('DashboardScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')] // [RELATION: Function('DashboardScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
// [RELATION: Function('DashboardScreen')] -> [CALLS] -> [Function('MainScaffold')] // [RELATION: Function('DashboardScreen')] -> [CALLS] -> [Function('MainScaffold')]
/** /**
* @summary Главная Composable-функция для экрана "Панель управления". * @summary The main Composable function for the "Dashboard" screen.
* @param viewModel ViewModel для этого экрана, предоставляется через Hilt. * @param viewModel The ViewModel for this screen, provided by Hilt.
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. * @param currentRoute The current route to highlight the active item in the Drawer.
* @param navigationActions Объект с навигационными действиями. * @param navigationActions The object with navigation actions.
* @sideeffect Вызывает навигационные лямбды при взаимодействии с UI. * @sideeffect Calls navigation lambdas upon UI interaction.
*/ */
@Composable @Composable
fun DashboardScreen( fun DashboardScreen(
@@ -82,11 +82,11 @@ fun DashboardScreen(
// [ENTITY: Function('DashboardContent')] // [ENTITY: Function('DashboardContent')]
// [RELATION: Function('DashboardContent')] -> [CONSUMES_STATE] -> [SealedInterface('DashboardUiState')] // [RELATION: Function('DashboardContent')] -> [CONSUMES_STATE] -> [SealedInterface('DashboardUiState')]
/** /**
* @summary Отображает основной контент экрана в зависимости от uiState. * @summary Displays the main content of the screen depending on the uiState.
* @param modifier Модификатор для стилизации. * @param modifier A modifier for styling.
* @param uiState Текущее состояние UI экрана. * @param uiState The current UI state of the screen.
* @param onLocationClick Лямбда-обработчик нажатия на местоположение. * @param onLocationClick A lambda handler for clicking on a location.
* @param onLabelClick Лямбда-обработчик нажатия на метку. * @param onLabelClick A lambda handler for clicking on a label.
*/ */
@Composable @Composable
private fun DashboardContent( private fun DashboardContent(
@@ -132,8 +132,8 @@ private fun DashboardContent(
// [ENTITY: Function('StatisticsSection')] // [ENTITY: Function('StatisticsSection')]
// [RELATION: Function('StatisticsSection')] -> [DEPENDS_ON] -> [DataClass('GroupStatistics')] // [RELATION: Function('StatisticsSection')] -> [DEPENDS_ON] -> [DataClass('GroupStatistics')]
/** /**
* @summary Секция для отображения общей статистики. * @summary Section for displaying general statistics.
* @param statistics Объект со статистическими данными. * @param statistics The object with statistical data.
*/ */
@Composable @Composable
private fun StatisticsSection(statistics: GroupStatistics) { private fun StatisticsSection(statistics: GroupStatistics) {
@@ -164,9 +164,9 @@ private fun StatisticsSection(statistics: GroupStatistics) {
// [ENTITY: Function('StatisticCard')] // [ENTITY: Function('StatisticCard')]
/** /**
* @summary Карточка для отображения одного статистического показателя. * @summary Card for displaying a single statistical indicator.
* @param title Название показателя. * @param title The name of the indicator.
* @param value Значение показателя. * @param value The value of the indicator.
*/ */
@Composable @Composable
private fun StatisticCard(title: String, value: String) { private fun StatisticCard(title: String, value: String) {
@@ -180,8 +180,8 @@ private fun StatisticCard(title: String, value: String) {
// [ENTITY: Function('RecentlyAddedSection')] // [ENTITY: Function('RecentlyAddedSection')]
// [RELATION: Function('RecentlyAddedSection')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')] // [RELATION: Function('RecentlyAddedSection')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')]
/** /**
* @summary Секция для отображения недавно добавленных элементов. * @summary Section for displaying recently added items.
* @param items Список элементов для отображения. * @param items The list of items to display.
*/ */
@Composable @Composable
private fun RecentlyAddedSection(items: List<ItemSummary>) { private fun RecentlyAddedSection(items: List<ItemSummary>) {
@@ -213,8 +213,8 @@ private fun RecentlyAddedSection(items: List<ItemSummary>) {
// [ENTITY: Function('ItemCard')] // [ENTITY: Function('ItemCard')]
// [RELATION: Function('ItemCard')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')] // [RELATION: Function('ItemCard')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')]
/** /**
* @summary Карточка для отображения краткой информации об элементе. * @summary Card for displaying brief information about an item.
* @param item Элемент для отображения. * @param item The item to display.
*/ */
@Composable @Composable
private fun ItemCard(item: ItemSummary) { private fun ItemCard(item: ItemSummary) {
@@ -236,9 +236,9 @@ private fun ItemCard(item: ItemSummary) {
// [ENTITY: Function('LocationsSection')] // [ENTITY: Function('LocationsSection')]
// [RELATION: Function('LocationsSection')] -> [DEPENDS_ON] -> [DataClass('LocationOutCount')] // [RELATION: Function('LocationsSection')] -> [DEPENDS_ON] -> [DataClass('LocationOutCount')]
/** /**
* @summary Секция для отображения местоположений в виде чипсов. * @summary Section for displaying locations as chips.
* @param locations Список местоположений. * @param locations The list of locations.
* @param onLocationClick Лямбда-обработчик нажатия на местоположение. * @param onLocationClick A lambda handler for clicking on a location.
*/ */
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
@Composable @Composable
@@ -265,9 +265,9 @@ private fun LocationsSection(locations: List<LocationOutCount>, onLocationClick:
// [ENTITY: Function('LabelsSection')] // [ENTITY: Function('LabelsSection')]
// [RELATION: Function('LabelsSection')] -> [DEPENDS_ON] -> [DataClass('LabelOut')] // [RELATION: Function('LabelsSection')] -> [DEPENDS_ON] -> [DataClass('LabelOut')]
/** /**
* @summary Секция для отображения меток в виде чипсов. * @summary Section for displaying labels as chips.
* @param labels Список меток. * @param labels The list of labels.
* @param onLabelClick Лямбда-обработчик нажатия на метку. * @param onLabelClick A lambda handler for clicking on a label.
*/ */
@OptIn(ExperimentalLayoutApi::class) @OptIn(ExperimentalLayoutApi::class)
@Composable @Composable

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.dashboard // [PACKAGE] com.homebox.lens.ui.screen.dashboard
// [FILE] DashboardUiState.kt // [FILE] DashboardUiState.kt
// [SEMANTICS] ui, state, dashboard // [SEMANTICS] app, ui, state, dashboard
package com.homebox.lens.ui.screen.dashboard package com.homebox.lens.ui.screen.dashboard
// [IMPORTS] // [IMPORTS]
@@ -12,8 +12,8 @@ import com.homebox.lens.domain.model.LocationOutCount
// [ENTITY: SealedInterface('DashboardUiState')] // [ENTITY: SealedInterface('DashboardUiState')]
/** /**
* @summary Определяет все возможные состояния для экрана "Дэшборд". * @summary Defines all possible states for the "Dashboard" screen.
* @invariant В любой момент времени экран может находиться только в одном из этих состояний. * @invariant At any given time, the screen can only be in one of these states.
*/ */
sealed interface DashboardUiState { sealed interface DashboardUiState {
// [ENTITY: DataClass('Success')] // [ENTITY: DataClass('Success')]
@@ -22,11 +22,11 @@ sealed interface DashboardUiState {
// [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('LabelOut')] // [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('LabelOut')]
// [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')] // [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')]
/** /**
* @summary Состояние успешной загрузки данных. * @summary The state of a successful data load.
* @param statistics Статистика по инвентарю. * @param statistics The inventory statistics.
* @param locations Список локаций со счетчиками. * @param locations The list of locations with counters.
* @param labels Список всех меток. * @param labels The list of all labels.
* @param recentlyAddedItems Список недавно добавленных товаров. * @param recentlyAddedItems The list of recently added items.
*/ */
data class Success( data class Success(
val statistics: GroupStatistics, val statistics: GroupStatistics,
@@ -38,15 +38,15 @@ sealed interface DashboardUiState {
// [ENTITY: DataClass('Error')] // [ENTITY: DataClass('Error')]
/** /**
* @summary Состояние ошибки во время загрузки данных. * @summary The state of an error during data loading.
* @param message Человекочитаемое сообщение об ошибке. * @param message A human-readable error message.
*/ */
data class Error(val message: String) : DashboardUiState data class Error(val message: String) : DashboardUiState
// [END_ENTITY: DataClass('Error')] // [END_ENTITY: DataClass('Error')]
// [ENTITY: Object('Loading')] // [ENTITY: Object('Loading')]
/** /**
* @summary Состояние, когда данные для экрана загружаются. * @summary The state when data for the screen is being loaded.
*/ */
data object Loading : DashboardUiState data object Loading : DashboardUiState
// [END_ENTITY: Object('Loading')] // [END_ENTITY: Object('Loading')]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.dashboard // [PACKAGE] com.homebox.lens.ui.screen.dashboard
// [FILE] DashboardViewModel.kt // [FILE] DashboardViewModel.kt
// [SEMANTICS] ui_logic, dashboard, state_management, sealed_state, parallel_data_loading, timber_logging // [SEMANTICS] app, ui, viewmodel, dashboard
package com.homebox.lens.ui.screen.dashboard package com.homebox.lens.ui.screen.dashboard
// [IMPORTS] // [IMPORTS]
@@ -24,10 +24,10 @@ import javax.inject.Inject
// [RELATION: ViewModel('DashboardViewModel')] -> [DEPENDS_ON] -> [UseCase('GetRecentlyAddedItemsUseCase')] // [RELATION: ViewModel('DashboardViewModel')] -> [DEPENDS_ON] -> [UseCase('GetRecentlyAddedItemsUseCase')]
// [RELATION: ViewModel('DashboardViewModel')] -> [EMITS_STATE] -> [SealedInterface('DashboardUiState')] // [RELATION: ViewModel('DashboardViewModel')] -> [EMITS_STATE] -> [SealedInterface('DashboardUiState')]
/** /**
* @summary ViewModel для главного экрана (Dashboard). * @summary ViewModel for the main screen (Dashboard).
* @description Оркестрирует загрузку данных для Dashboard, используя строгую модель состояний * @description Orchestrates the loading of data for the Dashboard, using a strict state model
* (`DashboardUiState`), и обрабатывает параллельные запросы без состояний гонки. * (`DashboardUiState`), and handles parallel requests without race conditions.
* @invariant `uiState` всегда является одним из состояний, определенных в `DashboardUiState`. * @invariant `uiState` is always one of the states defined in `DashboardUiState`.
*/ */
@HiltViewModel @HiltViewModel
class DashboardViewModel @Inject constructor( class DashboardViewModel @Inject constructor(
@@ -46,10 +46,10 @@ class DashboardViewModel @Inject constructor(
// [ENTITY: Function('loadDashboardData')] // [ENTITY: Function('loadDashboardData')]
/** /**
* @summary Загружает все необходимые данные для экрана Dashboard. * @summary Loads all necessary data for the Dashboard screen.
* @description Выполняет UseCase'ы параллельно и обновляет UI, переключая его * @description Executes UseCases in parallel and updates the UI by switching it
* между состояниями `Loading`, `Success` и `Error` из `DashboardUiState`. * between the `Loading`, `Success`, and `Error` states from `DashboardUiState`.
* @sideeffect Асинхронно обновляет `_uiState` одним из состояний `DashboardUiState`. * @sideeffect Asynchronously updates `_uiState` with one of the `DashboardUiState` states.
*/ */
fun loadDashboardData() { fun loadDashboardData() {
viewModelScope.launch { viewModelScope.launch {

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.inventorylist // [PACKAGE] com.homebox.lens.ui.screen.inventorylist
// [FILE] InventoryListScreen.kt // [FILE] InventoryListScreen.kt
// [SEMANTICS] ui, screen, inventory, list // [SEMANTICS] app, ui, screen, list
package com.homebox.lens.ui.screen.inventorylist package com.homebox.lens.ui.screen.inventorylist
@@ -17,9 +17,9 @@ import com.homebox.lens.ui.common.MainScaffold
// [RELATION: Function('InventoryListScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')] // [RELATION: Function('InventoryListScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
// [RELATION: Function('InventoryListScreen')] -> [CALLS] -> [Function('MainScaffold')] // [RELATION: Function('InventoryListScreen')] -> [CALLS] -> [Function('MainScaffold')]
/** /**
* @summary Composable-функция для экрана "Список инвентаря". * @summary Composable function for the "Inventory List" screen.
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. * @param currentRoute The current route to highlight the active item in the Drawer.
* @param navigationActions Объект с навигационными действиями. * @param navigationActions The object with navigation actions.
*/ */
@Composable @Composable
fun InventoryListScreen( fun InventoryListScreen(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.inventorylist // [PACKAGE] com.homebox.lens.ui.screen.inventorylist
// [FILE] InventoryListViewModel.kt // [FILE] InventoryListViewModel.kt
// [SEMANTICS] ui, viewmodel, inventory_list // [SEMANTICS] app, ui, viewmodel, list
package com.homebox.lens.ui.screen.inventorylist package com.homebox.lens.ui.screen.inventorylist
// [IMPORTS] // [IMPORTS]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.itemdetails // [PACKAGE] com.homebox.lens.ui.screen.itemdetails
// [FILE] ItemDetailsScreen.kt // [FILE] ItemDetailsScreen.kt
// [SEMANTICS] ui, screen, item, details // [SEMANTICS] app, ui, screen, details
package com.homebox.lens.ui.screen.itemdetails package com.homebox.lens.ui.screen.itemdetails
@@ -17,9 +17,9 @@ import com.homebox.lens.ui.common.MainScaffold
// [RELATION: Function('ItemDetailsScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')] // [RELATION: Function('ItemDetailsScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
// [RELATION: Function('ItemDetailsScreen')] -> [CALLS] -> [Function('MainScaffold')] // [RELATION: Function('ItemDetailsScreen')] -> [CALLS] -> [Function('MainScaffold')]
/** /**
* @summary Composable-функция для экрана "Детали элемента". * @summary Composable function for the "Item Details" screen.
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. * @param currentRoute The current route to highlight the active item in the Drawer.
* @param navigationActions Объект с навигационными действиями. * @param navigationActions The object with navigation actions.
*/ */
@Composable @Composable
fun ItemDetailsScreen( fun ItemDetailsScreen(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.itemdetails // [PACKAGE] com.homebox.lens.ui.screen.itemdetails
// [FILE] ItemDetailsViewModel.kt // [FILE] ItemDetailsViewModel.kt
// [SEMANTICS] ui, viewmodel, item_details // [SEMANTICS] app, ui, viewmodel, details
package com.homebox.lens.ui.screen.itemdetails package com.homebox.lens.ui.screen.itemdetails
// [IMPORTS] // [IMPORTS]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.itemedit // [PACKAGE] com.homebox.lens.ui.screen.itemedit
// [FILE] ItemEditScreen.kt // [FILE] ItemEditScreen.kt
// [SEMANTICS] ui, screen, item, edit // [SEMANTICS] app, ui, screen, edit
package com.homebox.lens.ui.screen.itemedit package com.homebox.lens.ui.screen.itemedit
@@ -21,13 +21,17 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowDropDown import androidx.compose.material.icons.filled.ArrowDropDown
import androidx.compose.material.icons.filled.DateRange import androidx.compose.material.icons.filled.DateRange
import androidx.compose.material.icons.filled.Save import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DatePicker import androidx.compose.material3.DatePicker
import androidx.compose.material3.DatePickerDialog import androidx.compose.material3.DatePickerDialog
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
@@ -38,6 +42,7 @@ import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberDatePickerState import androidx.compose.material3.rememberDatePickerState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
@@ -67,12 +72,12 @@ import java.util.Locale
// [RELATION: Composable('ItemEditScreen')] -> [CONSUMES_STATE] -> [DataClass('ItemEditUiState')] // [RELATION: Composable('ItemEditScreen')] -> [CONSUMES_STATE] -> [DataClass('ItemEditUiState')]
// [RELATION: Composable('ItemEditScreen')] -> [CALLS] -> [Composable('MainScaffold')] // [RELATION: Composable('ItemEditScreen')] -> [CALLS] -> [Composable('MainScaffold')]
/** /**
* @summary Composable-функция для экрана "Редактирование элемента". * @summary Composable function for the "Edit Item" screen.
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. * @param currentRoute The current route to highlight the active item in the Drawer.
* @param navigationActions Объект с навигационными действиями. * @param navigationActions The object with navigation actions.
* @param itemId ID элемента для редактирования. Null, если создается новый элемент. * @param itemId The ID of the item to edit. Null if a new item is being created.
* @param viewModel ViewModel для управления состоянием экрана. * @param viewModel The ViewModel for managing the screen's state.
* @param onSaveSuccess Callback, вызываемый после успешного сохранения товара. * @param onSaveSuccess A callback invoked after the item is successfully saved.
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@@ -164,34 +169,124 @@ fun ItemEditScreen(
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
// [AI_NOTE]: Location selection will require a separate component or screen.
OutlinedTextField( // Location Dropdown
value = item.location?.name ?: "", var locationExpanded by remember { mutableStateOf(false) }
onValueChange = { /* TODO: Implement location selection */ }, ExposedDropdownMenuBox(
label = { Text(stringResource(R.string.item_edit_location)) }, expanded = locationExpanded,
readOnly = true, onExpandedChange = { locationExpanded = !locationExpanded }
trailingIcon = { ) {
IconButton(onClick = { /* TODO: Implement location selection */ }) { OutlinedTextField(
Icon(Icons.Filled.ArrowDropDown, contentDescription = stringResource(R.string.item_edit_select_location)) value = item.location?.name ?: "",
} onValueChange = { },
}, label = { Text(stringResource(R.string.item_edit_location)) },
modifier = Modifier.fillMaxWidth() readOnly = true,
) trailingIcon = {
Spacer(modifier = Modifier.height(8.dp)) ExposedDropdownMenuDefaults.TrailingIcon(expanded = locationExpanded)
// [AI_NOTE]: Label selection will require a separate component or screen. },
OutlinedTextField( modifier = Modifier
value = item.labels.joinToString { it.name }, .fillMaxWidth()
onValueChange = { /* TODO: Implement label selection */ }, .menuAnchor()
label = { Text(stringResource(R.string.item_edit_labels)) }, )
readOnly = true, ExposedDropdownMenu(
trailingIcon = { expanded = locationExpanded,
IconButton(onClick = { /* TODO: Implement label selection */ }) { onDismissRequest = { locationExpanded = false }
Icon(Icons.Filled.ArrowDropDown, contentDescription = stringResource(R.string.item_edit_select_labels)) ) {
} uiState.allLocations.forEach { location ->
}, DropdownMenuItem(
modifier = Modifier.fillMaxWidth() text = { Text(location.name) },
) onClick = {
viewModel.updateLocation(location)
locationExpanded = false
}
)
}
}
}
Spacer(modifier = Modifier.height(8.dp))
// Labels Dialog
var showLabelsDialog by remember { mutableStateOf(false) }
OutlinedTextField(
value = item.labels.joinToString { it.name },
onValueChange = { },
label = { Text(stringResource(R.string.item_edit_labels)) },
readOnly = true,
modifier = Modifier
.fillMaxWidth()
.clickable { showLabelsDialog = true },
trailingIcon = {
Icon(Icons.Filled.ArrowDropDown, contentDescription = stringResource(R.string.item_edit_select_labels))
}
)
if (showLabelsDialog) {
// This state will hold the temporary selections within the dialog
val tempSelectedLabels = remember { mutableStateOf(item.labels.toSet()) }
AlertDialog(
onDismissRequest = { showLabelsDialog = false },
title = { Text(stringResource(R.string.item_edit_select_labels)) },
text = {
Column {
uiState.allLabels.forEach { label ->
val isChecked = tempSelectedLabels.value.contains(label)
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.clickable {
val currentSelection = tempSelectedLabels.value.toMutableSet()
if (isChecked) {
currentSelection.remove(label)
} else {
currentSelection.add(label)
}
tempSelectedLabels.value = currentSelection
}
.padding(vertical = 8.dp)
) {
Checkbox(
checked = isChecked,
onCheckedChange = {
val currentSelection = tempSelectedLabels.value.toMutableSet()
if (it) {
currentSelection.add(label)
} else {
currentSelection.remove(label)
}
tempSelectedLabels.value = currentSelection
}
)
Text(
text = label.name,
modifier = Modifier.padding(start = 8.dp)
)
}
}
}
},
confirmButton = {
TextButton(
onClick = {
// Update the ViewModel with the final selection
viewModel.updateLabels(tempSelectedLabels.value.toList())
showLabelsDialog = false
}
) {
Text(stringResource(R.string.dialog_ok))
}
},
dismissButton = {
TextButton(onClick = { showLabelsDialog = false }) {
Text(stringResource(R.string.dialog_cancel))
}
}
)
}
} }
} }

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.itemedit // [PACKAGE] com.homebox.lens.ui.screen.itemedit
// [FILE] ItemEditViewModel.kt // [FILE] ItemEditViewModel.kt
// [SEMANTICS] ui, viewmodel, item_edit // [SEMANTICS] app, ui, viewmodel, edit
package com.homebox.lens.ui.screen.itemedit package com.homebox.lens.ui.screen.itemedit
@@ -16,6 +16,7 @@ import com.homebox.lens.domain.usecase.CreateItemUseCase
import com.homebox.lens.domain.usecase.GetAllLabelsUseCase import com.homebox.lens.domain.usecase.GetAllLabelsUseCase
import com.homebox.lens.domain.usecase.GetAllLocationsUseCase import com.homebox.lens.domain.usecase.GetAllLocationsUseCase
import com.homebox.lens.domain.usecase.GetItemDetailsUseCase import com.homebox.lens.domain.usecase.GetItemDetailsUseCase
import com.homebox.lens.data.mapper.toDomain
import com.homebox.lens.domain.usecase.UpdateItemUseCase import com.homebox.lens.domain.usecase.UpdateItemUseCase
import com.homebox.lens.ui.mapper.ItemMapper import com.homebox.lens.ui.mapper.ItemMapper
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
@@ -59,6 +60,8 @@ data class ItemEditUiState(
* @param createItemUseCase Use case for creating a new item. * @param createItemUseCase Use case for creating a new item.
* @param updateItemUseCase Use case for updating an existing item. * @param updateItemUseCase Use case for updating an existing item.
* @param getItemDetailsUseCase Use case for fetching item details. * @param getItemDetailsUseCase Use case for fetching item details.
* @param getAllLocationsUseCase Use case for fetching all locations.
* @param getAllLabelsUseCase Use case for fetching all labels.
* @param itemMapper Mapper for converting between domain and UI item models. * @param itemMapper Mapper for converting between domain and UI item models.
*/ */
@HiltViewModel @HiltViewModel
@@ -141,7 +144,7 @@ class ItemEditViewModel @Inject constructor(
Timber.i("[INFO][ACTION][fetching_all_locations] Fetching all locations.") Timber.i("[INFO][ACTION][fetching_all_locations] Fetching all locations.")
val allLocations = getAllLocationsUseCase().map { Location(it.id, it.name) } val allLocations = getAllLocationsUseCase().map { Location(it.id, it.name) }
Timber.i("[INFO][ACTION][fetching_all_labels] Fetching all labels.") Timber.i("[INFO][ACTION][fetching_all_labels] Fetching all labels.")
val allLabels = getAllLabelsUseCase().map { Label(it.id, it.name) } val allLabels = getAllLabelsUseCase().map { it.toDomain() }
_uiState.value = _uiState.value.copy(allLocations = allLocations, allLabels = allLabels) _uiState.value = _uiState.value.copy(allLocations = allLocations, allLabels = allLabels)
Timber.i("[INFO][ACTION][all_locations_labels_fetched] Successfully fetched all locations and labels.") Timber.i("[INFO][ACTION][all_locations_labels_fetched] Successfully fetched all locations and labels.")
} catch (e: Exception) { } catch (e: Exception) {

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.labeledit // [PACKAGE] com.homebox.lens.ui.screen.labeledit
// [FILE] LabelEditScreen.kt // [FILE] LabelEditScreen.kt
// [SEMANTICS] ui, screen, label, edit // [SEMANTICS] app, ui, screen, edit, label
package com.homebox.lens.ui.screen.labeledit package com.homebox.lens.ui.screen.labeledit
@@ -24,10 +24,10 @@ import com.homebox.lens.ui.components.LoadingOverlay
// [ENTITY: Function('LabelEditScreen')] // [ENTITY: Function('LabelEditScreen')]
// [RELATION: Function('LabelEditScreen')] -> [DEPENDS_ON] -> [ViewModel('LabelEditViewModel')] // [RELATION: Function('LabelEditScreen')] -> [DEPENDS_ON] -> [ViewModel('LabelEditViewModel')]
/** /**
* @summary Composable-функция для экрана "Редактирование метки". * @summary Composable function for the "Edit Label" screen.
* @param labelId ID метки для редактирования или null для создания новой. * @param labelId The ID of the label to edit, or null to create a new one.
* @param onBack Навигация назад. * @param onBack Navigation back.
* @param onLabelSaved Действие после сохранения метки. * @param onLabelSaved Action after the label is saved.
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.labeledit // [PACKAGE] com.homebox.lens.ui.screen.labeledit
// [FILE] LabelEditViewModel.kt // [FILE] LabelEditViewModel.kt
// [SEMANTICS] ui, viewmodel, label_management // [SEMANTICS] app, ui, viewmodel, edit, label
package com.homebox.lens.ui.screen.labeledit package com.homebox.lens.ui.screen.labeledit

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.labelslist // [PACKAGE] com.homebox.lens.ui.screen.labelslist
// [FILE] LabelsListScreen.kt // [FILE] LabelsListScreen.kt
// [SEMANTICS] ui, labels_list, state_management, compose, dialog // [SEMANTICS] app, ui, screen, list, label
package com.homebox.lens.ui.screen.labelslist package com.homebox.lens.ui.screen.labelslist
// [IMPORTS] // [IMPORTS]
@@ -46,10 +46,10 @@ import timber.log.Timber
// [RELATION: Function('LabelsListScreen')] -> [DEPENDS_ON] -> [ViewModel('LabelsListViewModel')] // [RELATION: Function('LabelsListScreen')] -> [DEPENDS_ON] -> [ViewModel('LabelsListViewModel')]
// [RELATION: Function('LabelsListScreen')] -> [DEPENDS_ON] -> [Framework('NavController')] // [RELATION: Function('LabelsListScreen')] -> [DEPENDS_ON] -> [Framework('NavController')]
/** /**
* @summary Отображает экран со списком всех меток. * @summary Displays the screen with a list of all labels.
* @param currentRoute Текущий маршрут навигации. * @param currentRoute The current navigation route.
* @param navigationActions Объект, содержащий действия по навигации. * @param navigationActions The object containing navigation actions.
* @param viewModel ViewModel, предоставляющая состояние UI для экрана меток. * @param viewModel The ViewModel providing the UI state for the labels screen.
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@@ -116,10 +116,10 @@ fun LabelsListScreen(
// [ENTITY: Function('LabelsList')] // [ENTITY: Function('LabelsList')]
// [RELATION: Function('LabelsList')] -> [DEPENDS_ON] -> [DataClass('Label')] // [RELATION: Function('LabelsList')] -> [DEPENDS_ON] -> [DataClass('Label')]
/** /**
* @summary Composable-функция для отображения списка меток. * @summary Composable function for displaying a list of labels.
* @param labels Список объектов `Label` для отображения. * @param labels The list of `Label` objects to display.
* @param onLabelClick Лямбда-функция, вызываемая при нажатии на элемент списка. * @param onLabelClick A lambda function called when a list item is clicked.
* @param modifier Модификатор для настройки внешнего вида. * @param modifier A modifier for customizing the appearance.
*/ */
@Composable @Composable
private fun LabelsList( private fun LabelsList(
@@ -145,9 +145,9 @@ private fun LabelsList(
// [ENTITY: Function('LabelListItem')] // [ENTITY: Function('LabelListItem')]
// [RELATION: Function('LabelListItem')] -> [DEPENDS_ON] -> [DataClass('Label')] // [RELATION: Function('LabelListItem')] -> [DEPENDS_ON] -> [DataClass('Label')]
/** /**
* @summary Composable-функция для отображения одного элемента в списке меток. * @summary Composable function for displaying a single item in the list of labels.
* @param label Объект `Label`, который нужно отобразить. * @param label The `Label` object to display.
* @param onClick Лямбда-функция, вызываемая при нажатии на элемент. * @param onClick A lambda function called when the item is clicked.
*/ */
@Composable @Composable
private fun LabelListItem( private fun LabelListItem(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.labelslist // [PACKAGE] com.homebox.lens.ui.screen.labelslist
// [FILE] LabelsListUiState.kt // [FILE] LabelsListUiState.kt
// [SEMANTICS] ui_state, sealed_interface, contract // [SEMANTICS] app, ui, state, list, label
package com.homebox.lens.ui.screen.labelslist package com.homebox.lens.ui.screen.labelslist
// [IMPORTS] // [IMPORTS]
@@ -9,17 +9,17 @@ import com.homebox.lens.domain.model.Label
// [ENTITY: SealedInterface('LabelsListUiState')] // [ENTITY: SealedInterface('LabelsListUiState')]
/** /**
* @summary Определяет все возможные состояния для UI экрана со списком меток. * @summary Defines all possible states for the UI of the screen with a list of labels.
* @description Использование sealed-интерфейса позволяет исчерпывающе обрабатывать все состояния в Composable-функциях. * @description Using a sealed interface allows for exhaustive handling of all states in Composable functions.
*/ */
sealed interface LabelsListUiState { sealed interface LabelsListUiState {
// [ENTITY: DataClass('Success')] // [ENTITY: DataClass('Success')]
// [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('Label')] // [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('Label')]
/** /**
* @summary Состояние успеха, содержит список меток и состояние диалога. * @summary The success state, contains the list of labels and the state of the dialog.
* @param labels Список меток для отображения. * @param labels The list of labels to display.
* @param isShowingCreateDialog Флаг, показывающий, должен ли быть отображен диалог создания метки. * @param isShowingCreateDialog A flag indicating whether the label creation dialog should be displayed.
* @invariant labels не может быть null. * @invariant labels cannot be null.
*/ */
data class Success( data class Success(
val labels: List<Label>, val labels: List<Label>,
@@ -29,17 +29,17 @@ sealed interface LabelsListUiState {
// [ENTITY: DataClass('Error')] // [ENTITY: DataClass('Error')]
/** /**
* @summary Состояние ошибки. * @summary The error state.
* @param message Текст ошибки для отображения пользователю. * @param message The error text to display to the user.
* @invariant message не может быть пустой. * @invariant message cannot be empty.
*/ */
data class Error(val message: String) : LabelsListUiState data class Error(val message: String) : LabelsListUiState
// [END_ENTITY: DataClass('Error')] // [END_ENTITY: DataClass('Error')]
// [ENTITY: Object('Loading')] // [ENTITY: Object('Loading')]
/** /**
* @summary Состояние загрузки данных. * @summary The data loading state.
* @description Указывает, что идет процесс загрузки меток. * @description Indicates that the process of loading labels is in progress.
*/ */
data object Loading : LabelsListUiState data object Loading : LabelsListUiState
// [END_ENTITY: Object('Loading')] // [END_ENTITY: Object('Loading')]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.labelslist // [PACKAGE] com.homebox.lens.ui.screen.labelslist
// [FILE] LabelsListViewModel.kt // [FILE] LabelsListViewModel.kt
// [SEMANTICS] ui_logic, labels_list, state_management, dialog_management // [SEMANTICS] app, ui, viewmodel, list, label
package com.homebox.lens.ui.screen.labelslist package com.homebox.lens.ui.screen.labelslist
// [IMPORTS] // [IMPORTS]
@@ -21,9 +21,9 @@ import javax.inject.Inject
// [RELATION: ViewModel('LabelsListViewModel')] -> [DEPENDS_ON] -> [UseCase('GetAllLabelsUseCase')] // [RELATION: ViewModel('LabelsListViewModel')] -> [DEPENDS_ON] -> [UseCase('GetAllLabelsUseCase')]
// [RELATION: ViewModel('LabelsListViewModel')] -> [EMITS_STATE] -> [SealedInterface('LabelsListUiState')] // [RELATION: ViewModel('LabelsListViewModel')] -> [EMITS_STATE] -> [SealedInterface('LabelsListUiState')]
/** /**
* @summary ViewModel для экрана со списком меток. * @summary ViewModel for the screen with a list of labels.
* @description Управляет состоянием экрана, загружает список меток, обрабатывает ошибки и управляет диалогом создания новой метки. * @description Manages the screen state, loads the list of labels, handles errors, and manages the dialog for creating a new label.
* @invariant `uiState` всегда является одним из состояний, определенных в `LabelsListUiState`. * @invariant `uiState` is always one of the states defined in `LabelsListUiState`.
*/ */
@HiltViewModel @HiltViewModel
class LabelsListViewModel @Inject constructor( class LabelsListViewModel @Inject constructor(
@@ -39,10 +39,10 @@ class LabelsListViewModel @Inject constructor(
// [ENTITY: Function('loadLabels')] // [ENTITY: Function('loadLabels')]
/** /**
* @summary Загружает список меток. * @summary Loads the list of labels.
* @description Выполняет `GetAllLabelsUseCase` и обновляет UI, переключая его * @description Executes `GetAllLabelsUseCase` and updates the UI by switching it
* между состояниями `Loading`, `Success` и `Error`. * between the `Loading`, `Success`, and `Error` states.
* @sideeffect Асинхронно обновляет `_uiState`. * @sideeffect Asynchronously updates `_uiState`.
*/ */
fun loadLabels() { fun loadLabels() {
viewModelScope.launch { viewModelScope.launch {
@@ -77,9 +77,9 @@ class LabelsListViewModel @Inject constructor(
// [ENTITY: Function('onShowCreateDialog')] // [ENTITY: Function('onShowCreateDialog')]
/** /**
* @summary Инициирует отображение диалога для создания метки. * @summary Initiates the display of the dialog for creating a label.
* @description Обновляет состояние `uiState`, устанавливая `isShowingCreateDialog` в `true`. * @description Updates the `uiState` by setting `isShowingCreateDialog` to `true`.
* @sideeffect Обновляет `_uiState`. * @sideeffect Updates `_uiState`.
*/ */
fun onShowCreateDialog() { fun onShowCreateDialog() {
Timber.i("[INFO][ACTION][show_create_dialog] Show create label dialog requested.") Timber.i("[INFO][ACTION][show_create_dialog] Show create label dialog requested.")
@@ -93,9 +93,9 @@ class LabelsListViewModel @Inject constructor(
// [ENTITY: Function('onDismissCreateDialog')] // [ENTITY: Function('onDismissCreateDialog')]
/** /**
* @summary Скрывает диалог создания метки. * @summary Hides the label creation dialog.
* @description Обновляет состояние `uiState`, устанавливая `isShowingCreateDialog` в `false`. * @description Updates the `uiState` by setting `isShowingCreateDialog` to `false`.
* @sideeffect Обновляет `_uiState`. * @sideeffect Updates `_uiState`.
*/ */
fun onDismissCreateDialog() { fun onDismissCreateDialog() {
Timber.i("[INFO][ACTION][dismiss_create_dialog] Dismiss create label dialog requested.") Timber.i("[INFO][ACTION][dismiss_create_dialog] Dismiss create label dialog requested.")
@@ -109,12 +109,12 @@ class LabelsListViewModel @Inject constructor(
// [ENTITY: Function('createLabel')] // [ENTITY: Function('createLabel')]
/** /**
* @summary Создает новую метку. [MVP_SCOPE] ЗАГЛУШКА. * @summary Creates a new label. [MVP_SCOPE] STUB.
* @description В текущей реализации (План Б, Этап 1), эта функция только логирует действие * @description In the current implementation (Plan B, Stage 1), this function only logs the action
* и скрывает диалог. Реальная логика сохранения будет добавлена на следующем этапе. * and hides the dialog. The actual save logic will be added in the next stage.
* @param name Название новой метки. * @param name The name of the new label.
* @precondition `name` не должен быть пустым. * @precondition `name` must not be blank.
* @sideeffect Логирует действие, обновляет `_uiState`, чтобы скрыть диалог. * @sideeffect Logs the action, updates `_uiState` to hide the dialog.
*/ */
fun createLabel(name: String) { fun createLabel(name: String) {
require(name.isNotBlank()) { "[CONTRACT_VIOLATION] Label name cannot be blank." } require(name.isNotBlank()) { "[CONTRACT_VIOLATION] Label name cannot be blank." }

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.locationedit // [PACKAGE] com.homebox.lens.ui.screen.locationedit
// [FILE] LocationEditScreen.kt // [FILE] LocationEditScreen.kt
// [SEMANTICS] ui, screen, location, edit // [SEMANTICS] app, ui, screen, edit, location
package com.homebox.lens.ui.screen.locationedit package com.homebox.lens.ui.screen.locationedit
@@ -19,8 +19,8 @@ import com.homebox.lens.R
// [ENTITY: Function('LocationEditScreen')] // [ENTITY: Function('LocationEditScreen')]
/** /**
* @summary Composable-функция для экрана "Редактирование местоположения". * @summary Composable function for the "Edit Location" screen.
* @param locationId ID местоположения для редактирования или "new" для создания. * @param locationId The ID of the location to edit, or "new" to create one.
*/ */
@Composable @Composable
fun LocationEditScreen( fun LocationEditScreen(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.locationslist // [PACKAGE] com.homebox.lens.ui.screen.locationslist
// [FILE] LocationsListScreen.kt // [FILE] LocationsListScreen.kt
// [SEMANTICS] ui, screen, locations, list // [SEMANTICS] app, ui, screen, list, location
package com.homebox.lens.ui.screen.locationslist package com.homebox.lens.ui.screen.locationslist
@@ -56,12 +56,12 @@ import com.homebox.lens.ui.theme.HomeboxLensTheme
// [RELATION: Function('LocationsListScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')] // [RELATION: Function('LocationsListScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
// [RELATION: Function('LocationsListScreen')] -> [CALLS] -> [Function('MainScaffold')] // [RELATION: Function('LocationsListScreen')] -> [CALLS] -> [Function('MainScaffold')]
/** /**
* @summary Composable-функция для экрана "Список местоположений". * @summary Composable function for the "List of Locations" screen.
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. * @param currentRoute The current route to highlight the active item in the Drawer.
* @param navigationActions Объект с навигационными действиями. * @param navigationActions The object with navigation actions.
* @param onLocationClick Лямбда-обработчик нажатия на местоположение. * @param onLocationClick A lambda handler for clicking on a location.
* @param onAddNewLocationClick Лямбда-обработчик нажатия на кнопку добавления нового местоположения. * @param onAddNewLocationClick A lambda handler for clicking the button to add a new location.
* @param viewModel ViewModel для этого экрана. * @param viewModel The ViewModel for this screen.
*/ */
@Composable @Composable
fun LocationsListScreen( fun LocationsListScreen(
@@ -104,12 +104,12 @@ fun LocationsListScreen(
// [ENTITY: Function('LocationsListContent')] // [ENTITY: Function('LocationsListContent')]
// [RELATION: Function('LocationsListContent')] -> [CONSUMES_STATE] -> [SealedInterface('LocationsListUiState')] // [RELATION: Function('LocationsListContent')] -> [CONSUMES_STATE] -> [SealedInterface('LocationsListUiState')]
/** /**
* @summary Отображает основной контент экрана в зависимости от `uiState`. * @summary Displays the main content of the screen depending on the `uiState`.
* @param modifier Модификатор для стилизации. * @param modifier A modifier for styling.
* @param uiState Текущее состояние UI. * @param uiState The current UI state.
* @param onLocationClick Лямбда-обработчик нажатия на местоположение. * @param onLocationClick A lambda handler for clicking on a location.
* @param onEditLocation Лямбда-обработчик для редактирования местоположения. * @param onEditLocation A lambda handler for editing a location.
* @param onDeleteLocation Лямбда-обработчик для удаления местоположения. * @param onDeleteLocation A lambda handler for deleting a location.
*/ */
@Composable @Composable
private fun LocationsListContent( private fun LocationsListContent(
@@ -167,11 +167,11 @@ private fun LocationsListContent(
// [ENTITY: Function('LocationCard')] // [ENTITY: Function('LocationCard')]
// [RELATION: Function('LocationCard')] -> [DEPENDS_ON] -> [DataClass('LocationOutCount')] // [RELATION: Function('LocationCard')] -> [DEPENDS_ON] -> [DataClass('LocationOutCount')]
/** /**
* @summary Карточка для отображения одного местоположения. * @summary Card for displaying a single location.
* @param location Данные о местоположении. * @param location The data about the location.
* @param onClick Лямбда-обработчик нажатия на карточку. * @param onClick A lambda handler for clicking on the card.
* @param onEditClick Лямбда-обработчик нажатия на "Редактировать". * @param onEditClick A lambda handler for clicking "Edit".
* @param onDeleteClick Лямбда-обработчик нажатия на "Удалить". * @param onDeleteClick A lambda handler for clicking "Delete".
*/ */
@Composable @Composable
private fun LocationCard( private fun LocationCard(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.locationslist // [PACKAGE] com.homebox.lens.ui.screen.locationslist
// [FILE] LocationsListUiState.kt // [FILE] LocationsListUiState.kt
// [SEMANTICS] ui, state, locations // [SEMANTICS] app, ui, state, list, location
package com.homebox.lens.ui.screen.locationslist package com.homebox.lens.ui.screen.locationslist
@@ -10,30 +10,30 @@ import com.homebox.lens.domain.model.LocationOutCount
// [ENTITY: SealedInterface('LocationsListUiState')] // [ENTITY: SealedInterface('LocationsListUiState')]
/** /**
* @summary Определяет возможные состояния UI для экрана списка местоположений. * @summary Defines the possible UI states for the list of locations screen.
* @see LocationsListViewModel * @see LocationsListViewModel
*/ */
sealed interface LocationsListUiState { sealed interface LocationsListUiState {
// [ENTITY: DataClass('Success')] // [ENTITY: DataClass('Success')]
// [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('LocationOutCount')] // [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('LocationOutCount')]
/** /**
* @summary Состояние успешной загрузки данных. * @summary The state of a successful data load.
* @param locations Список местоположений для отображения. * @param locations The list of locations to display.
*/ */
data class Success(val locations: List<LocationOutCount>) : LocationsListUiState data class Success(val locations: List<LocationOutCount>) : LocationsListUiState
// [END_ENTITY: DataClass('Success')] // [END_ENTITY: DataClass('Success')]
// [ENTITY: DataClass('Error')] // [ENTITY: DataClass('Error')]
/** /**
* @summary Состояние ошибки. * @summary The error state.
* @param message Сообщение об ошибке. * @param message The error message.
*/ */
data class Error(val message: String) : LocationsListUiState data class Error(val message: String) : LocationsListUiState
// [END_ENTITY: DataClass('Error')] // [END_ENTITY: DataClass('Error')]
// [ENTITY: Object('Loading')] // [ENTITY: Object('Loading')]
/** /**
* @summary Состояние загрузки данных. * @summary The data loading state.
*/ */
object Loading : LocationsListUiState object Loading : LocationsListUiState
// [END_ENTITY: Object('Loading')] // [END_ENTITY: Object('Loading')]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.locationslist // [PACKAGE] com.homebox.lens.ui.screen.locationslist
// [FILE] LocationsListViewModel.kt // [FILE] LocationsListViewModel.kt
// [SEMANTICS] ui, viewmodel, locations, hilt // [SEMANTICS] app, ui, viewmodel, list, location
package com.homebox.lens.ui.screen.locationslist package com.homebox.lens.ui.screen.locationslist
@@ -21,10 +21,10 @@ import javax.inject.Inject
// [RELATION: ViewModel('LocationsListViewModel')] -> [DEPENDS_ON] -> [UseCase('GetAllLocationsUseCase')] // [RELATION: ViewModel('LocationsListViewModel')] -> [DEPENDS_ON] -> [UseCase('GetAllLocationsUseCase')]
// [RELATION: ViewModel('LocationsListViewModel')] -> [EMITS_STATE] -> [SealedInterface('LocationsListUiState')] // [RELATION: ViewModel('LocationsListViewModel')] -> [EMITS_STATE] -> [SealedInterface('LocationsListUiState')]
/** /**
* @summary ViewModel для экрана списка местоположений. * @summary ViewModel for the list of locations screen.
* @param getAllLocationsUseCase Use case для получения всех местоположений. * @param getAllLocationsUseCase Use case for getting all locations.
* @property uiState Поток, содержащий текущее состояние UI. * @property uiState A flow containing the current UI state.
* @invariant `uiState` всегда отражает результат последней операции загрузки. * @invariant `uiState` always reflects the result of the last load operation.
*/ */
@HiltViewModel @HiltViewModel
class LocationsListViewModel @Inject constructor( class LocationsListViewModel @Inject constructor(
@@ -40,8 +40,8 @@ class LocationsListViewModel @Inject constructor(
// [ENTITY: Function('loadLocations')] // [ENTITY: Function('loadLocations')]
/** /**
* @summary Загружает список местоположений из репозитория. * @summary Loads the list of locations from the repository.
* @sideeffect Обновляет `_uiState` в зависимости от результата: Loading -> Success/Error. * @sideeffect Updates `_uiState` depending on the result: Loading -> Success/Error.
*/ */
fun loadLocations() { fun loadLocations() {
Timber.d("[DEBUG][ENTRYPOINT][loading_locations] Starting to load locations.") Timber.d("[DEBUG][ENTRYPOINT][loading_locations] Starting to load locations.")

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.search // [PACKAGE] com.homebox.lens.ui.screen.search
// [FILE] SearchScreen.kt // [FILE] SearchScreen.kt
// [SEMANTICS] ui, screen, search // [SEMANTICS] app, ui, screen, search
package com.homebox.lens.ui.screen.search package com.homebox.lens.ui.screen.search
@@ -17,9 +17,9 @@ import com.homebox.lens.ui.common.MainScaffold
// [RELATION: Function('SearchScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')] // [RELATION: Function('SearchScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
// [RELATION: Function('SearchScreen')] -> [CALLS] -> [Function('MainScaffold')] // [RELATION: Function('SearchScreen')] -> [CALLS] -> [Function('MainScaffold')]
/** /**
* @summary Composable-функция для экрана "Поиск". * @summary Composable function for the "Search" screen.
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. * @param currentRoute The current route to highlight the active item in the Drawer.
* @param navigationActions Объект с навигационными действиями. * @param navigationActions The object with navigation actions.
*/ */
@Composable @Composable
fun SearchScreen( fun SearchScreen(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.search // [PACKAGE] com.homebox.lens.ui.screen.search
// [FILE] SearchViewModel.kt // [FILE] SearchViewModel.kt
// [SEMANTICS] ui, viewmodel, search // [SEMANTICS] app, ui, viewmodel, search
package com.homebox.lens.ui.screen.search package com.homebox.lens.ui.screen.search
// [IMPORTS] // [IMPORTS]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.settings // [PACKAGE] com.homebox.lens.ui.screen.settings
// [FILE] SettingsScreen.kt // [FILE] SettingsScreen.kt
// [SEMANTICS] ui, screen, settings // [SEMANTICS] app, ui, screen, settings
package com.homebox.lens.ui.screen.settings package com.homebox.lens.ui.screen.settings
@@ -24,9 +24,9 @@ import com.homebox.lens.ui.common.MainScaffold
// [ENTITY: Function('SettingsScreen')] // [ENTITY: Function('SettingsScreen')]
// [RELATION: Function('SettingsScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')] // [RELATION: Function('SettingsScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')]
/** /**
* @summary Composable-функция для экрана настроек. * @summary Composable function for the settings screen.
* @param currentRoute Текущий маршрут навигации. * @param currentRoute The current navigation route.
* @param navigationActions Объект, содержащий действия по навигации. * @param navigationActions The object containing navigation actions.
*/ */
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.screen.setup // [PACKAGE] com.homebox.lens.ui.screen.setup
// [FILE] SetupScreen.kt // [FILE] SetupScreen.kt
// [SEMANTICS] ui, screen, setup, compose // [SEMANTICS] app, ui, screen, setup
@file:OptIn(ExperimentalMaterial3Api::class) @file:OptIn(ExperimentalMaterial3Api::class)
@@ -31,10 +31,10 @@ import com.homebox.lens.R
// [RELATION: Function('SetupScreen')] -> [DEPENDS_ON] -> [ViewModel('SetupViewModel')] // [RELATION: Function('SetupScreen')] -> [DEPENDS_ON] -> [ViewModel('SetupViewModel')]
// [RELATION: Function('SetupScreen')] -> [CALLS] -> [Function('SetupScreenContent')] // [RELATION: Function('SetupScreen')] -> [CALLS] -> [Function('SetupScreenContent')]
/** /**
* @summary Главная Composable-функция для экрана настройки соединения с сервером. * @summary The main Composable function for the server connection setup screen.
* @param viewModel ViewModel для этого экрана, предоставляется через Hilt. * @param viewModel The ViewModel for this screen, provided by Hilt.
* @param onSetupComplete Лямбда, вызываемая после успешной настройки и входа. * @param onSetupComplete A lambda invoked after successful setup and login.
* @sideeffect Вызывает `onSetupComplete` при изменении `uiState.isSetupComplete`. * @sideeffect Calls `onSetupComplete` when `uiState.isSetupComplete` changes.
*/ */
@Composable @Composable
fun SetupScreen( fun SetupScreen(
@@ -60,12 +60,12 @@ fun SetupScreen(
// [ENTITY: Function('SetupScreenContent')] // [ENTITY: Function('SetupScreenContent')]
// [RELATION: Function('SetupScreenContent')] -> [CONSUMES_STATE] -> [DataClass('SetupUiState')] // [RELATION: Function('SetupScreenContent')] -> [CONSUMES_STATE] -> [DataClass('SetupUiState')]
/** /**
* @summary Отображает контент экрана настройки: поля ввода и кнопку. * @summary Displays the content of the setup screen: input fields and a button.
* @param uiState Текущее состояние UI. * @param uiState The current UI state.
* @param onServerUrlChange Лямбда-обработчик изменения URL сервера. * @param onServerUrlChange A lambda handler for changing the server URL.
* @param onUsernameChange Лямбда-обработчик изменения имени пользователя. * @param onUsernameChange A lambda handler for changing the username.
* @param onPasswordChange Лямбда-обработчик изменения пароля. * @param onPasswordChange A lambda handler for changing the password.
* @param onConnectClick Лямбда-обработчик нажатия на кнопку "Подключиться". * @param onConnectClick A lambda handler for clicking the "Connect" button.
*/ */
@Composable @Composable
private fun SetupScreenContent( private fun SetupScreenContent(
@@ -75,11 +75,7 @@ private fun SetupScreenContent(
onPasswordChange: (String) -> Unit, onPasswordChange: (String) -> Unit,
onConnectClick: () -> Unit onConnectClick: () -> Unit
) { ) {
Scaffold( Scaffold { paddingValues ->
topBar = {
TopAppBar(title = { Text(stringResource(id = R.string.setup_title)) })
}
) { paddingValues ->
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -96,8 +92,12 @@ private fun SetupScreenContent(
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Text( Text(
text = stringResource(id = R.string.setup_title), text = stringResource(id = R.string.setup_title),
style = MaterialTheme.typography.headlineLarge, style = MaterialTheme.typography.headlineLarge
fontSize = 28.sp // Adjust font size as needed )
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "Enter your Homebox server details to connect.",
style = MaterialTheme.typography.bodyMedium
) )
Spacer(modifier = Modifier.height(24.dp)) Spacer(modifier = Modifier.height(24.dp))
@@ -106,8 +106,7 @@ private fun SetupScreenContent(
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) { ) {
Column( Column(
modifier = Modifier.padding(16.dp), modifier = Modifier.padding(16.dp)
horizontalAlignment = Alignment.CenterHorizontally
) { ) {
OutlinedTextField( OutlinedTextField(
value = uiState.serverUrl, value = uiState.serverUrl,
@@ -115,14 +114,14 @@ private fun SetupScreenContent(
label = { Text(stringResource(id = R.string.setup_server_url_label)) }, label = { Text(stringResource(id = R.string.setup_server_url_label)) },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField( OutlinedTextField(
value = uiState.username, value = uiState.username,
onValueChange = onUsernameChange, onValueChange = onUsernameChange,
label = { Text(stringResource(id = R.string.setup_username_label)) }, label = { Text(stringResource(id = R.string.setup_username_label)) },
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) )
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField( OutlinedTextField(
value = uiState.password, value = uiState.password,
onValueChange = onPasswordChange, onValueChange = onPasswordChange,
@@ -138,7 +137,7 @@ private fun SetupScreenContent(
enabled = !uiState.isLoading, enabled = !uiState.isLoading,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(56.dp) // Make button more prominent .height(50.dp)
) { ) {
if (uiState.isLoading) { if (uiState.isLoading) {
CircularProgressIndicator( CircularProgressIndicator(
@@ -146,15 +145,14 @@ private fun SetupScreenContent(
color = MaterialTheme.colorScheme.onPrimary color = MaterialTheme.colorScheme.onPrimary
) )
} else { } else {
Text(stringResource(id = R.string.setup_connect_button), fontSize = 18.sp) Text(stringResource(id = R.string.setup_connect_button))
} }
} }
uiState.error?.let { uiState.error?.let {
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( Text(
text = it, text = it,
color = MaterialTheme.colorScheme.error, color = MaterialTheme.colorScheme.error
style = MaterialTheme.typography.bodyMedium
) )
} }
} }

View File

@@ -1,9 +1,9 @@
// [PACKAGE] com.homebox.lens.ui.screen.splash // [PACKAGE] com.homebox.lens.ui.screen.splash
// [FILE] SplashScreen.kt // [FILE] SplashScreen.kt
// [SEMANTICS] ui, screen, splash, navigation, authentication_flow // [SEMANTICS] app, ui, screen, splash
package com.homebox.lens.ui.screen.splash package com.homebox.lens.ui.screen.splash
// [IMPORTS]
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.CircularProgressIndicator
@@ -16,42 +16,44 @@ import androidx.navigation.NavController
import com.homebox.lens.navigation.Screen import com.homebox.lens.navigation.Screen
import com.homebox.lens.ui.screen.setup.SetupViewModel import com.homebox.lens.ui.screen.setup.SetupViewModel
import timber.log.Timber import timber.log.Timber
// [END_IMPORTS]
// [ENTITY: Function('SplashScreen')] // [ENTITY: Composable('SplashScreen')]
// [RELATION: Composable('SplashScreen')] -> [DEPENDS_ON] -> [Framework('NavController')]
// [RELATION: Composable('SplashScreen')] -> [DEPENDS_ON] -> [ViewModel('SetupViewModel')]
/** /**
* @summary Displays a splash screen while checking if credentials are saved. * @summary A splash screen that checks for saved credentials and navigates accordingly.
* @param navController The NavController for navigation. * @param navController The navigation controller for navigating to the next screen.
* @param viewModel The SetupViewModel to check credential status. * @param viewModel The view model for checking credentials.
* @sideeffect Navigates to either SetupScreen or DashboardScreen based on credential status. * @sideeffect Navigates to either the Setup or Dashboard screen.
*/ */
@Composable @Composable
fun SplashScreen( fun SplashScreen(
navController: NavController, navController: NavController,
viewModel: SetupViewModel = hiltViewModel() viewModel: SetupViewModel = hiltViewModel()
) { ) {
Timber.d("[DEBUG][ENTRYPOINT][splash_screen_composable] SplashScreen entered.")
LaunchedEffect(key1 = true) {
Timber.i("[INFO][ACTION][checking_credentials_on_launch] Checking if credentials are saved on launch.")
val credentialsSaved = viewModel.areCredentialsSaved()
if (credentialsSaved) {
Timber.i("[INFO][ACTION][credentials_found_navigating_dashboard] Credentials found, navigating to Dashboard.")
navController.navigate(Screen.Dashboard.route) {
popUpTo(Screen.Splash.route) { inclusive = true }
}
} else {
Timber.i("[INFO][ACTION][no_credentials_found_navigating_setup] No credentials found, navigating to Setup.")
navController.navigate(Screen.Setup.route) {
popUpTo(Screen.Splash.route) { inclusive = true }
}
}
}
Box( Box(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
CircularProgressIndicator() CircularProgressIndicator()
} }
LaunchedEffect(Unit) {
Timber.d("[DEBUG][ACTION][checking_credentials] Checking for saved credentials on splash screen.")
val areCredentialsSaved = viewModel.areCredentialsSaved()
val destination = if (areCredentialsSaved) {
Timber.d("[DEBUG][SUCCESS][credentials_found] Credentials found, navigating to Dashboard.")
Screen.Dashboard.route
} else {
Timber.d("[DEBUG][FALLBACK][no_credentials] No credentials found, navigating to Setup.")
Screen.Setup.route
}
navController.navigate(destination) {
popUpTo(Screen.Splash.route) { inclusive = true }
}
}
} }
// [END_ENTITY: Function('SplashScreen')] // [END_ENTITY: Composable('SplashScreen')]
// [END_FILE_SplashScreen.kt]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.theme // [PACKAGE] com.homebox.lens.ui.theme
// [FILE] Color.kt // [FILE] Color.kt
// [SEMANTICS] ui, theme, color // [SEMANTICS] app, ui, theme, color
package com.homebox.lens.ui.theme package com.homebox.lens.ui.theme
// [IMPORTS] // [IMPORTS]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.theme // [PACKAGE] com.homebox.lens.ui.theme
// [FILE] Theme.kt // [FILE] Theme.kt
// [SEMANTICS] ui, theme // [SEMANTICS] app, ui, theme
package com.homebox.lens.ui.theme package com.homebox.lens.ui.theme
// [IMPORTS] // [IMPORTS]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.ui.theme // [PACKAGE] com.homebox.lens.ui.theme
// [FILE] Typography.kt // [FILE] Typography.kt
// [SEMANTICS] ui, theme, typography // [SEMANTICS] app, ui, theme, typography
package com.homebox.lens.ui.theme package com.homebox.lens.ui.theme
// [IMPORTS] // [IMPORTS]

View File

@@ -13,6 +13,7 @@
<!-- Content Descriptions --> <!-- Content Descriptions -->
<string name="cd_open_navigation_drawer">Открыть боковое меню</string> <string name="cd_open_navigation_drawer">Открыть боковое меню</string>
<string name="setup_subtitle">Enter your Homebox server details to connect.</string>
<string name="cd_scan_qr_code">Сканировать QR-код</string> <string name="cd_scan_qr_code">Сканировать QR-код</string>
<string name="cd_navigate_back">Вернуться назад</string> <string name="cd_navigate_back">Вернуться назад</string>
<string name="cd_add_new_location">Добавить новую локацию</string> <string name="cd_add_new_location">Добавить новую локацию</string>

View File

@@ -11,7 +11,7 @@ import retrofit2.http.*
// [ENTITY: Interface('HomeboxApiService')] // [ENTITY: Interface('HomeboxApiService')]
/** /**
* @summary Определяет эндпоинты для взаимодействия с Homebox API, используя DTO. * @summary Defines the endpoints for interacting with the Homebox API using DTOs.
*/ */
interface HomeboxApiService { interface HomeboxApiService {

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] CustomFieldDto.kt // [FILE] CustomFieldDto.kt
// [SEMANTICS] data_transfer_object, custom_field // [SEMANTICS] data, dto, custom_field
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
@@ -12,7 +12,7 @@ import com.homebox.lens.domain.model.CustomField
// [ENTITY: DataClass('CustomFieldDto')] // [ENTITY: DataClass('CustomFieldDto')]
/** /**
* @summary DTO для кастомного поля. * @summary DTO for a custom field.
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class CustomFieldDto( data class CustomFieldDto(
@@ -25,7 +25,7 @@ data class CustomFieldDto(
// [ENTITY: Function('toDomain')] // [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('CustomField')] // [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('CustomField')]
/** /**
* @summary Маппер из CustomFieldDto в доменную модель CustomField. * @summary Mapper from CustomFieldDto to the CustomField domain model.
*/ */
fun CustomFieldDto.toDomain(): CustomField { fun CustomFieldDto.toDomain(): CustomField {
return CustomField( return CustomField(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] GroupStatisticsDto.kt // [FILE] GroupStatisticsDto.kt
// [SEMANTICS] data_transfer_object, statistics // [SEMANTICS] data, dto, statistics
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
@@ -12,7 +12,7 @@ import com.homebox.lens.domain.model.GroupStatistics
// [ENTITY: DataClass('GroupStatisticsDto')] // [ENTITY: DataClass('GroupStatisticsDto')]
/** /**
* @summary DTO для статистики. * @summary DTO for statistics.
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class GroupStatisticsDto( data class GroupStatisticsDto(
@@ -28,7 +28,7 @@ data class GroupStatisticsDto(
// [ENTITY: Function('toDomain')] // [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('GroupStatistics')] // [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('GroupStatistics')]
/** /**
* @summary Маппер из GroupStatisticsDto в доменную модель GroupStatistics. * @summary Mapper from GroupStatisticsDto to the GroupStatistics domain model.
*/ */
fun GroupStatisticsDto.toDomain(): GroupStatistics { fun GroupStatisticsDto.toDomain(): GroupStatistics {
return GroupStatistics( return GroupStatistics(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] ImageDto.kt // [FILE] ImageDto.kt
// [SEMANTICS] data_transfer_object, image // [SEMANTICS] data, dto, image
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
@@ -12,10 +12,10 @@ import com.homebox.lens.domain.model.Image
// [ENTITY: DataClass('ImageDto')] // [ENTITY: DataClass('ImageDto')]
/** /**
* @summary DTO для изображения. * @summary DTO for an image.
* @param id Уникальный идентификатор. * @param id The unique identifier.
* @param path Путь к файлу. * @param path The path to the file.
* @param isPrimary Является ли основным. * @param isPrimary Whether it is the primary image.
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class ImageDto( data class ImageDto(
@@ -28,7 +28,7 @@ data class ImageDto(
// [ENTITY: Function('toDomain')] // [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('Image')] // [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('Image')]
/** /**
* @summary Маппер из ImageDto в доменную модель Image. * @summary Mapper from ImageDto to the Image domain model.
*/ */
fun ImageDto.toDomain(): Image { fun ImageDto.toDomain(): Image {
return Image( return Image(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] ItemAttachmentDto.kt // [FILE] ItemAttachmentDto.kt
// [SEMANTICS] data_transfer_object, attachment // [SEMANTICS] data, dto, attachment
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
@@ -12,7 +12,7 @@ import com.homebox.lens.domain.model.ItemAttachment
// [ENTITY: DataClass('ItemAttachmentDto')] // [ENTITY: DataClass('ItemAttachmentDto')]
/** /**
* @summary DTO для вложения. * @summary DTO for an attachment.
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class ItemAttachmentDto( data class ItemAttachmentDto(
@@ -28,7 +28,7 @@ data class ItemAttachmentDto(
// [ENTITY: Function('toDomain')] // [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('ItemAttachment')] // [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('ItemAttachment')]
/** /**
* @summary Маппер из ItemAttachmentDto в доменную модель ItemAttachment. * @summary Mapper from ItemAttachmentDto to the ItemAttachment domain model.
*/ */
fun ItemAttachmentDto.toDomain(): ItemAttachment { fun ItemAttachmentDto.toDomain(): ItemAttachment {
return ItemAttachment( return ItemAttachment(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] ItemCreateDto.kt // [FILE] ItemCreateDto.kt
// [SEMANTICS] data_transfer_object, item_creation // [SEMANTICS] data, dto, item_creation
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
@@ -12,7 +12,7 @@ import com.homebox.lens.domain.model.ItemCreate
// [ENTITY: DataClass('ItemCreateDto')] // [ENTITY: DataClass('ItemCreateDto')]
/** /**
* @summary DTO для создания вещи. * @summary DTO for creating an item.
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class ItemCreateDto( data class ItemCreateDto(
@@ -46,7 +46,7 @@ data class ItemCreateDto(
// [ENTITY: Function('toDto')] // [ENTITY: Function('toDto')]
// [RELATION: Function('toDto')] -> [RETURNS] -> [DataClass('ItemCreateDto')] // [RELATION: Function('toDto')] -> [RETURNS] -> [DataClass('ItemCreateDto')]
/** /**
* @summary Маппер из доменной модели ItemCreate в ItemCreateDto. * @summary Mapper from the ItemCreate domain model to ItemCreateDto.
*/ */
fun ItemCreate.toItemCreateDto(): ItemCreateDto { fun ItemCreate.toItemCreateDto(): ItemCreateDto {
return ItemCreateDto( return ItemCreateDto(

View File

@@ -1,74 +0,0 @@
// [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] ItemDto.kt
// [SEMANTICS] data, dto, api
package com.homebox.lens.data.api.dto
// [IMPORTS]
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import java.math.BigDecimal
// [END_IMPORTS]
// [ENTITY: DataClass('ItemOut')]
// [RELATION: DataClass('ItemOut')] -> [DEPENDS_ON] -> [DataClass('LocationOut')]
// [RELATION: DataClass('ItemOut')] -> [DEPENDS_ON] -> [DataClass('LabelOutDto')]
/**
* @summary DTO для полной информации о вещи (GET /v1/items/{id}).
*/
@JsonClass(generateAdapter = true)
data class ItemOut(
@Json(name = "id") val id: String,
@Json(name = "name") val name: String,
@Json(name = "description") val description: String?,
@Json(name = "image") val image: String?,
@Json(name = "location") val location: LocationOut?,
@Json(name = "labels") val labels: List<LabelOutDto>,
@Json(name = "value") val value: BigDecimal?,
@Json(name = "createdAt") val createdAt: String?
)
// [END_ENTITY: DataClass('ItemOut')]
// [ENTITY: DataClass('ItemSummary')]
// [RELATION: DataClass('ItemSummary')] -> [DEPENDS_ON] -> [DataClass('LocationOut')]
/**
* @summary DTO для краткой информации о вещи в списках (GET /v1/items).
*/
@JsonClass(generateAdapter = true)
data class ItemSummary(
@Json(name = "id") val id: String,
@Json(name = "name") val name: String,
@Json(name = "image") val image: String?,
@Json(name = "location") val location: LocationOut?,
@Json(name = "createdAt") val createdAt: String?
)
// [END_ENTITY: DataClass('ItemSummary')]
// [ENTITY: DataClass('ItemCreate')]
/**
* @summary DTO для создания новой вещи (POST /v1/items).
*/
@JsonClass(generateAdapter = true)
data class ItemCreate(
@Json(name = "name") val name: String,
@Json(name = "description") val description: String?,
@Json(name = "locationId") val locationId: String?,
@Json(name = "labelIds") val labelIds: List<String>?,
@Json(name = "value") val value: BigDecimal?
)
// [END_ENTITY: DataClass('ItemCreate')]
// [ENTITY: DataClass('ItemUpdate')]
/**
* @summary DTO для обновления вещи (PUT /v1/items/{id}).
*/
@JsonClass(generateAdapter = true)
data class ItemUpdate(
@Json(name = "name") val name: String,
@Json(name = "description") val description: String?,
@Json(name = "locationId") val locationId: String?,
@Json(name = "labelIds") val labelIds: List<String>?,
@Json(name = "value") val value: BigDecimal?
)
// [END_ENTITY: DataClass('ItemUpdate')]
// [END_FILE_ItemDto.kt]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] ItemOutDto.kt // [FILE] ItemOutDto.kt
// [SEMANTICS] data_transfer_object, item_detailed // [SEMANTICS] data, dto, item_detailed
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
@@ -50,3 +50,46 @@ data class ItemOutDto(
@Json(name = "updatedAt") val updatedAt: String @Json(name = "updatedAt") val updatedAt: String
) )
// [END_ENTITY: DataClass('ItemOutDto')] // [END_ENTITY: DataClass('ItemOutDto')]
// [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('ItemOut')]
/**
* @summary Mapper from ItemOutDto to the ItemOut domain model.
*/
fun ItemOutDto.toDomain(): ItemOut {
return ItemOut(
id = this.id,
name = this.name,
assetId = this.assetId,
description = this.description,
notes = this.notes,
serialNumber = this.serialNumber,
quantity = this.quantity,
isArchived = this.isArchived,
purchasePrice = this.purchasePrice,
purchaseTime = this.purchaseTime,
purchaseFrom = this.purchaseFrom,
warrantyExpires = this.warrantyExpires,
warrantyDetails = this.warrantyDetails,
lifetimeWarranty = this.lifetimeWarranty,
insured = this.insured,
manufacturer = this.manufacturer,
modelNumber = this.modelNumber,
soldPrice = this.soldPrice,
soldTime = this.soldTime,
soldTo = this.soldTo,
soldNotes = this.soldNotes,
syncChildItemsLocations = this.syncChildItemsLocations,
location = this.location?.toDomain(),
parent = this.parent?.toDomain(),
children = this.children.map { it.toDomain() },
labels = this.labels.map { it.toDomain() },
attachments = this.attachments.map { it.toDomain() },
images = this.images.map { it.toDomain() },
fields = this.fields.map { it.toDomain() },
maintenance = this.maintenance.map { it.toDomain() },
createdAt = this.createdAt,
updatedAt = this.updatedAt
)
}
// [END_ENTITY: Function('toDomain')]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] ItemSummaryDto.kt // [FILE] ItemSummaryDto.kt
// [SEMANTICS] data_transfer_object, item_summary // [SEMANTICS] data, dto, item_summary
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
@@ -28,3 +28,24 @@ data class ItemSummaryDto(
@Json(name = "updatedAt") val updatedAt: String @Json(name = "updatedAt") val updatedAt: String
) )
// [END_ENTITY: DataClass('ItemSummaryDto')] // [END_ENTITY: DataClass('ItemSummaryDto')]
// [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('ItemSummary')]
/**
* @summary Mapper from ItemSummaryDto to the ItemSummary domain model.
*/
fun ItemSummaryDto.toDomain(): ItemSummary {
return ItemSummary(
id = this.id,
name = this.name,
assetId = this.assetId,
image = this.image?.toDomain(),
isArchived = this.isArchived,
labels = this.labels.map { it.toDomain() },
location = this.location?.toDomain(),
value = this.value,
createdAt = this.createdAt,
updatedAt = this.updatedAt
)
}
// [END_ENTITY: Function('toDomain')]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] ItemUpdateDto.kt // [FILE] ItemUpdateDto.kt
// [SEMANTICS] data_transfer_object, item_update // [SEMANTICS] data, dto, item_update
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] LabelCreateDto.kt // [FILE] LabelCreateDto.kt
// [SEMANTICS] data_transfer_object, label, create, api // [SEMANTICS] data, dto, label, create
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
// [IMPORTS] // [IMPORTS]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] LabelOutDto.kt // [FILE] LabelOutDto.kt
// [SEMANTICS] data_transfer_object, label // [SEMANTICS] data, dto, label
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
@@ -26,4 +26,21 @@ data class LabelOutDto(
) )
// [END_ENTITY: DataClass('LabelOutDto')] // [END_ENTITY: DataClass('LabelOutDto')]
// [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('LabelOut')]
/**
* @summary Mapper from LabelOutDto to the LabelOut domain model.
*/
fun LabelOutDto.toDomain(): LabelOut {
return LabelOut(
id = this.id,
name = this.name,
description = this.description,
color = this.color ?: "#000000",
isArchived = this.isArchived ?: false,
createdAt = this.createdAt,
updatedAt = this.updatedAt
)
}
// [END_ENTITY: Function('toDomain')]
// [END_FILE_LabelOutDto.kt] // [END_FILE_LabelOutDto.kt]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] LabelSummaryDto.kt // [FILE] LabelSummaryDto.kt
// [SEMANTICS] data_transfer_object, label, summary, api, mapper // [SEMANTICS] data, dto, label, summary
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
// [IMPORTS] // [IMPORTS]

View File

@@ -1,15 +1,17 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] LabelUpdateDto.kt // [FILE] LabelUpdateDto.kt
// [SEMANTICS] data_transfer_object, label, update // [SEMANTICS] data, dto, label, update
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
// [IMPORTS] // [IMPORTS]
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import com.homebox.lens.domain.model.LabelUpdate
// [END_IMPORTS] // [END_IMPORTS]
// [ENTITY: DataClass('LabelUpdateDto')] // [ENTITY: DataClass('LabelUpdateDto')]
/**
* @summary DTO for updating a label.
*/
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class LabelUpdateDto( data class LabelUpdateDto(
@Json(name = "name") @Json(name = "name")

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] LocationCreateDto.kt // [FILE] LocationCreateDto.kt
// [SEMANTICS] data_transfer_object, location, create // [SEMANTICS] data, dto, location, create
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
// [IMPORTS] // [IMPORTS]
@@ -9,6 +9,9 @@ import com.squareup.moshi.JsonClass
// [END_IMPORTS] // [END_IMPORTS]
// [ENTITY: DataClass('LocationCreateDto')] // [ENTITY: DataClass('LocationCreateDto')]
/**
* @summary DTO for creating a location.
*/
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class LocationCreateDto( data class LocationCreateDto(
@Json(name = "name") @Json(name = "name")

View File

@@ -1,34 +0,0 @@
// [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] LocationDto.kt
// [SEMANTICS] data, dto, api, location
package com.homebox.lens.data.api.dto
// [IMPORTS]
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
// [END_IMPORTS]
// [ENTITY: DataClass('LocationOut')]
/**
* @summary DTO для информации о местоположении.
*/
@JsonClass(generateAdapter = true)
data class LocationOut(
@Json(name = "id") val id: String,
@Json(name = "name") val name: String
)
// [END_ENTITY: DataClass('LocationOut')]
// [ENTITY: DataClass('LocationOutCount')]
/**
* @summary DTO для информации о местоположении со счетчиком вещей.
*/
@JsonClass(generateAdapter = true)
data class LocationOutCount(
@Json(name = "id") val id: String,
@Json(name = "name") val name: String,
@Json(name = "itemCount") val itemCount: Int
)
// [END_ENTITY: DataClass('LocationOutCount')]
// [END_FILE_LocationDto.kt]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] LocationOutCountDto.kt // [FILE] LocationOutCountDto.kt
// [SEMANTICS] data_transfer_object, location, count // [SEMANTICS] data, dto, location, count
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
@@ -12,7 +12,7 @@ import com.homebox.lens.domain.model.LocationOutCount
// [ENTITY: DataClass('LocationOutCountDto')] // [ENTITY: DataClass('LocationOutCountDto')]
/** /**
* @summary DTO для местоположения со счетчиком. * @summary DTO for a location with an item count.
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class LocationOutCountDto( data class LocationOutCountDto(
@@ -27,4 +27,21 @@ data class LocationOutCountDto(
) )
// [END_ENTITY: DataClass('LocationOutCountDto')] // [END_ENTITY: DataClass('LocationOutCountDto')]
// [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('LocationOutCount')]
/**
* @summary Mapper from LocationOutCountDto to the LocationOutCount domain model.
*/
fun LocationOutCountDto.toDomain(): LocationOutCount {
return LocationOutCount(
id = this.id,
name = this.name,
color = this.color ?: "#000000",
isArchived = this.isArchived ?: false,
itemCount = this.itemCount,
createdAt = this.createdAt,
updatedAt = this.updatedAt
)
}
// [END_ENTITY: Function('toDomain')]
// [END_FILE_LocationOutCountDto.kt] // [END_FILE_LocationOutCountDto.kt]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] LocationOutDto.kt // [FILE] LocationOutDto.kt
// [SEMANTICS] data_transfer_object, location, output // [SEMANTICS] data, dto, location
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
// [IMPORTS] // [IMPORTS]
@@ -27,4 +27,20 @@ data class LocationOutDto(
) )
// [END_ENTITY: DataClass('LocationOutDto')] // [END_ENTITY: DataClass('LocationOutDto')]
// [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('LocationOut')]
/**
* @summary Mapper from LocationOutDto to the LocationOut domain model.
*/
fun LocationOutDto.toDomain(): LocationOut {
return LocationOut(
id = this.id,
name = this.name,
color = this.color,
isArchived = this.isArchived,
createdAt = this.createdAt,
updatedAt = this.updatedAt
)
}
// [END_ENTITY: Function('toDomain')]
// [END_FILE_LocationOutDto.kt] // [END_FILE_LocationOutDto.kt]

View File

@@ -1,15 +1,17 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] LocationUpdateDto.kt // [FILE] LocationUpdateDto.kt
// [SEMANTICS] data_transfer_object, location, update // [SEMANTICS] data, dto, location, update
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
// [IMPORTS] // [IMPORTS]
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import com.homebox.lens.domain.model.LocationUpdate
// [END_IMPORTS] // [END_IMPORTS]
// [ENTITY: DataClass('LocationUpdateDto')] // [ENTITY: DataClass('LocationUpdateDto')]
/**
* @summary DTO for updating a location.
*/
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class LocationUpdateDto( data class LocationUpdateDto(
@Json(name = "name") @Json(name = "name")

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] LoginFormDto.kt // [FILE] LoginFormDto.kt
// [SEMANTICS] data, dto, api, login // [SEMANTICS] data, dto, login
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
// [IMPORTS] // [IMPORTS]
@@ -9,6 +9,9 @@ import com.squareup.moshi.JsonClass
// [END_IMPORTS] // [END_IMPORTS]
// [ENTITY: DataClass('LoginFormDto')] // [ENTITY: DataClass('LoginFormDto')]
/**
* @summary DTO for the login form.
*/
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class LoginFormDto( data class LoginFormDto(
@Json(name = "username") val username: String, @Json(name = "username") val username: String,

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] MaintenanceEntryDto.kt // [FILE] MaintenanceEntryDto.kt
// [SEMANTICS] data_transfer_object, maintenance // [SEMANTICS] data, dto, maintenance
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
@@ -12,7 +12,7 @@ import com.homebox.lens.domain.model.MaintenanceEntry
// [ENTITY: DataClass('MaintenanceEntryDto')] // [ENTITY: DataClass('MaintenanceEntryDto')]
/** /**
* @summary DTO для записи об обслуживании. * @summary DTO for a maintenance entry.
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class MaintenanceEntryDto( data class MaintenanceEntryDto(
@@ -30,7 +30,7 @@ data class MaintenanceEntryDto(
// [ENTITY: Function('toDomain')] // [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('MaintenanceEntry')] // [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('MaintenanceEntry')]
/** /**
* @summary Маппер из MaintenanceEntryDto в доменную модель MaintenanceEntry. * @summary Mapper from MaintenanceEntryDto to the MaintenanceEntry domain model.
*/ */
fun MaintenanceEntryDto.toDomain(): MaintenanceEntry { fun MaintenanceEntryDto.toDomain(): MaintenanceEntry {
return MaintenanceEntry( return MaintenanceEntry(

View File

@@ -1,25 +0,0 @@
// [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] PaginationDto.kt
// [SEMANTICS] data, dto, api, pagination
package com.homebox.lens.data.api.dto
// [IMPORTS]
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
// [END_IMPORTS]
// [ENTITY: DataClass('PaginationResult')]
/**
* @summary DTO для пагинированных результатов от API.
*/
@JsonClass(generateAdapter = true)
data class PaginationResult<T>(
@Json(name = "items") val items: List<T>,
@Json(name = "page") val page: Int,
@Json(name = "pages") val pages: Int,
@Json(name = "total") val total: Int,
@Json(name = "pageSize") val pageSize: Int
)
// [END_ENTITY: DataClass('PaginationResult')]
// [END_FILE_PaginationDto.kt]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] PaginationResultDto.kt // [FILE] PaginationResultDto.kt
// [SEMANTICS] data_transfer_object, pagination // [SEMANTICS] data, dto, pagination
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
@@ -22,3 +22,18 @@ data class PaginationResultDto<T>(
@Json(name = "total") val total: Int @Json(name = "total") val total: Int
) )
// [END_ENTITY: DataClass('PaginationResultDto')] // [END_ENTITY: DataClass('PaginationResultDto')]
// [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('PaginationResult')]
/**
* @summary Mapper from PaginationResultDto to the PaginationResult domain model.
*/
fun <T, R> PaginationResultDto<T>.toDomain(mapper: (T) -> R): PaginationResult<R> {
return PaginationResult(
items = this.items.map(mapper),
page = this.page,
pageSize = this.pageSize,
total = this.total
)
}
// [END_ENTITY: Function('toDomain')]

View File

@@ -1,25 +0,0 @@
// [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] StatisticsDto.kt
// [SEMANTICS] data, dto, api, statistics
package com.homebox.lens.data.api.dto
// [IMPORTS]
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import java.math.BigDecimal
// [END_IMPORTS]
// [ENTITY: DataClass('GroupStatistics')]
/**
* @summary DTO для статистической информации.
*/
@JsonClass(generateAdapter = true)
data class GroupStatistics(
@Json(name = "totalValue") val totalValue: BigDecimal,
@Json(name = "totalItems") val totalItems: Int,
@Json(name = "locations") val locations: Int,
@Json(name = "labels") val labels: Int
)
// [END_ENTITY: DataClass('GroupStatistics')]
// [END_FILE_StatisticsDto.kt]

View File

@@ -1,14 +1,18 @@
// [PACKAGE] com.homebox.lens.data.api.dto // [PACKAGE] com.homebox.lens.data.api.dto
// [FILE] TokenResponseDto.kt // [FILE] TokenResponseDto.kt
// [SEMANTICS] data, dto, api, token // [SEMANTICS] data, dto, token
package com.homebox.lens.data.api.dto package com.homebox.lens.data.api.dto
// [IMPORTS] // [IMPORTS]
import com.homebox.lens.domain.model.TokenResponse
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
// [END_IMPORTS] // [END_IMPORTS]
// [ENTITY: DataClass('TokenResponseDto')] // [ENTITY: DataClass('TokenResponseDto')]
/**
* @summary DTO for the token response.
*/
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class TokenResponseDto( data class TokenResponseDto(
@Json(name = "token") val token: String, @Json(name = "token") val token: String,
@@ -16,4 +20,16 @@ data class TokenResponseDto(
@Json(name = "expiresAt") val expiresAt: String @Json(name = "expiresAt") val expiresAt: String
) )
// [END_ENTITY: DataClass('TokenResponseDto')] // [END_ENTITY: DataClass('TokenResponseDto')]
// [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('TokenResponse')]
/**
* @summary Mapper from TokenResponseDto to the TokenResponse domain model.
*/
fun TokenResponseDto.toDomain(): TokenResponse {
return TokenResponse(
token = this.token
)
}
// [END_ENTITY: Function('toDomain')]
// [END_FILE_TokenResponseDto.kt] // [END_FILE_TokenResponseDto.kt]

View File

@@ -1,30 +0,0 @@
// [PACKAGE] com.homebox.lens.data.api.mapper
// [FILE] TokenMapper.kt
// [SEMANTICS] mapper, data_conversion, clean_architecture
package com.homebox.lens.data.api.mapper
// [IMPORTS]
import com.homebox.lens.data.api.dto.TokenResponseDto
import com.homebox.lens.domain.model.TokenResponse
// [END_IMPORTS]
// [ENTITY: Function('toDomain')]
// [RELATION: Function('toDomain')] -> [RETURNS] -> [DataClass('TokenResponse')]
/**
* @summary Преобразует DTO-объект токена в доменную модель.
* @receiver [TokenResponseDto] объект из слоя данных.
* @return [TokenResponse] объект для доменного слоя.
* @throws IllegalArgumentException если токен в DTO пустой.
*/
fun TokenResponseDto.toDomain(): TokenResponse {
require(this.token.isNotBlank()) { "DTO token is blank, cannot map to domain model." }
val domainModel = TokenResponse(token = this.token)
check(domainModel.token.isNotBlank()) { "Domain model token is blank after mapping." }
return domainModel
}
// [END_ENTITY: Function('toDomain')]
// [END_FILE_TokenMapper.kt]

View File

@@ -1,19 +1,23 @@
// [PACKAGE] com.homebox.lens.data.api.model // [PACKAGE] com.homebox.lens.data.api.model
// [FILE] LoginRequest.kt // [FILE] LoginRequest.kt
// [SEMANTICS] dto, network, serialization, authentication // [SEMANTICS] data, dto, login
package com.homebox.lens.data.api.model package com.homebox.lens.data.api.model
import com.squareup.moshi.Json import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
// [IMPORTS]
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
// [END_IMPORTS]
// [ENTITY: DataClass('LoginRequest')]
/** /**
* [ENTITY: DataClass('LoginRequest')] * @summary DTO for the authentication request.
* [CONTRACT] * @property username The user's name.
* DTO (Data Transfer Object) для запроса на аутентификацию. * @property password The user's password.
* @property username Имя пользователя. * @invariant The properties must not be blank.
* @property password Пароль пользователя.
* @invariant Свойства не должны быть пустыми.
*/ */
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class LoginRequest( data class LoginRequest(
@@ -21,9 +25,9 @@ data class LoginRequest(
@Json(name = "password") val password: String @Json(name = "password") val password: String
) { ) {
init { init {
// [INVARIANT_CHECK] require(username.isNotBlank()) { "Username cannot be blank." }
require(username.isNotBlank()) { "[INVARIANT_FAILED] Username cannot be blank." } require(password.isNotBlank()) { "Password cannot be blank." }
require(password.isNotBlank()) { "[INVARIANT_FAILED] Password cannot be blank." }
} }
} }
// [END_ENTITY: DataClass('LoginRequest')]
// [END_FILE_LoginRequest.kt] // [END_FILE_LoginRequest.kt]

View File

@@ -10,7 +10,7 @@ import java.math.BigDecimal
// [ENTITY: Class('Converters')] // [ENTITY: Class('Converters')]
/** /**
* @summary Предоставляет TypeConverters для Room для типов, не поддерживаемых по умолчанию. * @summary Provides TypeConverters for Room for types not supported by default.
*/ */
class Converters { class Converters {
// [ENTITY: Function('fromString')] // [ENTITY: Function('fromString')]

View File

@@ -15,7 +15,7 @@ import com.homebox.lens.data.db.entity.*
// [ENTITY: Database('HomeboxDatabase')] // [ENTITY: Database('HomeboxDatabase')]
/** /**
* @summary Основной класс для работы с локальной базой данных Room. * @summary The main class for working with the local Room database.
*/ */
@Database( @Database(
entities = [ entities = [

View File

@@ -13,7 +13,7 @@ import kotlinx.coroutines.flow.Flow
// [ENTITY: Interface('ItemDao')] // [ENTITY: Interface('ItemDao')]
/** /**
* @summary Предоставляет методы для работы с 'items' в локальной БД. * @summary Provides methods for working with 'items' in the local DB.
*/ */
@Dao @Dao
interface ItemDao { interface ItemDao {

View File

@@ -13,7 +13,7 @@ import com.homebox.lens.data.db.entity.LabelEntity
// [ENTITY: Interface('LabelDao')] // [ENTITY: Interface('LabelDao')]
/** /**
* @summary Предоставляет методы для работы с 'labels' в локальной БД. * @summary Provides methods for working with 'labels' in the local DB.
*/ */
@Dao @Dao
interface LabelDao { interface LabelDao {

View File

@@ -13,7 +13,7 @@ import com.homebox.lens.data.db.entity.LocationEntity
// [ENTITY: Interface('LocationDao')] // [ENTITY: Interface('LocationDao')]
/** /**
* @summary Предоставляет методы для работы с 'locations' в локальной БД. * @summary Provides methods for working with 'locations' in the local DB.
*/ */
@Dao @Dao
interface LocationDao { interface LocationDao {

View File

@@ -10,7 +10,7 @@ import androidx.room.PrimaryKey
// [ENTITY: DatabaseTable('ItemEntity')] // [ENTITY: DatabaseTable('ItemEntity')]
/** /**
* @summary Представляет собой строку в таблице 'items' в локальной БД. * @summary Represents a row in the 'items' table in the local DB.
*/ */
@Entity(tableName = "items") @Entity(tableName = "items")
data class ItemEntity( data class ItemEntity(

View File

@@ -10,7 +10,7 @@ import androidx.room.Index
// [ENTITY: DatabaseTable('ItemLabelCrossRef')] // [ENTITY: DatabaseTable('ItemLabelCrossRef')]
/** /**
* @summary Таблица для связи "многие-ко-многим" между ItemEntity и LabelEntity. * @summary Table for the many-to-many relationship between ItemEntity and LabelEntity.
*/ */
@Entity( @Entity(
primaryKeys = ["itemId", "labelId"], primaryKeys = ["itemId", "labelId"],

View File

@@ -13,7 +13,7 @@ import androidx.room.Relation
// [RELATION: DataClass('ItemWithLabels')] -> [DEPENDS_ON] -> [DatabaseTable('ItemEntity')] // [RELATION: DataClass('ItemWithLabels')] -> [DEPENDS_ON] -> [DatabaseTable('ItemEntity')]
// [RELATION: DataClass('ItemWithLabels')] -> [DEPENDS_ON] -> [DatabaseTable('LabelEntity')] // [RELATION: DataClass('ItemWithLabels')] -> [DEPENDS_ON] -> [DatabaseTable('LabelEntity')]
/** /**
* @summary POJO для получения ItemEntity вместе со связанными LabelEntity. * @summary POJO for retrieving an ItemEntity with its associated LabelEntity objects.
*/ */
data class ItemWithLabels( data class ItemWithLabels(
@Embedded val item: ItemEntity, @Embedded val item: ItemEntity,

View File

@@ -10,7 +10,7 @@ import androidx.room.PrimaryKey
// [ENTITY: DatabaseTable('LabelEntity')] // [ENTITY: DatabaseTable('LabelEntity')]
/** /**
* @summary Представляет собой строку в таблице 'labels' в локальной БД. * @summary Represents a row in the 'labels' table in the local DB.
*/ */
@Entity(tableName = "labels") @Entity(tableName = "labels")
data class LabelEntity( data class LabelEntity(

View File

@@ -10,7 +10,7 @@ import androidx.room.PrimaryKey
// [ENTITY: DatabaseTable('LocationEntity')] // [ENTITY: DatabaseTable('LocationEntity')]
/** /**
* @summary Представляет собой строку в таблице 'locations' в локальной БД. * @summary Represents a row in the 'locations' table in the local DB.
*/ */
@Entity(tableName = "locations") @Entity(tableName = "locations")
data class LocationEntity( data class LocationEntity(

View File

@@ -11,7 +11,7 @@ import com.homebox.lens.domain.model.*
// [ENTITY: Function('ItemWithLabels.toDomainItemSummary')] // [ENTITY: Function('ItemWithLabels.toDomainItemSummary')]
// [RELATION: Function('ItemWithLabels.toDomainItemSummary')] -> [RETURNS] -> [DataClass('ItemSummary')] // [RELATION: Function('ItemWithLabels.toDomainItemSummary')] -> [RETURNS] -> [DataClass('ItemSummary')]
/** /**
* @summary Преобразует [ItemWithLabels] (сущность БД) в [ItemSummary] (доменную модель). * @summary Converts [ItemWithLabels] (DB entity) to [ItemSummary] (domain model).
*/ */
fun ItemWithLabels.toDomainItemSummary(): ItemSummary { fun ItemWithLabels.toDomainItemSummary(): ItemSummary {
return ItemSummary( return ItemSummary(
@@ -32,7 +32,7 @@ fun ItemWithLabels.toDomainItemSummary(): ItemSummary {
// [ENTITY: Function('ItemEntity.toDomainItem')] // [ENTITY: Function('ItemEntity.toDomainItem')]
// [RELATION: Function('ItemEntity.toDomainItem')] -> [RETURNS] -> [DataClass('Item')] // [RELATION: Function('ItemEntity.toDomainItem')] -> [RETURNS] -> [DataClass('Item')]
/** /**
* @summary Преобразует [ItemEntity] (сущность БД) в [Item] (доменную модель). * @summary Converts [ItemEntity] (DB entity) to [Item] (domain model).
*/ */
fun ItemEntity.toDomainItem(): Item { fun ItemEntity.toDomainItem(): Item {
return Item( return Item(
@@ -71,7 +71,7 @@ fun ItemEntity.toDomainItem(): Item {
// [ENTITY: Function('Item.toItemEntity')] // [ENTITY: Function('Item.toItemEntity')]
// [RELATION: Function('Item.toItemEntity')] -> [RETURNS] -> [DataClass('ItemEntity')] // [RELATION: Function('Item.toItemEntity')] -> [RETURNS] -> [DataClass('ItemEntity')]
/** /**
* @summary Преобразует [Item] (доменную модель) в [ItemEntity] (сущность БД). * @summary Converts [Item] (domain model) to [ItemEntity] (DB entity).
*/ */
fun Item.toItemEntity(): ItemEntity { fun Item.toItemEntity(): ItemEntity {
return ItemEntity( return ItemEntity(

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.di // [PACKAGE] com.homebox.lens.data.di
// [FILE] ApiModule.kt // [FILE] ApiModule.kt
// [SEMANTICS] di, networking // [SEMANTICS] data, di, networking
package com.homebox.lens.data.di package com.homebox.lens.data.di
// [IMPORTS] // [IMPORTS]
@@ -25,8 +25,8 @@ import javax.inject.Singleton
// [ENTITY: Module('ApiModule')] // [ENTITY: Module('ApiModule')]
/** /**
* @summary Hilt-модуль, отвечающий за создание и предоставление всех зависимостей, * @summary Hilt module responsible for creating and providing all dependencies
* необходимых для сетевого взаимодействия. * necessary for network interaction.
*/ */
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.di // [PACKAGE] com.homebox.lens.data.di
// [FILE] DatabaseModule.kt // [FILE] DatabaseModule.kt
// [SEMANTICS] di, hilt, database // [SEMANTICS] data, di, database
package com.homebox.lens.data.di package com.homebox.lens.data.di
// [IMPORTS] // [IMPORTS]
@@ -18,7 +18,7 @@ import javax.inject.Singleton
// [ENTITY: Module('DatabaseModule')] // [ENTITY: Module('DatabaseModule')]
/** /**
* @summary Предоставляет зависимости для работы с базой данных Room. * @summary Provides dependencies for working with the Room database.
*/ */
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.di // [PACKAGE] com.homebox.lens.data.di
// [FILE] RepositoryModule.kt // [FILE] RepositoryModule.kt
// [SEMANTICS] dependency_injection, hilt, module, binding // [SEMANTICS] data, di, repository
package com.homebox.lens.data.di package com.homebox.lens.data.di
@@ -20,8 +20,8 @@ import javax.inject.Singleton
// [ENTITY: Module('RepositoryModule')] // [ENTITY: Module('RepositoryModule')]
/** /**
* @summary Hilt-модуль для предоставления реализаций репозиториев. * @summary Hilt module for providing repository implementations.
* @description Использует `@Binds` для эффективного связывания интерфейсов с их реализациями. * @description Uses `@Binds` for efficient binding of interfaces to their implementations.
*/ */
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
@@ -30,7 +30,7 @@ abstract class RepositoryModule {
// [ENTITY: Function('bindItemRepository')] // [ENTITY: Function('bindItemRepository')]
// [RELATION: Function('bindItemRepository')] -> [PROVIDES] -> [Interface('ItemRepository')] // [RELATION: Function('bindItemRepository')] -> [PROVIDES] -> [Interface('ItemRepository')]
/** /**
* @summary Связывает интерфейс ItemRepository с его реализацией. * @summary Binds the ItemRepository interface to its implementation.
*/ */
@Binds @Binds
@Singleton @Singleton
@@ -42,7 +42,7 @@ abstract class RepositoryModule {
// [ENTITY: Function('bindCredentialsRepository')] // [ENTITY: Function('bindCredentialsRepository')]
// [RELATION: Function('bindCredentialsRepository')] -> [PROVIDES] -> [Interface('CredentialsRepository')] // [RELATION: Function('bindCredentialsRepository')] -> [PROVIDES] -> [Interface('CredentialsRepository')]
/** /**
* @summary Связывает интерфейс CredentialsRepository с его реализацией. * @summary Binds the CredentialsRepository interface to its implementation.
*/ */
@Binds @Binds
@Singleton @Singleton
@@ -54,7 +54,7 @@ abstract class RepositoryModule {
// [ENTITY: Function('bindAuthRepository')] // [ENTITY: Function('bindAuthRepository')]
// [RELATION: Function('bindAuthRepository')] -> [PROVIDES] -> [Interface('AuthRepository')] // [RELATION: Function('bindAuthRepository')] -> [PROVIDES] -> [Interface('AuthRepository')]
/** /**
* @summary Связывает интерфейс AuthRepository с его реализацией. * @summary Binds the AuthRepository interface to its implementation.
*/ */
@Binds @Binds
@Singleton @Singleton

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.di // [PACKAGE] com.homebox.lens.data.di
// [FILE] StorageModule.kt // [FILE] StorageModule.kt
// [SEMANTICS] di, hilt, storage // [SEMANTICS] data, di, storage
package com.homebox.lens.data.di package com.homebox.lens.data.di
// [IMPORTS] // [IMPORTS]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.mapper // [PACKAGE] com.homebox.lens.data.mapper
// [FILE] DomainToDto.kt // [FILE] DomainToDto.kt
// [SEMANTICS] data, mapper, domain, dto // [SEMANTICS] data, mapper
package com.homebox.lens.data.mapper package com.homebox.lens.data.mapper
// [IMPORTS] // [IMPORTS]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.mapper // [PACKAGE] com.homebox.lens.data.mapper
// [FILE] DtoToDomain.kt // [FILE] DtoToDomain.kt
// [SEMANTICS] data, mapper, dto, domain // [SEMANTICS] data, mapper
package com.homebox.lens.data.mapper package com.homebox.lens.data.mapper
// [IMPORTS] // [IMPORTS]
@@ -133,6 +133,13 @@ fun LabelOutDto.toDomainLabel(): DomainLabel {
name = this.name name = this.name
) )
} }
fun com.homebox.lens.domain.model.LabelOut.toDomain(): DomainLabel {
return DomainLabel(
id = this.id,
name = this.name
)
}
// [END_ENTITY: Function('LabelOutDto.toDomain')] // [END_ENTITY: Function('LabelOutDto.toDomain')]
// [ENTITY: Function('LocationOutDto.toDomain')] // [ENTITY: Function('LocationOutDto.toDomain')]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.repository // [PACKAGE] com.homebox.lens.data.repository
// [FILE] AuthRepositoryImpl.kt // [FILE] AuthRepositoryImpl.kt
// [SEMANTICS] data_implementation, authentication, repository // [SEMANTICS] data, repository, authentication
package com.homebox.lens.data.repository package com.homebox.lens.data.repository
@@ -30,10 +30,10 @@ import javax.inject.Inject
// [RELATION: Class('AuthRepositoryImpl')] -> [DEPENDS_ON] -> [Framework('OkHttpClient')] // [RELATION: Class('AuthRepositoryImpl')] -> [DEPENDS_ON] -> [Framework('OkHttpClient')]
// [RELATION: Class('AuthRepositoryImpl')] -> [DEPENDS_ON] -> [Framework('MoshiConverterFactory')] // [RELATION: Class('AuthRepositoryImpl')] -> [DEPENDS_ON] -> [Framework('MoshiConverterFactory')]
/** /**
* @summary Реализация репозитория для управления аутентификацией. * @summary Implementation of the repository for managing authentication.
* @param encryptedPrefs Защищенное хранилище для токена. * @param encryptedPrefs The secure storage for the token.
* @param okHttpClient Общий OkHttp клиент для переиспользования. * @param okHttpClient The shared OkHttp client for reuse.
* @param moshiConverterFactory Общий конвертер Moshi для переиспользования. * @param moshiConverterFactory The shared Moshi converter for reuse.
*/ */
class AuthRepositoryImpl @Inject constructor( class AuthRepositoryImpl @Inject constructor(
private val encryptedPrefs: SharedPreferences, private val encryptedPrefs: SharedPreferences,
@@ -47,10 +47,10 @@ class AuthRepositoryImpl @Inject constructor(
// [ENTITY: Function('login')] // [ENTITY: Function('login')]
/** /**
* @summary Реализует вход пользователя. Создает временный API сервис для выполнения запроса * @summary Implements user login. Creates a temporary API service to execute a request
* на указанный пользователем URL сервера. * to the server URL specified by the user.
* @param credentials Учетные данные пользователя, включая URL сервера. * @param credentials The user's credentials, including the server URL.
* @return [Result] с доменной моделью [TokenResponse] при успехе или [Exception] при ошибке. * @return A [Result] with a [TokenResponse] domain model on success or an [Exception] on failure.
*/ */
override suspend fun login(credentials: Credentials): Result<TokenResponse> { override suspend fun login(credentials: Credentials): Result<TokenResponse> {
require(credentials.serverUrl.isNotBlank()) { "Server URL cannot be blank." } require(credentials.serverUrl.isNotBlank()) { "Server URL cannot be blank." }

View File

@@ -20,10 +20,10 @@ import javax.inject.Inject
// [RELATION: Class('CredentialsRepositoryImpl')] -> [IMPLEMENTS] -> [Interface('CredentialsRepository')] // [RELATION: Class('CredentialsRepositoryImpl')] -> [IMPLEMENTS] -> [Interface('CredentialsRepository')]
// [RELATION: Class('CredentialsRepositoryImpl')] -> [DEPENDS_ON] -> [Framework('SharedPreferences')] // [RELATION: Class('CredentialsRepositoryImpl')] -> [DEPENDS_ON] -> [Framework('SharedPreferences')]
/** /**
* @summary Реализует репозиторий для управления учетными данными пользователя. * @summary Implements the repository for managing user credentials.
* @description Взаимодействует с зашифрованными SharedPreferences для сохранения и извлечения данных. * @description Interacts with encrypted SharedPreferences to save and retrieve data.
* @param encryptedPrefs Зашифрованное хранилище ключ-значение, предоставляемое Hilt. * @param encryptedPrefs The encrypted key-value store provided by Hilt.
* @invariant Состояние этого репозитория полностью зависит от содержимого `encryptedPrefs`. * @invariant The state of this repository is entirely dependent on the contents of `encryptedPrefs`.
*/ */
class CredentialsRepositoryImpl @Inject constructor( class CredentialsRepositoryImpl @Inject constructor(
private val encryptedPrefs: SharedPreferences private val encryptedPrefs: SharedPreferences
@@ -38,9 +38,9 @@ class CredentialsRepositoryImpl @Inject constructor(
// [ENTITY: Function('saveCredentials')] // [ENTITY: Function('saveCredentials')]
/** /**
* @summary Сохраняет основные учетные данные пользователя. * @summary Saves the user's primary credentials.
* @param credentials Объект с учетными данными для сохранения. * @param credentials The credentials object to save.
* @sideeffect Перезаписывает существующие учетные данные в SharedPreferences. * @sideeffect Overwrites existing credentials in SharedPreferences.
*/ */
override suspend fun saveCredentials(credentials: Credentials) { override suspend fun saveCredentials(credentials: Credentials) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@@ -56,8 +56,8 @@ class CredentialsRepositoryImpl @Inject constructor(
// [ENTITY: Function('getCredentials')] // [ENTITY: Function('getCredentials')]
/** /**
* @summary Извлекает сохраненные учетные данные пользователя в виде потока. * @summary Retrieves the saved user credentials as a Flow.
* @return Flow, который эммитит объект [Credentials] или null, если данные отсутствуют. * @return A Flow that emits a [Credentials] object or null if no data is present.
*/ */
override fun getCredentials(): Flow<Credentials?> = flow { override fun getCredentials(): Flow<Credentials?> = flow {
Timber.d("[DEBUG][ACTION][getting_credentials] Getting user credentials.") Timber.d("[DEBUG][ACTION][getting_credentials] Getting user credentials.")
@@ -77,9 +77,9 @@ class CredentialsRepositoryImpl @Inject constructor(
// [ENTITY: Function('saveToken')] // [ENTITY: Function('saveToken')]
/** /**
* @summary Сохраняет токен авторизации. * @summary Saves the authorization token.
* @param token Токен для сохранения. * @param token The token to save.
* @sideeffect Перезаписывает существующий токен в SharedPreferences. * @sideeffect Overwrites the existing token in SharedPreferences.
*/ */
override suspend fun saveToken(token: String) { override suspend fun saveToken(token: String) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
@@ -93,8 +93,8 @@ class CredentialsRepositoryImpl @Inject constructor(
// [ENTITY: Function('getToken')] // [ENTITY: Function('getToken')]
/** /**
* @summary Извлекает сохраненный токен авторизации. * @summary Retrieves the saved authorization token.
* @return Строка с токеном или null, если он не найден. * @return A string with the token or null if it is not found.
*/ */
override suspend fun getToken(): String? { override suspend fun getToken(): String? {
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
@@ -111,8 +111,8 @@ class CredentialsRepositoryImpl @Inject constructor(
// [ENTITY: Function('clearAllCredentials')] // [ENTITY: Function('clearAllCredentials')]
/** /**
* @summary Очищает все сохраненные учетные данные и токены. * @summary Clears all saved credentials and tokens.
* @sideeffect Удаляет все записи, связанные с учетными данными, из SharedPreferences. * @sideeffect Removes all records related to credentials from SharedPreferences.
*/ */
override suspend fun clearAllCredentials() { override suspend fun clearAllCredentials() {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.repository // [PACKAGE] com.homebox.lens.data.repository
// [FILE] ItemRepositoryImpl.kt // [FILE] ItemRepositoryImpl.kt
// [SEMANTICS] data_repository, implementation, items, labels // [SEMANTICS] data, repository, item
package com.homebox.lens.data.repository package com.homebox.lens.data.repository
// [IMPORTS] // [IMPORTS]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.data.security // [PACKAGE] com.homebox.lens.data.security
// [FILE] CryptoManager.kt // [FILE] CryptoManager.kt
// [SEMANTICS] data, security, cryptography // [SEMANTICS] data, security, crypto
package com.homebox.lens.data.security package com.homebox.lens.data.security
// [IMPORTS] // [IMPORTS]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.domain.model // [PACKAGE] com.homebox.lens.domain.model
// [FILE] CustomField.kt // [FILE] CustomField.kt
// [SEMANTICS] data_structure, entity, custom_field // [SEMANTICS] domain, model, custom_field
package com.homebox.lens.domain.model package com.homebox.lens.domain.model
// [ENTITY: DataClass('CustomField')] // [ENTITY: DataClass('CustomField')]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.domain.model // [PACKAGE] com.homebox.lens.domain.model
// [FILE] GroupStatistics.kt // [FILE] GroupStatistics.kt
// [SEMANTICS] data_structure, statistics // [SEMANTICS] domain, model, statistics
package com.homebox.lens.domain.model package com.homebox.lens.domain.model
// [ENTITY: DataClass('GroupStatistics')] // [ENTITY: DataClass('GroupStatistics')]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.domain.model // [PACKAGE] com.homebox.lens.domain.model
// [FILE] Image.kt // [FILE] Image.kt
// [SEMANTICS] data_structure, entity, image // [SEMANTICS] domain, model, image
package com.homebox.lens.domain.model package com.homebox.lens.domain.model
// [ENTITY: DataClass('Image')] // [ENTITY: DataClass('Image')]

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.domain.model // [PACKAGE] com.homebox.lens.domain.model
// [FILE] Item.kt // [FILE] Item.kt
// [SEMANTICS] domain, model // [SEMANTICS] domain, model, item, data_model
package com.homebox.lens.domain.model package com.homebox.lens.domain.model
// [IMPORTS] // [IMPORTS]
@@ -12,35 +12,40 @@ import com.homebox.lens.domain.model.Image
// [ENTITY: DataClass('Item')] // [ENTITY: DataClass('Item')]
// [RELATION: DataClass('Item')] -> [DEPENDS_ON] -> [DataClass('Location')] // [RELATION: DataClass('Item')] -> [DEPENDS_ON] -> [DataClass('Location')]
// [RELATION: DataClass('Item')] -> [DEPENDS_ON] -> [DataClass('Label')] // [RELATION: DataClass('Item')] -> [DEPENDS_ON] -> [DataClass('Label')]
// [RELATION: DataClass('Item')] -> [DEPENDS_ON] -> [DataClass('CustomField')]
/** /**
* @summary Представляет собой вещь в инвентаре. * @summary A comprehensive data model representing an inventory item.
* @param id Уникальный идентификатор вещи. * @param id The unique identifier for the item.
* @param name Название вещи. * @param name The name of the item.
* @param description Описание вещи. * @param description A detailed description of the item.
* @param image Url изображения. * @param quantity The number of units of this item.
* @param location Местоположение вещи. * @param image An optional URL to an image of the item.
* @param labels Список меток, присвоенных вещи. * @param location The physical or logical location of the item.
* @param purchasePrice Цена покупки вещи. * @param labels A list of tags or categories associated with the item.
* @param createdAt Дата создания. * @param purchasePrice The price at which the item was purchased.
* @param archived Архивирована ли вещь. * @param createdAt The timestamp when the item was created.
* @param assetId Идентификатор актива. * @param archived A flag indicating if the item is archived.
* @param fields Пользовательские поля. * @param assetId An external asset identifier.
* @param insured Застрахована ли вещь. * @param fields A list of custom-defined fields for the item.
* @param lifetimeWarranty Пожизненная гарантия. * @param insured A flag indicating if the item is insured.
* @param manufacturer Производитель. * @param lifetimeWarranty A flag indicating if the item has a lifetime warranty.
* @param modelNumber Номер модели. * @param manufacturer The manufacturer of the item.
* @param notes Дополнительные заметки. * @param modelNumber The model number of the item.
* @param parentId ID родительского элемента. * @param notes Any additional notes about the item.
* @param purchaseFrom Место покупки. * @param parentId The ID of a parent item, if this is a child item.
* @param purchaseTime Время покупки. * @param purchaseFrom The place where the item was purchased.
* @param serialNumber Серийный номер. * @param purchaseTime The timestamp of the purchase.
* @param soldNotes Заметки о продаже. * @param serialNumber The serial number of the item.
* @param soldPrice Цена продажи. * @param soldNotes Notes related to the sale of the item.
* @param soldTime Время продажи. * @param soldPrice The price at which the item was sold.
* @param soldTo Кому продано. * @param soldTime The timestamp of the sale.
* @param syncChildItemsLocations Синхронизировать местоположения дочерних элементов. * @param soldTo The person or entity the item was sold to.
* @param warrantyDetails Детали гарантии. * @param syncChildItemsLocations A flag to sync locations of child items.
* @param warrantyExpires Дата окончания гарантии. * @param warrantyDetails Details about the item's warranty.
* @param warrantyExpires The expiration date of the warranty.
* @invariant The 'id' must not be blank.
* @invariant The 'name' must not be blank.
* @invariant The 'quantity' must be non-negative.
*/ */
data class Item( data class Item(
val id: String, val id: String,

View File

@@ -1,6 +1,6 @@
// [PACKAGE] com.homebox.lens.domain.model // [PACKAGE] com.homebox.lens.domain.model
// [FILE] ItemAttachment.kt // [FILE] ItemAttachment.kt
// [SEMANTICS] data_structure, entity, attachment // [SEMANTICS] domain, model, attachment
package com.homebox.lens.domain.model package com.homebox.lens.domain.model
// [ENTITY: DataClass('ItemAttachment')] // [ENTITY: DataClass('ItemAttachment')]

View File

@@ -1,24 +1,38 @@
// [PACKAGE] com.homebox.lens.domain.model // [PACKAGE] com.homebox.lens.domain.model
// [FILE] ItemCreate.kt // [FILE] ItemCreate.kt
// [SEMANTICS] data_structure, entity, input, create // [SEMANTICS] domain, model, item_creation
package com.homebox.lens.domain.model package com.homebox.lens.domain.model
// [ENTITY: DataClass('ItemCreate')] // [ENTITY: DataClass('ItemCreate')]
// [RELATION: DataClass('ItemCreate')] -> [DEPENDS_ON] -> [DataClass('Location')]
// [RELATION: DataClass('ItemCreate')] -> [DEPENDS_ON] -> [DataClass('Label')]
/** /**
* @summary Модель данных для создания новой "Вещи". * @summary Data model for creating a new item.
* @param name Название вещи (обязательно). * @param name The name of the item (required).
* @param assetId Идентификатор актива. * @param description An optional description of the item.
* @param description Описание. * @param quantity The number of units of this item.
* @param notes Заметки. * @param archived A flag indicating if the item is archived.
* @param serialNumber Серийный номер. * @param assetId An external asset identifier.
* @param quantity Количество. * @param insured A flag indicating if the item is insured.
* @param value Стоимость. * @param lifetimeWarranty A flag indicating if the item has a lifetime warranty.
* @param purchasePrice Цена покупки. * @param manufacturer The manufacturer of the item.
* @param purchaseDate Дата покупки. * @param modelNumber The model number of the item.
* @param warrantyUntil Гарантия до. * @param notes Any additional notes about the item.
* @param locationId ID местоположения. * @param parentId The ID of a parent item, if this is a child item.
* @param parentId ID родительской вещи. * @param purchaseFrom The place where the item was purchased.
* @param labelIds Список ID меток. * @param purchasePrice The price at which the item was purchased.
* @param purchaseTime The timestamp of the purchase.
* @param serialNumber The serial number of the item.
* @param soldNotes Notes related to the sale of the item.
* @param soldPrice The price at which the item was sold.
* @param soldTime The timestamp of the sale.
* @param soldTo The person or entity the item was sold to.
* @param syncChildItemsLocations A flag to sync locations of child items.
* @param warrantyDetails Details about the item's warranty.
* @param warrantyExpires The expiration date of the warranty.
* @param locationId The ID of the item's location.
* @param labelIds A list of IDs for labels to associate with the item.
* @invariant The 'name' must not be blank.
*/ */
data class ItemCreate( data class ItemCreate(
val name: String, val name: String,

View File

@@ -1,43 +1,52 @@
// [PACKAGE] com.homebox.lens.domain.model // [PACKAGE] com.homebox.lens.domain.model
// [FILE] ItemOut.kt // [FILE] ItemOut.kt
// [SEMANTICS] data_structure, entity, detailed // [SEMANTICS] domain, model, item_detailed
package com.homebox.lens.domain.model package com.homebox.lens.domain.model
// [ENTITY: DataClass('ItemOut')] // [ENTITY: DataClass('ItemOut')]
// [RELATION: DataClass('ItemOut')] -> [DEPENDS_ON] -> [DataClass('LocationOut')]
// [RELATION: DataClass('ItemOut')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')]
// [RELATION: DataClass('ItemOut')] -> [DEPENDS_ON] -> [DataClass('LabelOut')]
// [RELATION: DataClass('ItemOut')] -> [DEPENDS_ON] -> [DataClass('ItemAttachment')]
// [RELATION: DataClass('ItemOut')] -> [DEPENDS_ON] -> [DataClass('Image')]
// [RELATION: DataClass('ItemOut')] -> [DEPENDS_ON] -> [DataClass('CustomField')]
// [RELATION: DataClass('ItemOut')] -> [DEPENDS_ON] -> [DataClass('MaintenanceEntry')]
/** /**
* @summary Полная модель данных для представления "Вещи" со всеми полями. * @summary A comprehensive data model representing a fully-detailed inventory item.
* @param id Уникальный идентификатор. * @param id The unique identifier for the item.
* @param name Название. * @param name The name of the item.
* @param assetId Идентификатор актива. * @param assetId An external asset identifier.
* @param description Описание. * @param description A detailed description of the item.
* @param notes Заметки. * @param notes Any additional notes about the item.
* @param serialNumber Серийный номер. * @param serialNumber The serial number of the item.
* @param quantity Количество. * @param quantity The number of units of this item.
* @param isArchived Флаг архивации. * @param isArchived A flag indicating if the item is archived.
* @param purchasePrice Цена покупки. * @param purchasePrice The price at which the item was purchased.
* @param purchaseTime Время покупки. * @param purchaseTime The timestamp of the purchase.
* @param purchaseFrom Место покупки. * @param purchaseFrom The place where the item was purchased.
* @param warrantyExpires Дата окончания гарантии. * @param warrantyExpires The expiration date of the warranty.
* @param warrantyDetails Детали гарантии. * @param warrantyDetails Details about the item's warranty.
* @param lifetimeWarranty Пожизненная гарантия. * @param lifetimeWarranty A flag indicating if the item has a lifetime warranty.
* @param insured Застрахована ли вещь. * @param insured A flag indicating if the item is insured.
* @param manufacturer Производитель. * @param manufacturer The manufacturer of the item.
* @param modelNumber Номер модели. * @param modelNumber The model number of the item.
* @param soldPrice Цена продажи. * @param soldPrice The price at which the item was sold.
* @param soldTime Время продажи. * @param soldTime The timestamp of the sale.
* @param soldTo Кому продано. * @param soldTo The person or entity the item was sold to.
* @param soldNotes Заметки о продаже. * @param soldNotes Notes related to the sale of the item.
* @param syncChildItemsLocations Синхронизировать местоположения дочерних элементов. * @param syncChildItemsLocations A flag to sync locations of child items.
* @param location Местоположение. * @param location The physical or logical location of the item.
* @param parent Родительская вещь (если есть). * @param parent A summary of the parent item, if one exists.
* @param children Дочерние вещи. * @param children A list of summaries for child items.
* @param labels Список меток. * @param labels A list of tags or categories associated with the item.
* @param attachments Список вложений. * @param attachments A list of file attachments for the item.
* @param images Список изображений. * @param images A list of images for the item.
* @param fields Список кастомных полей. * @param fields A list of custom-defined fields for the item.
* @param maintenance Список записей об обслуживании. * @param maintenance A list of maintenance records for the item.
* @param createdAt Дата и время создания. * @param createdAt The timestamp when the item was created.
* @param updatedAt Дата и время последнего обновления. * @param updatedAt The timestamp when the item was last updated.
* @invariant The 'id' must not be blank.
* @invariant The 'name' must not be blank.
*/ */
data class ItemOut( data class ItemOut(
val id: String, val id: String,

View File

@@ -1,21 +1,26 @@
// [PACKAGE] com.homebox.lens.domain.model // [PACKAGE] com.homebox.lens.domain.model
// [FILE] ItemSummary.kt // [FILE] ItemSummary.kt
// [SEMANTICS] data_structure, entity, summary // [SEMANTICS] domain, model, item_summary
package com.homebox.lens.domain.model package com.homebox.lens.domain.model
// [ENTITY: DataClass('ItemSummary')] // [ENTITY: DataClass('ItemSummary')]
// [RELATION: DataClass('ItemSummary')] -> [DEPENDS_ON] -> [DataClass('Image')]
// [RELATION: DataClass('ItemSummary')] -> [DEPENDS_ON] -> [DataClass('LabelOut')]
// [RELATION: DataClass('ItemSummary')] -> [DEPENDS_ON] -> [DataClass('LocationOut')]
/** /**
* @summary Сокращенная модель данных для представления "Вещи" в списках. * @summary A summarized data model for representing an item in a list.
* @param id Уникальный идентификатор вещи. * @param id The unique identifier for the item.
* @param name Название вещи. * @param name The name of the item.
* @param assetId Идентификатор актива. * @param assetId An external asset identifier.
* @param image Основное изображение. Может быть null. * @param image The primary image for the item, if available.
* @param isArchived Флаг архивации. * @param isArchived A flag indicating if the item is archived.
* @param labels Список меток. * @param labels A list of tags or categories associated with the item.
* @param location Местоположение. Может быть null. * @param location The physical or logical location of the item.
* @param value Стоимость. * @param value The monetary value of the item.
* @param createdAt Дата и время создания. * @param createdAt The timestamp when the item was created.
* @param updatedAt Дата и время последнего обновления. * @param updatedAt The timestamp when the item was last updated.
* @invariant The 'id' must not be blank.
* @invariant The 'name' must not be blank.
*/ */
data class ItemSummary( data class ItemSummary(
val id: String, val id: String,

View File

@@ -1,25 +1,39 @@
// [PACKAGE] com.homebox.lens.domain.model // [PACKAGE] com.homebox.lens.domain.model
// [FILE] ItemUpdate.kt // [FILE] ItemUpdate.kt
// [SEMANTICS] data_structure, entity, input, update // [SEMANTICS] domain, model, item_update
package com.homebox.lens.domain.model package com.homebox.lens.domain.model
// [ENTITY: DataClass('ItemUpdate')] // [ENTITY: DataClass('ItemUpdate')]
// [RELATION: DataClass('ItemUpdate')] -> [DEPENDS_ON] -> [DataClass('Location')]
// [RELATION: DataClass('ItemUpdate')] -> [DEPENDS_ON] -> [DataClass('Label')]
/** /**
* @summary Модель данных для обновления существующей "Вещи". * @summary Data model for updating an existing item.
* @param name Название вещи. * @param id The unique identifier of the item to update.
* @param assetId Идентификатор актива. * @param name The new name of the item.
* @param description Описание. * @param description An optional new description for the item.
* @param notes Заметки. * @param quantity The new number of units of this item.
* @param serialNumber Серийный номер. * @param archived A flag indicating if the item is archived.
* @param quantity Количество. * @param assetId An external asset identifier.
* @param isArchived Флаг архивации. * @param insured A flag indicating if the item is insured.
* @param value Стоимость. * @param lifetimeWarranty A flag indicating if the item has a lifetime warranty.
* @param purchasePrice Цена покупки. * @param manufacturer The manufacturer of the item.
* @param purchaseDate Дата покупки. * @param modelNumber The model number of the item.
* @param warrantyUntil Гарантия до. * @param notes Any additional notes about the item.
* @param locationId ID местоположения. * @param parentId The ID of a parent item, if this is a child item.
* @param parentId ID родительской вещи. * @param purchaseFrom The place where the item was purchased.
* @param labelIds Список ID меток для полной замены. * @param purchasePrice The price at which the item was purchased.
* @param purchaseTime The timestamp of the purchase.
* @param serialNumber The serial number of the item.
* @param soldNotes Notes related to the sale of the item.
* @param soldPrice The price at which the item was sold.
* @param soldTime The timestamp of the sale.
* @param soldTo The person or entity the item was sold to.
* @param syncChildItemsLocations A flag to sync locations of child items.
* @param warrantyDetails Details about the item's warranty.
* @param warrantyExpires The expiration date of the warranty.
* @param locationId The ID of the item's new location.
* @param labelIds A list of IDs for labels to associate with the item, replacing existing ones.
* @invariant The 'id' must not be blank.
*/ */
data class ItemUpdate( data class ItemUpdate(
val id: String, val id: String,

View File

@@ -1,13 +1,15 @@
// [PACKAGE] com.homebox.lens.domain.model // [PACKAGE] com.homebox.lens.domain.model
// [FILE] Label.kt // [FILE] Label.kt
// [SEMANTICS] domain, model // [SEMANTICS] domain, model, label
package com.homebox.lens.domain.model package com.homebox.lens.domain.model
// [ENTITY: DataClass('Label')] // [ENTITY: DataClass('Label')]
/** /**
* @summary Представляет собой метку (тег), которую можно присвоить вещи. * @summary Represents a label or tag that can be assigned to an item.
* @param id Уникальный идентификатор метки. * @param id The unique identifier of the label.
* @param name Название метки. * @param name The name of the label.
* @invariant The 'id' must not be blank.
* @invariant The 'name' must not be blank.
*/ */
data class Label( data class Label(
val id: String, val id: String,

View File

@@ -1,14 +1,15 @@
// [PACKAGE] com.homebox.lens.domain.model // [PACKAGE] com.homebox.lens.domain.model
// [FILE] LabelCreate.kt // [FILE] LabelCreate.kt
// [SEMANTICS] data_structure, contract, label, create // [SEMANTICS] domain, model, label, create
package com.homebox.lens.domain.model package com.homebox.lens.domain.model
// [ENTITY: DataClass('LabelCreate')] // [ENTITY: DataClass('LabelCreate')]
/** /**
* @summary Модель с данными, необходимыми для создания новой метки. * @summary Data model for creating a new label.
* @param name Название новой метки. Обязательное поле. * @param name The name of the new label (required).
* @param color Цвет метки в формате HEX. Необязательное поле. * @param color The color of the label in HEX format (optional).
* @invariant name не может быть пустым. * @param description An optional description for the label.
* @invariant 'name' cannot be blank.
*/ */
data class LabelCreate( data class LabelCreate(
val name: String, val name: String,

View File

@@ -1,17 +1,20 @@
// [PACKAGE] com.homebox.lens.domain.model // [PACKAGE] com.homebox.lens.domain.model
// [FILE] LabelOut.kt // [FILE] LabelOut.kt
// [SEMANTICS] data_structure, entity, label // [SEMANTICS] domain, model, label
package com.homebox.lens.domain.model package com.homebox.lens.domain.model
// [ENTITY: DataClass('LabelOut')] // [ENTITY: DataClass('LabelOut')]
/** /**
* @summary Модель данных для представления метки (тега). * @summary Data model for representing a label (tag).
* @param id Уникальный идентификатор. * @param id The unique identifier.
* @param name Название метки. * @param name The name of the label.
* @param color Цвет метки в формате HEX (например, "#FF0000"). * @param description An optional description for the label.
* @param isArchived Флаг, указывающий, заархивирована ли метка. * @param color The color of the label in HEX format (e.g., "#FF0000").
* @param createdAt Дата и время создания. * @param isArchived A flag indicating if the label is archived.
* @param updatedAt Дата и время последнего обновления. * @param createdAt The timestamp when the label was created.
* @param updatedAt The timestamp when the label was last updated.
* @invariant The 'id' must not be blank.
* @invariant The 'name' must not be blank.
*/ */
data class LabelOut( data class LabelOut(
val id: String, val id: String,

Some files were not shown because too many files have changed in this diff Show More