diff --git a/feature/savings-account/src/commonMain/kotlin/org/mifos/mobile/feature/savingsaccount/savingsAccount/SavingsAccountScreen.kt b/feature/savings-account/src/commonMain/kotlin/org/mifos/mobile/feature/savingsaccount/savingsAccount/SavingsAccountScreen.kt index 65a81db239..301002cf49 100644 --- a/feature/savings-account/src/commonMain/kotlin/org/mifos/mobile/feature/savingsaccount/savingsAccount/SavingsAccountScreen.kt +++ b/feature/savings-account/src/commonMain/kotlin/org/mifos/mobile/feature/savingsaccount/savingsAccount/SavingsAccountScreen.kt @@ -40,6 +40,7 @@ import mifos_mobile.feature.savings_account.generated.resources.feature_account_ import mifos_mobile.feature.savings_account.generated.resources.feature_savings_account import mifos_mobile.feature.savings_account.generated.resources.feature_savings_account_dashboard import mifos_mobile.feature.savings_account.generated.resources.feature_savings_account_items +import mifos_mobile.feature.savings_account.generated.resources.feature_savings_filter_pending_account import mifos_mobile.feature.savings_account.generated.resources.feature_savings_no_accounts_found import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.stringResource @@ -52,7 +53,7 @@ import org.mifos.mobile.core.designsystem.theme.AppColors import org.mifos.mobile.core.designsystem.theme.DesignToken import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme import org.mifos.mobile.core.designsystem.theme.MifosTypography -import org.mifos.mobile.core.model.LoanStatus +import org.mifos.mobile.core.model.SavingStatus import org.mifos.mobile.core.ui.component.EmptyDataView import org.mifos.mobile.core.ui.component.MifosAccountCard import org.mifos.mobile.core.ui.component.MifosDashboardCard @@ -60,7 +61,6 @@ import org.mifos.mobile.core.ui.component.MifosErrorComponent import org.mifos.mobile.core.ui.component.MifosProgressIndicator import org.mifos.mobile.core.ui.utils.EventsEffect import org.mifos.mobile.core.ui.utils.ScreenUiState -import kotlin.collections.orEmpty @Composable fun SavingsAccountScreen( @@ -91,29 +91,21 @@ fun SavingsAccountScreen( EventsEffect(viewModel.eventFlow) { event -> when (event) { is SavingsAccountsEvent.NavigateBack -> navigateBack.invoke() - is SavingsAccountsEvent.AccountClicked -> { onAccountClicked(Constants.SAVINGS_ACCOUNT, event.accountId) } - - is SavingsAccountsEvent.LoadingCompleted -> { - onLoadingCompleted.invoke() - } + is SavingsAccountsEvent.LoadingCompleted -> onLoadingCompleted.invoke() } } SavingsAccountDialog( dialogState = state.dialogState, - onAction = remember(viewModel) { - { viewModel.trySendAction(it) } - }, + onAction = remember(viewModel) { { viewModel.trySendAction(it) } }, ) SavingsAccountContent( state = state, - onAction = remember(viewModel) { - { viewModel.trySendAction(it) } - }, + onAction = remember(viewModel) { { viewModel.trySendAction(it) } }, filtersClicked = filtersClicked, ) } @@ -131,7 +123,6 @@ internal fun SavingsAccountDialog( isRetryEnabled = true, ) } - null -> Unit } } @@ -210,19 +201,7 @@ internal fun SavingsAccountContent( ) } - Row( - horizontalArrangement = Arrangement.spacedBy(DesignToken.spacing.largeIncreased), - ) { - // TODO : un-implemented feature, - // commenting because user won't feels its good ,uncomment and implement it - // Icon( - // modifier = Modifier - // .clickable {} - // .size(20.dp), - // imageVector = MifosIcons.SearchNew, - // contentDescription = - // stringResource(Res.string.content_description_search), - // ) + Row(horizontalArrangement = Arrangement.spacedBy(DesignToken.spacing.largeIncreased)) { Icon( modifier = Modifier .clickable { filtersClicked() } @@ -254,38 +233,41 @@ internal fun SavingsAccountContent( ) } } else { + val accounts = state.savingsAccount.orEmpty() LazyColumn( modifier = Modifier .fillMaxSize() .weight(1f), ) { - item { - Spacer(modifier = Modifier.height(DesignToken.spacing.small)) - } - items(state.savingsAccount.orEmpty()) { account -> + item { Spacer(modifier = Modifier.height(DesignToken.spacing.small)) } + + items(accounts) { account -> val color = when (account.status?.value) { - LoanStatus.ACTIVE.status -> AppColors.customEnable - LoanStatus.SUBMIT_AND_PENDING_APPROVAL.status -> AppColors.customYellow - LoanStatus.WITHDRAWN.status, LoanStatus.MATURED.status -> - MaterialTheme.colorScheme.error + SavingStatus.ACTIVE.status -> AppColors.customEnable + SavingStatus.SUBMIT_AND_PENDING_APPROVAL.status -> AppColors.customYellow + SavingStatus.INACTIVE.status -> MaterialTheme.colorScheme.error else -> MaterialTheme.colorScheme.onSurface } + val accountStatus = if (account.status?.active == true) { + CurrencyFormatter.format( + account.accountBalance, + account.currency?.code, + account.currency?.decimalPlaces, + ) + } else { + if (account.status?.value == SavingStatus.SUBMIT_AND_PENDING_APPROVAL.status) { + stringResource(Res.string.feature_savings_filter_pending_account) + } else { + account.status?.value ?: "" + } + } + MifosAccountCard( accountId = account.id, accountNumber = account.accountNo, accountType = account.productName, - accountStatus = ( - if (account.status?.active == true) { - CurrencyFormatter.format( - account.accountBalance, - account.currency?.code, - account.currency?.decimalPlaces, - ) - } else { - account.status?.value ?: "" - } - ), + accountStatus = accountStatus, accountStatusColor = color, onAccountClick = { onAction( diff --git a/feature/savings-account/src/commonMain/kotlin/org/mifos/mobile/feature/savingsaccount/savingsAccount/SavingsAccountViewmodel.kt b/feature/savings-account/src/commonMain/kotlin/org/mifos/mobile/feature/savingsaccount/savingsAccount/SavingsAccountViewmodel.kt index c766eeb5a9..10a3e0b3d9 100644 --- a/feature/savings-account/src/commonMain/kotlin/org/mifos/mobile/feature/savingsaccount/savingsAccount/SavingsAccountViewmodel.kt +++ b/feature/savings-account/src/commonMain/kotlin/org/mifos/mobile/feature/savingsaccount/savingsAccount/SavingsAccountViewmodel.kt @@ -23,11 +23,11 @@ import org.mifos.mobile.core.common.DataState import org.mifos.mobile.core.data.repository.AccountsRepository import org.mifos.mobile.core.data.util.NetworkMonitor import org.mifos.mobile.core.datastore.UserPreferencesRepository +import org.mifos.mobile.core.model.SavingStatus import org.mifos.mobile.core.model.entity.accounts.savings.SavingAccount import org.mifos.mobile.core.model.entity.client.ClientAccounts import org.mifos.mobile.core.ui.utils.BaseViewModel import org.mifos.mobile.core.ui.utils.ScreenUiState -import org.mifos.mobile.core.ui.utils.ScreenUiState.Network import org.mifos.mobile.feature.savingsaccount.utils.FilterUtil import kotlin.collections.orEmpty @@ -55,9 +55,7 @@ class SavingsAccountViewmodel( observeNetwork() } - /** - * Observes the network connectivity status and updates state accordingly. - */ + /** Observes the network connectivity status and updates state accordingly. */ private fun observeNetwork() { viewModelScope.launch { networkMonitor.isOnline @@ -140,9 +138,9 @@ class SavingsAccountViewmodel( } /** - * Retries the data fetching process. If the network is unavailable, it shows - * a network error dialog. Otherwise, it triggers the `loadAccounts` `fetchClient`, - * `fetchLonPurpose` function. + * A helper function to update the mutable state flow. + * + * @param update A lambda function that takes the current state and returns a new state. */ private fun retry() { viewModelScope.launch { @@ -155,17 +153,15 @@ class SavingsAccountViewmodel( } /** - * Toggles visibility of the total savings amount in UI. - */ + * Toggles visibility of total savings amount in UI. + * */ private fun handleAmountVisible() { mutableStateFlow.update { it.copy(isAmountVisible = !state.isAmountVisible) } } - /** - * Dismisses any active dialog in the UI. - */ + /** Dismisses any active dialog. */ private fun handleDismissDialog() { mutableStateFlow.update { it.copy(dialogState = null) @@ -230,9 +226,11 @@ class SavingsAccountViewmodel( is DataState.Success -> { val allSavings = dataState.data.savingsAccounts.orEmpty() val filtered = filterAccounts(selectedFilters, allSavings) + val sortedAccounts = sortAccountsByStatus(filtered) updateState { it.copy( - decimals = filtered.firstOrNull()?.currency?.decimalPlaces ?: 2, + decimals = sortedAccounts.firstOrNull()?.currency?.decimalPlaces + ?: allSavings.firstOrNull()?.currency?.decimalPlaces ?: 2, ) } @@ -242,12 +240,12 @@ class SavingsAccountViewmodel( updateState { val isEmptyAccounts = allSavings.isEmpty() - val isFilteredEmpty = filtered.isEmpty() + val isFilteredEmpty = sortedAccounts.isEmpty() it.copy( - items = filtered.size, + items = sortedAccounts.size, isFilteredEmpty = isFilteredEmpty, - savingsAccount = filtered, + savingsAccount = sortedAccounts, originalAccounts = allSavings, selectedFilters = selectedFilters, currency = allSavings.firstOrNull()?.currency?.displaySymbol, @@ -280,7 +278,6 @@ class SavingsAccountViewmodel( } else { accounts } - return filteredByStatus.distinct() } @@ -289,6 +286,11 @@ class SavingsAccountViewmodel( * * @param accounts List of [SavingAccount] to compute totals from. */ + private fun sortAccountsByStatus(accounts: List): List { + return accounts.sortedWith(compareBy { state.statusOrder.indexOf(it.status?.value) }) + } + + /** Calculates total savings balance and updates state. */ private fun getTotalSavingAmount(accounts: List?) { var amount = 0.0 var items = 0 @@ -352,6 +354,14 @@ data class SavingsAccountState( val uiState: ScreenUiState? = ScreenUiState.Loading, val networkStatus: Boolean = false, + + /** Order of statuses for consistent sorting */ + val statusOrder: List = listOf( + SavingStatus.ACTIVE.status, + SavingStatus.SUBMIT_AND_PENDING_APPROVAL.status, + SavingStatus.CLOSED.status, + SavingStatus.INACTIVE.status, + ), ) { /**