Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -52,15 +53,14 @@ 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
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(
Expand Down Expand Up @@ -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,
)
}
Expand All @@ -131,7 +123,6 @@ internal fun SavingsAccountDialog(
isRetryEnabled = true,
)
}

null -> Unit
}
}
Expand Down Expand Up @@ -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() }
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand All @@ -68,15 +66,7 @@ class SavingsAccountViewmodel(
}
}

/**
* Handles changes in network connectivity.
*
* It updates the `networkStatus` state. If the network is offline, it sets the
* `uiState` to [ScreenUiState.Network]. If the network is online, it
* automatically triggers a data fetch to refresh the content.
*
* @param isOnline A boolean indicating the current network status.
*/
/** Handles changes in network connectivity. */
private fun handleNetworkStatus(isOnline: Boolean) {
updateState { it.copy(networkStatus = isOnline) }

Expand All @@ -99,11 +89,7 @@ class SavingsAccountViewmodel(
}
}

/**
* A helper function to update the mutable state flow.
*
* @param update A lambda function that takes the current state and returns a new state.
*/
/** A helper function to update the mutable state flow. */
private fun updateState(update: (SavingsAccountState) -> SavingsAccountState) {
mutableStateFlow.update(update)
}
Expand Down Expand Up @@ -139,11 +125,7 @@ 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.
*/
/** Retries data fetching depending on network availability. */
private fun retry() {
viewModelScope.launch {
if (!state.networkStatus) {
Expand All @@ -154,33 +136,22 @@ 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)
}
}

/**
* Fetches accounts from the repository and applies filters.
* If cached data is available, it uses it directly.
*
* @param selectedFilters List of selected filters to apply.
*/
private fun loadAccounts(
selectedFilters: List<StringResource?>,
) {
/** Loads savings accounts for the current client. */
private fun loadAccounts(selectedFilters: List<StringResource?>) {
viewModelScope.launch {
updateState { it.copy(uiState = ScreenUiState.Loading) }
accountsRepositoryImpl.loadAccounts(
Expand All @@ -197,12 +168,7 @@ class SavingsAccountViewmodel(
}
}

/**
* Handles the result of the repository call and updates the state.
*
* @param dataState Result of fetching savings accounts (Success, Error, Loading).
* @param selectedFilters Filters applied to the list.
*/
/** Handles repository response and updates UI state accordingly. */
private fun handleReceivedAccounts(
dataState: DataState<ClientAccounts>,
selectedFilters: List<StringResource?>,
Expand Down Expand Up @@ -230,9 +196,10 @@ 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 ?: 2,
)
}

Expand All @@ -242,12 +209,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,
Expand All @@ -262,13 +229,7 @@ class SavingsAccountViewmodel(
}
}

/**
* Filters the accounts based on the selected filters (status).
*
* @param selectedFilters List of selected labels for filtering.
* @param accounts Original unfiltered list of accounts.
* @return List of accounts that match the applied filters.
*/
/** Filters the accounts based on the selected filters (status). */
private fun filterAccounts(
selectedFilters: List<StringResource?>,
accounts: List<SavingAccount>,
Expand All @@ -280,15 +241,15 @@ class SavingsAccountViewmodel(
} else {
accounts
}

return filteredByStatus.distinct()
}

/**
* Calculates the total savings balance and updates state.
*
* @param accounts List of [SavingAccount] to compute totals from.
*/
/** Sorts accounts based on the defined status order. */
private fun sortAccountsByStatus(accounts: List<SavingAccount>): List<SavingAccount> {
return accounts.sortedWith(compareBy { state.statusOrder.indexOf(it.status?.value) })
}

/** Calculates total savings balance and updates state. */
private fun getTotalSavingAmount(accounts: List<SavingAccount>?) {
var amount = 0.0
var items = 0
Expand Down Expand Up @@ -352,6 +313,14 @@ data class SavingsAccountState(
val uiState: ScreenUiState? = ScreenUiState.Loading,

val networkStatus: Boolean = false,

/** Order of statuses for consistent sorting */
val statusOrder: List<String> = listOf(
SavingStatus.ACTIVE.status,
SavingStatus.SUBMIT_AND_PENDING_APPROVAL.status,
SavingStatus.CLOSED.status,
SavingStatus.INACTIVE.status,
),
) {

/**
Expand Down
Loading