From a608766e06ad871c0a57c18765f08aff299d4dc2 Mon Sep 17 00:00:00 2001 From: busya Date: Sun, 24 Aug 2025 13:46:04 +0300 Subject: [PATCH] feat: Add semantic enrichment to all Kotlin files --- .../java/com/homebox/lens/MainActivity.kt | 26 ++-- .../java/com/homebox/lens/MainApplication.kt | 20 +-- .../com/homebox/lens/navigation/NavGraph.kt | 26 ++-- .../lens/navigation/NavigationActions.kt | 70 ++++++--- .../com/homebox/lens/navigation/Screen.kt | 93 ++++++------ .../com/homebox/lens/ui/common/AppDrawer.kt | 22 ++- .../homebox/lens/ui/common/MainScaffold.kt | 13 +- .../ui/screen/dashboard/DashboardScreen.kt | 128 ++++++++++------- .../ui/screen/dashboard/DashboardUiState.kt | 45 +++--- .../ui/screen/dashboard/DashboardViewModel.kt | 31 ++-- .../inventorylist/InventoryListScreen.kt | 16 ++- .../inventorylist/InventoryListViewModel.kt | 15 +- .../screen/itemdetails/ItemDetailsScreen.kt | 16 ++- .../itemdetails/ItemDetailsViewModel.kt | 13 +- .../lens/ui/screen/itemedit/ItemEditScreen.kt | 14 +- .../ui/screen/itemedit/ItemEditViewModel.kt | 15 +- .../ui/screen/labelslist/LabelsListScreen.kt | 55 +++---- .../ui/screen/labelslist/LabelsListUiState.kt | 38 +++-- .../screen/labelslist/LabelsListViewModel.kt | 49 +++---- .../screen/locationedit/LocationEditScreen.kt | 9 +- .../locationslist/LocationsListScreen.kt | 37 +++-- .../locationslist/LocationsListUiState.kt | 15 +- .../locationslist/LocationsListViewModel.kt | 20 ++- .../lens/ui/screen/search/SearchScreen.kt | 16 ++- .../lens/ui/screen/search/SearchViewModel.kt | 13 +- .../lens/ui/screen/setup/SetupScreen.kt | 20 +-- .../lens/ui/screen/setup/SetupUiState.kt | 20 +-- .../lens/ui/screen/setup/SetupViewModel.kt | 84 ++++------- .../java/com/homebox/lens/ui/theme/Color.kt | 4 +- .../java/com/homebox/lens/ui/theme/Theme.kt | 14 +- .../com/homebox/lens/ui/theme/Typography.kt | 10 +- buildSrc/src/main/java/Dependencies.kt | 8 +- data/build.gradle.kts | 3 + .../lens/data/api/HomeboxApiService.kt | 58 ++++---- .../lens/data/api/dto/CustomFieldDto.kt | 13 +- .../lens/data/api/dto/GroupStatisticsDto.kt | 22 ++- .../com/homebox/lens/data/api/dto/ImageDto.kt | 19 +-- .../lens/data/api/dto/ItemAttachmentDto.kt | 13 +- .../lens/data/api/dto/ItemCreateDto.kt | 13 +- .../com/homebox/lens/data/api/dto/ItemDto.kt | 28 ++-- .../homebox/lens/data/api/dto/ItemOutDto.kt | 13 +- .../lens/data/api/dto/ItemSummaryDto.kt | 13 +- .../lens/data/api/dto/ItemUpdateDto.kt | 13 +- .../lens/data/api/dto/LabelCreateDto.kt | 18 +-- .../homebox/lens/data/api/dto/LabelOutDto.kt | 28 ++-- .../lens/data/api/dto/LabelSummaryDto.kt | 14 +- .../homebox/lens/data/api/dto/LocationDto.kt | 17 ++- .../lens/data/api/dto/LocationOutCountDto.kt | 29 ++-- .../lens/data/api/dto/LocationOutDto.kt | 13 +- .../homebox/lens/data/api/dto/LoginFormDto.kt | 8 +- .../lens/data/api/dto/MaintenanceEntryDto.kt | 13 +- .../lens/data/api/dto/PaginationDto.kt | 12 +- .../lens/data/api/dto/PaginationResultDto.kt | 13 +- .../lens/data/api/dto/StatisticsDto.kt | 12 +- .../lens/data/api/dto/TokenResponseDto.kt | 8 +- .../lens/data/api/mapper/TokenMapper.kt | 17 +-- .../com/homebox/lens/data/db/Converters.kt | 14 +- .../homebox/lens/data/db/HomeboxDatabase.kt | 12 +- .../com/homebox/lens/data/db/dao/ItemDao.kt | 26 +++- .../com/homebox/lens/data/db/dao/LabelDao.kt | 14 +- .../homebox/lens/data/db/dao/LocationDao.kt | 14 +- .../homebox/lens/data/db/entity/ItemEntity.kt | 12 +- .../lens/data/db/entity/ItemLabelCrossRef.kt | 12 +- .../lens/data/db/entity/ItemWithLabels.kt | 14 +- .../lens/data/db/entity/LabelEntity.kt | 12 +- .../lens/data/db/entity/LocationEntity.kt | 10 +- .../com/homebox/lens/data/db/entity/Mapper.kt | 30 ++-- .../com/homebox/lens/data/di/ApiModule.kt | 70 ++++----- .../homebox/lens/data/di/DatabaseModule.kt | 29 ++-- .../homebox/lens/data/di/RepositoryModule.kt | 30 ++-- .../com/homebox/lens/data/di/StorageModule.kt | 20 ++- .../data/repository/AuthRepositoryImpl.kt | 43 +++--- .../repository/CredentialsRepositoryImpl.kt | 51 ++++--- .../repository/EncryptedPreferencesWrapper.kt | 48 +++++-- .../data/repository/ItemRepositoryImpl.kt | 105 +++++++------- .../lens/data/security/CryptoManager.kt | 36 +++-- .../homebox/lens/domain/model/Credentials.kt | 13 +- .../homebox/lens/domain/model/CustomField.kt | 13 +- .../lens/domain/model/GroupStatistics.kt | 15 +- .../com/homebox/lens/domain/model/Image.kt | 13 +- .../com/homebox/lens/domain/model/Item.kt | 28 ++-- .../lens/domain/model/ItemAttachment.kt | 19 +-- .../homebox/lens/domain/model/ItemCreate.kt | 33 ++--- .../com/homebox/lens/domain/model/ItemOut.kt | 51 +++---- .../homebox/lens/domain/model/ItemSummary.kt | 27 ++-- .../homebox/lens/domain/model/ItemUpdate.kt | 35 ++--- .../com/homebox/lens/domain/model/Label.kt | 12 +- .../homebox/lens/domain/model/LabelCreate.kt | 16 +-- .../com/homebox/lens/domain/model/LabelOut.kt | 19 +-- .../homebox/lens/domain/model/LabelSummary.kt | 12 +- .../com/homebox/lens/domain/model/Location.kt | 12 +- .../homebox/lens/domain/model/LocationOut.kt | 19 +-- .../lens/domain/model/LocationOutCount.kt | 21 +-- .../lens/domain/model/MaintenanceEntry.kt | 25 ++-- .../lens/domain/model/PaginationResult.kt | 17 +-- .../com/homebox/lens/domain/model/Result.kt | 21 +-- .../homebox/lens/domain/model/Statistics.kt | 18 +-- .../lens/domain/model/TokenResponse.kt | 13 +- .../lens/domain/repository/AuthRepository.kt | 24 ++-- .../repository/CredentialsRepository.kt | 31 ++-- .../lens/domain/repository/ItemRepository.kt | 135 +++++++++++++++--- .../lens/domain/usecase/CreateItemUseCase.kt | 28 ++-- .../lens/domain/usecase/CreateLabelUseCase.kt | 16 +-- .../lens/domain/usecase/DeleteItemUseCase.kt | 23 +-- .../domain/usecase/GetAllLabelsUseCase.kt | 20 ++- .../domain/usecase/GetAllLocationsUseCase.kt | 20 ++- .../domain/usecase/GetItemDetailsUseCase.kt | 28 ++-- .../usecase/GetRecentlyAddedItemsUseCase.kt | 34 +++-- .../domain/usecase/GetStatisticsUseCase.kt | 21 ++- .../lens/domain/usecase/LoginUseCase.kt | 35 +++-- .../lens/domain/usecase/SearchItemsUseCase.kt | 19 +-- .../domain/usecase/SyncInventoryUseCase.kt | 23 +-- .../lens/domain/usecase/UpdateItemUseCase.kt | 28 ++-- 113 files changed, 1671 insertions(+), 1253 deletions(-) diff --git a/app/src/main/java/com/homebox/lens/MainActivity.kt b/app/src/main/java/com/homebox/lens/MainActivity.kt index 30cf331..0752d4c 100644 --- a/app/src/main/java/com/homebox/lens/MainActivity.kt +++ b/app/src/main/java/com/homebox/lens/MainActivity.kt @@ -1,8 +1,9 @@ // [PACKAGE] com.homebox.lens // [FILE] MainActivity.kt - +// [SEMANTICS] ui, activity, entrypoint package com.homebox.lens +// [IMPORTS] import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent @@ -16,20 +17,23 @@ import androidx.compose.ui.tooling.preview.Preview import com.homebox.lens.navigation.NavGraph import com.homebox.lens.ui.theme.HomeboxLensTheme import dagger.hilt.android.AndroidEntryPoint +import timber.log.Timber +// [END_IMPORTS] -// [CONTRACT] +// [ENTITY: Activity('MainActivity')] /** - * [ENTITY: Activity('MainActivity')] - * [PURPOSE] Главная и единственная Activity в приложении. + * @summary Главная и единственная Activity в приложении. */ @AndroidEntryPoint class MainActivity : ComponentActivity() { - // [LIFECYCLE] + // [ENTITY: Function('onCreate')] + // [RELATION: Function('onCreate')] -> [CALLS] -> [Function('HomeboxLensTheme')] + // [RELATION: Function('onCreate')] -> [CALLS] -> [Function('NavGraph')] override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + Timber.d("[DEBUG][LIFECYCLE][creating_activity] MainActivity created.") setContent { HomeboxLensTheme { - // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background @@ -39,9 +43,11 @@ class MainActivity : ComponentActivity() { } } } + // [END_ENTITY: Function('onCreate')] } +// [END_ENTITY: Activity('MainActivity')] -// [HELPER] +// [ENTITY: Function('Greeting')] @Composable fun Greeting(name: String, modifier: Modifier = Modifier) { Text( @@ -49,8 +55,9 @@ fun Greeting(name: String, modifier: Modifier = Modifier) { modifier = modifier ) } +// [END_ENTITY: Function('Greeting')] -// [PREVIEW] +// [ENTITY: Function('GreetingPreview')] @Preview(showBackground = true) @Composable fun GreetingPreview() { @@ -58,5 +65,6 @@ fun GreetingPreview() { Greeting("Android") } } +// [END_ENTITY: Function('GreetingPreview')] -// [END_FILE_MainActivity.kt] +// [END_FILE_MainActivity.kt] \ No newline at end of file diff --git a/app/src/main/java/com/homebox/lens/MainApplication.kt b/app/src/main/java/com/homebox/lens/MainApplication.kt index cb631d5..bdb7afb 100644 --- a/app/src/main/java/com/homebox/lens/MainApplication.kt +++ b/app/src/main/java/com/homebox/lens/MainApplication.kt @@ -1,28 +1,30 @@ // [PACKAGE] com.homebox.lens // [FILE] MainApplication.kt - +// [SEMANTICS] application, hilt, timber package com.homebox.lens +// [IMPORTS] import android.app.Application -import com.homebox.lens.BuildConfig import dagger.hilt.android.HiltAndroidApp import timber.log.Timber +// [END_IMPORTS] -// [CONTRACT] +// [ENTITY: Application('MainApplication')] /** - * [ENTITY: Application('MainApplication')] - * [PURPOSE] Точка входа в приложение. Инициализирует Hilt и Timber. + * @summary Точка входа в приложение. Инициализирует Hilt и Timber. */ @HiltAndroidApp class MainApplication : Application() { - // [LIFECYCLE] + + // [ENTITY: Function('onCreate')] override fun onCreate() { super.onCreate() - // [ACTION] Initialize Timber for logging if (BuildConfig.DEBUG) { Timber.plant(Timber.DebugTree()) + Timber.d("[DEBUG][INITIALIZATION][timber_planted] Timber DebugTree planted.") } } + // [END_ENTITY: Function('onCreate')] } - -// [END_FILE_MainApplication.kt] +// [END_ENTITY: Application('MainApplication')] +// [END_FILE_MainApplication.kt] \ No newline at end of file diff --git a/app/src/main/java/com/homebox/lens/navigation/NavGraph.kt b/app/src/main/java/com/homebox/lens/navigation/NavGraph.kt index bbc3fe6..2ae975e 100644 --- a/app/src/main/java/com/homebox/lens/navigation/NavGraph.kt +++ b/app/src/main/java/com/homebox/lens/navigation/NavGraph.kt @@ -22,11 +22,13 @@ import com.homebox.lens.ui.screen.locationedit.LocationEditScreen import com.homebox.lens.ui.screen.locationslist.LocationsListScreen import com.homebox.lens.ui.screen.search.SearchScreen import com.homebox.lens.ui.screen.setup.SetupScreen +// [END_IMPORTS] -// [CORE-LOGIC] +// [ENTITY: Function('NavGraph')] +// [RELATION: Function('NavGraph')] -> [DEPENDS_ON] -> [Framework('NavHostController')] +// [RELATION: Function('NavGraph')] -> [CREATES_INSTANCE_OF] -> [Class('NavigationActions')] /** - * [CONTRACT] - * Определяет граф навигации для всего приложения с использованием Jetpack Compose Navigation. + * @summary Определяет граф навигации для всего приложения с использованием Jetpack Compose Navigation. * @param navController Контроллер навигации. * @see Screen * @sideeffect Регистрирует все экраны и управляет состоянием навигации. @@ -36,21 +38,17 @@ import com.homebox.lens.ui.screen.setup.SetupScreen fun NavGraph( navController: NavHostController = rememberNavController() ) { - // [STATE] val navBackStackEntry by navController.currentBackStackEntryAsState() val currentRoute = navBackStackEntry?.destination?.route - // [HELPER] val navigationActions = remember(navController) { NavigationActions(navController) } - // [ACTION] NavHost( navController = navController, startDestination = Screen.Setup.route ) { - // [COMPOSABLE_SETUP] composable(route = Screen.Setup.route) { SetupScreen(onSetupComplete = { navController.navigate(Screen.Dashboard.route) { @@ -58,45 +56,39 @@ fun NavGraph( } }) } - // [COMPOSABLE_DASHBOARD] composable(route = Screen.Dashboard.route) { DashboardScreen( currentRoute = currentRoute, navigationActions = navigationActions ) } - // [COMPOSABLE_INVENTORY_LIST] composable(route = Screen.InventoryList.route) { InventoryListScreen( currentRoute = currentRoute, navigationActions = navigationActions ) } - // [COMPOSABLE_ITEM_DETAILS] composable(route = Screen.ItemDetails.route) { ItemDetailsScreen( currentRoute = currentRoute, navigationActions = navigationActions ) } - // [COMPOSABLE_ITEM_EDIT] composable(route = Screen.ItemEdit.route) { ItemEditScreen( currentRoute = currentRoute, navigationActions = navigationActions ) } - // [COMPOSABLE_LABELS_LIST] composable(Screen.LabelsList.route) { LabelsListScreen(navController = navController) } - // [COMPOSABLE_LOCATIONS_LIST] composable(route = Screen.LocationsList.route) { LocationsListScreen( currentRoute = currentRoute, navigationActions = navigationActions, onLocationClick = { locationId -> - // TODO: Navigate to a pre-filtered inventory list screen + // [AI_NOTE]: Navigate to a pre-filtered inventory list screen navController.navigate(Screen.InventoryList.route) }, onAddNewLocationClick = { @@ -104,14 +96,12 @@ fun NavGraph( } ) } - // [COMPOSABLE_LOCATION_EDIT] composable(route = Screen.LocationEdit.route) { backStackEntry -> val locationId = backStackEntry.arguments?.getString("locationId") LocationEditScreen( locationId = locationId ) } - // [COMPOSABLE_SEARCH] composable(route = Screen.Search.route) { SearchScreen( currentRoute = currentRoute, @@ -119,6 +109,6 @@ fun NavGraph( ) } } - // [END_FUNCTION_NavGraph] } -// [END_FILE_NavGraph.kt] +// [END_ENTITY: Function('NavGraph')] +// [END_FILE_NavGraph.kt] \ No newline at end of file diff --git a/app/src/main/java/com/homebox/lens/navigation/NavigationActions.kt b/app/src/main/java/com/homebox/lens/navigation/NavigationActions.kt index 3d4db3a..056d19a 100644 --- a/app/src/main/java/com/homebox/lens/navigation/NavigationActions.kt +++ b/app/src/main/java/com/homebox/lens/navigation/NavigationActions.kt @@ -2,70 +2,100 @@ // [FILE] NavigationActions.kt // [SEMANTICS] navigation, controller, actions package com.homebox.lens.navigation + +// [IMPORTS] import androidx.navigation.NavHostController -// [CORE-LOGIC] +import timber.log.Timber +// [END_IMPORTS] + +// [ENTITY: Class('NavigationActions')] +// [RELATION: Class('NavigationActions')] -> [DEPENDS_ON] -> [Framework('NavHostController')] /** -[CONTRACT] -@summary Класс-обертка над NavHostController для предоставления типизированных навигационных действий. -@param navController Контроллер Jetpack Navigation. -@invariant Все навигационные действия должны использовать предоставленный navController. + * @summary Класс-обертка над NavHostController для предоставления типизированных навигационных действий. + * @param navController Контроллер Jetpack Navigation. + * @invariant Все навигационные действия должны использовать предоставленный navController. */ class NavigationActions(private val navController: NavHostController) { -// [ACTION] + + // [ENTITY: Function('navigateToDashboard')] /** - [CONTRACT] - @summary Навигация на главный экран. - @sideeffect Очищает back stack до главного экрана, чтобы избежать циклов. + * @summary Навигация на главный экран. + * @sideeffect Очищает back stack до главного экрана, чтобы избежать циклов. */ fun navigateToDashboard() { + Timber.i("[INFO][ACTION][navigate_to_dashboard] Navigating to Dashboard.") navController.navigate(Screen.Dashboard.route) { -// Используем popUpTo для удаления всех экранов до dashboard из back stack -// Это предотвращает создание большой стопки экранов при навигации через drawer popUpTo(navController.graph.startDestinationId) launchSingleTop = true } } - // [ACTION] + // [END_ENTITY: Function('navigateToDashboard')] + + // [ENTITY: Function('navigateToLocations')] fun navigateToLocations() { + Timber.i("[INFO][ACTION][navigate_to_locations] Navigating to Locations.") navController.navigate(Screen.LocationsList.route) { launchSingleTop = true } } - // [ACTION] + // [END_ENTITY: Function('navigateToLocations')] + + // [ENTITY: Function('navigateToLabels')] fun navigateToLabels() { + Timber.i("[INFO][ACTION][navigate_to_labels] Navigating to Labels.") navController.navigate(Screen.LabelsList.route) { launchSingleTop = true } } - // [ACTION] + // [END_ENTITY: Function('navigateToLabels')] + + // [ENTITY: Function('navigateToSearch')] fun navigateToSearch() { + Timber.i("[INFO][ACTION][navigate_to_search] Navigating to Search.") navController.navigate(Screen.Search.route) { launchSingleTop = true } } - // [ACTION] + // [END_ENTITY: Function('navigateToSearch')] + + // [ENTITY: Function('navigateToInventoryListWithLabel')] fun navigateToInventoryListWithLabel(labelId: String) { + Timber.i("[INFO][ACTION][navigate_to_inventory_with_label] Navigating to Inventory with label: %s", labelId) val route = Screen.InventoryList.withFilter("label", labelId) navController.navigate(route) } - // [ACTION] + // [END_ENTITY: Function('navigateToInventoryListWithLabel')] + + // [ENTITY: Function('navigateToInventoryListWithLocation')] fun navigateToInventoryListWithLocation(locationId: String) { + Timber.i("[INFO][ACTION][navigate_to_inventory_with_location] Navigating to Inventory with location: %s", locationId) val route = Screen.InventoryList.withFilter("location", locationId) navController.navigate(route) } - // [ACTION] + // [END_ENTITY: Function('navigateToInventoryListWithLocation')] + + // [ENTITY: Function('navigateToCreateItem')] fun navigateToCreateItem() { + Timber.i("[INFO][ACTION][navigate_to_create_item] Navigating to Create Item.") navController.navigate(Screen.ItemEdit.createRoute("new")) } - // [ACTION] + // [END_ENTITY: Function('navigateToCreateItem')] + + // [ENTITY: Function('navigateToLogout')] fun navigateToLogout() { + Timber.i("[INFO][ACTION][navigate_to_logout] Navigating to Logout.") navController.navigate(Screen.Setup.route) { popUpTo(Screen.Dashboard.route) { inclusive = true } } } - // [ACTION] + // [END_ENTITY: Function('navigateToLogout')] + + // [ENTITY: Function('navigateBack')] fun navigateBack() { + Timber.i("[INFO][ACTION][navigate_back] Navigating back.") navController.popBackStack() } + // [END_ENTITY: Function('navigateBack')] } -// [END_FILE_NavigationActions.kt] \ No newline at end of file +// [END_ENTITY: Class('NavigationActions')] +// [END_FILE_NavigationActions.kt] diff --git a/app/src/main/java/com/homebox/lens/navigation/Screen.kt b/app/src/main/java/com/homebox/lens/navigation/Screen.kt index 6014abb..0100b33 100644 --- a/app/src/main/java/com/homebox/lens/navigation/Screen.kt +++ b/app/src/main/java/com/homebox/lens/navigation/Screen.kt @@ -3,99 +3,110 @@ // [SEMANTICS] navigation, routes, sealed_class package com.homebox.lens.navigation -// [CORE-LOGIC] +// [ENTITY: SealedClass('Screen')] /** - * [CONTRACT] - * Запечатанный класс для определения маршрутов навигации в приложении. - * Обеспечивает типобезопасность при навигации. - * @property route Строковый идентификатор маршрута. + * @summary Запечатанный класс для определения маршрутов навигации в приложении. + * @description Обеспечивает типобезопасность при навигации. + * @param route Строковый идентификатор маршрута. */ sealed class Screen(val route: String) { - // [STATE] + // [ENTITY: Object('Setup')] data object Setup : Screen("setup_screen") + // [END_ENTITY: Object('Setup')] + + // [ENTITY: Object('Dashboard')] data object Dashboard : Screen("dashboard_screen") + // [END_ENTITY: Object('Dashboard')] + + // [ENTITY: Object('InventoryList')] data object InventoryList : Screen("inventory_list_screen") { + // [ENTITY: Function('withFilter')] /** - * [CONTRACT] - * Создает маршрут для экрана списка инвентаря с параметром фильтра. + * @summary Создает маршрут для экрана списка инвентаря с параметром фильтра. * @param key Ключ фильтра (например, "label" или "location"). * @param value Значение фильтра (например, ID метки или местоположения). * @return Строку полного маршрута с query-параметром. * @throws IllegalArgumentException если ключ или значение пустые. - * @sideeffect [ARCH-IMPLICATION] NavGraph должен быть настроен для приема этого опционального query-параметра (например, 'navArgument("label") { nullable = true }'). */ - // [HELPER] fun withFilter(key: String, value: String): String { - // [PRECONDITION] - require(key.isNotBlank()) { "[PRECONDITION_FAILED] Filter key cannot be blank." } - require(value.isNotBlank()) { "[PRECONDITION_FAILED] Filter value cannot be blank." } - // [ACTION] + require(key.isNotBlank()) { "Filter key cannot be blank." } + require(value.isNotBlank()) { "Filter value cannot be blank." } val constructedRoute = "inventory_list_screen?$key=$value" - // [POSTCONDITION] - check(constructedRoute.contains("?$key=$value")) { "[POSTCONDITION_FAILED] Route must contain the filter query." } + check(constructedRoute.contains("?$key=$value")) { "Route must contain the filter query." } return constructedRoute } + // [END_ENTITY: Function('withFilter')] } + // [END_ENTITY: Object('InventoryList')] + // [ENTITY: Object('ItemDetails')] data object ItemDetails : Screen("item_details_screen/{itemId}") { + // [ENTITY: Function('createRoute')] /** - * [CONTRACT] - * Создает маршрут для экрана деталей элемента с указанным ID. + * @summary Создает маршрут для экрана деталей элемента с указанным ID. * @param itemId ID элемента для отображения. * @return Строку полного маршрута. * @throws IllegalArgumentException если itemId пустой. */ - // [HELPER] fun createRoute(itemId: String): String { - // [PRECONDITION] - require(itemId.isNotBlank()) { "[PRECONDITION_FAILED] itemId не может быть пустым." } - // [ACTION] + require(itemId.isNotBlank()) { "itemId не может быть пустым." } val route = "item_details_screen/$itemId" - // [POSTCONDITION] - check(route.endsWith(itemId)) { "[POSTCONDITION_FAILED] Маршрут должен заканчиваться на itemId." } + check(route.endsWith(itemId)) { "Маршрут должен заканчиваться на itemId." } return route } + // [END_ENTITY: Function('createRoute')] } + // [END_ENTITY: Object('ItemDetails')] + + // [ENTITY: Object('ItemEdit')] data object ItemEdit : Screen("item_edit_screen/{itemId}") { + // [ENTITY: Function('createRoute')] /** - * [CONTRACT] - * Создает маршрут для экрана редактирования элемента с указанным ID. + * @summary Создает маршрут для экрана редактирования элемента с указанным ID. * @param itemId ID элемента для редактирования. * @return Строку полного маршрута. * @throws IllegalArgumentException если itemId пустой. */ - // [HELPER] fun createRoute(itemId: String): String { - // [PRECONDITION] - require(itemId.isNotBlank()) { "[PRECONDITION_FAILED] itemId не может быть пустым." } - // [ACTION] + require(itemId.isNotBlank()) { "itemId не может быть пустым." } val route = "item_edit_screen/$itemId" - // [POSTCONDITION] - check(route.endsWith(itemId)) { "[POSTCONDITION_FAILED] Маршрут должен заканчиваться на itemId." } + check(route.endsWith(itemId)) { "Маршрут должен заканчиваться на itemId." } return route } + // [END_ENTITY: Function('createRoute')] } + // [END_ENTITY: Object('ItemEdit')] + + // [ENTITY: Object('LabelsList')] data object LabelsList : Screen("labels_list_screen") + // [END_ENTITY: Object('LabelsList')] + + // [ENTITY: Object('LocationsList')] data object LocationsList : Screen("locations_list_screen") + // [END_ENTITY: Object('LocationsList')] + + // [ENTITY: Object('LocationEdit')] data object LocationEdit : Screen("location_edit_screen/{locationId}") { + // [ENTITY: Function('createRoute')] /** - * [CONTRACT] - * Создает маршрут для экрана редактирования местоположения с указанным ID. + * @summary Создает маршрут для экрана редактирования местоположения с указанным ID. * @param locationId ID местоположения для редактирования. * @return Строку полного маршрута. * @throws IllegalArgumentException если locationId пустой. */ - // [HELPER] fun createRoute(locationId: String): String { - // [PRECONDITION] - require(locationId.isNotBlank()) { "[PRECONDITION_FAILED] locationId не может быть пустым." } - // [ACTION] + require(locationId.isNotBlank()) { "locationId не может быть пустым." } val route = "location_edit_screen/$locationId" - // [POSTCONDITION] - check(route.endsWith(locationId)) { "[POSTCONDITION_FAILED] Маршрут должен заканчиваться на locationId." } + check(route.endsWith(locationId)) { "Маршрут должен заканчиваться на locationId." } return route } + // [END_ENTITY: Function('createRoute')] } + // [END_ENTITY: Object('LocationEdit')] + + // [ENTITY: Object('Search')] data object Search : Screen("search_screen") + // [END_ENTITY: Object('Search')] } -// [END_FILE_Screen.kt] \ No newline at end of file +// [END_ENTITY: SealedClass('Screen')] +// [END_FILE_Screen.kt] diff --git a/app/src/main/java/com/homebox/lens/ui/common/AppDrawer.kt b/app/src/main/java/com/homebox/lens/ui/common/AppDrawer.kt index 176d749..1cc14fe 100644 --- a/app/src/main/java/com/homebox/lens/ui/common/AppDrawer.kt +++ b/app/src/main/java/com/homebox/lens/ui/common/AppDrawer.kt @@ -1,6 +1,9 @@ // [PACKAGE] com.homebox.lens.ui.common // [FILE] AppDrawer.kt +// [SEMANTICS] ui, common, navigation_drawer package com.homebox.lens.ui.common + +// [IMPORTS] import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height @@ -22,12 +25,15 @@ import androidx.compose.ui.unit.dp import com.homebox.lens.R import com.homebox.lens.navigation.NavigationActions import com.homebox.lens.navigation.Screen +// [END_IMPORTS] + +// [ENTITY: Function('AppDrawerContent')] +// [RELATION: Function('AppDrawerContent')] -> [DEPENDS_ON] -> [Class('NavigationActions')] /** -[CONTRACT] -@summary Контент для бокового навигационного меню (Drawer). -@param currentRoute Текущий маршрут для подсветки активного элемента. -@param navigationActions Объект с навигационными действиями. -@param onCloseDrawer Лямбда для закрытия бокового меню. + * @summary Контент для бокового навигационного меню (Drawer). + * @param currentRoute Текущий маршрут для подсветки активного элемента. + * @param navigationActions Объект с навигационными действиями. + * @param onCloseDrawer Лямбда для закрытия бокового меню. */ @Composable internal fun AppDrawerContent( @@ -84,7 +90,7 @@ internal fun AppDrawerContent( onCloseDrawer() } ) -// TODO: Add Profile and Tools items + // [AI_NOTE]: Add Profile and Tools items Divider() NavigationDrawerItem( label = { Text(stringResource(id = R.string.logout)) }, @@ -95,4 +101,6 @@ internal fun AppDrawerContent( } ) } -} \ No newline at end of file +} +// [END_ENTITY: Function('AppDrawerContent')] +// [END_FILE_AppDrawer.kt] diff --git a/app/src/main/java/com/homebox/lens/ui/common/MainScaffold.kt b/app/src/main/java/com/homebox/lens/ui/common/MainScaffold.kt index b366974..0072a1f 100644 --- a/app/src/main/java/com/homebox/lens/ui/common/MainScaffold.kt +++ b/app/src/main/java/com/homebox/lens/ui/common/MainScaffold.kt @@ -15,10 +15,12 @@ import androidx.compose.ui.res.stringResource import com.homebox.lens.R import com.homebox.lens.navigation.NavigationActions import kotlinx.coroutines.launch +// [END_IMPORTS] -// [UI_COMPONENT] +// [ENTITY: Function('MainScaffold')] +// [RELATION: Function('MainScaffold')] -> [DEPENDS_ON] -> [Class('NavigationActions')] +// [RELATION: Function('MainScaffold')] -> [CALLS] -> [Function('AppDrawerContent')] /** - * [CONTRACT] * @summary Общая обертка для экранов, включающая Scaffold и Navigation Drawer. * @param topBarTitle Заголовок для TopAppBar. * @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. @@ -37,11 +39,9 @@ fun MainScaffold( topBarActions: @Composable () -> Unit = {}, content: @Composable (PaddingValues) -> Unit ) { - // [STATE] val drawerState = rememberDrawerState(initialValue = DrawerValue.Closed) val scope = rememberCoroutineScope() - // [CORE-LOGIC] ModalNavigationDrawer( drawerState = drawerState, drawerContent = { @@ -68,10 +68,9 @@ fun MainScaffold( ) } ) { paddingValues -> - // [ACTION] content(paddingValues) } } - // [END_FUNCTION_MainScaffold] } -// [END_FILE_MainScaffold.kt] +// [END_ENTITY: Function('MainScaffold')] +// [END_FILE_MainScaffold.kt] \ No newline at end of file diff --git a/app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardScreen.kt b/app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardScreen.kt index 775cd5c..34e3d56 100644 --- a/app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardScreen.kt +++ b/app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardScreen.kt @@ -2,6 +2,7 @@ // [FILE] DashboardScreen.kt // [SEMANTICS] ui, screen, dashboard, compose, navigation package com.homebox.lens.ui.screen.dashboard + // [IMPORTS] import androidx.compose.foundation.background import androidx.compose.foundation.layout.* @@ -29,14 +30,18 @@ import com.homebox.lens.navigation.NavigationActions import com.homebox.lens.ui.common.MainScaffold import com.homebox.lens.ui.theme.HomeboxLensTheme import timber.log.Timber -// [ENTRYPOINT] +// [END_IMPORTS] + +// [ENTITY: Function('DashboardScreen')] +// [RELATION: Function('DashboardScreen')] -> [DEPENDS_ON] -> [ViewModel('DashboardViewModel')] +// [RELATION: Function('DashboardScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')] +// [RELATION: Function('DashboardScreen')] -> [CALLS] -> [Function('MainScaffold')] /** -[CONTRACT] -@summary Главная Composable-функция для экрана "Панель управления". -@param viewModel ViewModel для этого экрана, предоставляется через Hilt. -@param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. -@param navigationActions Объект с навигационными действиями. -@sideeffect Вызывает навигационные лямбды при взаимодействии с UI. + * @summary Главная Composable-функция для экрана "Панель управления". + * @param viewModel ViewModel для этого экрана, предоставляется через Hilt. + * @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. + * @param navigationActions Объект с навигационными действиями. + * @sideeffect Вызывает навигационные лямбды при взаимодействии с UI. */ @Composable fun DashboardScreen( @@ -44,9 +49,7 @@ fun DashboardScreen( currentRoute: String?, navigationActions: NavigationActions ) { -// [STATE] val uiState by viewModel.uiState.collectAsState() -// [UI_COMPONENT] MainScaffold( topBarTitle = stringResource(id = R.string.dashboard_title), currentRoute = currentRoute, @@ -55,7 +58,7 @@ fun DashboardScreen( IconButton(onClick = { navigationActions.navigateToSearch() }) { Icon( Icons.Default.Search, - contentDescription = stringResource(id = R.string.cd_scan_qr_code) // TODO: Rename string resource + contentDescription = stringResource(id = R.string.cd_scan_qr_code) // [AI_NOTE]: Rename string resource ) } } @@ -64,25 +67,26 @@ fun DashboardScreen( modifier = Modifier.padding(paddingValues), uiState = uiState, onLocationClick = { location -> - Timber.i("[ACTION] Location chip clicked: ${location.id}. Navigating...") + Timber.i("[INFO][ACTION][navigate_to_inventory_with_location] Location chip clicked: ${location.id}. Navigating...") navigationActions.navigateToInventoryListWithLocation(location.id) }, onLabelClick = { label -> - Timber.i("[ACTION] Label chip clicked: ${label.id}. Navigating...") + Timber.i("[INFO][ACTION][navigate_to_inventory_with_label] Label chip clicked: ${label.id}. Navigating...") navigationActions.navigateToInventoryListWithLabel(label.id) } ) } -// [END_FUNCTION_DashboardScreen] } -// [HELPER] +// [END_ENTITY: Function('DashboardScreen')] + +// [ENTITY: Function('DashboardContent')] +// [RELATION: Function('DashboardContent')] -> [CONSUMES_STATE] -> [SealedInterface('DashboardUiState')] /** -[CONTRACT] -@summary Отображает основной контент экрана в зависимости от uiState. -@param modifier Модификатор для стилизации. -@param uiState Текущее состояние UI экрана. -@param onLocationClick Лямбда-обработчик нажатия на местоположение. -@param onLabelClick Лямбда-обработчик нажатия на метку. + * @summary Отображает основной контент экрана в зависимости от uiState. + * @param modifier Модификатор для стилизации. + * @param uiState Текущее состояние UI экрана. + * @param onLocationClick Лямбда-обработчик нажатия на местоположение. + * @param onLabelClick Лямбда-обработчик нажатия на метку. */ @Composable private fun DashboardContent( @@ -91,7 +95,6 @@ private fun DashboardContent( onLocationClick: (LocationOutCount) -> Unit, onLabelClick: (LabelOut) -> Unit ) { -// [CORE-LOGIC] when (uiState) { is DashboardUiState.Loading -> { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { @@ -123,13 +126,14 @@ private fun DashboardContent( } } } -// [END_FUNCTION_DashboardContent] } -// [UI_COMPONENT] +// [END_ENTITY: Function('DashboardContent')] + +// [ENTITY: Function('StatisticsSection')] +// [RELATION: Function('StatisticsSection')] -> [DEPENDS_ON] -> [DataClass('GroupStatistics')] /** -[CONTRACT] -@summary Секция для отображения общей статистики. -@param statistics Объект со статистическими данными. + * @summary Секция для отображения общей статистики. + * @param statistics Объект со статистическими данными. */ @Composable private fun StatisticsSection(statistics: GroupStatistics) { @@ -156,12 +160,13 @@ private fun StatisticsSection(statistics: GroupStatistics) { } } } -// [UI_COMPONENT] +// [END_ENTITY: Function('StatisticsSection')] + +// [ENTITY: Function('StatisticCard')] /** -[CONTRACT] -@summary Карточка для отображения одного статистического показателя. -@param title Название показателя. -@param value Значение показателя. + * @summary Карточка для отображения одного статистического показателя. + * @param title Название показателя. + * @param value Значение показателя. */ @Composable private fun StatisticCard(title: String, value: String) { @@ -170,11 +175,13 @@ private fun StatisticCard(title: String, value: String) { Text(text = value, style = MaterialTheme.typography.headlineSmall, textAlign = TextAlign.Center) } } -// [UI_COMPONENT] +// [END_ENTITY: Function('StatisticCard')] + +// [ENTITY: Function('RecentlyAddedSection')] +// [RELATION: Function('RecentlyAddedSection')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')] /** -[CONTRACT] -@summary Секция для отображения недавно добавленных элементов. -@param items Список элементов для отображения. + * @summary Секция для отображения недавно добавленных элементов. + * @param items Список элементов для отображения. */ @Composable private fun RecentlyAddedSection(items: List) { @@ -201,17 +208,19 @@ private fun RecentlyAddedSection(items: List) { } } } -// [UI_COMPONENT] +// [END_ENTITY: Function('RecentlyAddedSection')] + +// [ENTITY: Function('ItemCard')] +// [RELATION: Function('ItemCard')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')] /** -[CONTRACT] -@summary Карточка для отображения краткой информации об элементе. -@param item Элемент для отображения. + * @summary Карточка для отображения краткой информации об элементе. + * @param item Элемент для отображения. */ @Composable private fun ItemCard(item: ItemSummary) { Card(modifier = Modifier.width(150.dp)) { Column(modifier = Modifier.padding(8.dp)) { -// TODO: Add image here from item.image + // [AI_NOTE]: Add image here from item.image Spacer(modifier = Modifier .height(80.dp) .fillMaxWidth() @@ -222,12 +231,14 @@ private fun ItemCard(item: ItemSummary) { } } } -// [UI_COMPONENT] +// [END_ENTITY: Function('ItemCard')] + +// [ENTITY: Function('LocationsSection')] +// [RELATION: Function('LocationsSection')] -> [DEPENDS_ON] -> [DataClass('LocationOutCount')] /** -[CONTRACT] -@summary Секция для отображения местоположений в виде чипсов. -@param locations Список местоположений. -@param onLocationClick Лямбда-обработчик нажатия на местоположение. + * @summary Секция для отображения местоположений в виде чипсов. + * @param locations Список местоположений. + * @param onLocationClick Лямбда-обработчик нажатия на местоположение. */ @OptIn(ExperimentalLayoutApi::class) @Composable @@ -249,12 +260,14 @@ private fun LocationsSection(locations: List, onLocationClick: } } } -// [UI_COMPONENT] +// [END_ENTITY: Function('LocationsSection')] + +// [ENTITY: Function('LabelsSection')] +// [RELATION: Function('LabelsSection')] -> [DEPENDS_ON] -> [DataClass('LabelOut')] /** -[CONTRACT] -@summary Секция для отображения меток в виде чипсов. -@param labels Список меток. -@param onLabelClick Лямбда-обработчик нажатия на метку. + * @summary Секция для отображения меток в виде чипсов. + * @param labels Список меток. + * @param onLabelClick Лямбда-обработчик нажатия на метку. */ @OptIn(ExperimentalLayoutApi::class) @Composable @@ -276,7 +289,9 @@ private fun LabelsSection(labels: List, onLabelClick: (LabelOut) -> Un } } } -// [PREVIEW] +// [END_ENTITY: Function('LabelsSection')] + +// [ENTITY: Function('DashboardContentSuccessPreview')] @Preview(showBackground = true, name = "Dashboard Success State") @Composable fun DashboardContentSuccessPreview() { @@ -310,7 +325,9 @@ fun DashboardContentSuccessPreview() { ) } } -// [PREVIEW] +// [END_ENTITY: Function('DashboardContentSuccessPreview')] + +// [ENTITY: Function('DashboardContentLoadingPreview')] @Preview(showBackground = true, name = "Dashboard Loading State") @Composable fun DashboardContentLoadingPreview() { @@ -322,7 +339,9 @@ fun DashboardContentLoadingPreview() { ) } } -// [PREVIEW] +// [END_ENTITY: Function('DashboardContentLoadingPreview')] + +// [ENTITY: Function('DashboardContentErrorPreview')] @Preview(showBackground = true, name = "Dashboard Error State") @Composable fun DashboardContentErrorPreview() { @@ -334,4 +353,5 @@ fun DashboardContentErrorPreview() { ) } } -// [END_FILE_DashboardScreen.kt] \ No newline at end of file +// [END_ENTITY: Function('DashboardContentErrorPreview')] +// [END_FILE_DashboardScreen.kt] diff --git a/app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardUiState.kt b/app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardUiState.kt index a4fe49e..28b442e 100644 --- a/app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardUiState.kt +++ b/app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardUiState.kt @@ -1,48 +1,55 @@ // [PACKAGE] com.homebox.lens.ui.screen.dashboard -// [FILE] app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardUiState.kt +// [FILE] DashboardUiState.kt // [SEMANTICS] ui, state, dashboard - -// [IMPORTS] package com.homebox.lens.ui.screen.dashboard +// [IMPORTS] import com.homebox.lens.domain.model.GroupStatistics +import com.homebox.lens.domain.model.ItemSummary import com.homebox.lens.domain.model.LabelOut import com.homebox.lens.domain.model.LocationOutCount +// [END_IMPORTS] -// [CORE-LOGIC] // [ENTITY: SealedInterface('DashboardUiState')] /** - * [CONTRACT] - * Определяет все возможные состояния для экрана "Дэшборд". + * @summary Определяет все возможные состояния для экрана "Дэшборд". * @invariant В любой момент времени экран может находиться только в одном из этих состояний. */ sealed interface DashboardUiState { + // [ENTITY: DataClass('Success')] + // [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('GroupStatistics')] + // [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('LocationOutCount')] + // [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('LabelOut')] + // [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')] /** - * [CONTRACT] - * Состояние успешной загрузки данных. - * @property statistics Статистика по инвентарю. - * @property locations Список локаций со счетчиками. - * @property labels Список всех меток. - * @property recentlyAddedItems Список недавно добавленных товаров. + * @summary Состояние успешной загрузки данных. + * @param statistics Статистика по инвентарю. + * @param locations Список локаций со счетчиками. + * @param labels Список всех меток. + * @param recentlyAddedItems Список недавно добавленных товаров. */ data class Success( val statistics: GroupStatistics, val locations: List, val labels: List, - val recentlyAddedItems: List + val recentlyAddedItems: List ) : DashboardUiState + // [END_ENTITY: DataClass('Success')] + // [ENTITY: DataClass('Error')] /** - * [CONTRACT] - * Состояние ошибки во время загрузки данных. - * @property message Человекочитаемое сообщение об ошибке. + * @summary Состояние ошибки во время загрузки данных. + * @param message Человекочитаемое сообщение об ошибке. */ data class Error(val message: String) : DashboardUiState + // [END_ENTITY: DataClass('Error')] + // [ENTITY: Object('Loading')] /** - * [CONTRACT] - * Состояние, когда данные для экрана загружаются. + * @summary Состояние, когда данные для экрана загружаются. */ data object Loading : DashboardUiState + // [END_ENTITY: Object('Loading')] } -// [END_FILE_DashboardUiState.kt] \ No newline at end of file +// [END_ENTITY: SealedInterface('DashboardUiState')] +// [END_FILE_DashboardUiState.kt] diff --git a/app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardViewModel.kt b/app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardViewModel.kt index 2dce373..3acb812 100644 --- a/app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardViewModel.kt +++ b/app/src/main/java/com/homebox/lens/ui/screen/dashboard/DashboardViewModel.kt @@ -2,6 +2,7 @@ // [FILE] DashboardViewModel.kt // [SEMANTICS] ui_logic, dashboard, state_management, sealed_state, parallel_data_loading, timber_logging package com.homebox.lens.ui.screen.dashboard + // [IMPORTS] import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -9,19 +10,20 @@ import com.homebox.lens.domain.usecase.GetAllLabelsUseCase import com.homebox.lens.domain.usecase.GetAllLocationsUseCase import com.homebox.lens.domain.usecase.GetRecentlyAddedItemsUseCase import com.homebox.lens.domain.usecase.GetStatisticsUseCase -import com.homebox.lens.ui.screen.dashboard.DashboardUiState import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject +// [END_IMPORTS] -// [VIEWMODEL] // [ENTITY: ViewModel('DashboardViewModel')] +// [RELATION: ViewModel('DashboardViewModel')] -> [DEPENDS_ON] -> [UseCase('GetStatisticsUseCase')] +// [RELATION: ViewModel('DashboardViewModel')] -> [DEPENDS_ON] -> [UseCase('GetAllLocationsUseCase')] +// [RELATION: ViewModel('DashboardViewModel')] -> [DEPENDS_ON] -> [UseCase('GetAllLabelsUseCase')] +// [RELATION: ViewModel('DashboardViewModel')] -> [DEPENDS_ON] -> [UseCase('GetRecentlyAddedItemsUseCase')] +// [RELATION: ViewModel('DashboardViewModel')] -> [EMITS_STATE] -> [SealedInterface('DashboardUiState')] /** - * [CONTRACT] * @summary ViewModel для главного экрана (Dashboard). * @description Оркестрирует загрузку данных для Dashboard, используя строгую модель состояний * (`DashboardUiState`), и обрабатывает параллельные запросы без состояний гонки. @@ -35,30 +37,24 @@ class DashboardViewModel @Inject constructor( private val getRecentlyAddedItemsUseCase: GetRecentlyAddedItemsUseCase ) : ViewModel() { - // [STATE] private val _uiState = MutableStateFlow(DashboardUiState.Loading) - // [FIX] Добавлен получатель (receiver) `_uiState` для вызова asStateFlow(). - // [REASON] `asStateFlow()` является функцией-расширением для `MutableStateFlow` и - // должна вызываться на его экземпляре, чтобы создать публичную, неизменяемую версию потока. val uiState = _uiState.asStateFlow() - // [LIFECYCLE_HANDLER] init { loadDashboardData() } + // [ENTITY: Function('loadDashboardData')] /** - * [CONTRACT] * @summary Загружает все необходимые данные для экрана Dashboard. * @description Выполняет UseCase'ы параллельно и обновляет UI, переключая его * между состояниями `Loading`, `Success` и `Error` из `DashboardUiState`. * @sideeffect Асинхронно обновляет `_uiState` одним из состояний `DashboardUiState`. */ fun loadDashboardData() { - // [ENTRYPOINT] viewModelScope.launch { _uiState.value = DashboardUiState.Loading - Timber.i("[ACTION] Starting dashboard data collection.") + Timber.i("[INFO][ENTRYPOINT][loading_dashboard_data] Starting dashboard data collection.") val statsFlow = flow { emit(getStatisticsUseCase()) } val locationsFlow = flow { emit(getAllLocationsUseCase()) } @@ -73,16 +69,17 @@ class DashboardViewModel @Inject constructor( recentlyAddedItems = recentItems ) }.catch { exception -> - Timber.e(exception, "[ERROR] Failed to load dashboard data. State -> Error.") + Timber.e(exception, "[ERROR][EXCEPTION][loading_failed] Failed to load dashboard data. State -> Error.") _uiState.value = DashboardUiState.Error( message = exception.message ?: "Could not load dashboard data." ) }.collect { successState -> - Timber.i("[SUCCESS] Dashboard data loaded successfully. State -> Success.") + Timber.i("[INFO][SUCCESS][dashboard_data_loaded] Dashboard data loaded successfully. State -> Success.") _uiState.value = successState } } } - // [END_CLASS_DashboardViewModel] + // [END_ENTITY: Function('loadDashboardData')] } -// [END_FILE_DashboardViewModel.kt] \ No newline at end of file +// [END_ENTITY: ViewModel('DashboardViewModel')] +// [END_FILE_DashboardViewModel.kt] diff --git a/app/src/main/java/com/homebox/lens/ui/screen/inventorylist/InventoryListScreen.kt b/app/src/main/java/com/homebox/lens/ui/screen/inventorylist/InventoryListScreen.kt index abf1924..3becc28 100644 --- a/app/src/main/java/com/homebox/lens/ui/screen/inventorylist/InventoryListScreen.kt +++ b/app/src/main/java/com/homebox/lens/ui/screen/inventorylist/InventoryListScreen.kt @@ -11,10 +11,12 @@ import androidx.compose.ui.res.stringResource import com.homebox.lens.R import com.homebox.lens.navigation.NavigationActions import com.homebox.lens.ui.common.MainScaffold +// [END_IMPORTS] -// [ENTRYPOINT] +// [ENTITY: Function('InventoryListScreen')] +// [RELATION: Function('InventoryListScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')] +// [RELATION: Function('InventoryListScreen')] -> [CALLS] -> [Function('MainScaffold')] /** - * [CONTRACT] * @summary Composable-функция для экрана "Список инвентаря". * @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. * @param navigationActions Объект с навигационными действиями. @@ -24,14 +26,14 @@ fun InventoryListScreen( currentRoute: String?, navigationActions: NavigationActions ) { - // [UI_COMPONENT] MainScaffold( topBarTitle = stringResource(id = R.string.inventory_list_title), currentRoute = currentRoute, navigationActions = navigationActions ) { - // [CORE-LOGIC] - Text(text = "TODO: Inventory List Screen") + // [AI_NOTE]: Implement Inventory List Screen UI + Text(text = "Inventory List Screen") } - // [END_FUNCTION_InventoryListScreen] -} \ No newline at end of file +} +// [END_ENTITY: Function('InventoryListScreen')] +// [END_FILE_InventoryListScreen.kt] diff --git a/app/src/main/java/com/homebox/lens/ui/screen/inventorylist/InventoryListViewModel.kt b/app/src/main/java/com/homebox/lens/ui/screen/inventorylist/InventoryListViewModel.kt index 69069d6..6ddcc31 100644 --- a/app/src/main/java/com/homebox/lens/ui/screen/inventorylist/InventoryListViewModel.kt +++ b/app/src/main/java/com/homebox/lens/ui/screen/inventorylist/InventoryListViewModel.kt @@ -1,16 +1,21 @@ // [PACKAGE] com.homebox.lens.ui.screen.inventorylist // [FILE] InventoryListViewModel.kt - +// [SEMANTICS] ui, viewmodel, inventory_list package com.homebox.lens.ui.screen.inventorylist +// [IMPORTS] import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject +// [END_IMPORTS] -// [VIEWMODEL] +// [ENTITY: ViewModel('InventoryListViewModel')] +/** + * @summary ViewModel for the inventory list screen. + */ @HiltViewModel class InventoryListViewModel @Inject constructor() : ViewModel() { - // [STATE] - // TODO: Implement UI state + // [AI_NOTE]: Implement UI state } -// [END_FILE_InventoryListViewModel.kt] +// [END_ENTITY: ViewModel('InventoryListViewModel')] +// [END_FILE_InventoryListViewModel.kt] \ No newline at end of file diff --git a/app/src/main/java/com/homebox/lens/ui/screen/itemdetails/ItemDetailsScreen.kt b/app/src/main/java/com/homebox/lens/ui/screen/itemdetails/ItemDetailsScreen.kt index 6b78f9e..1feb48a 100644 --- a/app/src/main/java/com/homebox/lens/ui/screen/itemdetails/ItemDetailsScreen.kt +++ b/app/src/main/java/com/homebox/lens/ui/screen/itemdetails/ItemDetailsScreen.kt @@ -11,10 +11,12 @@ import androidx.compose.ui.res.stringResource import com.homebox.lens.R import com.homebox.lens.navigation.NavigationActions import com.homebox.lens.ui.common.MainScaffold +// [END_IMPORTS] -// [ENTRYPOINT] +// [ENTITY: Function('ItemDetailsScreen')] +// [RELATION: Function('ItemDetailsScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')] +// [RELATION: Function('ItemDetailsScreen')] -> [CALLS] -> [Function('MainScaffold')] /** - * [CONTRACT] * @summary Composable-функция для экрана "Детали элемента". * @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. * @param navigationActions Объект с навигационными действиями. @@ -24,14 +26,14 @@ fun ItemDetailsScreen( currentRoute: String?, navigationActions: NavigationActions ) { - // [UI_COMPONENT] MainScaffold( topBarTitle = stringResource(id = R.string.item_details_title), currentRoute = currentRoute, navigationActions = navigationActions ) { - // [CORE-LOGIC] - Text(text = "TODO: Item Details Screen") + // [AI_NOTE]: Implement Item Details Screen UI + Text(text = "Item Details Screen") } - // [END_FUNCTION_ItemDetailsScreen] -} \ No newline at end of file +} +// [END_ENTITY: Function('ItemDetailsScreen')] +// [END_FILE_ItemDetailsScreen.kt] diff --git a/app/src/main/java/com/homebox/lens/ui/screen/itemdetails/ItemDetailsViewModel.kt b/app/src/main/java/com/homebox/lens/ui/screen/itemdetails/ItemDetailsViewModel.kt index 6a591a8..104c5c3 100644 --- a/app/src/main/java/com/homebox/lens/ui/screen/itemdetails/ItemDetailsViewModel.kt +++ b/app/src/main/java/com/homebox/lens/ui/screen/itemdetails/ItemDetailsViewModel.kt @@ -1,16 +1,21 @@ // [PACKAGE] com.homebox.lens.ui.screen.itemdetails // [FILE] ItemDetailsViewModel.kt - +// [SEMANTICS] ui, viewmodel, item_details package com.homebox.lens.ui.screen.itemdetails +// [IMPORTS] import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject +// [END_IMPORTS] -// [VIEWMODEL] +// [ENTITY: ViewModel('ItemDetailsViewModel')] +/** + * @summary ViewModel for the item details screen. + */ @HiltViewModel class ItemDetailsViewModel @Inject constructor() : ViewModel() { - // [STATE] - // TODO: Implement UI state + // [AI_NOTE]: Implement UI state } +// [END_ENTITY: ViewModel('ItemDetailsViewModel')] // [END_FILE_ItemDetailsViewModel.kt] diff --git a/app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditScreen.kt b/app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditScreen.kt index 957024f..548928f 100644 --- a/app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditScreen.kt +++ b/app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditScreen.kt @@ -11,10 +11,12 @@ import androidx.compose.ui.res.stringResource import com.homebox.lens.R import com.homebox.lens.navigation.NavigationActions import com.homebox.lens.ui.common.MainScaffold +// [END_IMPORTS] -// [ENTRYPOINT] +// [ENTITY: Function('ItemEditScreen')] +// [RELATION: Function('ItemEditScreen')] -> [DEPENDS_ON] -> [Class('NavigationActions')] +// [RELATION: Function('ItemEditScreen')] -> [CALLS] -> [Function('MainScaffold')] /** - * [CONTRACT] * @summary Composable-функция для экрана "Редактирование элемента". * @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer. * @param navigationActions Объект с навигационными действиями. @@ -24,14 +26,14 @@ fun ItemEditScreen( currentRoute: String?, navigationActions: NavigationActions ) { - // [UI_COMPONENT] MainScaffold( topBarTitle = stringResource(id = R.string.item_edit_title), currentRoute = currentRoute, navigationActions = navigationActions ) { - // [CORE-LOGIC] - Text(text = "TODO: Item Edit Screen") + // [AI_NOTE]: Implement Item Edit Screen UI + Text(text = "Item Edit Screen") } - // [END_FUNCTION_ItemEditScreen] } +// [END_ENTITY: Function('ItemEditScreen')] +// [END_FILE_ItemEditScreen.kt] \ No newline at end of file diff --git a/app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditViewModel.kt b/app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditViewModel.kt index 975f01d..790830a 100644 --- a/app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditViewModel.kt +++ b/app/src/main/java/com/homebox/lens/ui/screen/itemedit/ItemEditViewModel.kt @@ -1,16 +1,21 @@ // [PACKAGE] com.homebox.lens.ui.screen.itemedit // [FILE] ItemEditViewModel.kt - +// [SEMANTICS] ui, viewmodel, item_edit package com.homebox.lens.ui.screen.itemedit +// [IMPORTS] import androidx.lifecycle.ViewModel import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject +// [END_IMPORTS] -// [VIEWMODEL] +// [ENTITY: ViewModel('ItemEditViewModel')] +/** + * @summary ViewModel for the item edit screen. + */ @HiltViewModel class ItemEditViewModel @Inject constructor() : ViewModel() { - // [STATE] - // TODO: Implement UI state + // [AI_NOTE]: Implement UI state } -// [END_FILE_ItemEditViewModel.kt] +// [END_ENTITY: ViewModel('ItemEditViewModel')] +// [END_FILE_ItemEditViewModel.kt] \ No newline at end of file diff --git a/app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt b/app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt index c094235..f594a1b 100644 --- a/app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt +++ b/app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListScreen.kt @@ -45,23 +45,15 @@ import com.homebox.lens.R import com.homebox.lens.domain.model.Label import com.homebox.lens.navigation.Screen import timber.log.Timber +// [END_IMPORTS] -// [SECTION] Main Screen Composable - +// [ENTITY: Function('LabelsListScreen')] +// [RELATION: Function('LabelsListScreen')] -> [DEPENDS_ON] -> [ViewModel('LabelsListViewModel')] +// [RELATION: Function('LabelsListScreen')] -> [DEPENDS_ON] -> [Framework('NavController')] /** - * [CONTRACT] * @summary Отображает экран со списком всех меток. - * @description Главная Composable-функция для экрана меток. Она использует Scaffold для структуры, - * получает состояние от `LabelsListViewModel`, обрабатывает навигацию и делегирует отображение - * списка и диалогов вспомогательным Composable-функциям. - * * @param navController Контроллер навигации для перемещения между экранами. * @param viewModel ViewModel, предоставляющая состояние UI для экрана меток. - * - * @precondition `navController` должен быть корректно инициализирован и способен обрабатывать навигационные события. - * @precondition `viewModel` должен быть доступен через Hilt. - * @postcondition Экран исчерпывающе обрабатывает все состояния из `LabelsListUiState` (Loading, Success, Error). - * @sideeffect Пользовательские действия (клики) инициируют вызовы ViewModel и навигационные команды через `navController`. */ @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -69,18 +61,15 @@ fun LabelsListScreen( navController: NavController, viewModel: LabelsListViewModel = hiltViewModel() ) { - // [ENTRYPOINT] val uiState by viewModel.uiState.collectAsState() - // [CORE-LOGIC] Scaffold( topBar = { TopAppBar( title = { Text(text = stringResource(id = R.string.screen_title_labels)) }, navigationIcon = { - // [ACTION] Handle back navigation IconButton(onClick = { - Timber.i("[ACTION] Navigate up initiated.") + Timber.i("[INFO][ACTION][navigate_up] Navigate up initiated.") navController.navigateUp() }) { Icon( @@ -92,9 +81,8 @@ fun LabelsListScreen( ) }, floatingActionButton = { - // [ACTION] Handle create new label initiation FloatingActionButton(onClick = { - Timber.i("[ACTION] FAB clicked: Initiate create new label flow.") + Timber.i("[INFO][ACTION][show_create_dialog] FAB clicked: Initiate create new label flow.") viewModel.onShowCreateDialog() }) { Icon( @@ -122,7 +110,6 @@ fun LabelsListScreen( .padding(paddingValues), contentAlignment = Alignment.Center ) { - // [CORE-LOGIC] State-driven UI rendering when (currentState) { is LabelsListUiState.Loading -> { CircularProgressIndicator() @@ -137,9 +124,7 @@ fun LabelsListScreen( LabelsList( labels = currentState.labels, onLabelClick = { label -> - // [ACTION] Handle label click - Timber.i("[ACTION] Label clicked: ${label.id}. Navigating to inventory list.") - // [DESIGN-DECISION] Использовать существующий экран списка инвентаря, передавая фильтр. + Timber.i("[INFO][ACTION][navigate_to_inventory] Label clicked: ${label.id}. Navigating to inventory list.") val route = Screen.InventoryList.withFilter("label", label.id) navController.navigate(route) } @@ -149,14 +134,12 @@ fun LabelsListScreen( } } } - // [COHERENCE_CHECK_PASSED] } -// [END_FUNCTION] LabelsListScreen - -// [SECTION] Helper Composables +// [END_ENTITY: Function('LabelsListScreen')] +// [ENTITY: Function('LabelsList')] +// [RELATION: Function('LabelsList')] -> [DEPENDS_ON] -> [DataClass('Label')] /** - * [CONTRACT] * @summary Composable-функция для отображения списка меток. * @param labels Список объектов `Label` для отображения. * @param onLabelClick Лямбда-функция, вызываемая при нажатии на элемент списка. @@ -168,7 +151,6 @@ private fun LabelsList( onLabelClick: (Label) -> Unit, modifier: Modifier = Modifier ) { - // [CORE-LOGIC] LazyColumn( modifier = modifier.fillMaxSize(), contentPadding = PaddingValues(16.dp), @@ -182,10 +164,11 @@ private fun LabelsList( } } } -// [END_FUNCTION] LabelsList +// [END_ENTITY: Function('LabelsList')] +// [ENTITY: Function('LabelListItem')] +// [RELATION: Function('LabelListItem')] -> [DEPENDS_ON] -> [DataClass('Label')] /** - * [CONTRACT] * @summary Composable-функция для отображения одного элемента в списке меток. * @param label Объект `Label`, который нужно отобразить. * @param onClick Лямбда-функция, вызываемая при нажатии на элемент. @@ -195,7 +178,6 @@ private fun LabelListItem( label: Label, onClick: () -> Unit ) { - // [CORE-LOGIC] ListItem( headlineContent = { Text(text = label.name) }, leadingContent = { @@ -207,10 +189,10 @@ private fun LabelListItem( modifier = Modifier.clickable(onClick = onClick) ) } -// [END_FUNCTION] LabelListItem +// [END_ENTITY: Function('LabelListItem')] +// [ENTITY: Function('CreateLabelDialog')] /** - * [CONTRACT] * @summary Диалоговое окно для создания новой метки. * @param onConfirm Лямбда-функция, вызываемая при подтверждении создания с именем метки. * @param onDismiss Лямбда-функция, вызываемая при закрытии диалога. @@ -220,11 +202,9 @@ private fun CreateLabelDialog( onConfirm: (String) -> Unit, onDismiss: () -> Unit ) { - // [STATE] var text by remember { mutableStateOf("") } val isConfirmEnabled = text.isNotBlank() - // [CORE-LOGIC] AlertDialog( onDismissRequest = onDismiss, title = { Text(text = stringResource(R.string.dialog_title_create_label)) }, @@ -252,6 +232,5 @@ private fun CreateLabelDialog( } ) } -// [END_FUNCTION] CreateLabelDialog - -// [END_FILE] LabelsListScreen.kt \ No newline at end of file +// [END_ENTITY: Function('CreateLabelDialog')] +// [END_FILE_LabelsListScreen.kt] \ No newline at end of file diff --git a/app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListUiState.kt b/app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListUiState.kt index c176505..a53f005 100644 --- a/app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListUiState.kt +++ b/app/src/main/java/com/homebox/lens/ui/screen/labelslist/LabelsListUiState.kt @@ -2,35 +2,47 @@ // [FILE] LabelsListUiState.kt // [SEMANTICS] ui_state, sealed_interface, contract package com.homebox.lens.ui.screen.labelslist + // [IMPORTS] import com.homebox.lens.domain.model.Label -// [CONTRACT] +// [END_IMPORTS] + +// [ENTITY: SealedInterface('LabelsListUiState')] /** -[CONTRACT] -@summary Определяет все возможные состояния для UI экрана со списком меток. -@description Использование sealed-интерфейса позволяет исчерпывающе обрабатывать все состояния в Composable-функциях. + * @summary Определяет все возможные состояния для UI экрана со списком меток. + * @description Использование sealed-интерфейса позволяет исчерпывающе обрабатывать все состояния в Composable-функциях. */ sealed interface LabelsListUiState { + // [ENTITY: DataClass('Success')] + // [RELATION: DataClass('Success')] -> [DEPENDS_ON] -> [DataClass('Label')] /** - @summary Состояние успеха, содержит список меток и состояние диалога. - @property labels Список меток для отображения. - @property isShowingCreateDialog Флаг, показывающий, должен ли быть отображен диалог создания метки. - @invariant labels не может быть null. + * @summary Состояние успеха, содержит список меток и состояние диалога. + * @param labels Список меток для отображения. + * @param isShowingCreateDialog Флаг, показывающий, должен ли быть отображен диалог создания метки. + * @invariant labels не может быть null. */ data class Success( val labels: List