REFACTOR END
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// [FILE] build.gradle.kts
|
||||
// [SEMANTICS] build, configuration, module, feature, dashboard
|
||||
// [FILE] feature/dashboard/build.gradle.kts
|
||||
// [SEMANTICS] build, dashboard, feature_module
|
||||
// [PURPOSE] Build script for the feature:dashboard module.
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
@@ -16,9 +17,6 @@ android {
|
||||
defaultConfig {
|
||||
minSdk = Versions.minSdk
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -26,7 +24,7 @@ android {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
"proguard-rules.pro",
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -40,7 +38,6 @@ android {
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
@@ -49,9 +46,23 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// [MODULE_DEPENDENCY] Core modules
|
||||
implementation(project(":domain"))
|
||||
// [MODULE_DEPENDENCY] Data module
|
||||
implementation(project(":data"))
|
||||
// [MODULE_DEPENDENCY] Domain module
|
||||
implementation(project(":domain"))
|
||||
// [MODULE_DEPENDENCY] Feature modules for navigation
|
||||
implementation(project(":feature:inventorylist"))
|
||||
implementation(project(":feature:itemdetails"))
|
||||
implementation(project(":feature:itemedit"))
|
||||
implementation(project(":feature:labeledit"))
|
||||
implementation(project(":feature:labelslist"))
|
||||
implementation(project(":feature:locationedit"))
|
||||
implementation(project(":feature:locationslist"))
|
||||
implementation(project(":feature:scan"))
|
||||
implementation(project(":feature:search"))
|
||||
implementation(project(":feature:settings"))
|
||||
implementation(project(":feature:setup"))
|
||||
implementation(project(":ui:common"))
|
||||
|
||||
// [DEPENDENCY] AndroidX
|
||||
implementation(Libs.coreKtx)
|
||||
@@ -61,9 +72,13 @@ dependencies {
|
||||
// [DEPENDENCY] Compose
|
||||
implementation(Libs.composeUi)
|
||||
implementation(Libs.composeUiGraphics)
|
||||
implementation(Libs.composeFoundation)
|
||||
implementation(Libs.composeUiToolingPreview)
|
||||
implementation(Libs.composeMaterial3)
|
||||
implementation("androidx.compose.material:material-icons-extended-android:1.6.8")
|
||||
implementation(Libs.composeFoundationLayout)
|
||||
implementation(Libs.composeMaterialIconsExtended)
|
||||
implementation(Libs.composeFoundationLayout)
|
||||
implementation(Libs.composeMaterialIconsExtended)
|
||||
implementation(Libs.navigationCompose)
|
||||
implementation(Libs.hiltNavigationCompose)
|
||||
|
||||
@@ -92,4 +107,4 @@ kapt {
|
||||
correctErrorTypes = true
|
||||
}
|
||||
|
||||
// [END_FILE_build.gradle.kts]
|
||||
// [END_FILE_feature/dashboard/build.gradle.kts]
|
||||
|
||||
@@ -4,29 +4,26 @@
|
||||
package com.homebox.lens.feature.dashboard
|
||||
|
||||
// [IMPORTS]
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.navigation.NavGraphBuilder
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.composable
|
||||
import com.homebox.lens.navigation.NavigationActions
|
||||
import com.homebox.lens.ui.common.NavigationActions
|
||||
// [END_IMPORTS]
|
||||
|
||||
// [ENTITY: Function('addDashboardScreen')]
|
||||
// [RELATION: Function('addDashboardScreen')] -> [DEPENDS_ON] -> [Function('DashboardScreen')]
|
||||
/**
|
||||
* @summary Extension function for NavGraphBuilder to add the Dashboard screen to the navigation graph.
|
||||
* @description Registers the Dashboard route and composes the DashboardScreen with appropriate navigation actions and common UI components.
|
||||
* @param route The route string for the Dashboard screen.
|
||||
* @param currentRoute The current navigation route, used for highlighting.
|
||||
* @param navigateToScan Lambda for navigating to the scan screen.
|
||||
* @param navigateToSearch Lambda for navigating to the search screen.
|
||||
* @param navigateToInventoryListWithLocation Lambda for navigating to inventory filtered by location.
|
||||
* @param navigateToInventoryListWithLabel Lambda for navigating to inventory filtered by label.
|
||||
* @param MainScaffoldContent Composable lambda for the main scaffold structure.
|
||||
* @param HomeboxLensTheme Composable lambda for applying the application theme.
|
||||
* @sideeffect Adds a composable route for the Dashboard screen.
|
||||
*/
|
||||
// [ANCHOR:addDashboardScreen:Function]
|
||||
// [RELATION:DEPENDS_ON:DashboardScreen]
|
||||
// [CONTRACT:addDashboardScreen]
|
||||
// [PURPOSE] Extension function for NavGraphBuilder to add the Dashboard screen to the navigation graph. Registers the Dashboard route and composes the DashboardScreen with appropriate navigation actions and common UI components.
|
||||
// [PARAM:route:String] The route string for the Dashboard screen.
|
||||
// [PARAM:currentRoute:String] The current navigation route, used for highlighting.
|
||||
// [PARAM:navigateToScan:Unit] Lambda for navigating to the scan screen.
|
||||
// [PARAM:navigateToSearch:Unit] Lambda for navigating to the search screen.
|
||||
// [PARAM:navigateToInventoryListWithLocation:Unit] Lambda for navigating to inventory filtered by location.
|
||||
// [PARAM:navigateToInventoryListWithLabel:Unit] Lambda for navigating to inventory filtered by label.
|
||||
// [PARAM:navigationActions:NavigationActions] Объект с навигационными действиями.
|
||||
// [PARAM:navController:NavHostController] Контроллер навигации.
|
||||
// [SIDE_EFFECT] Adds a composable route for the Dashboard screen.
|
||||
// [END_CONTRACT:addDashboardScreen]
|
||||
fun NavGraphBuilder.addDashboardScreen(
|
||||
route: String,
|
||||
currentRoute: String?,
|
||||
@@ -36,14 +33,6 @@ fun NavGraphBuilder.addDashboardScreen(
|
||||
navigateToInventoryListWithLabel: (String) -> Unit,
|
||||
navigationActions: NavigationActions,
|
||||
navController: NavHostController,
|
||||
MainScaffoldContent: @Composable (
|
||||
topBarTitle: String,
|
||||
currentRoute: String?,
|
||||
navigationActions: NavigationActions,
|
||||
topBarActions: @Composable () -> Unit,
|
||||
content: @Composable (PaddingValues) -> Unit
|
||||
) -> Unit,
|
||||
HomeboxLensTheme: @Composable (content: @Composable () -> Unit) -> Unit
|
||||
) {
|
||||
composable(route = route) {
|
||||
DashboardScreen(
|
||||
@@ -52,12 +41,10 @@ fun NavGraphBuilder.addDashboardScreen(
|
||||
navigateToSearch = navigateToSearch,
|
||||
navigateToInventoryListWithLocation = navigateToInventoryListWithLocation,
|
||||
navigateToInventoryListWithLabel = navigateToInventoryListWithLabel,
|
||||
MainScaffoldContent = MainScaffoldContent,
|
||||
HomeboxLensTheme = HomeboxLensTheme,
|
||||
navigationActions = navigationActions,
|
||||
navController = navController
|
||||
navController = navController,
|
||||
)
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('addDashboardScreen')]
|
||||
// [END_FILE_DashboardNavigation.kt]
|
||||
// [END_ANCHOR:addDashboardScreen]
|
||||
// [END_FILE_DashboardNavigation.kt]
|
||||
|
||||
@@ -4,10 +4,18 @@
|
||||
package com.homebox.lens.feature.dashboard
|
||||
|
||||
// [IMPORTS]
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.layout.ExperimentalLayoutApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.FlowRow
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
@@ -16,7 +24,13 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.QrCodeScanner
|
||||
import androidx.compose.material.icons.filled.Search
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SuggestionChip
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
@@ -29,29 +43,30 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.homebox.lens.domain.model.*
|
||||
import com.homebox.lens.feature.dashboard.R
|
||||
import com.homebox.lens.navigation.NavigationActions
|
||||
import com.homebox.lens.ui.theme.HomeboxLensTheme
|
||||
import com.homebox.lens.ui.common.mainScaffold
|
||||
import com.homebox.lens.ui.common.NavigationActions
|
||||
import com.homebox.lens.feature.dashboard.ui.theme.HomeboxLensTheme
|
||||
import timber.log.Timber
|
||||
// [END_IMPORTS]
|
||||
|
||||
// [ENTITY: Function('DashboardScreen')]
|
||||
// [RELATION: Function('DashboardScreen')] -> [DEPENDS_ON] -> [ViewModel('DashboardViewModel')]
|
||||
// [RELATION: Function('DashboardScreen')] -> [CALLS] -> [Function('MainScaffold')]
|
||||
/**
|
||||
* @summary Главная Composable-функция для экрана "Панель управления".
|
||||
* @param viewModel ViewModel для этого экрана, предоставляется через Hilt.
|
||||
* @param currentRoute Текущий маршрут для подсветки активного элемента в Drawer.
|
||||
* @param navigateToScan Лямбда для навигации на экран сканирования.
|
||||
* @param navigateToSearch Лямбда для навигации на экран поиска.
|
||||
* @param navigateToInventoryListWithLocation Лямбда для навигации на список инвентаря с фильтром по локации.
|
||||
* @param navigateToInventoryListWithLabel Лямбда для навигации на список инвентаря с фильтром по метке.
|
||||
* @param MainScaffoldContent Composable-функция для отображения основного Scaffold.
|
||||
* @param HomeboxLensTheme Composable-функция для применения темы.
|
||||
* @sideeffect Вызывает навигационные лямбды при взаимодействии с UI.
|
||||
*/
|
||||
// [ANCHOR:DashboardScreen:Function]
|
||||
// [RELATION:CALLS:DashboardViewModel]
|
||||
// [RELATION:CALLS:mainScaffold]
|
||||
// [CONTRACT:DashboardScreen]
|
||||
// [PURPOSE] Главная Composable-функция для экрана "Панель управления".
|
||||
// [PARAM:viewModel:DashboardViewModel] ViewModel для этого экрана, предоставляется через Hilt.
|
||||
// [PARAM:currentRoute:String] Текущий маршрут для подсветки активного элемента в Drawer.
|
||||
// [PARAM:navigateToScan:Unit] Лямбда для навигации на экран сканирования.
|
||||
// [PARAM:navigateToSearch:Unit] Лямбда для навигации на экран поиска.
|
||||
// [PARAM:navigateToInventoryListWithLocation:Unit] Лямбда для навигации на список инвентаря с фильтром по локации.
|
||||
// [PARAM:navigateToInventoryListWithLabel:Unit] Лямбда для навигации на список инвентаря с фильтром по метке.
|
||||
// [PARAM:navigationActions:NavigationActions] Объект с навигационными действиями.
|
||||
// [PARAM:navController:NavHostController] Контроллер навигации.
|
||||
// [SIDE_EFFECT] Вызывает навигационные лямбды при взаимодействии с UI.
|
||||
// [END_CONTRACT:DashboardScreen]
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
fun DashboardScreen(
|
||||
viewModel: DashboardViewModel = hiltViewModel(),
|
||||
@@ -62,17 +77,6 @@ fun DashboardScreen(
|
||||
navigateToInventoryListWithLabel: (String) -> Unit,
|
||||
navigationActions: NavigationActions,
|
||||
navController: NavHostController,
|
||||
MainScaffoldContent: @Composable (
|
||||
topBarTitle: String,
|
||||
currentRoute: String?,
|
||||
navigationActions: NavigationActions,
|
||||
onNavigateUp: (() -> Unit)?,
|
||||
topBarActions: @Composable () -> Unit,
|
||||
snackbarHost: @Composable () -> Unit,
|
||||
floatingActionButton: @Composable () -> Unit,
|
||||
content: @Composable (PaddingValues) -> Unit
|
||||
) -> Unit,
|
||||
HomeboxLensTheme: @Composable (content: @Composable () -> Unit) -> Unit
|
||||
) {
|
||||
val uiState by viewModel.uiState.collectAsState()
|
||||
|
||||
@@ -81,60 +85,60 @@ fun DashboardScreen(
|
||||
}
|
||||
|
||||
HomeboxLensTheme {
|
||||
MainScaffoldContent(
|
||||
topBarTitle = stringResource(id = R.string.dashboard_title),
|
||||
mainScaffold(
|
||||
topBarTitle = stringResource(id = com.homebox.lens.feature.dashboard.R.string.dashboard_title),
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
onNavigateUp = null, // Dashboard doesn't have an "Up" button
|
||||
topBarActions = {
|
||||
IconButton(onClick = navigateToScan) {
|
||||
Icon(
|
||||
Icons.Default.QrCodeScanner,
|
||||
contentDescription = stringResource(id = R.string.cd_scan_qr_code)
|
||||
Icons.Filled.QrCodeScanner,
|
||||
contentDescription = stringResource(id = com.homebox.lens.feature.dashboard.R.string.cd_scan_qr_code),
|
||||
)
|
||||
}
|
||||
IconButton(onClick = navigateToSearch) {
|
||||
Icon(
|
||||
Icons.Default.Search,
|
||||
contentDescription = stringResource(id = R.string.cd_search)
|
||||
contentDescription = stringResource(id = com.homebox.lens.feature.dashboard.R.string.cd_search),
|
||||
)
|
||||
}
|
||||
},
|
||||
snackbarHost = {}, // Not used in Dashboard
|
||||
floatingActionButton = {} // Not used in Dashboard
|
||||
snackbarHost = { },
|
||||
floatingActionButton = { },
|
||||
) { paddingValues ->
|
||||
DashboardContent(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
uiState = uiState,
|
||||
onLocationClick = { location ->
|
||||
Timber.i("[INFO][ACTION][navigate_to_inventory_with_location] Location chip clicked: ${location.id}. Navigating...")
|
||||
Timber.i("[INFO][ACTION][navigate_to_inventory_with_location]", "Location chip clicked", "locationId", location.id)
|
||||
navigateToInventoryListWithLocation(location.id)
|
||||
},
|
||||
onLabelClick = { label ->
|
||||
Timber.i("[INFO][ACTION][navigate_to_inventory_with_label] Label chip clicked: ${label.id}. Navigating...")
|
||||
Timber.i("[INFO][ACTION][navigate_to_inventory_with_label]", "Label chip clicked", "labelId", label.id)
|
||||
navigateToInventoryListWithLabel(label.id)
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('DashboardScreen')]
|
||||
// [END_ANCHOR:DashboardScreen]
|
||||
|
||||
// [ENTITY: Function('DashboardContent')]
|
||||
// [RELATION: Function('DashboardContent')] -> [CONSUMES_STATE] -> [SealedInterface('DashboardUiState')]
|
||||
/**
|
||||
* @summary Отображает основной контент экрана в зависимости от uiState.
|
||||
* @param modifier Модификатор для стилизации.
|
||||
* @param uiState Текущее состояние UI экрана.
|
||||
* @param onLocationClick Лямбда-обработчик нажатия на местоположение.
|
||||
* @param onLabelClick Лямбда-обработчик нажатия на метку.
|
||||
*/
|
||||
// [ANCHOR:DashboardContent:Function]
|
||||
// [RELATION:CONSUMES_STATE:DashboardUiState]
|
||||
// [CONTRACT:DashboardContent]
|
||||
// [PURPOSE] Отображает основной контент экрана в зависимости от uiState.
|
||||
// [PARAM:modifier:Modifier] Модификатор для стилизации.
|
||||
// [PARAM:uiState:DashboardUiState] Текущее состояние UI экрана.
|
||||
// [PARAM:onLocationClick:Unit] Лямбда-обработчик нажатия на местоположение.
|
||||
// [PARAM:onLabelClick:Unit] Лямбда-обработчик нажатия на метку.
|
||||
// [END_CONTRACT:DashboardContent]
|
||||
@Composable
|
||||
private fun DashboardContent(
|
||||
modifier: Modifier = Modifier,
|
||||
uiState: DashboardUiState,
|
||||
onLocationClick: (LocationOutCount) -> Unit,
|
||||
onLabelClick: (LabelOut) -> Unit
|
||||
onLabelClick: (LabelOut) -> Unit,
|
||||
) {
|
||||
when (uiState) {
|
||||
is DashboardUiState.Loading -> {
|
||||
@@ -147,16 +151,17 @@ private fun DashboardContent(
|
||||
Text(
|
||||
text = uiState.message,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
textAlign = TextAlign.Center
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
is DashboardUiState.Success -> {
|
||||
LazyColumn(
|
||||
modifier = modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||
modifier =
|
||||
modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp),
|
||||
) {
|
||||
item { Spacer(modifier = Modifier.height(8.dp)) }
|
||||
item { StatisticsSection(statistics = uiState.statistics) }
|
||||
@@ -168,77 +173,102 @@ private fun DashboardContent(
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('DashboardContent')]
|
||||
// [END_ANCHOR:DashboardContent]
|
||||
|
||||
// [ENTITY: Function('StatisticsSection')]
|
||||
// [RELATION: Function('StatisticsSection')] -> [DEPENDS_ON] -> [DataClass('GroupStatistics')]
|
||||
/**
|
||||
* @summary Секция для отображения общей статистики.
|
||||
* @param statistics Объект со статистическими данными.
|
||||
*/
|
||||
// [ANCHOR:StatisticsSection:Function]
|
||||
// [RELATION:DEPENDS_ON:GroupStatistics]
|
||||
// [CONTRACT:StatisticsSection]
|
||||
// [PURPOSE] Секция для отображения общей статистики.
|
||||
// [PARAM:statistics:GroupStatistics] Объект со статистическими данными.
|
||||
// [END_CONTRACT:StatisticsSection]
|
||||
@Composable
|
||||
private fun StatisticsSection(statistics: GroupStatistics) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.dashboard_section_quick_stats),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
text = stringResource(id = com.homebox.lens.feature.dashboard.R.string.dashboard_section_quick_stats),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
Card {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(2),
|
||||
modifier = Modifier
|
||||
.height(120.dp)
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
modifier =
|
||||
Modifier
|
||||
.height(120.dp)
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
item { StatisticCard(title = stringResource(id = R.string.dashboard_stat_total_items), value = statistics.items.toString()) }
|
||||
item { StatisticCard(title = stringResource(id = R.string.dashboard_stat_total_value), value = statistics.totalValue.toString()) }
|
||||
item { StatisticCard(title = stringResource(id = R.string.dashboard_stat_total_labels), value = statistics.labels.toString()) }
|
||||
item { StatisticCard(title = stringResource(id = R.string.dashboard_stat_total_locations), value = statistics.locations.toString()) }
|
||||
item {
|
||||
StatisticCard(
|
||||
title = stringResource(id = com.homebox.lens.feature.dashboard.R.string.dashboard_stat_total_items),
|
||||
value = statistics.items.toString(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
StatisticCard(
|
||||
title = stringResource(id = com.homebox.lens.feature.dashboard.R.string.dashboard_stat_total_value),
|
||||
value = statistics.totalValue.toString(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
StatisticCard(
|
||||
title = stringResource(id = com.homebox.lens.feature.dashboard.R.string.dashboard_stat_total_labels),
|
||||
value = statistics.labels.toString(),
|
||||
)
|
||||
}
|
||||
item {
|
||||
StatisticCard(
|
||||
title = stringResource(id = com.homebox.lens.feature.dashboard.R.string.dashboard_stat_total_locations),
|
||||
value = statistics.locations.toString(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('StatisticsSection')]
|
||||
// [END_ANCHOR:StatisticsSection]
|
||||
|
||||
// [ENTITY: Function('StatisticCard')]
|
||||
/**
|
||||
* @summary Карточка для отображения одного статистического показателя.
|
||||
* @param title Название показателя.
|
||||
* @param value Значение показателя.
|
||||
*/
|
||||
// [ANCHOR:StatisticCard:Function]
|
||||
// [CONTRACT:StatisticCard]
|
||||
// [PURPOSE] Карточка для отображения одного статистического показателя.
|
||||
// [PARAM:title:String] Название показателя.
|
||||
// [PARAM:value:String] Значение показателя.
|
||||
// [END_CONTRACT:StatisticCard]
|
||||
@Composable
|
||||
private fun StatisticCard(title: String, value: String) {
|
||||
private fun StatisticCard(
|
||||
title: String,
|
||||
value: String,
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center) {
|
||||
Text(text = title, style = MaterialTheme.typography.labelMedium, textAlign = TextAlign.Center)
|
||||
Text(text = value, style = MaterialTheme.typography.headlineSmall, textAlign = TextAlign.Center)
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('StatisticCard')]
|
||||
// [END_ANCHOR:StatisticCard]
|
||||
|
||||
// [ENTITY: Function('RecentlyAddedSection')]
|
||||
// [RELATION: Function('RecentlyAddedSection')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')]
|
||||
/**
|
||||
* @summary Секция для отображения недавно добавленных элементов.
|
||||
* @param items Список элементов для отображения.
|
||||
*/
|
||||
// [ANCHOR:RecentlyAddedSection:Function]
|
||||
// [RELATION:DEPENDS_ON:ItemSummary]
|
||||
// [CONTRACT:RecentlyAddedSection]
|
||||
// [PURPOSE] Секция для отображения недавно добавленных элементов.
|
||||
// [PARAM:items:List<ItemSummary>] Список элементов для отображения.
|
||||
// [END_CONTRACT:RecentlyAddedSection]
|
||||
@Composable
|
||||
private fun RecentlyAddedSection(items: List<ItemSummary>) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.dashboard_section_recently_added),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
text = stringResource(id = com.homebox.lens.feature.dashboard.R.string.dashboard_section_recently_added),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
if (items.isEmpty()) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.items_not_found),
|
||||
text = stringResource(id = com.homebox.lens.feature.dashboard.R.string.items_not_found),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 16.dp),
|
||||
textAlign = TextAlign.Center
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 16.dp),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
} else {
|
||||
LazyRow(horizontalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
@@ -249,126 +279,189 @@ private fun RecentlyAddedSection(items: List<ItemSummary>) {
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('RecentlyAddedSection')]
|
||||
// [END_ANCHOR:RecentlyAddedSection]
|
||||
|
||||
// [ENTITY: Function('ItemCard')]
|
||||
// [RELATION: Function('ItemCard')] -> [DEPENDS_ON] -> [DataClass('ItemSummary')]
|
||||
/**
|
||||
* @summary Карточка для отображения краткой информации об элементе.
|
||||
* @param item Элемент для отображения.
|
||||
*/
|
||||
// [ANCHOR:ItemCard:Function]
|
||||
// [RELATION:DEPENDS_ON:ItemSummary]
|
||||
// [CONTRACT:ItemCard]
|
||||
// [PURPOSE] Карточка для отображения краткой информации об элементе.
|
||||
// [PARAM:item:ItemSummary] Элемент для отображения.
|
||||
// [END_CONTRACT:ItemCard]
|
||||
@Composable
|
||||
private fun ItemCard(item: ItemSummary) {
|
||||
Card(modifier = Modifier.width(150.dp)) {
|
||||
Column(modifier = Modifier.padding(8.dp)) {
|
||||
// [AI_NOTE]: Add image here from item.image
|
||||
Spacer(modifier = Modifier
|
||||
.height(80.dp)
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.secondaryContainer))
|
||||
Spacer(
|
||||
modifier =
|
||||
Modifier
|
||||
.height(80.dp)
|
||||
.fillMaxWidth()
|
||||
.background(MaterialTheme.colorScheme.secondaryContainer),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(text = item.name, style = MaterialTheme.typography.titleSmall, maxLines = 1)
|
||||
Text(text = item.location?.name ?: stringResource(id = R.string.no_location), style = MaterialTheme.typography.bodySmall, maxLines = 1)
|
||||
Text(
|
||||
text = item.location?.name ?: stringResource(id = com.homebox.lens.feature.dashboard.R.string.items_not_found),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('ItemCard')]
|
||||
// [END_ANCHOR:ItemCard]
|
||||
|
||||
// [ENTITY: Function('LocationsSection')]
|
||||
// [RELATION: Function('LocationsSection')] -> [DEPENDS_ON] -> [DataClass('LocationOutCount')]
|
||||
/**
|
||||
* @summary Секция для отображения местоположений в виде чипсов.
|
||||
* @param locations Список местоположений.
|
||||
* @param onLocationClick Лямбда-обработчик нажатия на местоположение.
|
||||
*/
|
||||
// [ANCHOR:LocationsSection:Function]
|
||||
// [RELATION:DEPENDS_ON:LocationOutCount]
|
||||
// [CONTRACT:LocationsSection]
|
||||
// [PURPOSE] Секция для отображения местоположений в виде чипсов.
|
||||
// [PARAM:locations:List<LocationOutCount>] Список местоположений.
|
||||
// [PARAM:onLocationClick:Unit] Лямбда-обработчик нажатия на местоположение.
|
||||
// [END_CONTRACT:LocationsSection]
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun LocationsSection(locations: List<LocationOutCount>, onLocationClick: (LocationOutCount) -> Unit) {
|
||||
private fun LocationsSection(
|
||||
locations: List<LocationOutCount>,
|
||||
onLocationClick: (LocationOutCount) -> Unit,
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.dashboard_section_locations),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
text = stringResource(id = com.homebox.lens.feature.dashboard.R.string.dashboard_section_locations),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
locations.forEach { location ->
|
||||
SuggestionChip(
|
||||
onClick = { onLocationClick(location) },
|
||||
label = { Text(stringResource(id = R.string.location_chip_label, location.name, location.itemCount)) }
|
||||
onClick = {
|
||||
Timber.i("[INFO][ACTION][location_chip_click]", "Location chip clicked", "locationId", location.id)
|
||||
onLocationClick(location)
|
||||
},
|
||||
label = { Text(stringResource(id = com.homebox.lens.feature.dashboard.R.string.location_chip_label, location.name, location.itemCount)) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('LocationsSection')]
|
||||
// [END_ANCHOR:LocationsSection]
|
||||
|
||||
// [ENTITY: Function('LabelsSection')]
|
||||
// [RELATION: Function('LabelsSection')] -> [DEPENDS_ON] -> [DataClass('LabelOut')]
|
||||
/**
|
||||
* @summary Секция для отображения меток в виде чипсов.
|
||||
* @param labels Список меток.
|
||||
* @param onLabelClick Лямбда-обработчик нажатия на метку.
|
||||
*/
|
||||
// [ANCHOR:LabelsSection:Function]
|
||||
// [RELATION:DEPENDS_ON:LabelOut]
|
||||
// [CONTRACT:LabelsSection]
|
||||
// [PURPOSE] Секция для отображения меток в виде чипсов.
|
||||
// [PARAM:labels:List<LabelOut>] Список меток.
|
||||
// [PARAM:onLabelClick:Unit] Лямбда-обработчик нажатия на метку.
|
||||
// [END_CONTRACT:LabelsSection]
|
||||
@OptIn(ExperimentalLayoutApi::class)
|
||||
@Composable
|
||||
private fun LabelsSection(labels: List<LabelOut>, onLabelClick: (LabelOut) -> Unit) {
|
||||
private fun LabelsSection(
|
||||
labels: List<LabelOut>,
|
||||
onLabelClick: (LabelOut) -> Unit,
|
||||
) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.dashboard_section_labels),
|
||||
style = MaterialTheme.typography.titleMedium
|
||||
text = stringResource(id = com.homebox.lens.feature.dashboard.R.string.dashboard_section_labels),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
FlowRow(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
labels.forEach { label ->
|
||||
SuggestionChip(
|
||||
onClick = { onLabelClick(label) },
|
||||
label = { Text(label.name) }
|
||||
onClick = {
|
||||
Timber.i("[INFO][ACTION][label_chip_click]", "Label chip clicked", "labelId", label.id)
|
||||
onLabelClick(label)
|
||||
},
|
||||
label = { Text(label.name) },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('LabelsSection')]
|
||||
// [END_ANCHOR:LabelsSection]
|
||||
|
||||
// [ENTITY: Function('DashboardContentSuccessPreview')]
|
||||
// [ANCHOR:DashboardContentSuccessPreview:Function]
|
||||
@Preview(showBackground = true, name = "Dashboard Success State")
|
||||
@Composable
|
||||
fun DashboardContentSuccessPreview() {
|
||||
val previewState = DashboardUiState.Success(
|
||||
statistics = GroupStatistics(
|
||||
items = 123,
|
||||
totalValue = 9999.99,
|
||||
locations = 5,
|
||||
labels = 8
|
||||
),
|
||||
locations = listOf(
|
||||
LocationOutCount(id="1", name="Office", color = "#FF0000", isArchived = false, itemCount = 10, createdAt = "", updatedAt = ""),
|
||||
LocationOutCount(id="2", name="Garage", color = "#00FF00", isArchived = false, itemCount = 5, createdAt = "", updatedAt = ""),
|
||||
LocationOutCount(id="3",name="Living Room", color = "#0000FF", isArchived = false, itemCount = 15, createdAt = "", updatedAt = ""),
|
||||
LocationOutCount(id="4",name="Kitchen", color = "#FFFF00", isArchived = false, itemCount = 20, createdAt = "", updatedAt = ""),
|
||||
LocationOutCount(id="5",name="Basement", color = "#00FFFF", isArchived = false, itemCount = 3, createdAt = "", updatedAt = "")
|
||||
),
|
||||
labels = listOf(
|
||||
LabelOut(id="1", name="electronics", color = "#FF0000", isArchived = false, createdAt = "", updatedAt = ""),
|
||||
LabelOut(id="2", name="important", color = "#00FF00", isArchived = false, createdAt = "", updatedAt = ""),
|
||||
LabelOut(id="3", name="seasonal", color = "#0000FF", isArchived = false, createdAt = "", updatedAt = ""),
|
||||
LabelOut(id="4", name="hobby", color = "#FFFF00", isArchived = false, createdAt = "", updatedAt = "")
|
||||
),
|
||||
recentlyAddedItems = emptyList()
|
||||
)
|
||||
val previewState =
|
||||
DashboardUiState.Success(
|
||||
statistics =
|
||||
GroupStatistics(
|
||||
items = 123,
|
||||
totalValue = 9999.99,
|
||||
locations = 5,
|
||||
labels = 8,
|
||||
),
|
||||
locations =
|
||||
listOf(
|
||||
LocationOutCount(
|
||||
id = "1",
|
||||
name = "Office",
|
||||
color = "#FF0000",
|
||||
isArchived = false,
|
||||
itemCount = 10,
|
||||
createdAt = "",
|
||||
updatedAt = "",
|
||||
),
|
||||
LocationOutCount(
|
||||
id = "2",
|
||||
name = "Garage",
|
||||
color = "#00FF00",
|
||||
isArchived = false,
|
||||
itemCount = 5,
|
||||
createdAt = "",
|
||||
updatedAt = "",
|
||||
),
|
||||
LocationOutCount(
|
||||
id = "3",
|
||||
name = "Living Room",
|
||||
color = "#0000FF",
|
||||
isArchived = false,
|
||||
itemCount = 15,
|
||||
createdAt = "",
|
||||
updatedAt = "",
|
||||
),
|
||||
LocationOutCount(
|
||||
id = "4",
|
||||
name = "Kitchen",
|
||||
color = "#FFFF00",
|
||||
isArchived = false,
|
||||
itemCount = 20,
|
||||
createdAt = "",
|
||||
updatedAt = "",
|
||||
),
|
||||
LocationOutCount(
|
||||
id = "5",
|
||||
name = "Basement",
|
||||
color = "#00FFFF",
|
||||
isArchived = false,
|
||||
itemCount = 3,
|
||||
createdAt = "",
|
||||
updatedAt = "",
|
||||
),
|
||||
),
|
||||
labels =
|
||||
listOf(
|
||||
LabelOut(id = "1", name = "electronics", color = "#FF0000", isArchived = false, createdAt = "", updatedAt = ""),
|
||||
LabelOut(id = "2", name = "important", color = "#00FF00", isArchived = false, createdAt = "", updatedAt = ""),
|
||||
LabelOut(id = "3", name = "seasonal", color = "#0000FF", isArchived = false, createdAt = "", updatedAt = ""),
|
||||
LabelOut(id = "4", name = "hobby", color = "#FFFF00", isArchived = false, createdAt = "", updatedAt = ""),
|
||||
),
|
||||
recentlyAddedItems = emptyList(),
|
||||
)
|
||||
HomeboxLensTheme {
|
||||
DashboardContent(
|
||||
uiState = previewState,
|
||||
onLocationClick = {},
|
||||
onLabelClick = {}
|
||||
onLabelClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('DashboardContentSuccessPreview')]
|
||||
// [END_ANCHOR:DashboardContentSuccessPreview]
|
||||
|
||||
// [ENTITY: Function('DashboardContentLoadingPreview')]
|
||||
// [ANCHOR:DashboardContentLoadingPreview:Function]
|
||||
@Preview(showBackground = true, name = "Dashboard Loading State")
|
||||
@Composable
|
||||
fun DashboardContentLoadingPreview() {
|
||||
@@ -376,23 +469,23 @@ fun DashboardContentLoadingPreview() {
|
||||
DashboardContent(
|
||||
uiState = DashboardUiState.Loading,
|
||||
onLocationClick = {},
|
||||
onLabelClick = {}
|
||||
onLabelClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('DashboardContentLoadingPreview')]
|
||||
// [END_ANCHOR:DashboardContentLoadingPreview]
|
||||
|
||||
// [ENTITY: Function('DashboardContentErrorPreview')]
|
||||
// [ANCHOR:DashboardContentErrorPreview:Function]
|
||||
@Preview(showBackground = true, name = "Dashboard Error State")
|
||||
@Composable
|
||||
fun DashboardContentErrorPreview() {
|
||||
HomeboxLensTheme {
|
||||
DashboardContent(
|
||||
uiState = DashboardUiState.Error(stringResource(id = R.string.error_loading_failed)),
|
||||
uiState = DashboardUiState.Error(stringResource(id = com.homebox.lens.feature.dashboard.R.string.error_loading_failed)),
|
||||
onLocationClick = {},
|
||||
onLabelClick = {}
|
||||
onLabelClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
// [END_ENTITY: Function('DashboardContentErrorPreview')]
|
||||
// [END_FILE_DashboardScreen.kt]
|
||||
// [END_ANCHOR:DashboardContentErrorPreview]
|
||||
// [END_FILE_DashboardScreen.kt]
|
||||
|
||||
@@ -10,46 +10,46 @@ import com.homebox.lens.domain.model.LabelOut
|
||||
import com.homebox.lens.domain.model.LocationOutCount
|
||||
// [END_IMPORTS]
|
||||
|
||||
// [ENTITY: SealedInterface('DashboardUiState')]
|
||||
/**
|
||||
* @summary Определяет все возможные состояния для экрана "Дэшборд".
|
||||
* @invariant В любой момент времени экран может находиться только в одном из этих состояний.
|
||||
*/
|
||||
// [ANCHOR:DashboardUiState:SealedInterface]
|
||||
// [CONTRACT:DashboardUiState]
|
||||
// [PURPOSE] Определяет все возможные состояния для экрана "Дэшборд".
|
||||
// [INVARIANT] В любой момент времени экран может находиться только в одном из этих состояний.
|
||||
// [END_CONTRACT:DashboardUiState]
|
||||
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')]
|
||||
/**
|
||||
* @summary Состояние успешной загрузки данных.
|
||||
* @param statistics Статистика по инвентарю.
|
||||
* @param locations Список локаций со счетчиками.
|
||||
* @param labels Список всех меток.
|
||||
* @param recentlyAddedItems Список недавно добавленных товаров.
|
||||
*/
|
||||
// [ANCHOR:Success:DataClass]
|
||||
// [RELATION:DEPENDS_ON:GroupStatistics]
|
||||
// [RELATION:DEPENDS_ON:LocationOutCount]
|
||||
// [RELATION:DEPENDS_ON:LabelOut]
|
||||
// [RELATION:DEPENDS_ON:ItemSummary]
|
||||
// [CONTRACT:Success]
|
||||
// [PURPOSE] Состояние успешной загрузки данных.
|
||||
// [PARAM:statistics:GroupStatistics] Статистика по инвентарю.
|
||||
// [PARAM:locations:List<LocationOutCount>] Список локаций со счетчиками.
|
||||
// [PARAM:labels:List<LabelOut>] Список всех меток.
|
||||
// [PARAM:recentlyAddedItems:List<ItemSummary>] Список недавно добавленных товаров.
|
||||
// [END_CONTRACT:Success]
|
||||
data class Success(
|
||||
val statistics: GroupStatistics,
|
||||
val locations: List<LocationOutCount>,
|
||||
val labels: List<LabelOut>,
|
||||
val recentlyAddedItems: List<ItemSummary>
|
||||
val recentlyAddedItems: List<ItemSummary>,
|
||||
) : DashboardUiState
|
||||
// [END_ENTITY: DataClass('Success')]
|
||||
// [END_ANCHOR:Success]
|
||||
|
||||
// [ENTITY: DataClass('Error')]
|
||||
/**
|
||||
* @summary Состояние ошибки во время загрузки данных.
|
||||
* @param message Человекочитаемое сообщение об ошибке.
|
||||
*/
|
||||
// [ANCHOR:Error:DataClass]
|
||||
// [CONTRACT:Error]
|
||||
// [PURPOSE] Состояние ошибки во время загрузки данных.
|
||||
// [PARAM:message:String] Человекочитаемое сообщение об ошибке.
|
||||
// [END_CONTRACT:Error]
|
||||
data class Error(val message: String) : DashboardUiState
|
||||
// [END_ENTITY: DataClass('Error')]
|
||||
// [END_ANCHOR:Error]
|
||||
|
||||
// [ENTITY: Object('Loading')]
|
||||
/**
|
||||
* @summary Состояние, когда данные для экрана загружаются.
|
||||
*/
|
||||
// [ANCHOR:Loading:Object]
|
||||
// [CONTRACT:Loading]
|
||||
// [PURPOSE] Состояние, когда данные для экрана загружаются.
|
||||
// [END_CONTRACT:Loading]
|
||||
data object Loading : DashboardUiState
|
||||
// [END_ENTITY: Object('Loading')]
|
||||
// [END_ANCHOR:Loading]
|
||||
}
|
||||
// [END_ENTITY: SealedInterface('DashboardUiState')]
|
||||
// [END_FILE_DashboardUiState.kt]
|
||||
// [END_ANCHOR:DashboardUiState]
|
||||
// [END_FILE_DashboardUiState.kt]
|
||||
|
||||
@@ -17,71 +17,67 @@ import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
// [END_IMPORTS]
|
||||
|
||||
|
||||
// [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')]
|
||||
/**
|
||||
* @summary ViewModel для главного экрана (Dashboard).
|
||||
* @description Оркестрирует загрузку данных для Dashboard, используя строгую модель состояний
|
||||
* (`DashboardUiState`), и обрабатывает параллельные запросы без состояний гонки.
|
||||
* @invariant `uiState` всегда является одним из состояний, определенных в `DashboardUiState`.
|
||||
*/
|
||||
// [ANCHOR:DashboardViewModel:ViewModel]
|
||||
// [RELATION:DEPENDS_ON:GetStatisticsUseCase]
|
||||
// [RELATION:DEPENDS_ON:GetAllLocationsUseCase]
|
||||
// [RELATION:DEPENDS_ON:GetAllLabelsUseCase]
|
||||
// [RELATION:DEPENDS_ON:GetRecentlyAddedItemsUseCase]
|
||||
// [RELATION:EMITS_STATE:DashboardUiState]
|
||||
// [CONTRACT:DashboardViewModel]
|
||||
// [PURPOSE] ViewModel для главного экрана (Dashboard). Оркестрирует загрузку данных для Dashboard, используя строгую модель состояний (`DashboardUiState`), и обрабатывает параллельные запросы без состояний гонки.
|
||||
// [INVARIANT] `uiState` всегда является одним из состояний, определенных в `DashboardUiState`.
|
||||
// [END_CONTRACT:DashboardViewModel]
|
||||
@HiltViewModel
|
||||
class DashboardViewModel @Inject constructor(
|
||||
private val getStatisticsUseCase: GetStatisticsUseCase,
|
||||
private val getAllLocationsUseCase: GetAllLocationsUseCase,
|
||||
private val getAllLabelsUseCase: GetAllLabelsUseCase,
|
||||
private val getRecentlyAddedItemsUseCase: GetRecentlyAddedItemsUseCase
|
||||
) : ViewModel() {
|
||||
class DashboardViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
private val getStatisticsUseCase: GetStatisticsUseCase,
|
||||
private val getAllLocationsUseCase: GetAllLocationsUseCase,
|
||||
private val getAllLabelsUseCase: GetAllLabelsUseCase,
|
||||
private val getRecentlyAddedItemsUseCase: GetRecentlyAddedItemsUseCase,
|
||||
) : ViewModel() {
|
||||
private val _uiState = MutableStateFlow<DashboardUiState>(DashboardUiState.Loading)
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
private val _uiState = MutableStateFlow<DashboardUiState>(DashboardUiState.Loading)
|
||||
val uiState = _uiState.asStateFlow()
|
||||
// [ANCHOR:loadDashboardData:Function]
|
||||
// [CONTRACT:loadDashboardData]
|
||||
// [PURPOSE] Загружает все необходимые данные для экрана Dashboard. Выполняет UseCase'ы параллельно и обновляет UI, переключая его между состояниями `Loading`, `Success` и `Error` из `DashboardUiState`.
|
||||
// [SIDE_EFFECT] Асинхронно обновляет `_uiState` одним из состояний `DashboardUiState`.
|
||||
// [END_CONTRACT:loadDashboardData]
|
||||
fun loadDashboardData() {
|
||||
if (uiState.value is DashboardUiState.Success || uiState.value is DashboardUiState.Loading) {
|
||||
Timber.i("[INFO][SKIP][already_loaded] Dashboard data load skipped - already in progress or loaded.")
|
||||
return
|
||||
}
|
||||
viewModelScope.launch {
|
||||
_uiState.value = DashboardUiState.Loading
|
||||
Timber.i("[INFO][ENTRYPOINT][loading_dashboard_data] Starting dashboard data collection.")
|
||||
|
||||
val statsFlow = flow { emit(getStatisticsUseCase()) }
|
||||
val locationsFlow = flow { emit(getAllLocationsUseCase()) }
|
||||
val labelsFlow = flow { emit(getAllLabelsUseCase()) }
|
||||
val recentItemsFlow = getRecentlyAddedItemsUseCase(limit = 10)
|
||||
|
||||
// [ENTITY: Function('loadDashboardData')]
|
||||
/**
|
||||
* @summary Загружает все необходимые данные для экрана Dashboard.
|
||||
* @description Выполняет UseCase'ы параллельно и обновляет UI, переключая его
|
||||
* между состояниями `Loading`, `Success` и `Error` из `DashboardUiState`.
|
||||
* @sideeffect Асинхронно обновляет `_uiState` одним из состояний `DashboardUiState`.
|
||||
*/
|
||||
fun loadDashboardData() {
|
||||
if (uiState.value is DashboardUiState.Success || uiState.value is DashboardUiState.Loading) {
|
||||
Timber.i("[INFO][SKIP][already_loaded] Dashboard data load skipped - already in progress or loaded.")
|
||||
return
|
||||
}
|
||||
viewModelScope.launch {
|
||||
_uiState.value = DashboardUiState.Loading
|
||||
Timber.i("[INFO][ENTRYPOINT][loading_dashboard_data] Starting dashboard data collection.")
|
||||
|
||||
val statsFlow = flow { emit(getStatisticsUseCase()) }
|
||||
val locationsFlow = flow { emit(getAllLocationsUseCase()) }
|
||||
val labelsFlow = flow { emit(getAllLabelsUseCase()) }
|
||||
val recentItemsFlow = getRecentlyAddedItemsUseCase(limit = 10)
|
||||
|
||||
combine(statsFlow, locationsFlow, labelsFlow, recentItemsFlow) { stats, locations, labels, recentItems ->
|
||||
DashboardUiState.Success(
|
||||
statistics = stats,
|
||||
locations = locations,
|
||||
labels = labels,
|
||||
recentlyAddedItems = recentItems
|
||||
)
|
||||
}.catch { exception ->
|
||||
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("[INFO][SUCCESS][dashboard_data_loaded] Dashboard data loaded successfully. State -> Success.")
|
||||
_uiState.value = successState
|
||||
combine(statsFlow, locationsFlow, labelsFlow, recentItemsFlow) { stats, locations, labels, recentItems ->
|
||||
DashboardUiState.Success(
|
||||
statistics = stats,
|
||||
locations = locations,
|
||||
labels = labels,
|
||||
recentlyAddedItems = recentItems,
|
||||
)
|
||||
}.catch { exception ->
|
||||
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("[INFO][SUCCESS][dashboard_data_loaded] Dashboard data loaded successfully. State -> Success.")
|
||||
_uiState.value = successState
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_ANCHOR:loadDashboardData]
|
||||
}
|
||||
// [END_ENTITY: Function('loadDashboardData')]
|
||||
}
|
||||
// [END_ENTITY: ViewModel('DashboardViewModel')]
|
||||
// [END_FILE_DashboardViewModel.kt]
|
||||
// [END_ANCHOR:DashboardViewModel]
|
||||
// [END_FILE_DashboardViewModel.kt]
|
||||
|
||||
@@ -0,0 +1,161 @@
|
||||
// [PACKAGE] com.homebox.lens.navigation
|
||||
// [FILE] NavGraph.kt
|
||||
// [SEMANTICS] navigation, compose, nav_host
|
||||
|
||||
package com.homebox.lens.feature.dashboard.navigation
|
||||
|
||||
// [IMPORTS]
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.navigation.NavHostController
|
||||
import androidx.navigation.compose.NavHost
|
||||
import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import com.homebox.lens.feature.dashboard.addDashboardScreen
|
||||
import com.homebox.lens.ui.common.NavigationActions
|
||||
import com.homebox.lens.feature.inventorylist.InventoryListScreen
|
||||
import com.homebox.lens.feature.itemdetails.ItemDetailsScreen
|
||||
import com.homebox.lens.feature.itemedit.ItemEditScreen
|
||||
import com.homebox.lens.feature.labeledit.LabelEditScreen
|
||||
import com.homebox.lens.feature.labelslist.LabelsListScreen
|
||||
import com.homebox.lens.feature.locationedit.LocationEditScreen
|
||||
import com.homebox.lens.feature.locationslist.LocationsListScreen
|
||||
import com.homebox.lens.feature.scan.ScanScreen
|
||||
import com.homebox.lens.feature.search.SearchScreen
|
||||
import com.homebox.lens.feature.settings.SettingsScreen
|
||||
import com.homebox.lens.feature.setup.SetupScreen
|
||||
// [END_IMPORTS]
|
||||
|
||||
// [ANCHOR:NavGraph:Function]
|
||||
// [RELATION:DEPENDS_ON:NavHostController]
|
||||
// [RELATION:CREATES_INSTANCE_OF:NavigationActions]
|
||||
// [CONTRACT:NavGraph]
|
||||
// [PURPOSE] Определяет граф навигации для всего приложения с использованием Jetpack Compose Navigation.
|
||||
// [PARAM:navController:NavHostController] Контроллер навигации.
|
||||
// [SEE] Screen
|
||||
// [SIDE_EFFECT] Регистрирует все экраны и управляет состоянием навигации.
|
||||
// [INVARIANT] Стартовый экран - `Screen.Setup`.
|
||||
// [END_CONTRACT:NavGraph]
|
||||
@Composable
|
||||
fun navGraph(navController: NavHostController = rememberNavController()) {
|
||||
val navBackStackEntry by navController.currentBackStackEntryAsState()
|
||||
val currentRoute = navBackStackEntry?.destination?.route
|
||||
|
||||
val navigationActions =
|
||||
remember(navController) {
|
||||
NavigationActions(navController)
|
||||
}
|
||||
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = Screen.Setup.route,
|
||||
) {
|
||||
composable(route = Screen.Setup.route) {
|
||||
SetupScreen(onSetupComplete = {
|
||||
navController.navigate(Screen.Dashboard.route) {
|
||||
popUpTo(Screen.Setup.route) { inclusive = true }
|
||||
}
|
||||
})
|
||||
}
|
||||
addDashboardScreen(
|
||||
route = Screen.Dashboard.route,
|
||||
currentRoute = currentRoute,
|
||||
navigateToScan = navigationActions::navigateToScan,
|
||||
navigateToSearch = navigationActions::navigateToSearch,
|
||||
navigateToInventoryListWithLocation = navigationActions::navigateToInventoryListWithLocation,
|
||||
navigateToInventoryListWithLabel = navigationActions::navigateToInventoryListWithLabel,
|
||||
navigationActions = navigationActions,
|
||||
navController = navController,
|
||||
)
|
||||
composable(route = Screen.InventoryList.route) {
|
||||
InventoryListScreen(
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Screen.ItemDetails.route,
|
||||
arguments = listOf(navArgument("itemId") { nullable = true }),
|
||||
) { backStackEntry ->
|
||||
val itemId = backStackEntry.arguments?.getString("itemId")
|
||||
ItemDetailsScreen(
|
||||
itemId = itemId,
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Screen.ItemEdit.route,
|
||||
arguments = listOf(navArgument("itemId") { nullable = true }),
|
||||
) { backStackEntry ->
|
||||
val itemId = backStackEntry.arguments?.getString("itemId")
|
||||
ItemEditScreen(
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
itemId = itemId,
|
||||
onSaveSuccess = { navController.popBackStack() },
|
||||
)
|
||||
}
|
||||
composable(Screen.LabelsList.route) {
|
||||
LabelsListScreen(
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
)
|
||||
}
|
||||
composable(route = Screen.LocationsList.route) {
|
||||
LocationsListScreen(
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
onLocationClick = { locationId: String ->
|
||||
// [AI_NOTE]: Navigate to a pre-filtered inventory list screen
|
||||
navigationActions.navigateToInventoryListWithLocation(locationId)
|
||||
},
|
||||
onAddNewLocationClick = {
|
||||
navController.navigate(Screen.LocationEdit.createRoute("new"))
|
||||
},
|
||||
)
|
||||
}
|
||||
composable(route = Screen.LocationEdit.route) { backStackEntry ->
|
||||
val locationId = backStackEntry.arguments?.getString("locationId")
|
||||
LocationEditScreen(
|
||||
locationId = locationId,
|
||||
)
|
||||
}
|
||||
composable(
|
||||
route = Screen.LabelEdit.route,
|
||||
arguments = listOf(navArgument("labelId") { nullable = true }),
|
||||
) { backStackEntry ->
|
||||
val labelId = backStackEntry.arguments?.getString("labelId")
|
||||
LabelEditScreen(
|
||||
labelId = labelId,
|
||||
onBack = { navController.popBackStack() },
|
||||
onLabelSaved = { navController.popBackStack() },
|
||||
)
|
||||
}
|
||||
composable(route = Screen.Search.route) {
|
||||
SearchScreen(
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
)
|
||||
}
|
||||
composable(Screen.Settings.route) {
|
||||
SettingsScreen(
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
onNavigateUp = { navController.navigateUp() },
|
||||
)
|
||||
}
|
||||
composable(Screen.Scan.route) { backStackEntry ->
|
||||
ScanScreen(onBarcodeResult = { barcode: String ->
|
||||
val previousBackStackEntry = navController.previousBackStackEntry
|
||||
previousBackStackEntry?.savedStateHandle?.set("barcodeResult", barcode)
|
||||
navController.popBackStack()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
// [END_ANCHOR:NavGraph]
|
||||
// [END_FILE_NavGraph.kt]
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.homebox.lens.feature.dashboard.navigation
|
||||
|
||||
sealed class Screen(val route: String) {
|
||||
object Dashboard : Screen("dashboard")
|
||||
object InventoryList : Screen("inventoryList")
|
||||
object ItemDetails : Screen("itemDetails/{itemId}") {
|
||||
fun createRoute(itemId: String) = "itemDetails/$itemId"
|
||||
}
|
||||
object ItemEdit : Screen("itemEdit?itemId={itemId}") {
|
||||
fun createRoute(itemId: String?) = "itemEdit" + (itemId?.let { "?itemId=$it" } ?: "")
|
||||
}
|
||||
object LabelEdit : Screen("labelEdit?labelId={labelId}") {
|
||||
fun createRoute(labelId: String?) = "labelEdit" + (labelId?.let { "?labelId=$it" } ?: "")
|
||||
}
|
||||
object LabelsList : Screen("labelsList")
|
||||
object LocationEdit : Screen("locationEdit?locationId={locationId}") {
|
||||
fun createRoute(locationId: String?) = "locationEdit" + (locationId?.let { "?locationId=$it" } ?: "")
|
||||
}
|
||||
object LocationsList : Screen("locationsList")
|
||||
object Scan : Screen("scan")
|
||||
object Search : Screen("search")
|
||||
object Settings : Screen("settings")
|
||||
object Setup : Screen("setup")
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// [FILE] Color.kt
|
||||
// [SEMANTICS] ui, theme, color
|
||||
|
||||
package com.homebox.lens.feature.dashboard.ui.theme
|
||||
|
||||
// [IMPORTS]
|
||||
import androidx.compose.ui.graphics.Color
|
||||
// [END_IMPORTS]
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
|
||||
// [END_FILE_Color.kt]
|
||||
@@ -0,0 +1,93 @@
|
||||
// [FILE] Theme.kt
|
||||
// [SEMANTICS] ui, theme
|
||||
|
||||
package com.homebox.lens.feature.dashboard.ui.theme
|
||||
|
||||
// [IMPORTS]
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
import timber.log.Timber
|
||||
// [END_IMPORTS]
|
||||
|
||||
private val DarkColorScheme =
|
||||
darkColorScheme(
|
||||
primary = Purple80,
|
||||
secondary = PurpleGrey80,
|
||||
tertiary = Pink80,
|
||||
)
|
||||
|
||||
private val LightColorScheme =
|
||||
lightColorScheme(
|
||||
primary = Purple40,
|
||||
secondary = PurpleGrey40,
|
||||
tertiary = Pink40,
|
||||
)
|
||||
|
||||
// [ANCHOR:HomeboxLensTheme:Function]
|
||||
// [RELATION:DEPENDS_ON:Typography]
|
||||
// [RELATION:DEPENDS_ON:Color]
|
||||
// [CONTRACT:HomeboxLensTheme]
|
||||
// [PURPOSE] The main theme for the Homebox Lens application.
|
||||
// [PARAM:darkTheme:Boolean] Whether the theme should be dark or light.
|
||||
// [PARAM:dynamicColor:Boolean] Whether to use dynamic color (on Android 12+).
|
||||
// [PARAM:content:(@Composable () -> Unit)] The content to be displayed within the theme.
|
||||
// [SIDE_EFFECT] Sets the status bar color based on the theme.
|
||||
// [END_CONTRACT:HomeboxLensTheme]
|
||||
@Composable
|
||||
fun HomeboxLensTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val colorScheme =
|
||||
when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) {
|
||||
Timber.i("[INFO][THEME][dynamic_dark_theme]", "Applying dynamic dark theme")
|
||||
dynamicDarkColorScheme(context)
|
||||
} else {
|
||||
Timber.i("[INFO][THEME][dynamic_light_theme]", "Applying dynamic light theme")
|
||||
dynamicLightColorScheme(context)
|
||||
}
|
||||
}
|
||||
|
||||
darkTheme -> {
|
||||
Timber.i("[INFO][THEME][dark_theme]", "Applying static dark theme")
|
||||
DarkColorScheme
|
||||
}
|
||||
else -> {
|
||||
Timber.i("[INFO][THEME][light_theme]", "Applying static light theme")
|
||||
LightColorScheme
|
||||
}
|
||||
}
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
window.statusBarColor = colorScheme.primary.toArgb()
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
|
||||
Timber.i("[INFO][THEME][status_bar_color]", "Setting status bar color", "color", colorScheme.primary.toArgb())
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
// [END_ANCHOR:HomeboxLensTheme]
|
||||
// [END_FILE_Theme.kt]
|
||||
@@ -0,0 +1,30 @@
|
||||
// [FILE] Typography.kt
|
||||
// [SEMANTICS] ui, theme, typography
|
||||
|
||||
package com.homebox.lens.feature.dashboard.ui.theme
|
||||
|
||||
// [IMPORTS]
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
// [END_IMPORTS]
|
||||
|
||||
// [ANCHOR:Typography:DataStructure]
|
||||
// [CONTRACT:Typography]
|
||||
// [PURPOSE] Defines the typography for the application.
|
||||
// [END_CONTRACT:Typography]
|
||||
val Typography =
|
||||
Typography(
|
||||
bodyLarge =
|
||||
TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp,
|
||||
),
|
||||
)
|
||||
// [END_ANCHOR:Typography]
|
||||
// [END_FILE_Typography.kt]
|
||||
@@ -1,4 +1,3 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Dashboard Screen -->
|
||||
<string name="dashboard_title">Главная</string>
|
||||
@@ -16,10 +15,7 @@
|
||||
|
||||
<!-- Common -->
|
||||
<string name="items_not_found">Элементы не найдены</string>
|
||||
<string name="no_location">Нет локации</string>
|
||||
<string name="error_loading_failed">Не удалось загрузить данные. Пожалуйста, попробуйте еще раз.</string>
|
||||
|
||||
<!-- Content Descriptions -->
|
||||
<string name="cd_scan_qr_code">Сканировать QR/штрих-код</string>
|
||||
<string name="cd_search">Поиск</string>
|
||||
</resources>
|
||||
50
feature/inventorylist/build.gradle.kts
Normal file
50
feature/inventorylist/build.gradle.kts
Normal file
@@ -0,0 +1,50 @@
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.homebox.lens.feature.inventorylist"
|
||||
compileSdk = Versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.minSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":data"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":ui:common"))
|
||||
|
||||
implementation(Libs.coreKtx)
|
||||
implementation(Libs.lifecycleRuntime)
|
||||
implementation(Libs.activityCompose)
|
||||
|
||||
implementation(Libs.composeUi)
|
||||
implementation(Libs.composeUiGraphics)
|
||||
implementation(Libs.composeUiToolingPreview)
|
||||
implementation(Libs.composeMaterial3)
|
||||
implementation(Libs.composeMaterialIconsExtended)
|
||||
implementation(Libs.navigationCompose)
|
||||
implementation(Libs.hiltNavigationCompose)
|
||||
|
||||
implementation(Libs.hiltAndroid)
|
||||
kapt(Libs.hiltCompiler)
|
||||
|
||||
implementation(Libs.timber)
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.homebox.lens.feature.inventorylist
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.homebox.lens.ui.common.mainScaffold
|
||||
|
||||
@Composable
|
||||
fun InventoryListScreen(
|
||||
currentRoute: String?,
|
||||
navigationActions: com.homebox.lens.ui.common.NavigationActions,
|
||||
) {
|
||||
mainScaffold(
|
||||
topBarTitle = "Inventory",
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
) {
|
||||
Text(text = "Inventory List Screen")
|
||||
}
|
||||
}
|
||||
54
feature/itemdetails/build.gradle.kts
Normal file
54
feature/itemdetails/build.gradle.kts
Normal file
@@ -0,0 +1,54 @@
|
||||
// [FILE] feature/itemdetails/build.gradle.kts
|
||||
// [SEMANTICS] build, itemdetails, feature_module
|
||||
// [PURPOSE] Build script for the feature:itemdetails module.
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.homebox.lens.feature.itemdetails"
|
||||
compileSdk = Versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.minSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":data"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":ui:common"))
|
||||
|
||||
implementation(Libs.coreKtx)
|
||||
implementation(Libs.lifecycleRuntime)
|
||||
implementation(Libs.activityCompose)
|
||||
|
||||
implementation(Libs.composeUi)
|
||||
implementation(Libs.composeUiGraphics)
|
||||
implementation(Libs.composeUiToolingPreview)
|
||||
implementation(Libs.composeMaterial3)
|
||||
implementation(Libs.composeMaterialIconsExtended)
|
||||
implementation(Libs.navigationCompose)
|
||||
implementation(Libs.hiltNavigationCompose)
|
||||
|
||||
implementation(Libs.hiltAndroid)
|
||||
kapt(Libs.hiltCompiler)
|
||||
|
||||
implementation(Libs.timber)
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// [FILE] feature/itemdetails/src/main/java/com/homebox/lens/feature/itemdetails/ItemDetailsScreen.kt
|
||||
// [SEMANTICS] ui, screen, item, details
|
||||
// [PURPOSE] Composable for the Item Details screen.
|
||||
|
||||
package com.homebox.lens.feature.itemdetails
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.homebox.lens.ui.common.mainScaffold
|
||||
|
||||
// [ANCHOR:ItemDetailsScreen:Function]
|
||||
@Composable
|
||||
fun ItemDetailsScreen(
|
||||
itemId: String?,
|
||||
currentRoute: String?,
|
||||
navigationActions: com.homebox.lens.ui.common.NavigationActions,
|
||||
) {
|
||||
mainScaffold(
|
||||
topBarTitle = "Item Details",
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
) {
|
||||
Text(text = "Item Details Screen")
|
||||
}
|
||||
}
|
||||
// [END_ANCHOR:ItemDetailsScreen]
|
||||
// [END_FILE_feature/itemdetails/src/main/java/com/homebox/lens/feature/itemdetails/ItemDetailsScreen.kt]
|
||||
55
feature/itemedit/build.gradle.kts
Normal file
55
feature/itemedit/build.gradle.kts
Normal file
@@ -0,0 +1,55 @@
|
||||
// [FILE] feature/itemedit/build.gradle.kts
|
||||
// [SEMANTICS] build, itemedit, feature_module
|
||||
// [PURPOSE] Build script for the feature:itemedit module.
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.homebox.lens.feature.itemedit"
|
||||
compileSdk = Versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.minSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":data"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":ui:common"))
|
||||
implementation(project(":ui:common"))
|
||||
|
||||
implementation(Libs.coreKtx)
|
||||
implementation(Libs.lifecycleRuntime)
|
||||
implementation(Libs.activityCompose)
|
||||
|
||||
implementation(Libs.composeUi)
|
||||
implementation(Libs.composeUiGraphics)
|
||||
implementation(Libs.composeUiToolingPreview)
|
||||
implementation(Libs.composeMaterial3)
|
||||
implementation(Libs.composeMaterialIconsExtended)
|
||||
implementation(Libs.navigationCompose)
|
||||
implementation(Libs.hiltNavigationCompose)
|
||||
|
||||
implementation(Libs.hiltAndroid)
|
||||
kapt(Libs.hiltCompiler)
|
||||
|
||||
implementation(Libs.timber)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// [FILE] feature/itemedit/src/main/java/com/homebox/lens/feature/itemedit/ItemEditScreen.kt
|
||||
// [SEMANTICS] ui, screen, item, edit
|
||||
// [PURPOSE] Composable for the Item Edit screen.
|
||||
|
||||
package com.homebox.lens.feature.itemedit
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.homebox.lens.ui.common.mainScaffold
|
||||
|
||||
@Composable
|
||||
fun ItemEditScreen(
|
||||
currentRoute: String?,
|
||||
navigationActions: com.homebox.lens.ui.common.NavigationActions,
|
||||
itemId: String?,
|
||||
onSaveSuccess: () -> Unit,
|
||||
) {
|
||||
mainScaffold(
|
||||
topBarTitle = "Edit Item",
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
) {
|
||||
Text(text = "Item Edit Screen")
|
||||
}
|
||||
}
|
||||
54
feature/labeledit/build.gradle.kts
Normal file
54
feature/labeledit/build.gradle.kts
Normal file
@@ -0,0 +1,54 @@
|
||||
// [FILE] feature/labeledit/build.gradle.kts
|
||||
// [SEMANTICS] build, labeledit, feature_module
|
||||
// [PURPOSE] Build script for the feature:labeledit module.
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.homebox.lens.feature.labeledit"
|
||||
compileSdk = Versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.minSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":data"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":ui:common"))
|
||||
|
||||
implementation(Libs.coreKtx)
|
||||
implementation(Libs.lifecycleRuntime)
|
||||
implementation(Libs.activityCompose)
|
||||
|
||||
implementation(Libs.composeUi)
|
||||
implementation(Libs.composeUiGraphics)
|
||||
implementation(Libs.composeUiToolingPreview)
|
||||
implementation(Libs.composeMaterial3)
|
||||
implementation(Libs.composeMaterialIconsExtended)
|
||||
implementation(Libs.navigationCompose)
|
||||
implementation(Libs.hiltNavigationCompose)
|
||||
|
||||
implementation(Libs.hiltAndroid)
|
||||
kapt(Libs.hiltCompiler)
|
||||
|
||||
implementation(Libs.timber)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// [FILE] feature/labeledit/src/main/java/com/homebox/lens/feature/labeledit/LabelEditScreen.kt
|
||||
// [SEMANTICS] ui, screen, label, edit
|
||||
// [PURPOSE] Composable for the Label Edit screen.
|
||||
|
||||
package com.homebox.lens.feature.labeledit
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
fun LabelEditScreen(
|
||||
labelId: String?,
|
||||
onBack: () -> Unit,
|
||||
onLabelSaved: () -> Unit,
|
||||
) {
|
||||
Text(text = "Label Edit Screen")
|
||||
}
|
||||
54
feature/labelslist/build.gradle.kts
Normal file
54
feature/labelslist/build.gradle.kts
Normal file
@@ -0,0 +1,54 @@
|
||||
// [FILE] feature/labelslist/build.gradle.kts
|
||||
// [SEMANTICS] build, labelslist, feature_module
|
||||
// [PURPOSE] Build script for the feature:labelslist module.
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.homebox.lens.feature.labelslist"
|
||||
compileSdk = Versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.minSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":data"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":ui:common"))
|
||||
|
||||
implementation(Libs.coreKtx)
|
||||
implementation(Libs.lifecycleRuntime)
|
||||
implementation(Libs.activityCompose)
|
||||
|
||||
implementation(Libs.composeUi)
|
||||
implementation(Libs.composeUiGraphics)
|
||||
implementation(Libs.composeUiToolingPreview)
|
||||
implementation(Libs.composeMaterial3)
|
||||
implementation(Libs.composeMaterialIconsExtended)
|
||||
implementation(Libs.navigationCompose)
|
||||
implementation(Libs.hiltNavigationCompose)
|
||||
|
||||
implementation(Libs.hiltAndroid)
|
||||
kapt(Libs.hiltCompiler)
|
||||
|
||||
implementation(Libs.timber)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// [FILE] feature/labelslist/src/main/java/com/homebox/lens/feature/labelslist/LabelsListScreen.kt
|
||||
// [SEMANTICS] ui, screen, labels, list
|
||||
// [PURPOSE] Composable for the Labels List screen.
|
||||
|
||||
package com.homebox.lens.feature.labelslist
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.homebox.lens.ui.common.mainScaffold
|
||||
|
||||
@Composable
|
||||
fun LabelsListScreen(
|
||||
currentRoute: String?,
|
||||
navigationActions: com.homebox.lens.ui.common.NavigationActions,
|
||||
) {
|
||||
mainScaffold(
|
||||
topBarTitle = "Labels",
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
) {
|
||||
Text(text = "Labels List Screen")
|
||||
}
|
||||
}
|
||||
53
feature/locationedit/build.gradle.kts
Normal file
53
feature/locationedit/build.gradle.kts
Normal file
@@ -0,0 +1,53 @@
|
||||
// [FILE] feature/locationedit/build.gradle.kts
|
||||
// [SEMANTICS] build, locationedit, feature_module
|
||||
// [PURPOSE] Build script for the feature:locationedit module.
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.homebox.lens.feature.locationedit"
|
||||
compileSdk = Versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.minSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":data"))
|
||||
implementation(project(":domain"))
|
||||
|
||||
implementation(Libs.coreKtx)
|
||||
implementation(Libs.lifecycleRuntime)
|
||||
implementation(Libs.activityCompose)
|
||||
|
||||
implementation(Libs.composeUi)
|
||||
implementation(Libs.composeUiGraphics)
|
||||
implementation(Libs.composeUiToolingPreview)
|
||||
implementation(Libs.composeMaterial3)
|
||||
implementation(Libs.composeMaterialIconsExtended)
|
||||
implementation(Libs.navigationCompose)
|
||||
implementation(Libs.hiltNavigationCompose)
|
||||
|
||||
implementation(Libs.hiltAndroid)
|
||||
kapt(Libs.hiltCompiler)
|
||||
|
||||
implementation(Libs.timber)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// [FILE] feature/locationedit/src/main/java/com/homebox/lens/feature/locationedit/LocationEditScreen.kt
|
||||
// [SEMANTICS] ui, screen, location, edit
|
||||
// [PURPOSE] Composable for the Location Edit screen.
|
||||
|
||||
package com.homebox.lens.feature.locationedit
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
fun LocationEditScreen(
|
||||
locationId: String?,
|
||||
) {
|
||||
Text(text = "Location Edit Screen")
|
||||
}
|
||||
54
feature/locationslist/build.gradle.kts
Normal file
54
feature/locationslist/build.gradle.kts
Normal file
@@ -0,0 +1,54 @@
|
||||
// [FILE] feature/locationslist/build.gradle.kts
|
||||
// [SEMANTICS] build, locationslist, feature_module
|
||||
// [PURPOSE] Build script for the feature:locationslist module.
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.homebox.lens.feature.locationslist"
|
||||
compileSdk = Versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.minSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":data"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":ui:common"))
|
||||
|
||||
implementation(Libs.coreKtx)
|
||||
implementation(Libs.lifecycleRuntime)
|
||||
implementation(Libs.activityCompose)
|
||||
|
||||
implementation(Libs.composeUi)
|
||||
implementation(Libs.composeUiGraphics)
|
||||
implementation(Libs.composeUiToolingPreview)
|
||||
implementation(Libs.composeMaterial3)
|
||||
implementation(Libs.composeMaterialIconsExtended)
|
||||
implementation(Libs.navigationCompose)
|
||||
implementation(Libs.hiltNavigationCompose)
|
||||
|
||||
implementation(Libs.hiltAndroid)
|
||||
kapt(Libs.hiltCompiler)
|
||||
|
||||
implementation(Libs.timber)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// [FILE] feature/locationslist/src/main/java/com/homebox/lens/feature/locationslist/LocationsListScreen.kt
|
||||
// [SEMANTICS] ui, screen, locations, list
|
||||
// [PURPOSE] Composable for the Locations List screen.
|
||||
|
||||
package com.homebox.lens.feature.locationslist
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.homebox.lens.ui.common.mainScaffold
|
||||
|
||||
@Composable
|
||||
fun LocationsListScreen(
|
||||
currentRoute: String?,
|
||||
navigationActions: com.homebox.lens.ui.common.NavigationActions,
|
||||
onLocationClick: (String) -> Unit,
|
||||
onAddNewLocationClick: () -> Unit,
|
||||
) {
|
||||
mainScaffold(
|
||||
topBarTitle = "Locations",
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
) {
|
||||
Text(text = "Locations List Screen")
|
||||
}
|
||||
}
|
||||
54
feature/search/build.gradle.kts
Normal file
54
feature/search/build.gradle.kts
Normal file
@@ -0,0 +1,54 @@
|
||||
// [FILE] feature/search/build.gradle.kts
|
||||
// [SEMANTICS] build, search, feature_module
|
||||
// [PURPOSE] Build script for the feature:search module.
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.homebox.lens.feature.search"
|
||||
compileSdk = Versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.minSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":data"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":ui:common"))
|
||||
|
||||
implementation(Libs.coreKtx)
|
||||
implementation(Libs.lifecycleRuntime)
|
||||
implementation(Libs.activityCompose)
|
||||
|
||||
implementation(Libs.composeUi)
|
||||
implementation(Libs.composeUiGraphics)
|
||||
implementation(Libs.composeUiToolingPreview)
|
||||
implementation(Libs.composeMaterial3)
|
||||
implementation(Libs.composeMaterialIconsExtended)
|
||||
implementation(Libs.navigationCompose)
|
||||
implementation(Libs.hiltNavigationCompose)
|
||||
|
||||
implementation(Libs.hiltAndroid)
|
||||
kapt(Libs.hiltCompiler)
|
||||
|
||||
implementation(Libs.timber)
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
// [FILE] feature/search/src/main/java/com/homebox/lens/feature/search/SearchScreen.kt
|
||||
// [SEMANTICS] ui, screen, search
|
||||
// [PURPOSE] Composable for the Search screen.
|
||||
|
||||
package com.homebox.lens.feature.search
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.homebox.lens.ui.common.mainScaffold
|
||||
|
||||
@Composable
|
||||
fun SearchScreen(
|
||||
currentRoute: String?,
|
||||
navigationActions: com.homebox.lens.ui.common.NavigationActions,
|
||||
) {
|
||||
mainScaffold(
|
||||
topBarTitle = "Search",
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
) {
|
||||
Text(text = "Search Screen")
|
||||
}
|
||||
}
|
||||
55
feature/settings/build.gradle.kts
Normal file
55
feature/settings/build.gradle.kts
Normal file
@@ -0,0 +1,55 @@
|
||||
// [FILE] feature/settings/build.gradle.kts
|
||||
// [SEMANTICS] build, settings, feature_module
|
||||
// [PURPOSE] Build script for the feature:settings module.
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.homebox.lens.feature.settings"
|
||||
compileSdk = Versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.minSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":data"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":ui:common"))
|
||||
implementation(project(":ui:common"))
|
||||
|
||||
implementation(Libs.coreKtx)
|
||||
implementation(Libs.lifecycleRuntime)
|
||||
implementation(Libs.activityCompose)
|
||||
|
||||
implementation(Libs.composeUi)
|
||||
implementation(Libs.composeUiGraphics)
|
||||
implementation(Libs.composeUiToolingPreview)
|
||||
implementation(Libs.composeMaterial3)
|
||||
implementation(Libs.composeMaterialIconsExtended)
|
||||
implementation(Libs.navigationCompose)
|
||||
implementation(Libs.hiltNavigationCompose)
|
||||
|
||||
implementation(Libs.hiltAndroid)
|
||||
kapt(Libs.hiltCompiler)
|
||||
|
||||
implementation(Libs.timber)
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// [FILE] feature/settings/src/main/java/com/homebox/lens/feature/settings/SettingsScreen.kt
|
||||
// [SEMANTICS] ui, screen, settings
|
||||
// [PURPOSE] Composable for the Settings screen.
|
||||
|
||||
package com.homebox.lens.feature.settings
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.homebox.lens.ui.common.mainScaffold
|
||||
|
||||
@Composable
|
||||
fun SettingsScreen(
|
||||
currentRoute: String?,
|
||||
navigationActions: com.homebox.lens.ui.common.NavigationActions,
|
||||
onNavigateUp: () -> Unit,
|
||||
) {
|
||||
mainScaffold(
|
||||
topBarTitle = "Settings",
|
||||
currentRoute = currentRoute,
|
||||
navigationActions = navigationActions,
|
||||
onNavigateUp = onNavigateUp,
|
||||
) {
|
||||
Text(text = "Settings Screen")
|
||||
}
|
||||
}
|
||||
54
feature/setup/build.gradle.kts
Normal file
54
feature/setup/build.gradle.kts
Normal file
@@ -0,0 +1,54 @@
|
||||
// [FILE] feature/setup/build.gradle.kts
|
||||
// [SEMANTICS] build, setup, feature_module
|
||||
// [PURPOSE] Build script for the feature:setup module.
|
||||
|
||||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
id("com.google.dagger.hilt.android")
|
||||
id("kotlin-kapt")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.homebox.lens.feature.setup"
|
||||
compileSdk = Versions.compileSdk
|
||||
|
||||
defaultConfig {
|
||||
minSdk = Versions.minSdk
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":data"))
|
||||
implementation(project(":domain"))
|
||||
implementation(project(":ui:common"))
|
||||
|
||||
implementation(Libs.coreKtx)
|
||||
implementation(Libs.lifecycleRuntime)
|
||||
implementation(Libs.activityCompose)
|
||||
|
||||
implementation(Libs.composeUi)
|
||||
implementation(Libs.composeUiGraphics)
|
||||
implementation(Libs.composeUiToolingPreview)
|
||||
implementation(Libs.composeMaterial3)
|
||||
implementation(Libs.composeMaterialIconsExtended)
|
||||
implementation(Libs.navigationCompose)
|
||||
implementation(Libs.hiltNavigationCompose)
|
||||
|
||||
implementation(Libs.hiltAndroid)
|
||||
kapt(Libs.hiltCompiler)
|
||||
|
||||
implementation(Libs.timber)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// [FILE] feature/setup/src/main/java/com/homebox/lens/feature/setup/SetupScreen.kt
|
||||
// [SEMANTICS] ui, screen, setup
|
||||
// [PURPOSE] Composable for the Setup screen.
|
||||
|
||||
package com.homebox.lens.feature.setup
|
||||
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
@Composable
|
||||
fun SetupScreen(
|
||||
onSetupComplete: () -> Unit,
|
||||
) {
|
||||
Text(text = "Setup Screen")
|
||||
}
|
||||
Reference in New Issue
Block a user