diff --git a/data/src/commonMain/kotlin/com/carlosgub/myfinances/data/repository/FinanceRepositoryImpl.kt b/data/src/commonMain/kotlin/com/carlosgub/myfinances/data/repository/FinanceRepositoryImpl.kt index 528f37a..20c3900 100644 --- a/data/src/commonMain/kotlin/com/carlosgub/myfinances/data/repository/FinanceRepositoryImpl.kt +++ b/data/src/commonMain/kotlin/com/carlosgub/myfinances/data/repository/FinanceRepositoryImpl.kt @@ -17,7 +17,8 @@ import com.carlosgub.myfinances.domain.model.FinanceExpenses import com.carlosgub.myfinances.domain.model.FinanceLocalDate import com.carlosgub.myfinances.domain.model.FinanceModel import com.carlosgub.myfinances.domain.model.IncomeModel -import com.carlosgub.myfinances.domain.model.MonthDetailModel +import com.carlosgub.myfinances.domain.model.MonthDetailExpenseModel +import com.carlosgub.myfinances.domain.model.MonthDetailIncomeModel import com.carlosgub.myfinances.domain.model.MonthExpense import com.carlosgub.myfinances.domain.repository.FinanceRepository import kotlinx.coroutines.Dispatchers @@ -282,7 +283,7 @@ class FinanceRepositoryImpl( override suspend fun getExpenseMonthDetail( categoryEnum: com.carlosgub.myfinances.domain.model.CategoryEnum, monthKey: String, - ): Flow> = + ): Flow> = flow { databaseFinance.getExpenseMonthDetail(categoryEnum, monthKey).collect { result -> when (result) { @@ -321,9 +322,9 @@ class FinanceRepositoryImpl( } emit( GenericState.Success( - MonthDetailModel( + MonthDetailExpenseModel( monthAmount = monthAmount, - expenseModel = expenseScreenModelList, + expenseModelList = expenseScreenModelList, daySpent = daySpent, ), ), @@ -333,7 +334,7 @@ class FinanceRepositoryImpl( } } - override suspend fun getIncomeMonthDetail(monthKey: String): Flow> = + override suspend fun getIncomeMonthDetail(monthKey: String): Flow> = flow { databaseFinance .getIncomeMonthDetail(monthKey) @@ -342,16 +343,16 @@ class FinanceRepositoryImpl( is ResponseResult.Error -> GenericState.Error(result.error.message.orEmpty()) is ResponseResult.Success -> { val monthAmount = result.data.sumOf { it.amount } - val expenseScreenModelList = + val incomeScreenModelList = result.data - .map { expense -> + .map { income -> val localDate = - FinanceLocalDate(expense.dateInMillis.toLocalDate()) - ExpenseModel( - id = expense.id, - amount = expense.amount, - note = expense.note.replaceFirstChar { it.uppercase() }, - category = expense.category, + FinanceLocalDate(income.dateInMillis.toLocalDate()) + IncomeModel( + id = income.id, + amount = income.amount, + note = income.note.replaceFirstChar { it.uppercase() }, + category = income.category, localDateTime = localDate.localDateTime, date = localDate.date, monthKey = monthKey, @@ -371,16 +372,16 @@ class FinanceRepositoryImpl( .toInt(), dayOfMonth = day, ) - dateInternal to expenseScreenModelList + dateInternal to incomeScreenModelList .filter { expense -> expense.localDateTime == dateInternal }.sumOf { it.amount } } emit( GenericState.Success( - MonthDetailModel( + MonthDetailIncomeModel( monthAmount = monthAmount, - expenseModel = expenseScreenModelList, + incomeModelList = incomeScreenModelList, daySpent = daySpent, ), ), diff --git a/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/model/MonthDetailExpenseModel.kt b/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/model/MonthDetailExpenseModel.kt new file mode 100644 index 0000000..74bd97a --- /dev/null +++ b/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/model/MonthDetailExpenseModel.kt @@ -0,0 +1,9 @@ +package com.carlosgub.myfinances.domain.model + +import kotlinx.datetime.LocalDateTime + +data class MonthDetailExpenseModel( + val monthAmount: Long = 0L, + val daySpent: Map = mapOf(), + val expenseModelList: List = listOf(), +) diff --git a/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/model/MonthDetailModel.kt b/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/model/MonthDetailIncomeModel.kt similarity index 65% rename from domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/model/MonthDetailModel.kt rename to domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/model/MonthDetailIncomeModel.kt index 0aaa7cd..53a00ba 100644 --- a/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/model/MonthDetailModel.kt +++ b/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/model/MonthDetailIncomeModel.kt @@ -2,8 +2,8 @@ package com.carlosgub.myfinances.domain.model import kotlinx.datetime.LocalDateTime -data class MonthDetailModel( +data class MonthDetailIncomeModel( val monthAmount: Long = 0L, val daySpent: Map = mapOf(), - val expenseModel: List = listOf(), + val incomeModelList: List = listOf(), ) diff --git a/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/repository/FinanceRepository.kt b/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/repository/FinanceRepository.kt index 695a290..28f3815 100644 --- a/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/repository/FinanceRepository.kt +++ b/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/repository/FinanceRepository.kt @@ -5,7 +5,8 @@ import com.carlosgub.myfinances.domain.model.CategoryEnum import com.carlosgub.myfinances.domain.model.ExpenseModel import com.carlosgub.myfinances.domain.model.FinanceModel import com.carlosgub.myfinances.domain.model.IncomeModel -import com.carlosgub.myfinances.domain.model.MonthDetailModel +import com.carlosgub.myfinances.domain.model.MonthDetailExpenseModel +import com.carlosgub.myfinances.domain.model.MonthDetailIncomeModel import kotlinx.coroutines.flow.Flow import kotlinx.datetime.LocalDateTime @@ -57,9 +58,9 @@ interface FinanceRepository { suspend fun getExpenseMonthDetail( categoryEnum: CategoryEnum, monthKey: String, - ): Flow> + ): Flow> - suspend fun getIncomeMonthDetail(monthKey: String): Flow> + suspend fun getIncomeMonthDetail(monthKey: String): Flow> suspend fun getMonths(): Flow>>> } diff --git a/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/usecase/GetExpenseMonthDetailUseCase.kt b/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/usecase/GetExpenseMonthDetailUseCase.kt index 8c14ded..21c0b54 100644 --- a/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/usecase/GetExpenseMonthDetailUseCase.kt +++ b/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/usecase/GetExpenseMonthDetailUseCase.kt @@ -1,14 +1,14 @@ package com.carlosgub.myfinances.domain.usecase import com.carlosgub.myfinances.core.state.GenericState -import com.carlosgub.myfinances.domain.model.MonthDetailModel +import com.carlosgub.myfinances.domain.model.MonthDetailExpenseModel import com.carlosgub.myfinances.domain.repository.FinanceRepository import kotlinx.coroutines.flow.Flow class GetExpenseMonthDetailUseCase( private val financeRepository: FinanceRepository, ) { - suspend operator fun invoke(params: Params): Flow> = + suspend operator fun invoke(params: Params): Flow> = financeRepository.getExpenseMonthDetail( categoryEnum = params.categoryEnum, monthKey = params.monthKey, diff --git a/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/usecase/GetIncomeMonthDetailUseCase.kt b/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/usecase/GetIncomeMonthDetailUseCase.kt index a75c58a..e258e53 100644 --- a/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/usecase/GetIncomeMonthDetailUseCase.kt +++ b/domain/src/commonMain/kotlin/com.carlosgub.myfinances/domain/usecase/GetIncomeMonthDetailUseCase.kt @@ -1,14 +1,14 @@ package com.carlosgub.myfinances.domain.usecase import com.carlosgub.myfinances.core.state.GenericState -import com.carlosgub.myfinances.domain.model.MonthDetailModel +import com.carlosgub.myfinances.domain.model.MonthDetailIncomeModel import com.carlosgub.myfinances.domain.repository.FinanceRepository import kotlinx.coroutines.flow.Flow class GetIncomeMonthDetailUseCase( private val financeRepository: FinanceRepository, ) { - suspend operator fun invoke(params: Params): Flow> = + suspend operator fun invoke(params: Params): Flow> = financeRepository.getIncomeMonthDetail( monthKey = params.monthKey, ) diff --git a/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/App.kt b/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/App.kt index eecde00..5769c60 100644 --- a/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/App.kt +++ b/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/App.kt @@ -6,7 +6,8 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.graphics.Color import com.carlosgub.myfinances.core.navigation.LocalNavController import com.carlosgub.myfinances.core.utils.getCurrentMonthKey -import com.carlosgub.myfinances.presentation.screen.categorymonthdetail.CategoryMonthDetailScreen +import com.carlosgub.myfinances.presentation.screen.categorymonthdetailexpense.CategoryMonthDetailScreenExpense +import com.carlosgub.myfinances.presentation.screen.categorymonthdetailincome.CategoryMonthDetailScreenIncome import com.carlosgub.myfinances.presentation.screen.createexpense.CreateExpenseScreen import com.carlosgub.myfinances.presentation.screen.createincome.CreateIncomeScreen import com.carlosgub.myfinances.presentation.screen.editexpense.EditExpenseScreen @@ -62,12 +63,22 @@ fun App() { scene(route = Navigation.MonthsScreen.route) { MonthsScreen() } - scene(route = Navigation.CategoryMonthDetailScreen.route) { backStackEntry -> + scene(route = Navigation.CategoryMonthDetailExpenseScreen.route) { backStackEntry -> val monthKey = backStackEntry.path(NavArgs.MONTH_KEY.key)!! val categoryName = backStackEntry.path(NavArgs.CATEGORY_NAME.key)!! - CategoryMonthDetailScreen( + CategoryMonthDetailScreenExpense( + monthKey = monthKey, + categoryName = categoryName, + ) + } + scene(route = Navigation.CategoryMonthDetailIncomeScreen.route) { backStackEntry -> + val monthKey = + backStackEntry.path(NavArgs.MONTH_KEY.key)!! + val categoryName = + backStackEntry.path(NavArgs.CATEGORY_NAME.key)!! + CategoryMonthDetailScreenIncome( monthKey = monthKey, categoryName = categoryName, ) diff --git a/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/Navigation.kt b/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/Navigation.kt index c243bdf..46c8d62 100644 --- a/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/Navigation.kt +++ b/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/Navigation.kt @@ -23,12 +23,20 @@ sealed class Navigation( data object MonthsScreen : Navigation("MonthsScreen") - data object CategoryMonthDetailScreen : - Navigation("CategoryMonthDetailScreen/{${NavArgs.MONTH_KEY.key}}/{${NavArgs.CATEGORY_NAME.key}}") { + data object CategoryMonthDetailExpenseScreen : + Navigation("CategoryMonthDetailExpenseScreen/{${NavArgs.MONTH_KEY.key}}/{${NavArgs.CATEGORY_NAME.key}}") { fun createRoute( monthKey: String, categoryName: String, - ) = "CategoryMonthDetailScreen/$monthKey/$categoryName" + ) = "CategoryMonthDetailExpenseScreen/$monthKey/$categoryName" + } + + data object CategoryMonthDetailIncomeScreen : + Navigation("CategoryMonthDetailIncomeScreen/{${NavArgs.MONTH_KEY.key}}/{${NavArgs.CATEGORY_NAME.key}}") { + fun createRoute( + monthKey: String, + categoryName: String, + ) = "CategoryMonthDetailIncomeScreen/$monthKey/$categoryName" } } diff --git a/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/di/AppModule.kt b/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/di/AppModule.kt index 895acba..e148d5a 100644 --- a/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/di/AppModule.kt +++ b/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/di/AppModule.kt @@ -19,7 +19,8 @@ import com.carlosgub.myfinances.domain.usecase.GetIncomeUseCase import com.carlosgub.myfinances.domain.usecase.GetMonthsUseCase import com.carlosgub.myfinances.navigation.impl.AppNavigationImpl import com.carlosgub.myfinances.presentation.navigation.AppNavigation -import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetail.CategoryMonthDetailViewModel +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailexpense.CategoryMonthDetailExpenseViewModel +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailincome.CategoryMonthDetailIncomeViewModel import com.carlosgub.myfinances.presentation.viewmodel.createexpense.CreateExpenseViewModel import com.carlosgub.myfinances.presentation.viewmodel.createincome.CreateIncomeViewModel import com.carlosgub.myfinances.presentation.viewmodel.editexpense.EditExpenseViewModel @@ -74,8 +75,13 @@ val homeModule = } factory { - CategoryMonthDetailViewModel( + CategoryMonthDetailExpenseViewModel( getExpenseMonthDetailUseCase = get(), + ) + } + + factory { + CategoryMonthDetailIncomeViewModel( getIncomeMonthDetailUseCase = get(), ) } diff --git a/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/impl/AppNavigationImpl.kt b/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/impl/AppNavigationImpl.kt index 465b15d..551da14 100644 --- a/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/impl/AppNavigationImpl.kt +++ b/navigation/src/commonMain/kotlin/com/carlosgub/myfinances/navigation/impl/AppNavigationImpl.kt @@ -38,13 +38,26 @@ class AppNavigationImpl : AppNavigation { ) } - override fun navigateToMonthDetail( + override fun navigateToMonthExpenseDetail( navigator: Navigator, monthKey: String, categoryName: String, ) { navigator.navigate( - Navigation.CategoryMonthDetailScreen.createRoute( + Navigation.CategoryMonthDetailExpenseScreen.createRoute( + monthKey = monthKey, + categoryName = categoryName, + ), + ) + } + + override fun navigateToMonthIncomeDetail( + navigator: Navigator, + monthKey: String, + categoryName: String, + ) { + navigator.navigate( + Navigation.CategoryMonthDetailIncomeScreen.createRoute( monthKey = monthKey, categoryName = categoryName, ), diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/mapper/PresentationMappers.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/mapper/PresentationMappers.kt index 7cecc66..71d6730 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/mapper/PresentationMappers.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/mapper/PresentationMappers.kt @@ -3,10 +3,14 @@ package com.carlosgub.myfinances.presentation.mapper import com.carlosgub.myfinances.core.mapper.Mapper import com.carlosgub.myfinances.domain.model.ExpenseModel import com.carlosgub.myfinances.domain.model.FinanceModel -import com.carlosgub.myfinances.domain.model.MonthDetailModel +import com.carlosgub.myfinances.domain.model.IncomeModel +import com.carlosgub.myfinances.domain.model.MonthDetailExpenseModel +import com.carlosgub.myfinances.domain.model.MonthDetailIncomeModel import com.carlosgub.myfinances.presentation.model.ExpenseScreenModel import com.carlosgub.myfinances.presentation.model.FinanceScreenModel -import com.carlosgub.myfinances.presentation.model.MonthDetailScreenModel +import com.carlosgub.myfinances.presentation.model.IncomeScreenModel +import com.carlosgub.myfinances.presentation.model.MonthDetailExpenseScreenModel +import com.carlosgub.myfinances.presentation.model.MonthDetailIncomeScreenModel import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableMap @@ -22,12 +26,21 @@ object FinanceModelToFinanceScreenModel : Mapper { - override fun map(from: MonthDetailModel) = - MonthDetailScreenModel( +object MonthDetailExpenseModelToMonthDetailExpenseScreenModel : Mapper { + override fun map(from: MonthDetailExpenseModel) = + MonthDetailExpenseScreenModel( monthAmount = from.monthAmount, daySpent = from.daySpent.toImmutableMap(), - expenseScreenModel = from.expenseModel.map { ExpenseModelToExpenseScreenModel.map(it) }.toImmutableList(), + expenseScreenModelList = from.expenseModelList.map { ExpenseModelToExpenseScreenModel.map(it) }.toImmutableList(), + ) +} + +object MonthDetailIncomeModelToMonthDetailIncomeScreenModel : Mapper { + override fun map(from: MonthDetailIncomeModel) = + MonthDetailIncomeScreenModel( + monthAmount = from.monthAmount, + daySpent = from.daySpent.toImmutableMap(), + incomeScreenModelList = from.incomeModelList.map { IncomeModelToIncomeScreenModel.map(it) }.toImmutableList(), ) } @@ -42,3 +55,15 @@ object ExpenseModelToExpenseScreenModel : Mapper { + override fun map(from: IncomeModel) = + IncomeScreenModel( + amount = from.amount, + id = from.id, + note = from.note, + category = from.category, + localDateTime = from.localDateTime, + date = from.date, + ) +} diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/model/IncomeScreenModel.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/model/IncomeScreenModel.kt new file mode 100644 index 0000000..1ddbf8e --- /dev/null +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/model/IncomeScreenModel.kt @@ -0,0 +1,12 @@ +package com.carlosgub.myfinances.presentation.model + +import kotlinx.datetime.LocalDateTime + +data class IncomeScreenModel( + val id: Long, + val amount: Long, + val note: String, + val category: String, + val localDateTime: LocalDateTime, + val date: String, +) diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/model/MonthDetailExpenseScreenModel.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/model/MonthDetailExpenseScreenModel.kt new file mode 100644 index 0000000..1c0d0f9 --- /dev/null +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/model/MonthDetailExpenseScreenModel.kt @@ -0,0 +1,11 @@ +package com.carlosgub.myfinances.presentation.model + +import kotlinx.collections.immutable.ImmutableMap +import kotlinx.collections.immutable.persistentMapOf +import kotlinx.datetime.LocalDateTime + +data class MonthDetailExpenseScreenModel( + val monthAmount: Long = 0L, + val daySpent: ImmutableMap = persistentMapOf(), + val expenseScreenModelList: List = listOf(), +) diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/model/MonthDetailScreenModel.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/model/MonthDetailIncomeScreenModel.kt similarity index 73% rename from presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/model/MonthDetailScreenModel.kt rename to presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/model/MonthDetailIncomeScreenModel.kt index a7f8cf1..fab0541 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/model/MonthDetailScreenModel.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/model/MonthDetailIncomeScreenModel.kt @@ -4,8 +4,8 @@ import kotlinx.collections.immutable.ImmutableMap import kotlinx.collections.immutable.persistentMapOf import kotlinx.datetime.LocalDateTime -data class MonthDetailScreenModel( +data class MonthDetailIncomeScreenModel( val monthAmount: Long = 0L, val daySpent: ImmutableMap = persistentMapOf(), - val expenseScreenModel: List = listOf(), + val incomeScreenModelList: List = listOf(), ) diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/navigation/AppNavigation.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/navigation/AppNavigation.kt index d2a6000..c808d76 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/navigation/AppNavigation.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/navigation/AppNavigation.kt @@ -18,7 +18,13 @@ interface AppNavigation { id: Long, ) - fun navigateToMonthDetail( + fun navigateToMonthExpenseDetail( + navigator: Navigator, + monthKey: String, + categoryName: String, + ) + + fun navigateToMonthIncomeDetail( navigator: Navigator, monthKey: String, categoryName: String, diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetail/observer/CategoryMonthDetailObserver.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetail/observer/CategoryMonthDetailObserver.kt deleted file mode 100644 index 45f33ae..0000000 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetail/observer/CategoryMonthDetailObserver.kt +++ /dev/null @@ -1,42 +0,0 @@ -package com.carlosgub.myfinances.presentation.screen.categorymonthdetail.observer - -import com.carlosgub.myfinances.domain.model.CategoryEnum.Companion.getCategoryEnumFromName -import com.carlosgub.myfinances.domain.model.FinanceEnum -import com.carlosgub.myfinances.presentation.model.ExpenseScreenModel -import com.carlosgub.myfinances.presentation.navigation.AppNavigation -import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetail.CategoryMonthDetailScreenSideEffect -import moe.tlaster.precompose.navigation.Navigator - -fun categoryMonthDetailObserver( - sideEffect: CategoryMonthDetailScreenSideEffect, - navigator: Navigator, - appNavigation: AppNavigation, -) { - when (sideEffect) { - is CategoryMonthDetailScreenSideEffect.NavigateToMonthDetail -> - navigateToEditScreen( - navigator = navigator, - expenseScreenModel = sideEffect.expenseScreenModel, - appNavigation = appNavigation, - ) - } -} - -private fun navigateToEditScreen( - navigator: Navigator, - expenseScreenModel: ExpenseScreenModel, - appNavigation: AppNavigation, -) { - val financeEnum = getCategoryEnumFromName(name = expenseScreenModel.category).type - if (financeEnum == FinanceEnum.EXPENSE) { - appNavigation.navigateToEditExpense( - navigator = navigator, - id = expenseScreenModel.id, - ) - } else { - appNavigation.navigateToEditIncome( - navigator = navigator, - id = expenseScreenModel.id, - ) - } -} diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetail/CategoryMonthDetailScreen.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailexpense/categoryMonthDetailScreenIncome.kt similarity index 82% rename from presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetail/CategoryMonthDetailScreen.kt rename to presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailexpense/categoryMonthDetailScreenIncome.kt index ef96d47..5c3d4b3 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetail/CategoryMonthDetailScreen.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailexpense/categoryMonthDetailScreenIncome.kt @@ -1,4 +1,4 @@ -package com.carlosgub.myfinances.presentation.screen.categorymonthdetail +package com.carlosgub.myfinances.presentation.screen.categorymonthdetailexpense import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable @@ -8,9 +8,9 @@ import androidx.compose.ui.graphics.Color import com.carlosgub.myfinances.components.toolbar.Toolbar import com.carlosgub.myfinances.core.navigation.LocalNavController import com.carlosgub.myfinances.presentation.navigation.AppNavigation -import com.carlosgub.myfinances.presentation.screen.categorymonthdetail.content.CategoryMonthDetailContent -import com.carlosgub.myfinances.presentation.screen.categorymonthdetail.observer.categoryMonthDetailObserver -import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetail.CategoryMonthDetailViewModel +import com.carlosgub.myfinances.presentation.screen.categorymonthdetailexpense.content.CategoryMonthDetailExpenseContent +import com.carlosgub.myfinances.presentation.screen.categorymonthdetailexpense.observer.categoryMonthDetailExpenseObserver +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailexpense.CategoryMonthDetailExpenseViewModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -20,20 +20,20 @@ import org.jetbrains.compose.resources.stringResource import org.koin.compose.koinInject @Composable -fun CategoryMonthDetailScreen( +fun CategoryMonthDetailScreenExpense( monthKey: String, categoryName: String, modifier: Modifier = Modifier, ) { val navigator = LocalNavController.current val appNavigation: AppNavigation = koinInject() - val viewModel = koinViewModel(vmClass = CategoryMonthDetailViewModel::class) + val viewModel = koinViewModel(vmClass = CategoryMonthDetailExpenseViewModel::class) val state by viewModel.container.stateFlow.collectAsStateWithLifecycle() viewModel.setInitialConfiguration(monthKey = monthKey, category = categoryName) val scope = CoroutineScope(Dispatchers.Main) Scaffold( topBar = { - ExpenseMonthDetailToolbar( + CategoryMonthDetailExpenseToolbar( category = stringResource(state.category.categoryName), onBack = { navigator.popBackStack() @@ -42,7 +42,7 @@ fun CategoryMonthDetailScreen( }, modifier = modifier, ) { paddingValues -> - CategoryMonthDetailContent( + CategoryMonthDetailExpenseContent( paddingValues = paddingValues, state = state, expenseClicked = { expenseScreenModel -> @@ -52,7 +52,7 @@ fun CategoryMonthDetailScreen( } scope.launch { viewModel.container.sideEffectFlow.collect { sideEffect -> - categoryMonthDetailObserver( + categoryMonthDetailExpenseObserver( sideEffect = sideEffect, navigator = navigator, appNavigation = appNavigation, @@ -62,7 +62,7 @@ fun CategoryMonthDetailScreen( } @Composable -private fun ExpenseMonthDetailToolbar( +private fun CategoryMonthDetailExpenseToolbar( category: String, onBack: () -> Unit, ) { diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetail/content/CategoryMonthDetailContent.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailexpense/content/CategoryMonthDetailExpenseContent.kt similarity index 94% rename from presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetail/content/CategoryMonthDetailContent.kt rename to presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailexpense/content/CategoryMonthDetailExpenseContent.kt index 215436e..c279bc3 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetail/content/CategoryMonthDetailContent.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailexpense/content/CategoryMonthDetailExpenseContent.kt @@ -1,6 +1,6 @@ @file:OptIn(ExperimentalFoundationApi::class) -package com.carlosgub.myfinances.presentation.screen.categorymonthdetail.content +package com.carlosgub.myfinances.presentation.screen.categorymonthdetailexpense.content import androidx.compose.animation.core.tween import androidx.compose.foundation.ExperimentalFoundationApi @@ -34,7 +34,7 @@ import com.carlosgub.myfinances.components.divider.HorizontalDivider import com.carlosgub.myfinances.components.loading.Loading import com.carlosgub.myfinances.core.utils.toMoneyFormat import com.carlosgub.myfinances.presentation.model.ExpenseScreenModel -import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetail.CategoryMonthDetailScreenState +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailexpense.CategoryMonthDetailExpenseScreenState import com.carlosgub.myfinances.theme.Gray600 import com.carlosgub.myfinances.theme.Gray900 import com.carlosgub.myfinances.theme.White @@ -48,9 +48,9 @@ import piggybank.presentation.generated.resources.category_month_detail_data_zer import piggybank.presentation.generated.resources.category_month_detail_data_zero_title @Composable -fun CategoryMonthDetailContent( +fun CategoryMonthDetailExpenseContent( paddingValues: PaddingValues, - state: CategoryMonthDetailScreenState, + state: CategoryMonthDetailExpenseScreenState, expenseClicked: (ExpenseScreenModel) -> Unit, modifier: Modifier = Modifier, ) { @@ -80,8 +80,8 @@ fun CategoryMonthDetailContent( } @Composable -fun CategoryMonthDetailBody( - state: CategoryMonthDetailScreenState, +private fun CategoryMonthDetailBody( + state: CategoryMonthDetailExpenseScreenState, expenseClicked: (ExpenseScreenModel) -> Unit, modifier: Modifier = Modifier, ) { @@ -100,7 +100,7 @@ fun CategoryMonthDetailBody( containerColor = White, ), ) { - if (state.monthDetail.expenseScreenModel.isNotEmpty()) { + if (state.monthDetail.expenseScreenModelList.isNotEmpty()) { LazyColumn( modifier = Modifier .background(color = White) @@ -111,9 +111,9 @@ fun CategoryMonthDetailBody( end = spacing_6, ), ) { - itemsIndexed(state.monthDetail.expenseScreenModel) { count, expense -> + itemsIndexed(state.monthDetail.expenseScreenModelList) { index, expense -> Column { - if (count != 0) { + if (index != 0) { HorizontalDivider() } CategoryMonthExpenseItem( @@ -187,7 +187,7 @@ private fun CategoryMonthExpenseItem( } @Composable -private fun CategoryMonthDetailHeader(state: CategoryMonthDetailScreenState) { +private fun CategoryMonthDetailHeader(state: CategoryMonthDetailExpenseScreenState) { Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center, diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailexpense/observer/CategoryMonthDetailExpenseObserver.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailexpense/observer/CategoryMonthDetailExpenseObserver.kt new file mode 100644 index 0000000..55f7be1 --- /dev/null +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailexpense/observer/CategoryMonthDetailExpenseObserver.kt @@ -0,0 +1,32 @@ +package com.carlosgub.myfinances.presentation.screen.categorymonthdetailexpense.observer + +import com.carlosgub.myfinances.presentation.model.ExpenseScreenModel +import com.carlosgub.myfinances.presentation.navigation.AppNavigation +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailexpense.CategoryMonthDetailExpenseScreenSideEffect +import moe.tlaster.precompose.navigation.Navigator + +fun categoryMonthDetailExpenseObserver( + sideEffect: CategoryMonthDetailExpenseScreenSideEffect, + navigator: Navigator, + appNavigation: AppNavigation, +) { + when (sideEffect) { + is CategoryMonthDetailExpenseScreenSideEffect.NavigateToMonthDetail -> + navigateToEditScreen( + navigator = navigator, + expenseScreenModel = sideEffect.expenseScreenModel, + appNavigation = appNavigation, + ) + } +} + +private fun navigateToEditScreen( + navigator: Navigator, + expenseScreenModel: ExpenseScreenModel, + appNavigation: AppNavigation, +) { + appNavigation.navigateToEditExpense( + navigator = navigator, + id = expenseScreenModel.id, + ) +} diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailincome/CategoryMonthDetailScreenIncome.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailincome/CategoryMonthDetailScreenIncome.kt new file mode 100644 index 0000000..4df22fc --- /dev/null +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailincome/CategoryMonthDetailScreenIncome.kt @@ -0,0 +1,76 @@ +package com.carlosgub.myfinances.presentation.screen.categorymonthdetailincome + +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import com.carlosgub.myfinances.components.toolbar.Toolbar +import com.carlosgub.myfinances.core.navigation.LocalNavController +import com.carlosgub.myfinances.presentation.navigation.AppNavigation +import com.carlosgub.myfinances.presentation.screen.categorymonthdetailincome.content.CategoryMonthDetailIncomeContent +import com.carlosgub.myfinances.presentation.screen.categorymonthdetailincome.observer.categoryMonthDetailIncomeObserver +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailincome.CategoryMonthDetailIncomeViewModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import moe.tlaster.precompose.flow.collectAsStateWithLifecycle +import moe.tlaster.precompose.koin.koinViewModel +import org.jetbrains.compose.resources.stringResource +import org.koin.compose.koinInject + +@Composable +fun CategoryMonthDetailScreenIncome( + monthKey: String, + categoryName: String, + modifier: Modifier = Modifier, +) { + val navigator = LocalNavController.current + val appNavigation: AppNavigation = koinInject() + val viewModel = koinViewModel(vmClass = CategoryMonthDetailIncomeViewModel::class) + val state by viewModel.container.stateFlow.collectAsStateWithLifecycle() + viewModel.setInitialConfiguration(monthKey = monthKey, category = categoryName) + val scope = CoroutineScope(Dispatchers.Main) + Scaffold( + topBar = { + CategoryMonthDetailIncomeToolbar( + category = stringResource(state.category.categoryName), + onBack = { + navigator.popBackStack() + }, + ) + }, + modifier = modifier, + ) { paddingValues -> + CategoryMonthDetailIncomeContent( + paddingValues = paddingValues, + state = state, + incomeClicked = { incomeScreenModel -> + viewModel.navigateToEditIncome(incomeScreenModel) + }, + ) + } + scope.launch { + viewModel.container.sideEffectFlow.collect { sideEffect -> + categoryMonthDetailIncomeObserver( + sideEffect = sideEffect, + navigator = navigator, + appNavigation = appNavigation, + ) + } + } +} + +@Composable +private fun CategoryMonthDetailIncomeToolbar( + category: String, + onBack: () -> Unit, +) { + Toolbar( + backgroundColor = Color.White, + title = category, + hasNavigationIcon = true, + navigation = onBack, + contentColor = Color.Black, + ) +} diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailincome/content/CategoryMonthDetailIncomeContent.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailincome/content/CategoryMonthDetailIncomeContent.kt new file mode 100644 index 0000000..76df28d --- /dev/null +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailincome/content/CategoryMonthDetailIncomeContent.kt @@ -0,0 +1,218 @@ +@file:OptIn(ExperimentalFoundationApi::class) + +package com.carlosgub.myfinances.presentation.screen.categorymonthdetailincome.content + +import androidx.compose.animation.core.tween +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import com.carlosgub.myfinances.components.chart.FinanceLineChart +import com.carlosgub.myfinances.components.datazero.DataZero +import com.carlosgub.myfinances.components.divider.HorizontalDivider +import com.carlosgub.myfinances.components.loading.Loading +import com.carlosgub.myfinances.core.utils.toMoneyFormat +import com.carlosgub.myfinances.presentation.model.IncomeScreenModel +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailincome.CategoryMonthDetailIncomeScreenState +import com.carlosgub.myfinances.theme.Gray600 +import com.carlosgub.myfinances.theme.Gray900 +import com.carlosgub.myfinances.theme.White +import com.carlosgub.myfinances.theme.spacing_1 +import com.carlosgub.myfinances.theme.spacing_2 +import com.carlosgub.myfinances.theme.spacing_4 +import com.carlosgub.myfinances.theme.spacing_6 +import org.jetbrains.compose.resources.stringResource +import piggybank.presentation.generated.resources.Res +import piggybank.presentation.generated.resources.category_month_detail_data_zero_message +import piggybank.presentation.generated.resources.category_month_detail_data_zero_title + +@Composable +fun CategoryMonthDetailIncomeContent( + paddingValues: PaddingValues, + state: CategoryMonthDetailIncomeScreenState, + incomeClicked: (IncomeScreenModel) -> Unit, + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .background(color = White) + .padding( + top = paddingValues.calculateTopPadding(), + ), + ) { + Column( + modifier = Modifier.fillMaxWidth().weight(0.35f), + ) { + CategoryMonthDetailHeader( + state = state, + ) + } + Column( + modifier = Modifier.fillMaxWidth().weight(0.65f), + ) { + CategoryMonthDetailBody( + state = state, + incomeClicked = incomeClicked, + ) + } + } +} + +@Composable +private fun CategoryMonthDetailBody( + state: CategoryMonthDetailIncomeScreenState, + incomeClicked: (IncomeScreenModel) -> Unit, + modifier: Modifier = Modifier, +) { + if (state.showLoading || state.isInitialDataLoaded.not()) { + Loading() + } else { + Card( + modifier = modifier + .padding(top = spacing_2) + .fillMaxSize(), + shape = RoundedCornerShape(topStart = 40.dp, topEnd = 40.dp), + elevation = CardDefaults.cardElevation( + defaultElevation = 8.dp, + ), + colors = CardDefaults.cardColors( + containerColor = White, + ), + ) { + if (state.monthDetail.incomeScreenModelList.isNotEmpty()) { + LazyColumn( + modifier = Modifier + .background(color = White) + .fillMaxSize() + .padding( + top = spacing_6, + start = spacing_6, + end = spacing_6, + ), + ) { + itemsIndexed(state.monthDetail.incomeScreenModelList) { index, income -> + Column { + if (index != 0) { + HorizontalDivider() + } + CategoryMonthIncomeItem( + income = income, + incomeClicked = incomeClicked, + modifier = Modifier.animateItemPlacement( + animationSpec = tween(600), + ), + ) + } + } + } + } else { + DataZero( + title = stringResource(Res.string.category_month_detail_data_zero_title), + message = stringResource(Res.string.category_month_detail_data_zero_message), + modifier = Modifier + .background(color = White), + ) + } + } + } +} + +@OptIn(ExperimentalFoundationApi::class) +@Composable +private fun CategoryMonthIncomeItem( + income: IncomeScreenModel, + incomeClicked: (IncomeScreenModel) -> Unit, + modifier: Modifier = Modifier, +) { + Row( + modifier = modifier + .combinedClickable( + onClick = {}, + onLongClick = { + incomeClicked(income) + }, + ).padding(vertical = spacing_4), + ) { + Column( + modifier = Modifier.weight(1f).padding(end = spacing_4), + ) { + Text( + text = income.note, + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.Medium, + color = Gray900, + ) + Text( + text = income.date, + style = MaterialTheme.typography.labelMedium, + modifier = Modifier.padding(top = spacing_1), + color = Gray600, + fontWeight = FontWeight.Normal, + ) + } + Column( + horizontalAlignment = Alignment.End, + verticalArrangement = Arrangement.Top, + ) { + Text( + text = income.amount.toMoneyFormat(), + style = MaterialTheme.typography.bodySmall, + fontWeight = FontWeight.Medium, + textAlign = TextAlign.End, + color = Gray900, + ) + } + } +} + +@Composable +private fun CategoryMonthDetailHeader(state: CategoryMonthDetailIncomeScreenState) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center, + ) { + if (state.showLoading || state.isInitialDataLoaded.not()) { + Loading() + } else { + Box { + Column { + Spacer(modifier = Modifier.weight(0.2f)) + FinanceLineChart( + state.monthDetail.daySpent, + withYChart = false, + ) + } + Column { + Text( + text = state.monthDetail.monthAmount.toMoneyFormat(), + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold, + color = Gray900, + modifier = Modifier.padding(spacing_6), + ) + } + } + } + } +} diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailincome/observer/categoryMonthDetailIncomeObserver.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailincome/observer/categoryMonthDetailIncomeObserver.kt new file mode 100644 index 0000000..ddca727 --- /dev/null +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/categorymonthdetailincome/observer/categoryMonthDetailIncomeObserver.kt @@ -0,0 +1,31 @@ +package com.carlosgub.myfinances.presentation.screen.categorymonthdetailincome.observer + +import com.carlosgub.myfinances.presentation.navigation.AppNavigation +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailincome.CategoryMonthDetailIncomeScreenSideEffect +import moe.tlaster.precompose.navigation.Navigator + +fun categoryMonthDetailIncomeObserver( + sideEffect: CategoryMonthDetailIncomeScreenSideEffect, + navigator: Navigator, + appNavigation: AppNavigation, +) { + when (sideEffect) { + is CategoryMonthDetailIncomeScreenSideEffect.NavigateToMonthDetail -> + navigateToEditScreen( + navigator = navigator, + id = sideEffect.incomeScreenModel.id, + appNavigation = appNavigation, + ) + } +} + +private fun navigateToEditScreen( + navigator: Navigator, + id: Long, + appNavigation: AppNavigation, +) { + appNavigation.navigateToEditIncome( + navigator = navigator, + id = id, + ) +} diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/home/content/HomeBodyContent.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/home/content/HomeBodyContent.kt index 258b3ae..1869b66 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/home/content/HomeBodyContent.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/home/content/HomeBodyContent.kt @@ -319,7 +319,7 @@ private fun FinanceCategoryItem( verticalAlignment = Alignment.CenterVertically, modifier = Modifier .clickable { - intents.navigateToMonthDetail(expense.category.name) + intents.navigateToMonthDetail(expense.category) }.padding(vertical = spacing_3), ) { ExpenseIconProgress( diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/home/observer/HomeObserver.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/home/observer/HomeObserver.kt index 7818b01..963578f 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/home/observer/HomeObserver.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/screen/home/observer/HomeObserver.kt @@ -22,7 +22,13 @@ fun homeObserver( navigator = navigator, ) - is HomeScreenSideEffect.NavigateToMonthDetail -> appNavigation.navigateToMonthDetail( + is HomeScreenSideEffect.NavigateToMonthExpenseDetail -> appNavigation.navigateToMonthExpenseDetail( + navigator = navigator, + categoryName = sideEffect.categoryName, + monthKey = state.monthKey, + ) + + is HomeScreenSideEffect.NavigateToMonthIncomeDetail -> appNavigation.navigateToMonthIncomeDetail( navigator = navigator, categoryName = sideEffect.categoryName, monthKey = state.monthKey, diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailScreenIntents.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailExpenseScreenIntents.kt similarity index 83% rename from presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailScreenIntents.kt rename to presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailExpenseScreenIntents.kt index 99347eb..3a650b0 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailScreenIntents.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailExpenseScreenIntents.kt @@ -1,9 +1,9 @@ -package com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetail +package com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailexpense import com.carlosgub.myfinances.presentation.model.ExpenseScreenModel import kotlinx.coroutines.Job -interface CategoryMonthDetailScreenIntents { +interface CategoryMonthDetailExpenseScreenIntents { fun setInitialConfiguration( monthKey: String, category: String, diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailScreenSideEffect.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailExpenseScreenSideEffect.kt similarity index 63% rename from presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailScreenSideEffect.kt rename to presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailExpenseScreenSideEffect.kt index cea822d..8cb9081 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailScreenSideEffect.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailExpenseScreenSideEffect.kt @@ -1,9 +1,9 @@ -package com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetail +package com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailexpense import com.carlosgub.myfinances.presentation.model.ExpenseScreenModel -sealed class CategoryMonthDetailScreenSideEffect { +sealed class CategoryMonthDetailExpenseScreenSideEffect { data class NavigateToMonthDetail( val expenseScreenModel: ExpenseScreenModel, - ) : CategoryMonthDetailScreenSideEffect() + ) : CategoryMonthDetailExpenseScreenSideEffect() } diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailExpenseScreenState.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailExpenseScreenState.kt new file mode 100644 index 0000000..936e472 --- /dev/null +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailExpenseScreenState.kt @@ -0,0 +1,12 @@ +package com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailexpense + +import com.carlosgub.myfinances.domain.model.CategoryEnum +import com.carlosgub.myfinances.presentation.model.MonthDetailExpenseScreenModel + +data class CategoryMonthDetailExpenseScreenState( + val monthDetail: MonthDetailExpenseScreenModel = MonthDetailExpenseScreenModel(), + val showLoading: Boolean = false, + val monthKey: String = "", + val category: CategoryEnum = CategoryEnum.FOOD, + val isInitialDataLoaded: Boolean = false, +) diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailViewModel.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailExpenseViewModel.kt similarity index 57% rename from presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailViewModel.kt rename to presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailExpenseViewModel.kt index f1325e3..f7d5146 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailViewModel.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailExpenseViewModel.kt @@ -1,14 +1,12 @@ -package com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetail +package com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailexpense import androidx.annotation.VisibleForTesting import com.carlosgub.myfinances.core.state.GenericState import com.carlosgub.myfinances.domain.model.CategoryEnum.Companion.getCategoryEnumFromName -import com.carlosgub.myfinances.domain.model.FinanceEnum import com.carlosgub.myfinances.domain.usecase.GetExpenseMonthDetailUseCase -import com.carlosgub.myfinances.domain.usecase.GetIncomeMonthDetailUseCase -import com.carlosgub.myfinances.presentation.mapper.MonthDetailModelToMonthDetailScreenModel +import com.carlosgub.myfinances.presentation.mapper.MonthDetailExpenseModelToMonthDetailExpenseScreenModel import com.carlosgub.myfinances.presentation.model.ExpenseScreenModel -import com.carlosgub.myfinances.presentation.model.MonthDetailScreenModel +import com.carlosgub.myfinances.presentation.model.MonthDetailExpenseScreenModel import kotlinx.coroutines.Job import moe.tlaster.precompose.viewmodel.ViewModel import moe.tlaster.precompose.viewmodel.viewModelScope @@ -16,25 +14,18 @@ import org.orbitmvi.orbit.Container import org.orbitmvi.orbit.ContainerHost import org.orbitmvi.orbit.container -class CategoryMonthDetailViewModel( +class CategoryMonthDetailExpenseViewModel( private val getExpenseMonthDetailUseCase: GetExpenseMonthDetailUseCase, - private val getIncomeMonthDetailUseCase: GetIncomeMonthDetailUseCase, ) : ViewModel(), - ContainerHost, - CategoryMonthDetailScreenIntents { + ContainerHost, + CategoryMonthDetailExpenseScreenIntents { override fun getMonthDetail(): Job = intent { showLoading() - if (state.category.type == FinanceEnum.EXPENSE) { - observeExpense( - categoryEnum = state.category, - monthKey = state.monthKey, - ) - } else { - observeIncome( - monthKey = state.monthKey, - ) - } + observeExpense( + categoryEnum = state.category, + monthKey = state.monthKey, + ) } @VisibleForTesting @@ -49,21 +40,7 @@ class CategoryMonthDetailViewModel( ), ).collect { result -> when (result) { - is GenericState.Success -> setMonthDetailScreenModel(MonthDetailModelToMonthDetailScreenModel.map(result.data)) - else -> Unit - } - } - } - - @VisibleForTesting - suspend fun observeIncome(monthKey: String) { - getIncomeMonthDetailUseCase( - GetIncomeMonthDetailUseCase.Params( - monthKey = monthKey, - ), - ).collect { result -> - when (result) { - is GenericState.Success -> setMonthDetailScreenModel(MonthDetailModelToMonthDetailScreenModel.map(result.data)) + is GenericState.Success -> setMonthDetailScreenModel(MonthDetailExpenseModelToMonthDetailExpenseScreenModel.map(result.data)) else -> Unit } } @@ -72,7 +49,7 @@ class CategoryMonthDetailViewModel( override fun navigateToEditExpense(expenseScreenModel: ExpenseScreenModel): Job = intent { postSideEffect( - CategoryMonthDetailScreenSideEffect.NavigateToMonthDetail( + CategoryMonthDetailExpenseScreenSideEffect.NavigateToMonthDetail( expenseScreenModel, ), ) @@ -93,7 +70,7 @@ class CategoryMonthDetailViewModel( } @VisibleForTesting - fun setMonthDetailScreenModel(monthDetail: MonthDetailScreenModel): Job = + fun setMonthDetailScreenModel(monthDetail: MonthDetailExpenseScreenModel): Job = intent { reduce { state.copy( @@ -110,12 +87,12 @@ class CategoryMonthDetailViewModel( reduce { state.copy( showLoading = true, - monthDetail = MonthDetailScreenModel(), + monthDetail = MonthDetailExpenseScreenModel(), isInitialDataLoaded = false, ) } } - override val container: Container = - viewModelScope.container(CategoryMonthDetailScreenState()) + override val container: Container = + viewModelScope.container(CategoryMonthDetailExpenseScreenState()) } diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeScreenIntents.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeScreenIntents.kt new file mode 100644 index 0000000..7ac0d0b --- /dev/null +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeScreenIntents.kt @@ -0,0 +1,15 @@ +package com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailincome + +import com.carlosgub.myfinances.presentation.model.IncomeScreenModel +import kotlinx.coroutines.Job + +interface CategoryMonthDetailIncomeScreenIntents { + fun setInitialConfiguration( + monthKey: String, + category: String, + ): Job + + fun getMonthDetail(): Job + + fun navigateToEditIncome(incomeScreenModel: IncomeScreenModel): Job +} diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeScreenSideEffect.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeScreenSideEffect.kt new file mode 100644 index 0000000..250b2d0 --- /dev/null +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeScreenSideEffect.kt @@ -0,0 +1,9 @@ +package com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailincome + +import com.carlosgub.myfinances.presentation.model.IncomeScreenModel + +sealed class CategoryMonthDetailIncomeScreenSideEffect { + data class NavigateToMonthDetail( + val incomeScreenModel: IncomeScreenModel, + ) : CategoryMonthDetailIncomeScreenSideEffect() +} diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailScreenState.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeScreenState.kt similarity index 55% rename from presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailScreenState.kt rename to presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeScreenState.kt index 4f878f9..adb39c5 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailScreenState.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeScreenState.kt @@ -1,10 +1,10 @@ -package com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetail +package com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailincome import com.carlosgub.myfinances.domain.model.CategoryEnum -import com.carlosgub.myfinances.presentation.model.MonthDetailScreenModel +import com.carlosgub.myfinances.presentation.model.MonthDetailIncomeScreenModel -data class CategoryMonthDetailScreenState( - val monthDetail: MonthDetailScreenModel = MonthDetailScreenModel(), +data class CategoryMonthDetailIncomeScreenState( + val monthDetail: MonthDetailIncomeScreenModel = MonthDetailIncomeScreenModel(), val showLoading: Boolean = false, val monthKey: String = "", val category: CategoryEnum = CategoryEnum.FOOD, diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeViewModel.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeViewModel.kt new file mode 100644 index 0000000..49b600e --- /dev/null +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeViewModel.kt @@ -0,0 +1,93 @@ +package com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailincome + +import androidx.annotation.VisibleForTesting +import com.carlosgub.myfinances.core.state.GenericState +import com.carlosgub.myfinances.domain.model.CategoryEnum.Companion.getCategoryEnumFromName +import com.carlosgub.myfinances.domain.usecase.GetIncomeMonthDetailUseCase +import com.carlosgub.myfinances.presentation.mapper.MonthDetailIncomeModelToMonthDetailIncomeScreenModel +import com.carlosgub.myfinances.presentation.model.IncomeScreenModel +import com.carlosgub.myfinances.presentation.model.MonthDetailIncomeScreenModel +import kotlinx.coroutines.Job +import moe.tlaster.precompose.viewmodel.ViewModel +import moe.tlaster.precompose.viewmodel.viewModelScope +import org.orbitmvi.orbit.Container +import org.orbitmvi.orbit.ContainerHost +import org.orbitmvi.orbit.container + +class CategoryMonthDetailIncomeViewModel( + private val getIncomeMonthDetailUseCase: GetIncomeMonthDetailUseCase, +) : ViewModel(), + ContainerHost, + CategoryMonthDetailIncomeScreenIntents { + override fun getMonthDetail(): Job = + intent { + showLoading() + observeIncome( + monthKey = state.monthKey, + ) + } + + @VisibleForTesting + suspend fun observeIncome(monthKey: String) { + getIncomeMonthDetailUseCase( + GetIncomeMonthDetailUseCase.Params( + monthKey = monthKey, + ), + ).collect { result -> + when (result) { + is GenericState.Success -> setMonthDetailScreenModel(MonthDetailIncomeModelToMonthDetailIncomeScreenModel.map(result.data)) + else -> Unit + } + } + } + + override fun navigateToEditIncome(incomeScreenModel: IncomeScreenModel): Job = intent { + postSideEffect( + CategoryMonthDetailIncomeScreenSideEffect.NavigateToMonthDetail( + incomeScreenModel, + ), + ) + } + + + override fun setInitialConfiguration( + monthKey: String, + category: String, + ): Job = + intent { + reduce { + state.copy( + monthKey = monthKey, + category = getCategoryEnumFromName(category), + ) + } + getMonthDetail() + } + + @VisibleForTesting + fun setMonthDetailScreenModel(monthDetail: MonthDetailIncomeScreenModel): Job = + intent { + reduce { + state.copy( + monthDetail = monthDetail, + showLoading = false, + isInitialDataLoaded = true, + ) + } + } + + @VisibleForTesting + fun showLoading(): Job = + intent { + reduce { + state.copy( + showLoading = true, + monthDetail = MonthDetailIncomeScreenModel(), + isInitialDataLoaded = false, + ) + } + } + + override val container: Container = + viewModelScope.container(CategoryMonthDetailIncomeScreenState()) +} diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/home/HomeScreenIntents.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/home/HomeScreenIntents.kt index 67d12d1..1edd338 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/home/HomeScreenIntents.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/home/HomeScreenIntents.kt @@ -1,5 +1,6 @@ package com.carlosgub.myfinances.presentation.viewmodel.home +import com.carlosgub.myfinances.domain.model.CategoryEnum import kotlinx.coroutines.Job interface HomeScreenIntents { @@ -9,7 +10,7 @@ interface HomeScreenIntents { fun navigateToMonths(): Job - fun navigateToMonthDetail(categoryName: String): Job + fun navigateToMonthDetail(category: CategoryEnum): Job fun navigateToAddExpense(): Job diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/home/HomeScreenSideEffect.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/home/HomeScreenSideEffect.kt index 0bb86e9..6fa2a94 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/home/HomeScreenSideEffect.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/home/HomeScreenSideEffect.kt @@ -3,7 +3,11 @@ package com.carlosgub.myfinances.presentation.viewmodel.home sealed class HomeScreenSideEffect { data object NavigateToMonths : HomeScreenSideEffect() - data class NavigateToMonthDetail( + data class NavigateToMonthExpenseDetail( + val categoryName: String, + ) : HomeScreenSideEffect() + + data class NavigateToMonthIncomeDetail( val categoryName: String, ) : HomeScreenSideEffect() diff --git a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/home/HomeViewModel.kt b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/home/HomeViewModel.kt index 5670840..9e06f93 100644 --- a/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/home/HomeViewModel.kt +++ b/presentation/src/commonMain/kotlin/com/carlosgub/myfinances/presentation/viewmodel/home/HomeViewModel.kt @@ -2,6 +2,8 @@ package com.carlosgub.myfinances.presentation.viewmodel.home import androidx.annotation.VisibleForTesting import com.carlosgub.myfinances.core.state.GenericState +import com.carlosgub.myfinances.domain.model.CategoryEnum +import com.carlosgub.myfinances.domain.model.FinanceEnum import com.carlosgub.myfinances.domain.usecase.GetFinanceUseCase import com.carlosgub.myfinances.presentation.mapper.FinanceModelToFinanceScreenModel import com.carlosgub.myfinances.presentation.model.FinanceScreenModel @@ -82,9 +84,13 @@ class HomeViewModel( postSideEffect(HomeScreenSideEffect.NavigateToAddIncome) } - override fun navigateToMonthDetail(categoryName: String): Job = + override fun navigateToMonthDetail(category: CategoryEnum): Job = intent { - postSideEffect(HomeScreenSideEffect.NavigateToMonthDetail(categoryName)) + if(category.type==FinanceEnum.EXPENSE){ + postSideEffect(HomeScreenSideEffect.NavigateToMonthExpenseDetail(category.name)) + }else{ + postSideEffect(HomeScreenSideEffect.NavigateToMonthIncomeDetail(category.name)) + } } override fun navigateToMonths(): Job = diff --git a/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/data/repository/impl/FakeFinanceRepositoryImpl.kt b/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/data/repository/impl/FakeFinanceRepositoryImpl.kt index c632301..ef997c1 100644 --- a/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/data/repository/impl/FakeFinanceRepositoryImpl.kt +++ b/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/data/repository/impl/FakeFinanceRepositoryImpl.kt @@ -4,7 +4,8 @@ import com.carlosgub.myfinances.core.state.GenericState import com.carlosgub.myfinances.domain.model.ExpenseModel import com.carlosgub.myfinances.domain.model.FinanceModel import com.carlosgub.myfinances.domain.model.IncomeModel -import com.carlosgub.myfinances.domain.model.MonthDetailModel +import com.carlosgub.myfinances.domain.model.MonthDetailExpenseModel +import com.carlosgub.myfinances.domain.model.MonthDetailIncomeModel import com.carlosgub.myfinances.domain.repository.FinanceRepository import com.carlosgub.myfinances.test.mock.expenseModelOne import com.carlosgub.myfinances.test.mock.financeModelMock @@ -68,14 +69,14 @@ internal class FakeFinanceRepositoryImpl : FinanceRepository { override suspend fun getExpenseMonthDetail( categoryEnum: com.carlosgub.myfinances.domain.model.CategoryEnum, monthKey: String, - ): Flow> = + ): Flow> = flow { emit( GenericState.Success(monthExpenseDetailModel), ) } - override suspend fun getIncomeMonthDetail(monthKey: String): Flow> = + override suspend fun getIncomeMonthDetail(monthKey: String): Flow> = flow { emit( GenericState.Success(monthIncomeDetailModel), diff --git a/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/mock/DatabaseFinanceDataSourceMock.kt b/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/mock/DatabaseFinanceDataSourceMock.kt index e5fb2be..b6146f2 100644 --- a/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/mock/DatabaseFinanceDataSourceMock.kt +++ b/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/mock/DatabaseFinanceDataSourceMock.kt @@ -13,12 +13,15 @@ import com.carlosgub.myfinances.domain.model.FinanceExpenses import com.carlosgub.myfinances.domain.model.FinanceLocalDate import com.carlosgub.myfinances.domain.model.FinanceModel import com.carlosgub.myfinances.domain.model.IncomeModel -import com.carlosgub.myfinances.domain.model.MonthDetailModel +import com.carlosgub.myfinances.domain.model.MonthDetailExpenseModel +import com.carlosgub.myfinances.domain.model.MonthDetailIncomeModel import com.carlosgub.myfinances.domain.model.MonthExpense import com.carlosgub.myfinances.domain.model.MonthModel import com.carlosgub.myfinances.presentation.model.ExpenseScreenModel import com.carlosgub.myfinances.presentation.model.FinanceScreenModel -import com.carlosgub.myfinances.presentation.model.MonthDetailScreenModel +import com.carlosgub.myfinances.presentation.model.IncomeScreenModel +import com.carlosgub.myfinances.presentation.model.MonthDetailExpenseScreenModel +import com.carlosgub.myfinances.presentation.model.MonthDetailIncomeScreenModel import expense.Expense import income.Income import kotlinx.collections.immutable.persistentListOf @@ -133,7 +136,7 @@ val incomeModelOne = ) val incomeModelForMonthDetailOne = - ExpenseModel( + IncomeModel( id = incomeOne.id, amount = incomeOne.amount, note = incomeOne.note.replaceFirstChar { it.uppercase() }, @@ -147,7 +150,7 @@ val incomeLocalDateTwo = FinanceLocalDate(incomeTwo.dateInMillis.toLocalDate()) val incomeModelForMonthDetailTwo = - ExpenseModel( + IncomeModel( id = incomeTwo.id, amount = incomeTwo.amount, note = incomeTwo.note.replaceFirstChar { it.uppercase() }, @@ -161,7 +164,7 @@ val incomeLocalDateThree = FinanceLocalDate(incomeThree.dateInMillis.toLocalDate()) val incomeModelForMonthDetailThree = - ExpenseModel( + IncomeModel( id = incomeThree.id, amount = incomeThree.amount, note = incomeThree.note.replaceFirstChar { it.uppercase() }, @@ -265,7 +268,7 @@ val expenseScreenModelThree = ) val incomeScreenModelOne = - ExpenseScreenModel( + IncomeScreenModel( id = incomeOne.id, amount = incomeOne.amount, note = incomeOne.note.replaceFirstChar { it.uppercase() }, @@ -275,7 +278,7 @@ val incomeScreenModelOne = ) val incomeScreenModelTwo = - ExpenseScreenModel( + IncomeScreenModel( id = incomeTwo.id, amount = incomeTwo.amount, note = incomeTwo.note.replaceFirstChar { it.uppercase() }, @@ -285,7 +288,7 @@ val incomeScreenModelTwo = ) val incomeScreenModelThree = - ExpenseScreenModel( + IncomeScreenModel( id = incomeThree.id, amount = incomeThree.amount, note = incomeThree.note.replaceFirstChar { it.uppercase() }, @@ -400,16 +403,16 @@ val daySpentMonthExpenseDetailModel = }.toImmutableMap() val monthExpenseDetailModel = - MonthDetailModel( + MonthDetailExpenseModel( monthAmount = expenseOne.amount, - expenseModel = listOf(expenseModelForMonthScreenOne), + expenseModelList = listOf(expenseModelForMonthScreenOne), daySpent = daySpentMonthExpenseDetailModel, ) val monthExpenseDetailScreenModel = - MonthDetailScreenModel( + MonthDetailExpenseScreenModel( monthAmount = expenseOne.amount, - expenseScreenModel = listOf(expenseScreenModelOne), + expenseScreenModelList = listOf(expenseScreenModelOne), daySpent = daySpentMonthExpenseDetailModel, ) @@ -429,16 +432,16 @@ val daySpentMonthIncomeDetailScreenModel = }.toImmutableMap() val monthIncomeDetailModel = - MonthDetailModel( + MonthDetailIncomeModel( monthAmount = incomeList.sumOf { it.amount }, - expenseModel = incomeModelList, + incomeModelList = incomeModelList, daySpent = daySpentMonthIncomeDetailScreenModel, ) val monthIncomeDetailScreenModel = - MonthDetailScreenModel( + MonthDetailIncomeScreenModel( monthAmount = incomeList.sumOf { it.amount }, - expenseScreenModel = incomeScreenModelList, + incomeScreenModelList = incomeScreenModelList, daySpent = daySpentMonthIncomeDetailScreenModel, ) diff --git a/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailViewModelTest.kt b/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailViewModelExpenseTest.kt similarity index 66% rename from test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailViewModelTest.kt rename to test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailViewModelExpenseTest.kt index f2034a2..3301d0f 100644 --- a/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/categorymonthdetail/CategoryMonthDetailViewModelTest.kt +++ b/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/categorymonthdetailexpense/CategoryMonthDetailViewModelExpenseTest.kt @@ -1,34 +1,30 @@ -package com.carlosgub.myfinances.test.presentation.viewmodel.categorymonthdetail +package com.carlosgub.myfinances.test.presentation.viewmodel.categorymonthdetailexpense import com.carlosgub.myfinances.core.utils.getCurrentMonthKey import com.carlosgub.myfinances.domain.usecase.GetExpenseMonthDetailUseCase -import com.carlosgub.myfinances.domain.usecase.GetIncomeMonthDetailUseCase -import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetail.CategoryMonthDetailScreenSideEffect -import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetail.CategoryMonthDetailScreenState -import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetail.CategoryMonthDetailViewModel +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailexpense.CategoryMonthDetailExpenseScreenSideEffect +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailexpense.CategoryMonthDetailExpenseScreenState +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailexpense.CategoryMonthDetailExpenseViewModel import com.carlosgub.myfinances.test.data.repository.impl.FakeFinanceRepositoryImpl import com.carlosgub.myfinances.test.mock.expenseScreenModelOne import com.carlosgub.myfinances.test.mock.monthExpenseDetailScreenModel -import com.carlosgub.myfinances.test.mock.monthIncomeDetailScreenModel import kotlinx.coroutines.test.runTest import org.orbitmvi.orbit.test.test import kotlin.test.Test -class CategoryMonthDetailViewModelTest { +class CategoryMonthDetailViewModelExpenseTest { private val fakeFinanceRepositoryImpl = FakeFinanceRepositoryImpl() private var getExpenseMonthDetailUseCase = GetExpenseMonthDetailUseCase(fakeFinanceRepositoryImpl) - private var getIncomeMonthDetailUseCase = GetIncomeMonthDetailUseCase(fakeFinanceRepositoryImpl) private var categoryMonthDetailViewModel = - CategoryMonthDetailViewModel( + CategoryMonthDetailExpenseViewModel( getExpenseMonthDetailUseCase = getExpenseMonthDetailUseCase, - getIncomeMonthDetailUseCase = getIncomeMonthDetailUseCase, ) @Test fun `Get Months Detail Expense`() = runTest { - categoryMonthDetailViewModel.test(this, CategoryMonthDetailScreenState()) { + categoryMonthDetailViewModel.test(this, CategoryMonthDetailExpenseScreenState()) { expectInitialState() containerHost.getMonthDetail() expectState { @@ -46,36 +42,10 @@ class CategoryMonthDetailViewModelTest { } } - @Test - fun `Get Months Detail Income`() = - runTest { - categoryMonthDetailViewModel.test( - this, - CategoryMonthDetailScreenState( - category = com.carlosgub.myfinances.domain.model.CategoryEnum.WORK, - ), - ) { - expectInitialState() - containerHost.getMonthDetail() - expectState { - copy( - showLoading = true, - ) - } - expectState { - copy( - monthDetail = monthIncomeDetailScreenModel, - showLoading = false, - isInitialDataLoaded = true, - ) - } - } - } - @Test fun `Observe Expense`() = runTest { - val state = CategoryMonthDetailScreenState() + val state = CategoryMonthDetailExpenseScreenState() categoryMonthDetailViewModel.test( this, state, @@ -95,36 +65,14 @@ class CategoryMonthDetailViewModelTest { } } - @Test - fun `Observe Income`() = - runTest { - val state = CategoryMonthDetailScreenState() - categoryMonthDetailViewModel.test( - this, - state, - ) { - expectInitialState() - containerHost.observeIncome( - monthKey = state.monthKey, - ) - expectState { - copy( - monthDetail = monthIncomeDetailScreenModel, - showLoading = false, - isInitialDataLoaded = true, - ) - } - } - } - @Test fun `Navigate To Edit Expense`() = runTest { - categoryMonthDetailViewModel.test(this, CategoryMonthDetailScreenState()) { + categoryMonthDetailViewModel.test(this, CategoryMonthDetailExpenseScreenState()) { expectInitialState() containerHost.navigateToEditExpense(expenseScreenModelOne) expectSideEffect( - CategoryMonthDetailScreenSideEffect.NavigateToMonthDetail( + CategoryMonthDetailExpenseScreenSideEffect.NavigateToMonthDetail( expenseScreenModelOne, ), ) @@ -134,7 +82,7 @@ class CategoryMonthDetailViewModelTest { @Test fun `Set Initial Configuration`() = runTest { - categoryMonthDetailViewModel.test(this, CategoryMonthDetailScreenState()) { + categoryMonthDetailViewModel.test(this, CategoryMonthDetailExpenseScreenState()) { expectInitialState() containerHost.setInitialConfiguration( monthKey = getCurrentMonthKey(), @@ -153,7 +101,7 @@ class CategoryMonthDetailViewModelTest { } expectState { copy( - monthDetail = monthIncomeDetailScreenModel, + monthDetail = monthExpenseDetailScreenModel, showLoading = false, isInitialDataLoaded = true, ) @@ -166,7 +114,7 @@ class CategoryMonthDetailViewModelTest { runTest { categoryMonthDetailViewModel.test( this, - CategoryMonthDetailScreenState(), + CategoryMonthDetailExpenseScreenState(), ) { expectInitialState() containerHost.setMonthDetailScreenModel(monthExpenseDetailScreenModel) @@ -185,7 +133,7 @@ class CategoryMonthDetailViewModelTest { runTest { categoryMonthDetailViewModel.test( this, - CategoryMonthDetailScreenState(), + CategoryMonthDetailExpenseScreenState(), ) { expectInitialState() containerHost.showLoading() diff --git a/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeViewModelTest.kt b/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeViewModelTest.kt new file mode 100644 index 0000000..c4a7f9d --- /dev/null +++ b/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/categorymonthdetailincome/CategoryMonthDetailIncomeViewModelTest.kt @@ -0,0 +1,151 @@ +package com.carlosgub.myfinances.test.presentation.viewmodel.categorymonthdetailincome + +import com.carlosgub.myfinances.core.utils.getCurrentMonthKey +import com.carlosgub.myfinances.domain.model.CategoryEnum +import com.carlosgub.myfinances.domain.usecase.GetIncomeMonthDetailUseCase +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailincome.CategoryMonthDetailIncomeScreenSideEffect +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailincome.CategoryMonthDetailIncomeScreenState +import com.carlosgub.myfinances.presentation.viewmodel.categorymonthdetailincome.CategoryMonthDetailIncomeViewModel +import com.carlosgub.myfinances.test.data.repository.impl.FakeFinanceRepositoryImpl +import com.carlosgub.myfinances.test.mock.incomeScreenModelOne +import com.carlosgub.myfinances.test.mock.monthIncomeDetailScreenModel +import kotlinx.coroutines.test.runTest +import org.orbitmvi.orbit.test.test +import kotlin.test.Test + +class CategoryMonthDetailIncomeViewModelTest { + private val fakeFinanceRepositoryImpl = FakeFinanceRepositoryImpl() + private var getIncomeMonthDetailUseCase = GetIncomeMonthDetailUseCase(fakeFinanceRepositoryImpl) + private var categoryMonthDetailViewModel = + CategoryMonthDetailIncomeViewModel( + getIncomeMonthDetailUseCase = getIncomeMonthDetailUseCase, + ) + + @Test + fun `Get Months Detail Income`() = + runTest { + categoryMonthDetailViewModel.test( + this, + CategoryMonthDetailIncomeScreenState( + category = CategoryEnum.WORK, + ), + ) { + expectInitialState() + containerHost.getMonthDetail() + expectState { + copy( + showLoading = true, + ) + } + expectState { + copy( + monthDetail = monthIncomeDetailScreenModel, + showLoading = false, + isInitialDataLoaded = true, + ) + } + } + } + + @Test + fun `Observe Income`() = + runTest { + val state = CategoryMonthDetailIncomeScreenState() + categoryMonthDetailViewModel.test( + this, + state, + ) { + expectInitialState() + containerHost.observeIncome( + monthKey = state.monthKey, + ) + expectState { + copy( + monthDetail = monthIncomeDetailScreenModel, + showLoading = false, + isInitialDataLoaded = true, + ) + } + } + } + + @Test + fun `Navigate To Edit Income`() = + runTest { + categoryMonthDetailViewModel.test(this, CategoryMonthDetailIncomeScreenState()) { + expectInitialState() + containerHost.navigateToEditIncome(incomeScreenModelOne) + expectSideEffect( + CategoryMonthDetailIncomeScreenSideEffect.NavigateToMonthDetail( + incomeScreenModelOne, + ), + ) + } + } + + @Test + fun `Set Initial Configuration`() = + runTest { + categoryMonthDetailViewModel.test(this, CategoryMonthDetailIncomeScreenState()) { + expectInitialState() + containerHost.setInitialConfiguration( + monthKey = getCurrentMonthKey(), + category = com.carlosgub.myfinances.domain.model.CategoryEnum.WORK.name, + ) + expectState { + copy( + monthKey = getCurrentMonthKey(), + category = com.carlosgub.myfinances.domain.model.CategoryEnum.WORK, + ) + } + expectState { + copy( + showLoading = true, + ) + } + expectState { + copy( + monthDetail = monthIncomeDetailScreenModel, + showLoading = false, + isInitialDataLoaded = true, + ) + } + } + } + + @Test + fun `Set Month Detail Screen Model`() = + runTest { + categoryMonthDetailViewModel.test( + this, + CategoryMonthDetailIncomeScreenState(), + ) { + expectInitialState() + containerHost.setMonthDetailScreenModel(monthIncomeDetailScreenModel) + expectState { + copy( + monthDetail = monthIncomeDetailScreenModel, + showLoading = false, + isInitialDataLoaded = true, + ) + } + } + } + + @Test + fun `Show Loading`() = + runTest { + categoryMonthDetailViewModel.test( + this, + CategoryMonthDetailIncomeScreenState(), + ) { + expectInitialState() + containerHost.showLoading() + expectState { + copy( + showLoading = true, + ) + } + } + } +} diff --git a/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/home/HomeViewModelTest.kt b/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/home/HomeViewModelTest.kt index a8b3539..c408b2a 100644 --- a/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/home/HomeViewModelTest.kt +++ b/test/src/commonTest/kotlin/com/carlosgub/myfinances/test/presentation/viewmodel/home/HomeViewModelTest.kt @@ -112,13 +112,24 @@ class HomeViewModelTest { } @Test - fun `Navigate To Month Detail`() = + fun `Navigate To Month Detail Expense`() = runTest { - val expectedCategoryName = com.carlosgub.myfinances.domain.model.CategoryEnum.HOME.name + val expectedCategory = com.carlosgub.myfinances.domain.model.CategoryEnum.HOME homeViewModel.test(this, HomeScreenState()) { expectInitialState() - containerHost.navigateToMonthDetail(expectedCategoryName) - expectSideEffect(HomeScreenSideEffect.NavigateToMonthDetail(expectedCategoryName)) + containerHost.navigateToMonthDetail(expectedCategory) + expectSideEffect(HomeScreenSideEffect.NavigateToMonthExpenseDetail(expectedCategory.name)) + } + } + + @Test + fun `Navigate To Month Detail Income`() = + runTest { + val expectedCategory = com.carlosgub.myfinances.domain.model.CategoryEnum.WORK + homeViewModel.test(this, HomeScreenState()) { + expectInitialState() + containerHost.navigateToMonthDetail(expectedCategory) + expectSideEffect(HomeScreenSideEffect.NavigateToMonthIncomeDetail(expectedCategory.name)) } } }