mirror of
https://github.com/openMF/mifos-mobile.git
synced 2026-02-06 11:26:51 +00:00
Refactor- [:feature:savings] Apply & Fix Detekt, Ktlint Rules (#2688)
Jira Tasks - [MM-76](https://mifosforge.jira.com/browse/MM-76)
This commit is contained in:
parent
aed62abb79
commit
25389dd8a9
@ -20,6 +20,7 @@ import androidx.compose.material3.ButtonColors
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.ButtonElevation
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -144,6 +145,21 @@ fun MifosOutlinedTextButton(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MifosOutlinedButton(
|
||||
textResId: Int,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
OutlinedButton(
|
||||
onClick = onClick,
|
||||
modifier = modifier,
|
||||
content = {
|
||||
Text(text = stringResource(id = textResId))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MifosIconTextButton(
|
||||
text: String,
|
||||
|
||||
@ -1,3 +1,12 @@
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
plugins {
|
||||
alias(libs.plugins.mifos.android.feature)
|
||||
alias(libs.plugins.mifos.android.library.compose)
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file,
|
||||
You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
-->
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
||||
@ -1,3 +1,12 @@
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.navigation
|
||||
|
||||
import androidx.navigation.NavController
|
||||
@ -13,20 +22,28 @@ import org.mifos.mobile.core.common.Constants.SAVINGS_ID
|
||||
import org.mifos.mobile.core.common.Constants.TRANSFER_PAY_FROM
|
||||
import org.mifos.mobile.core.common.Constants.TRANSFER_PAY_TO
|
||||
import org.mifos.mobile.core.common.Constants.TRANSFER_TYPE
|
||||
import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations
|
||||
import org.mifos.mobile.core.model.entity.payload.ReviewTransferPayload
|
||||
import org.mifos.mobile.core.model.enums.ChargeType
|
||||
import org.mifos.mobile.core.model.enums.LoanState
|
||||
import org.mifos.mobile.core.model.enums.SavingsAccountState
|
||||
import org.mifos.mobile.core.model.enums.TransferType
|
||||
import org.mifos.mobile.feature.savings.savings_account.SavingsAccountDetailScreen
|
||||
import org.mifos.mobile.feature.savings.savings_account_application.SavingsAccountApplicationScreen
|
||||
import org.mifos.mobile.feature.savings.savings_account_transaction.SavingsAccountTransactionScreen
|
||||
import org.mifos.mobile.feature.savings.savings_account_withdraw.SavingsAccountWithdrawScreen
|
||||
import org.mifos.mobile.feature.savings.savings_make_transfer.SavingsMakeTransferScreen
|
||||
import org.mifos.mobile.feature.savings.savingsAccount.SavingsAccountDetailScreen
|
||||
import org.mifos.mobile.feature.savings.savingsAccountApplication.SavingsAccountApplicationScreen
|
||||
import org.mifos.mobile.feature.savings.savingsAccountTransaction.SavingsAccountTransactionScreen
|
||||
import org.mifos.mobile.feature.savings.savingsAccountWithdraw.SavingsAccountWithdrawScreen
|
||||
import org.mifos.mobile.feature.savings.savingsMakeTransfer.SavingsMakeTransferScreen
|
||||
|
||||
fun NavController.navigateToSavingsMakeTransfer(accountId: Long, outstandingBalance: Double? = null, transferType: String) {
|
||||
navigate(SavingsNavigation.SavingsMakeTransfer.passArguments(accountId, (outstandingBalance ?: 0.0).toString(), transferType))
|
||||
fun NavController.navigateToSavingsMakeTransfer(
|
||||
accountId: Long,
|
||||
outstandingBalance: Double? = null,
|
||||
transferType: String,
|
||||
) {
|
||||
navigate(
|
||||
SavingsNavigation.SavingsMakeTransfer.passArguments(
|
||||
accountId,
|
||||
(outstandingBalance ?: 0.0).toString(),
|
||||
transferType,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun NavController.navigateToSavingsDetailScreen(savingsId: Long) {
|
||||
@ -37,8 +54,8 @@ fun NavController.navigateToSavingsApplicationScreen() {
|
||||
navigate(
|
||||
SavingsNavigation.SavingsApplication.passArguments(
|
||||
savingsId = -1L,
|
||||
savingsAccountState = SavingsAccountState.CREATE
|
||||
)
|
||||
savingsAccountState = SavingsAccountState.CREATE,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -47,7 +64,7 @@ fun NavGraphBuilder.savingsNavGraph(
|
||||
viewQrCode: (String) -> Unit,
|
||||
viewCharges: (ChargeType) -> Unit,
|
||||
reviewTransfer: (ReviewTransferPayload, TransferType) -> Unit,
|
||||
callHelpline: () -> Unit
|
||||
callHelpline: () -> Unit,
|
||||
) {
|
||||
navigation(
|
||||
startDestination = SavingsNavigation.SavingsDetail.route,
|
||||
@ -55,14 +72,39 @@ fun NavGraphBuilder.savingsNavGraph(
|
||||
) {
|
||||
savingsDetailRoute(
|
||||
callUs = callHelpline,
|
||||
deposit = { navController.navigateToSavingsMakeTransfer(accountId = it, transferType = TRANSFER_PAY_TO) },
|
||||
makeTransfer = { navController.navigateToSavingsMakeTransfer(accountId = it, transferType = TRANSFER_PAY_FROM) },
|
||||
deposit = {
|
||||
navController.navigateToSavingsMakeTransfer(
|
||||
accountId = it,
|
||||
transferType = TRANSFER_PAY_TO,
|
||||
)
|
||||
},
|
||||
makeTransfer = {
|
||||
navController.navigateToSavingsMakeTransfer(
|
||||
accountId = it,
|
||||
transferType = TRANSFER_PAY_FROM,
|
||||
)
|
||||
},
|
||||
navigateBack = navController::popBackStack,
|
||||
updateSavingsAccount = { navController.navigate(SavingsNavigation.SavingsApplication.passArguments(savingsId = it, savingsAccountState = SavingsAccountState.UPDATE)) },
|
||||
updateSavingsAccount = {
|
||||
navController.navigate(
|
||||
SavingsNavigation.SavingsApplication.passArguments(
|
||||
savingsId = it,
|
||||
savingsAccountState = SavingsAccountState.UPDATE,
|
||||
),
|
||||
)
|
||||
},
|
||||
viewCharges = { viewCharges(ChargeType.SAVINGS) },
|
||||
viewQrCode = viewQrCode,
|
||||
viewTransaction = { navController.navigate(SavingsNavigation.SavingsTransaction.passArguments(it)) },
|
||||
withdrawSavingsAccount = { navController.navigate(SavingsNavigation.SavingsWithdraw.passArguments(it)) }
|
||||
viewTransaction = {
|
||||
navController.navigate(
|
||||
SavingsNavigation.SavingsTransaction.passArguments(it),
|
||||
)
|
||||
},
|
||||
withdrawSavingsAccount = {
|
||||
navController.navigate(
|
||||
SavingsNavigation.SavingsWithdraw.passArguments(it),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
savingsApplication(
|
||||
@ -79,7 +121,7 @@ fun NavGraphBuilder.savingsNavGraph(
|
||||
|
||||
savingsMakeTransfer(
|
||||
navigateBack = navController::popBackStack,
|
||||
reviewTransfer = reviewTransfer
|
||||
reviewTransfer = reviewTransfer,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -93,13 +135,13 @@ fun NavGraphBuilder.savingsDetailRoute(
|
||||
viewCharges: () -> Unit,
|
||||
viewQrCode: (String) -> Unit,
|
||||
callUs: () -> Unit,
|
||||
deposit: (Long) -> Unit
|
||||
deposit: (Long) -> Unit,
|
||||
) {
|
||||
composable(
|
||||
route = SavingsNavigation.SavingsDetail.route,
|
||||
arguments = listOf(
|
||||
navArgument(name = SAVINGS_ID) { type = NavType.LongType },
|
||||
)
|
||||
),
|
||||
) {
|
||||
SavingsAccountDetailScreen(
|
||||
navigateBack = navigateBack,
|
||||
@ -116,14 +158,16 @@ fun NavGraphBuilder.savingsDetailRoute(
|
||||
}
|
||||
|
||||
fun NavGraphBuilder.savingsApplication(
|
||||
navigateBack: () -> Unit
|
||||
navigateBack: () -> Unit,
|
||||
) {
|
||||
composable(
|
||||
route = SavingsNavigation.SavingsApplication.route,
|
||||
arguments = listOf(
|
||||
navArgument(name = SAVINGS_ID) { type = NavType.LongType },
|
||||
navArgument(Constants.SAVINGS_ACCOUNT_STATE) { type = NavType.EnumType(SavingsAccountState::class.java) },
|
||||
)
|
||||
navArgument(Constants.SAVINGS_ACCOUNT_STATE) {
|
||||
type = NavType.EnumType(SavingsAccountState::class.java)
|
||||
},
|
||||
),
|
||||
) {
|
||||
SavingsAccountApplicationScreen(
|
||||
navigateBack = navigateBack,
|
||||
@ -136,7 +180,7 @@ fun NavGraphBuilder.savingsTransaction(
|
||||
) {
|
||||
composable(
|
||||
route = SavingsNavigation.SavingsTransaction.route,
|
||||
arguments = listOf(navArgument(name = SAVINGS_ID) { type = NavType.LongType })
|
||||
arguments = listOf(navArgument(name = SAVINGS_ID) { type = NavType.LongType }),
|
||||
) {
|
||||
SavingsAccountTransactionScreen(
|
||||
navigateBack = navigateBack,
|
||||
@ -149,7 +193,7 @@ fun NavGraphBuilder.savingsWithdraw(
|
||||
) {
|
||||
composable(
|
||||
route = SavingsNavigation.SavingsWithdraw.route,
|
||||
arguments = listOf(navArgument(SAVINGS_ID) { type = NavType.LongType })
|
||||
arguments = listOf(navArgument(SAVINGS_ID) { type = NavType.LongType }),
|
||||
) {
|
||||
SavingsAccountWithdrawScreen(
|
||||
navigateBack = { navigateBack() },
|
||||
@ -159,7 +203,7 @@ fun NavGraphBuilder.savingsWithdraw(
|
||||
|
||||
fun NavGraphBuilder.savingsMakeTransfer(
|
||||
navigateBack: () -> Unit,
|
||||
reviewTransfer: (ReviewTransferPayload, TransferType) -> Unit
|
||||
reviewTransfer: (ReviewTransferPayload, TransferType) -> Unit,
|
||||
) {
|
||||
composable(
|
||||
route = SavingsNavigation.SavingsMakeTransfer.route,
|
||||
@ -171,12 +215,12 @@ fun NavGraphBuilder.savingsMakeTransfer(
|
||||
defaultValue = null
|
||||
},
|
||||
navArgument(name = TRANSFER_TYPE) { type = NavType.StringType },
|
||||
)
|
||||
),
|
||||
) {
|
||||
SavingsMakeTransferScreen(
|
||||
navigateBack = navigateBack,
|
||||
onCancelledClicked = navigateBack,
|
||||
reviewTransfer = reviewTransfer
|
||||
reviewTransfer = reviewTransfer,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,19 @@
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.navigation
|
||||
|
||||
import org.mifos.mobile.core.common.Constants
|
||||
import org.mifos.mobile.core.common.Constants.ACCOUNT_ID
|
||||
import org.mifos.mobile.core.common.Constants.OUTSTANDING_BALANCE
|
||||
import org.mifos.mobile.core.common.Constants.SAVINGS_ACCOUNT_STATE
|
||||
import org.mifos.mobile.core.common.Constants.SAVINGS_ID
|
||||
import org.mifos.mobile.core.common.Constants.TRANSFER_TYPE
|
||||
import org.mifos.mobile.core.model.enums.LoanState
|
||||
import org.mifos.mobile.core.model.enums.SavingsAccountState
|
||||
|
||||
const val SAVINGS_NAVIGATION_ROUTE_BASE = "savings_route"
|
||||
@ -18,22 +25,26 @@ const val SAVINGS_MAKE_TRANSFER_SCREEN_ROUTE = "savings_make_transfer_screen_rou
|
||||
|
||||
sealed class SavingsNavigation(val route: String) {
|
||||
data object SavingsBase : SavingsNavigation(
|
||||
route = SAVINGS_NAVIGATION_ROUTE_BASE
|
||||
route = SAVINGS_NAVIGATION_ROUTE_BASE,
|
||||
)
|
||||
|
||||
data object SavingsDetail : SavingsNavigation(
|
||||
route = "$SAVINGS_DETAIL_SCREEN_ROUTE/{$SAVINGS_ID}"
|
||||
route = "$SAVINGS_DETAIL_SCREEN_ROUTE/{$SAVINGS_ID}",
|
||||
) {
|
||||
fun passArguments(savingsId: Long) = "$SAVINGS_DETAIL_SCREEN_ROUTE/$savingsId"
|
||||
}
|
||||
|
||||
data object SavingsApplication : SavingsNavigation(
|
||||
route = "$SAVINGS_APPLICATION_SCREEN_ROUTE/{${SAVINGS_ID}}/{${SAVINGS_ACCOUNT_STATE}}") {
|
||||
fun passArguments(savingsId: Long, savingsAccountState: SavingsAccountState) = "$SAVINGS_APPLICATION_SCREEN_ROUTE/${savingsId}/${savingsAccountState}"
|
||||
route = "$SAVINGS_APPLICATION_SCREEN_ROUTE/{${SAVINGS_ID}}/{${SAVINGS_ACCOUNT_STATE}}",
|
||||
) {
|
||||
fun passArguments(
|
||||
savingsId: Long,
|
||||
savingsAccountState: SavingsAccountState,
|
||||
) = "$SAVINGS_APPLICATION_SCREEN_ROUTE/$savingsId/$savingsAccountState"
|
||||
}
|
||||
|
||||
data object SavingsTransaction : SavingsNavigation(
|
||||
route = "$SAVINGS_TRANSACTION_SCREEN_ROUTE/{${SAVINGS_ID}}"
|
||||
route = "$SAVINGS_TRANSACTION_SCREEN_ROUTE/{${SAVINGS_ID}}",
|
||||
) {
|
||||
fun passArguments(savingsId: Long): String {
|
||||
return "$SAVINGS_TRANSACTION_SCREEN_ROUTE/$savingsId"
|
||||
@ -41,7 +52,7 @@ sealed class SavingsNavigation(val route: String) {
|
||||
}
|
||||
|
||||
data object SavingsWithdraw : SavingsNavigation(
|
||||
route = "$SAVINGS_WITHDRAW_SCREEN_ROUTE/{${SAVINGS_ID}}"
|
||||
route = "$SAVINGS_WITHDRAW_SCREEN_ROUTE/{${SAVINGS_ID}}",
|
||||
) {
|
||||
fun passArguments(savingsId: Long): String {
|
||||
return "$SAVINGS_WITHDRAW_SCREEN_ROUTE/$savingsId"
|
||||
@ -49,9 +60,13 @@ sealed class SavingsNavigation(val route: String) {
|
||||
}
|
||||
|
||||
data object SavingsMakeTransfer : SavingsNavigation(
|
||||
route = "$SAVINGS_MAKE_TRANSFER_SCREEN_ROUTE/{$ACCOUNT_ID}/{$OUTSTANDING_BALANCE}/{$TRANSFER_TYPE}"
|
||||
route = "$SAVINGS_MAKE_TRANSFER_SCREEN_ROUTE/{$ACCOUNT_ID}/{$OUTSTANDING_BALANCE}/{$TRANSFER_TYPE}",
|
||||
) {
|
||||
fun passArguments(accountId: Long, outstandingBalance: String? = null, transferType: String): String {
|
||||
fun passArguments(
|
||||
accountId: Long,
|
||||
outstandingBalance: String? = null,
|
||||
transferType: String,
|
||||
): String {
|
||||
return "$SAVINGS_MAKE_TRANSFER_SCREEN_ROUTE/$accountId/$outstandingBalance/$transferType"
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccount
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
@ -26,18 +35,20 @@ import org.mifos.mobile.feature.savings.R
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SavingAccountsDetailViewModel @Inject constructor(
|
||||
internal class SavingAccountsDetailViewModel @Inject constructor(
|
||||
private val savingsAccountRepositoryImp: SavingsAccountRepository,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
private var preferencesHelper: PreferencesHelper
|
||||
private var preferencesHelper: PreferencesHelper,
|
||||
) : ViewModel() {
|
||||
|
||||
val savingsId = savedStateHandle.getStateFlow<Long?>(key = Constants.SAVINGS_ID, initialValue = null)
|
||||
val savingsId =
|
||||
savedStateHandle.getStateFlow<Long?>(key = Constants.SAVINGS_ID, initialValue = null)
|
||||
|
||||
val savingAccountsDetailUiState = savingsId
|
||||
.flatMapLatest {
|
||||
savingsAccountRepositoryImp.getSavingsWithAssociations(
|
||||
savingsId.value, Constants.TRANSACTIONS,
|
||||
savingsId.value,
|
||||
Constants.TRANSACTIONS,
|
||||
)
|
||||
}
|
||||
.asResult()
|
||||
@ -50,7 +61,7 @@ class SavingAccountsDetailViewModel @Inject constructor(
|
||||
}.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5_000),
|
||||
initialValue = SavingsAccountDetailUiState.Loading
|
||||
initialValue = SavingsAccountDetailUiState.Loading,
|
||||
)
|
||||
|
||||
fun getQrString(savingsWithAssociations: SavingsWithAssociations?): String {
|
||||
@ -62,13 +73,13 @@ class SavingAccountsDetailViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SavingsAccountDetailUiState {
|
||||
internal sealed class SavingsAccountDetailUiState {
|
||||
data object Loading : SavingsAccountDetailUiState()
|
||||
data object Error : SavingsAccountDetailUiState()
|
||||
data class Success(val savingAccount: SavingsWithAssociations) : SavingsAccountDetailUiState()
|
||||
}
|
||||
|
||||
fun Status.getStatusColorAndText(): Pair<Color, Int> {
|
||||
internal fun Status.getStatusColorAndText(): Pair<Color, Int> {
|
||||
return when {
|
||||
this.active == true -> Pair(DepositGreen, R.string.active)
|
||||
this.approved == true -> Pair(Blue, R.string.need_approval)
|
||||
@ -77,5 +88,3 @@ fun Status.getStatusColorAndText(): Pair<Color, Int> {
|
||||
else -> Pair(Color.Black, R.string.closed)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccount
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@ -15,7 +24,6 @@ import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -27,47 +35,49 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.mifos.mobile.core.common.utils.CurrencyUtil
|
||||
import org.mifos.mobile.core.common.utils.DateHelper
|
||||
import org.mifos.mobile.core.common.utils.SymbolsUtils
|
||||
import org.mifos.mobile.core.designsystem.components.MifosOutlinedButton
|
||||
import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociations
|
||||
import org.mifos.mobile.core.model.entity.accounts.savings.Status
|
||||
import org.mifos.mobile.core.ui.component.MifosLinkText
|
||||
import org.mifos.mobile.core.ui.component.MifosTextTitleDescDoubleLine
|
||||
import org.mifos.mobile.core.ui.component.MonitorListItemWithIcon
|
||||
import org.mifos.mobile.core.common.utils.CurrencyUtil
|
||||
import org.mifos.mobile.core.common.utils.DateHelper
|
||||
import org.mifos.mobile.core.common.utils.SymbolsUtils
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountDetailContent(
|
||||
internal fun SavingsAccountDetailContent(
|
||||
savingsAccount: SavingsWithAssociations,
|
||||
deposit: () -> Unit,
|
||||
makeTransfer: () -> Unit,
|
||||
viewTransaction: () -> Unit,
|
||||
viewCharges: () -> Unit,
|
||||
viewQrCode: (SavingsWithAssociations) -> Unit,
|
||||
callUs: () -> Unit
|
||||
callUs: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
val currencySymbol = savingsAccount.currency?.displaySymbol ?: "$"
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.verticalScroll(scrollState)
|
||||
.padding(16.dp)
|
||||
.padding(16.dp),
|
||||
) {
|
||||
AccountDetailsCard(
|
||||
makeTransfer = makeTransfer,
|
||||
deposit = deposit,
|
||||
currencySymbol = currencySymbol,
|
||||
savingsAccount = savingsAccount,
|
||||
currencySymbol = currencySymbol
|
||||
deposit = deposit,
|
||||
makeTransfer = makeTransfer,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
LastTransactionCard(
|
||||
savingsWithAssociations = savingsAccount,
|
||||
currencySymbol = currencySymbol
|
||||
currencySymbol = currencySymbol,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
@ -75,7 +85,7 @@ fun SavingsAccountDetailContent(
|
||||
SavingsMonitorComponent(
|
||||
viewTransaction = viewTransaction,
|
||||
viewCharges = viewCharges,
|
||||
viewQrCode = { viewQrCode.invoke(savingsAccount) }
|
||||
viewQrCode = { viewQrCode.invoke(savingsAccount) },
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
@ -84,29 +94,33 @@ fun SavingsAccountDetailContent(
|
||||
Text(
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
text = stringResource(id = R.string.need_help)
|
||||
text = stringResource(id = R.string.need_help),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(5.dp))
|
||||
MifosLinkText(
|
||||
text = stringResource(id = R.string.help_line_number),
|
||||
onClick = { callUs.invoke() },
|
||||
isUnderlined = false
|
||||
onClick = callUs,
|
||||
isUnderlined = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AccountDetailsCard(
|
||||
modifier: Modifier = Modifier,
|
||||
private fun AccountDetailsCard(
|
||||
currencySymbol: String,
|
||||
savingsAccount: SavingsWithAssociations,
|
||||
deposit: () -> Unit,
|
||||
makeTransfer: () -> Unit,
|
||||
currencySymbol: String
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
OutlinedCard(modifier = modifier) {
|
||||
Column(modifier = Modifier.padding(14.dp)) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(14.dp),
|
||||
) {
|
||||
MifosTextTitleDescDoubleLine(
|
||||
title = stringResource(id = R.string.account_balance),
|
||||
description = stringResource(
|
||||
@ -114,17 +128,19 @@ fun AccountDetailsCard(
|
||||
currencySymbol,
|
||||
CurrencyUtil.formatCurrency(
|
||||
context = context,
|
||||
amt = savingsAccount.summary?.accountBalance ?: 0.0
|
||||
)
|
||||
amt = savingsAccount.summary?.accountBalance ?: 0.0,
|
||||
),
|
||||
),
|
||||
descriptionStyle = MaterialTheme.typography.bodyLarge.copy(
|
||||
fontWeight = FontWeight.Bold,
|
||||
),
|
||||
descriptionStyle = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
StatusField(
|
||||
title = stringResource(id = R.string.account_status),
|
||||
accountStatus = savingsAccount.status ?: Status()
|
||||
accountStatus = savingsAccount.status ?: Status(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@ -142,9 +158,9 @@ fun AccountDetailsCard(
|
||||
description = stringResource(
|
||||
id = R.string.double_and_string,
|
||||
savingsAccount.getNominalAnnualInterestRate(),
|
||||
SymbolsUtils.PERCENT
|
||||
SymbolsUtils.PERCENT,
|
||||
),
|
||||
descriptionStyle = MaterialTheme.typography.bodyLarge
|
||||
descriptionStyle = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@ -158,12 +174,12 @@ fun AccountDetailsCard(
|
||||
CurrencyUtil.formatCurrency(
|
||||
context = context,
|
||||
amt = savingsAccount.summary?.totalDeposits ?: 0.0,
|
||||
)
|
||||
),
|
||||
)
|
||||
} else {
|
||||
stringResource(id = R.string.not_available)
|
||||
},
|
||||
descriptionStyle = MaterialTheme.typography.bodyLarge
|
||||
descriptionStyle = MaterialTheme.typography.bodyLarge,
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@ -173,7 +189,8 @@ fun AccountDetailsCard(
|
||||
descriptionStyle = MaterialTheme.typography.bodyLarge,
|
||||
description = if (savingsAccount.summary?.totalDeposits != null) {
|
||||
stringResource(
|
||||
id = R.string.string_and_string, currencySymbol,
|
||||
id = R.string.string_and_string,
|
||||
currencySymbol,
|
||||
CurrencyUtil.formatCurrency(
|
||||
context = context,
|
||||
amt = savingsAccount.summary?.totalWithdrawals ?: 0.0,
|
||||
@ -181,35 +198,44 @@ fun AccountDetailsCard(
|
||||
)
|
||||
} else {
|
||||
stringResource(id = R.string.no_withdrawals)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
|
||||
OutlinedButton(onClick = {
|
||||
if(savingsAccount.status?.active == true) {
|
||||
deposit()
|
||||
}
|
||||
}) { Text(text = stringResource(id = R.string.deposit)) }
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
) {
|
||||
MifosOutlinedButton(
|
||||
textResId = R.string.deposit,
|
||||
onClick = {
|
||||
if (savingsAccount.status?.active == true) {
|
||||
deposit()
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
|
||||
OutlinedButton(onClick = {
|
||||
if(savingsAccount.status?.active == true) {
|
||||
makeTransfer()
|
||||
}
|
||||
}) { Text(text = stringResource(id = R.string.make_transfer)) }
|
||||
MifosOutlinedButton(
|
||||
textResId = R.string.make_transfer,
|
||||
onClick = {
|
||||
if (savingsAccount.status?.active == true) {
|
||||
makeTransfer()
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LastTransactionCard(
|
||||
private fun LastTransactionCard(
|
||||
savingsWithAssociations: SavingsWithAssociations,
|
||||
currencySymbol: String,
|
||||
modifier: Modifier = Modifier
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val isTransactionEmpty = savingsWithAssociations.transactions.isEmpty()
|
||||
@ -219,7 +245,7 @@ fun LastTransactionCard(
|
||||
text = stringResource(id = R.string.last_trans),
|
||||
style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
@ -235,9 +261,9 @@ fun LastTransactionCard(
|
||||
stringResource(
|
||||
id = R.string.string_and_double,
|
||||
currencySymbol,
|
||||
savingsWithAssociations.transactions[0].amount ?: 0.0
|
||||
savingsWithAssociations.transactions[0].amount ?: 0.0,
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
if (!isTransactionEmpty) {
|
||||
@ -247,7 +273,7 @@ fun LastTransactionCard(
|
||||
descriptionStyle = MaterialTheme.typography.bodyLarge,
|
||||
description = DateHelper.getDateAsString(
|
||||
savingsWithAssociations.lastActiveTransactionDate,
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@ -257,12 +283,13 @@ fun LastTransactionCard(
|
||||
title = stringResource(id = R.string.min_required_balance),
|
||||
descriptionStyle = MaterialTheme.typography.bodyLarge,
|
||||
description = stringResource(
|
||||
id = R.string.string_and_string, currencySymbol,
|
||||
id = R.string.string_and_string,
|
||||
currencySymbol,
|
||||
CurrencyUtil.formatCurrency(
|
||||
context = context,
|
||||
amt = savingsWithAssociations.minRequiredOpeningBalance ?: 0.0,
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -271,11 +298,11 @@ fun LastTransactionCard(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SavingsMonitorComponent(
|
||||
modifier: Modifier = Modifier,
|
||||
private fun SavingsMonitorComponent(
|
||||
viewTransaction: () -> Unit,
|
||||
viewCharges: () -> Unit,
|
||||
viewQrCode: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(modifier = modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
@ -291,37 +318,38 @@ fun SavingsMonitorComponent(
|
||||
titleId = R.string.transactions,
|
||||
subTitleId = R.string.view_transactions,
|
||||
iconId = R.drawable.ic_compare_arrows_black_24dp,
|
||||
onClick = viewTransaction
|
||||
onClick = viewTransaction,
|
||||
)
|
||||
MonitorListItemWithIcon(
|
||||
titleId = R.string.savings_charges,
|
||||
subTitleId = R.string.view_charges,
|
||||
iconId = R.drawable.ic_charges,
|
||||
onClick = viewCharges
|
||||
onClick = viewCharges,
|
||||
)
|
||||
MonitorListItemWithIcon(
|
||||
titleId = R.string.qr_code,
|
||||
subTitleId = R.string.view_qr_code,
|
||||
iconId = R.drawable.ic_qrcode_scan,
|
||||
onClick = viewQrCode
|
||||
onClick = viewQrCode,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun StatusField(
|
||||
private fun StatusField(
|
||||
title: String,
|
||||
accountStatus: Status
|
||||
accountStatus: Status,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val (color, textResId) = accountStatus.getStatusColorAndText()
|
||||
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(modifier = modifier.fillMaxWidth()) {
|
||||
Text(
|
||||
text = title,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
modifier = Modifier
|
||||
.alpha(0.7f)
|
||||
.fillMaxWidth()
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
@ -329,14 +357,14 @@ fun StatusField(
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.background(color = color)
|
||||
.size(15.dp)
|
||||
.size(15.dp),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(
|
||||
text = stringResource(id = textResId),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccount
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@ -6,7 +15,6 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import org.mifos.mobile.core.designsystem.components.MifosScaffold
|
||||
@ -15,11 +23,11 @@ import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociatio
|
||||
import org.mifos.mobile.core.ui.component.EmptyDataView
|
||||
import org.mifos.mobile.core.ui.component.MifosErrorComponent
|
||||
import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay
|
||||
import org.mifos.mobile.core.ui.utils.DevicePreviews
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountDetailScreen(
|
||||
viewModel: SavingAccountsDetailViewModel = hiltViewModel(),
|
||||
internal fun SavingsAccountDetailScreen(
|
||||
navigateBack: () -> Unit,
|
||||
updateSavingsAccount: (Long) -> Unit,
|
||||
withdrawSavingsAccount: (Long) -> Unit,
|
||||
@ -28,27 +36,30 @@ fun SavingsAccountDetailScreen(
|
||||
viewCharges: () -> Unit,
|
||||
viewQrCode: (String) -> Unit,
|
||||
callUs: () -> Unit,
|
||||
deposit: (Long) -> Unit
|
||||
deposit: (Long) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: SavingAccountsDetailViewModel = hiltViewModel(),
|
||||
) {
|
||||
val uiState by viewModel.savingAccountsDetailUiState.collectAsStateWithLifecycle()
|
||||
val savingsId = viewModel.savingsId.collectAsStateWithLifecycle().value ?: -1L
|
||||
|
||||
SavingsAccountDetailScreen(
|
||||
uiState = uiState,
|
||||
navigateBack = navigateBack,
|
||||
updateSavingsAccount = { updateSavingsAccount(viewModel.savingsId.value ?: -1L) },
|
||||
withdrawSavingsAccount = { withdrawSavingsAccount(viewModel.savingsId.value ?: -1L) },
|
||||
makeTransfer = { makeTransfer(viewModel.savingsId.value ?: -1L) },
|
||||
viewTransaction = { viewTransaction(viewModel.savingsId.value ?: -1L) },
|
||||
modifier = modifier,
|
||||
updateSavingsAccount = { updateSavingsAccount(savingsId) },
|
||||
withdrawSavingsAccount = { withdrawSavingsAccount(savingsId) },
|
||||
makeTransfer = { makeTransfer(savingsId) },
|
||||
viewTransaction = { viewTransaction(savingsId) },
|
||||
viewCharges = viewCharges,
|
||||
viewQrCode = { viewQrCode(viewModel.getQrString(it)) },
|
||||
callUs = callUs,
|
||||
deposit = { deposit(viewModel.savingsId.value ?: -1L) },
|
||||
deposit = { deposit(savingsId) },
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountDetailScreen(
|
||||
private fun SavingsAccountDetailScreen(
|
||||
uiState: SavingsAccountDetailUiState,
|
||||
navigateBack: () -> Unit,
|
||||
updateSavingsAccount: () -> Unit,
|
||||
@ -59,32 +70,30 @@ fun SavingsAccountDetailScreen(
|
||||
viewQrCode: (SavingsWithAssociations) -> Unit,
|
||||
callUs: () -> Unit,
|
||||
deposit: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
MifosScaffold(
|
||||
topBar = {
|
||||
SavingsAccountDetailTopBar(
|
||||
navigateBack = navigateBack,
|
||||
updateSavingsAccount = updateSavingsAccount,
|
||||
withdrawSavingsAccount = withdrawSavingsAccount
|
||||
withdrawSavingsAccount = withdrawSavingsAccount,
|
||||
)
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
) {
|
||||
Box(modifier = Modifier.padding(it)) {
|
||||
when (uiState) {
|
||||
is SavingsAccountDetailUiState.Error -> {
|
||||
MifosErrorComponent()
|
||||
}
|
||||
is SavingsAccountDetailUiState.Error -> MifosErrorComponent()
|
||||
|
||||
is SavingsAccountDetailUiState.Loading -> {
|
||||
MifosProgressIndicatorOverlay()
|
||||
}
|
||||
is SavingsAccountDetailUiState.Loading -> MifosProgressIndicatorOverlay()
|
||||
|
||||
is SavingsAccountDetailUiState.Success -> {
|
||||
if (uiState.savingAccount.status?.submittedAndPendingApproval == true) {
|
||||
EmptyDataView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
icon = R.drawable.ic_assignment_turned_in_black_24dp,
|
||||
error = R.string.approval_pending
|
||||
error = R.string.approval_pending,
|
||||
)
|
||||
} else {
|
||||
SavingsAccountDetailContent(
|
||||
@ -94,7 +103,7 @@ fun SavingsAccountDetailScreen(
|
||||
viewTransaction = viewTransaction,
|
||||
viewQrCode = viewQrCode,
|
||||
callUs = callUs,
|
||||
deposit = deposit
|
||||
deposit = deposit,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -103,13 +112,21 @@ fun SavingsAccountDetailScreen(
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showSystemUi = true)
|
||||
@DevicePreviews
|
||||
@Composable
|
||||
fun SavingsAccountDetailScreenPreview() {
|
||||
private fun SavingsAccountDetailScreenPreview() {
|
||||
MifosMobileTheme {
|
||||
SavingsAccountDetailScreen(
|
||||
uiState = SavingsAccountDetailUiState.Loading,
|
||||
{}, {}, {}, {}, {}, {}, {}, {}, {}
|
||||
navigateBack = {},
|
||||
updateSavingsAccount = {},
|
||||
withdrawSavingsAccount = {},
|
||||
makeTransfer = {},
|
||||
viewTransaction = {},
|
||||
viewCharges = {},
|
||||
viewQrCode = {},
|
||||
callUs = {},
|
||||
deposit = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,8 +1,14 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccount
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@ -19,70 +25,76 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import org.mifos.mobile.core.designsystem.icons.MifosIcons
|
||||
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
|
||||
import org.mifos.mobile.core.ui.utils.DevicePreviews
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SavingsAccountDetailTopBar(
|
||||
internal fun SavingsAccountDetailTopBar(
|
||||
navigateBack: () -> Unit,
|
||||
updateSavingsAccount: () -> Unit,
|
||||
withdrawSavingsAccount: () -> Unit
|
||||
withdrawSavingsAccount: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
|
||||
TopAppBar(
|
||||
modifier = Modifier,
|
||||
modifier = modifier,
|
||||
title = { Text(text = stringResource(id = R.string.saving_account_details)) },
|
||||
navigationIcon = {
|
||||
IconButton(
|
||||
onClick = { navigateBack.invoke() }
|
||||
onClick = { navigateBack.invoke() },
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.ArrowBack,
|
||||
imageVector = MifosIcons.ArrowBack,
|
||||
contentDescription = "Back Arrow",
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = MaterialTheme.colorScheme.background
|
||||
containerColor = MaterialTheme.colorScheme.background,
|
||||
),
|
||||
actions = {
|
||||
IconButton(onClick = { showMenu = !showMenu }) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.MoreVert,
|
||||
imageVector = MifosIcons.MoreVert,
|
||||
contentDescription = "Menu",
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
|
||||
DropdownMenu(
|
||||
expanded = showMenu,
|
||||
onDismissRequest = { showMenu = false }
|
||||
onDismissRequest = { showMenu = false },
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(text = stringResource(id = R.string.update_savings_account))
|
||||
},
|
||||
onClick = { updateSavingsAccount.invoke() }
|
||||
onClick = { updateSavingsAccount.invoke() },
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = {
|
||||
Text(text = stringResource(id = R.string.withdraw_savings_account))
|
||||
},
|
||||
onClick = { withdrawSavingsAccount.invoke() }
|
||||
onClick = { withdrawSavingsAccount.invoke() },
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@DevicePreviews
|
||||
@Composable
|
||||
fun SavingsAccountDetailTopBarPreview() {
|
||||
private fun SavingsAccountDetailTopBarPreview() {
|
||||
MifosMobileTheme {
|
||||
SavingsAccountDetailTopBar({}, {}, {})
|
||||
SavingsAccountDetailTopBar(
|
||||
navigateBack = {},
|
||||
updateSavingsAccount = {},
|
||||
withdrawSavingsAccount = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account_application
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccountApplication
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
@ -9,10 +18,6 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.ArrowDropDown
|
||||
import androidx.compose.material.icons.filled.ArrowDropUp
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
@ -35,24 +40,26 @@ import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.mifos.mobile.core.common.utils.getTodayFormatted
|
||||
import org.mifos.mobile.core.designsystem.components.MifosButton
|
||||
import org.mifos.mobile.core.designsystem.icons.MifosIcons
|
||||
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
|
||||
import org.mifos.mobile.core.model.entity.templates.savings.SavingsAccountTemplate
|
||||
import org.mifos.mobile.core.ui.utils.DevicePreviews
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountApplicationContent(
|
||||
internal fun SavingsAccountApplicationContent(
|
||||
submit: (Int, Int, showToast: (Int) -> Unit) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
existingProduct: String? = null,
|
||||
savingsAccountTemplate: SavingsAccountTemplate? = null,
|
||||
submit: (Int, Int, showToast: (Int) -> Unit) -> Unit
|
||||
) {
|
||||
var selectProductId by rememberSaveable { mutableIntStateOf(-1) }
|
||||
val context = LocalContext.current
|
||||
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
|
||||
Column(modifier = modifier.padding(16.dp)) {
|
||||
OutlinedCard(
|
||||
colors = CardDefaults.outlinedCardColors(
|
||||
containerColor = MaterialTheme.colorScheme.background,
|
||||
@ -61,12 +68,12 @@ fun SavingsAccountApplicationContent(
|
||||
Column(modifier = Modifier.padding(20.dp)) {
|
||||
TitleBodyRow(
|
||||
titleText = stringResource(R.string.client_name),
|
||||
bodyText = savingsAccountTemplate?.clientName ?: ""
|
||||
bodyText = savingsAccountTemplate?.clientName ?: "",
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
TitleBodyRow(
|
||||
titleText = stringResource(R.string.submission_date),
|
||||
bodyText = getTodayFormatted()
|
||||
bodyText = getTodayFormatted(),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -75,40 +82,41 @@ fun SavingsAccountApplicationContent(
|
||||
|
||||
SelectProductIdDropDown(
|
||||
existingProduct = existingProduct,
|
||||
selectProductId = { selectProductId = it },
|
||||
savingsAccountTemplate = savingsAccountTemplate,
|
||||
selectProductId = { selectProductId = it }
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
Button(
|
||||
MifosButton(
|
||||
textResId = R.string.submit,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = {
|
||||
submit(selectProductId, savingsAccountTemplate?.clientId ?: -1) {
|
||||
showToast(context, it)
|
||||
}
|
||||
},
|
||||
content = { Text(text = stringResource(id = R.string.submit)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showToast(context: Context, messageResId: Int){
|
||||
private fun showToast(context: Context, messageResId: Int) {
|
||||
Toast.makeText(context, context.getString(messageResId), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SelectProductIdDropDown(
|
||||
private fun SelectProductIdDropDown(
|
||||
existingProduct: String?,
|
||||
selectProductId: (Int) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
savingsAccountTemplate: SavingsAccountTemplate? = null,
|
||||
selectProductId: (Int) -> Unit
|
||||
) {
|
||||
var expanded by remember { mutableStateOf(false) }
|
||||
var selectedProduct by remember { mutableStateOf(existingProduct ?: "") }
|
||||
val productOptions = savingsAccountTemplate?.productOptions.orEmpty()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
modifier = modifier,
|
||||
) {
|
||||
OutlinedTextField(
|
||||
value = selectedProduct,
|
||||
@ -122,22 +130,25 @@ fun SelectProductIdDropDown(
|
||||
colors = TextFieldDefaults.colors(
|
||||
disabledTextColor = MaterialTheme.colorScheme.onSurface,
|
||||
disabledContainerColor = MaterialTheme.colorScheme.background,
|
||||
disabledLabelColor = MaterialTheme.colorScheme.onSurface
|
||||
disabledLabelColor = MaterialTheme.colorScheme.onSurface,
|
||||
),
|
||||
trailingIcon = {
|
||||
Icon(
|
||||
imageVector = if (expanded) Icons.Filled.ArrowDropUp
|
||||
else Icons.Filled.ArrowDropDown,
|
||||
imageVector = if (expanded) {
|
||||
MifosIcons.ArrowDropUp
|
||||
} else {
|
||||
MifosIcons.ArrowDropDown
|
||||
},
|
||||
contentDescription = "Dropdown",
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
DropdownMenu(
|
||||
expanded = expanded,
|
||||
onDismissRequest = { expanded = false },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 4.dp)
|
||||
.padding(horizontal = 4.dp),
|
||||
) {
|
||||
productOptions.forEach { product ->
|
||||
DropdownMenuItem(
|
||||
@ -146,7 +157,7 @@ fun SelectProductIdDropDown(
|
||||
selectedProduct = product.name ?: ""
|
||||
expanded = false
|
||||
},
|
||||
text = { Text(text = product.name ?: "") }
|
||||
text = { Text(text = product.name ?: "") },
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -154,30 +165,40 @@ fun SelectProductIdDropDown(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TitleBodyRow(titleText: String, bodyText: String, modifier: Modifier = Modifier) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
private fun TitleBodyRow(
|
||||
titleText: String,
|
||||
bodyText: String,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = titleText,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
modifier = Modifier
|
||||
.alpha(0.7f)
|
||||
.weight(2f)
|
||||
.weight(2f),
|
||||
)
|
||||
Text(
|
||||
text = bodyText,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
modifier = Modifier.weight(3f),
|
||||
textAlign = TextAlign.Center
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Preview(showSystemUi = true)
|
||||
@DevicePreviews
|
||||
@Composable
|
||||
fun SavingsAccountApplicationContentPreview() {
|
||||
private fun SavingsAccountApplicationContentPreview() {
|
||||
MifosMobileTheme {
|
||||
SavingsAccountApplicationContent(null, null, {i, j, k -> })
|
||||
SavingsAccountApplicationContent(
|
||||
submit = { _, _, _ -> },
|
||||
existingProduct = null,
|
||||
savingsAccountTemplate = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account_application
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccountApplication
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@ -12,7 +21,6 @@ import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import org.mifos.mobile.core.designsystem.components.MifosScaffold
|
||||
@ -22,29 +30,32 @@ import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociatio
|
||||
import org.mifos.mobile.core.model.enums.SavingsAccountState
|
||||
import org.mifos.mobile.core.ui.component.MifosErrorComponent
|
||||
import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay
|
||||
import org.mifos.mobile.core.ui.utils.DevicePreviews
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountApplicationScreen(
|
||||
viewModel: SavingsAccountApplicationViewModel = hiltViewModel(),
|
||||
internal fun SavingsAccountApplicationScreen(
|
||||
navigateBack: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: SavingsAccountApplicationViewModel = hiltViewModel(),
|
||||
) {
|
||||
val uiState by viewModel.savingsAccountApplicationUiState.collectAsStateWithLifecycle()
|
||||
|
||||
SavingsAccountApplicationScreen(
|
||||
uiState = uiState,
|
||||
navigateBack = navigateBack,
|
||||
submit = viewModel::onSubmit
|
||||
submit = viewModel::onSubmit,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountApplicationScreen(
|
||||
private fun SavingsAccountApplicationScreen(
|
||||
uiState: SavingsAccountApplicationUiState,
|
||||
savingsWithAssociations: SavingsWithAssociations? = null,
|
||||
navigateBack: () -> Unit,
|
||||
submit: (Int, Int, showToast: (Int) -> Unit) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
savingsWithAssociations: SavingsWithAssociations? = null,
|
||||
) {
|
||||
var topBarTitleText by rememberSaveable { mutableStateOf("") }
|
||||
val context = LocalContext.current
|
||||
@ -53,15 +64,14 @@ fun SavingsAccountApplicationScreen(
|
||||
topBar = {
|
||||
MifosTopBar(
|
||||
navigateBack = navigateBack,
|
||||
title = { Text(text = topBarTitleText) }
|
||||
title = { Text(text = topBarTitleText) },
|
||||
)
|
||||
},
|
||||
modifier = modifier,
|
||||
content = {
|
||||
Box(modifier = Modifier.padding(it)) {
|
||||
when (uiState) {
|
||||
is SavingsAccountApplicationUiState.Error -> {
|
||||
MifosErrorComponent()
|
||||
}
|
||||
is SavingsAccountApplicationUiState.Error -> MifosErrorComponent()
|
||||
|
||||
is SavingsAccountApplicationUiState.Loading -> {
|
||||
MifosProgressIndicatorOverlay()
|
||||
@ -75,9 +85,9 @@ fun SavingsAccountApplicationScreen(
|
||||
|
||||
topBarTitleText = stringResource(id = titleResourceId)
|
||||
SavingsAccountApplicationContent(
|
||||
submit = submit,
|
||||
existingProduct = existingProduct,
|
||||
savingsAccountTemplate = uiState.template,
|
||||
submit = submit
|
||||
)
|
||||
}
|
||||
|
||||
@ -86,21 +96,28 @@ fun SavingsAccountApplicationScreen(
|
||||
SavingsAccountState.CREATE -> R.string.new_saving_account_created_successfully
|
||||
else -> R.string.saving_account_updated_successfully
|
||||
}
|
||||
Toast.makeText(context, stringResource(id = messageResourceId), Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
context,
|
||||
stringResource(id = messageResourceId),
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
navigateBack.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showSystemUi = true)
|
||||
@DevicePreviews
|
||||
@Composable
|
||||
fun SavingsAccountApplicationScreenPreview() {
|
||||
private fun SavingsAccountApplicationScreenPreview() {
|
||||
MifosMobileTheme {
|
||||
SavingsAccountApplicationScreen(SavingsAccountApplicationUiState.Success(requestType = SavingsAccountState.UPDATE), null, {}, { i, j, k -> })
|
||||
SavingsAccountApplicationScreen(
|
||||
SavingsAccountApplicationUiState.Success(requestType = SavingsAccountState.UPDATE),
|
||||
navigateBack = {},
|
||||
submit = { _, _, _ -> },
|
||||
savingsWithAssociations = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account_application
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccountApplication
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
@ -7,6 +16,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@ -22,44 +32,46 @@ import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociatio
|
||||
import org.mifos.mobile.core.model.entity.templates.savings.SavingsAccountTemplate
|
||||
import org.mifos.mobile.core.model.enums.SavingsAccountState
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
import org.mifos.mobile.feature.savings.savingsAccountApplication.SavingsAccountApplicationUiState.Loading
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SavingsAccountApplicationViewModel @Inject constructor(
|
||||
internal class SavingsAccountApplicationViewModel @Inject constructor(
|
||||
private val savingsAccountRepositoryImp: SavingsAccountRepository,
|
||||
private val preferencesHelper: PreferencesHelper,
|
||||
savedStateHandle: SavedStateHandle
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
|
||||
val savingsAccountApplicationUiState: StateFlow<SavingsAccountApplicationUiState> get() = _savingsAccountApplicationUiState
|
||||
private val _savingsAccountApplicationUiState =
|
||||
MutableStateFlow<SavingsAccountApplicationUiState>(SavingsAccountApplicationUiState.Loading)
|
||||
|
||||
|
||||
private val clientId get() = preferencesHelper.clientId
|
||||
|
||||
val savingsId = savedStateHandle.getStateFlow(key = Constants.SAVINGS_ID, initialValue = -1L)
|
||||
val savingsAccountState = savedStateHandle.getStateFlow(
|
||||
private val savingsId =
|
||||
savedStateHandle.getStateFlow(key = Constants.SAVINGS_ID, initialValue = -1L)
|
||||
private val savingsAccountState = savedStateHandle.getStateFlow(
|
||||
key = Constants.SAVINGS_ACCOUNT_STATE,
|
||||
initialValue = SavingsAccountState.CREATE
|
||||
initialValue = SavingsAccountState.CREATE,
|
||||
)
|
||||
|
||||
var savingsWithAssociations: StateFlow<SavingsWithAssociations?> = savingsId
|
||||
private val _savingsAccountApplicationUiState =
|
||||
MutableStateFlow<SavingsAccountApplicationUiState>(Loading)
|
||||
val savingsAccountApplicationUiState = _savingsAccountApplicationUiState.asStateFlow()
|
||||
|
||||
private val savingsWithAssociations: StateFlow<SavingsWithAssociations?> = savingsId
|
||||
.flatMapLatest {
|
||||
savingsAccountRepositoryImp.getSavingsWithAssociations(
|
||||
savingsId.value, Constants.TRANSACTIONS,
|
||||
savingsId.value,
|
||||
Constants.TRANSACTIONS,
|
||||
)
|
||||
}
|
||||
.also { loadSavingsAccountApplicationTemplate() }
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5000),
|
||||
initialValue = null
|
||||
initialValue = null,
|
||||
)
|
||||
|
||||
fun loadSavingsAccountApplicationTemplate() {
|
||||
private fun loadSavingsAccountApplicationTemplate() {
|
||||
viewModelScope.launch {
|
||||
_savingsAccountApplicationUiState.value = SavingsAccountApplicationUiState.Loading
|
||||
_savingsAccountApplicationUiState.value = Loading
|
||||
savingsAccountRepositoryImp.getSavingAccountApplicationTemplate(clientId).catch { e ->
|
||||
_savingsAccountApplicationUiState.value =
|
||||
SavingsAccountApplicationUiState.Error(e.message)
|
||||
@ -67,15 +79,15 @@ class SavingsAccountApplicationViewModel @Inject constructor(
|
||||
_savingsAccountApplicationUiState.value =
|
||||
SavingsAccountApplicationUiState.ShowUserInterface(
|
||||
it,
|
||||
savingsAccountState.value
|
||||
savingsAccountState.value,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun submitSavingsAccountApplication(payload: SavingsAccountApplicationPayload?) {
|
||||
private fun submitSavingsAccountApplication(payload: SavingsAccountApplicationPayload?) {
|
||||
viewModelScope.launch {
|
||||
_savingsAccountApplicationUiState.value = SavingsAccountApplicationUiState.Loading
|
||||
_savingsAccountApplicationUiState.value = Loading
|
||||
savingsAccountRepositoryImp.submitSavingAccountApplication(payload).catch { e ->
|
||||
_savingsAccountApplicationUiState.value =
|
||||
SavingsAccountApplicationUiState.Error(e.message)
|
||||
@ -86,9 +98,9 @@ class SavingsAccountApplicationViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun updateSavingsAccount(accountId: Long?, payload: SavingsAccountUpdatePayload?) {
|
||||
private fun updateSavingsAccount(accountId: Long?, payload: SavingsAccountUpdatePayload?) {
|
||||
viewModelScope.launch {
|
||||
_savingsAccountApplicationUiState.value = SavingsAccountApplicationUiState.Loading
|
||||
_savingsAccountApplicationUiState.value = Loading
|
||||
savingsAccountRepositoryImp.updateSavingsAccount(accountId, payload).catch { e ->
|
||||
_savingsAccountApplicationUiState.value =
|
||||
SavingsAccountApplicationUiState.Error(e.message)
|
||||
@ -133,14 +145,13 @@ class SavingsAccountApplicationViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SavingsAccountApplicationUiState {
|
||||
internal sealed class SavingsAccountApplicationUiState {
|
||||
data object Loading : SavingsAccountApplicationUiState()
|
||||
data class Error(val errorMessage: String?) : SavingsAccountApplicationUiState()
|
||||
data class Success(val requestType: SavingsAccountState) : SavingsAccountApplicationUiState()
|
||||
data class ShowUserInterface(
|
||||
val template: SavingsAccountTemplate,
|
||||
val requestType: SavingsAccountState
|
||||
val requestType: SavingsAccountState,
|
||||
) :
|
||||
SavingsAccountApplicationUiState()
|
||||
}
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account_transaction
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccountTransaction
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@ -32,12 +41,13 @@ import org.mifos.mobile.core.model.entity.accounts.savings.Transactions
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountTransactionContent(
|
||||
internal fun SavingsAccountTransactionContent(
|
||||
transactionList: List<Transactions>,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
modifier = modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
LazyColumn {
|
||||
items(items = transactionList) {
|
||||
@ -45,7 +55,7 @@ fun SavingsAccountTransactionContent(
|
||||
HorizontalDivider(
|
||||
thickness = 1.dp,
|
||||
color = Color.Gray,
|
||||
modifier = Modifier.padding(vertical = 4.dp)
|
||||
modifier = Modifier.padding(vertical = 4.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -54,42 +64,44 @@ fun SavingsAccountTransactionContent(
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 8.dp, horizontal = 10.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.Bottom
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.need_help),
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Spacer(modifier = Modifier.width(2.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.help_line_number),
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountTransactionListItem(transaction: Transactions) {
|
||||
private fun SavingsAccountTransactionListItem(
|
||||
transaction: Transactions,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 6.dp)
|
||||
.padding(vertical = 6.dp),
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(
|
||||
id = getTransactionTriangleResId(transaction.transactionType)
|
||||
id = getTransactionTriangleResId(transaction.transactionType),
|
||||
),
|
||||
contentDescription = stringResource(id = R.string.savings_account_transaction),
|
||||
modifier = Modifier
|
||||
.size(56.dp)
|
||||
.padding(4.dp)
|
||||
.padding(4.dp),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(4.dp)
|
||||
modifier = Modifier.padding(4.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@ -98,47 +110,47 @@ fun SavingsAccountTransactionListItem(transaction: Transactions) {
|
||||
Text(
|
||||
text = DateHelper.getDateAsString(transaction.date),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.string_and_string,
|
||||
transaction.currency?.displaySymbol ?: transaction.currency?.code ?: "",
|
||||
CurrencyUtil.formatCurrency(context, transaction.amount,)
|
||||
CurrencyUtil.formatCurrency(context, transaction.amount),
|
||||
),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
text = transaction.transactionType?.value ?: "",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
modifier = Modifier.alpha(0.7f),
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(
|
||||
id = R.string.string_and_string,
|
||||
transaction.currency?.displaySymbol ?: transaction.currency?.code ?: "",
|
||||
CurrencyUtil.formatCurrency(context, transaction.runningBalance)
|
||||
CurrencyUtil.formatCurrency(context, transaction.runningBalance),
|
||||
),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
modifier = Modifier.alpha(0.7f),
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Text(
|
||||
text = transaction.paymentDetailData?.paymentType?.name.toString(),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
modifier = Modifier.alpha(0.7f),
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -147,8 +159,8 @@ fun SavingsAccountTransactionListItem(transaction: Transactions) {
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun SavingsAccountTransactionContentPreview() {
|
||||
private fun SavingsAccountTransactionContentPreview() {
|
||||
MifosMobileTheme {
|
||||
SavingsAccountTransactionContent(transactionList = listOf())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account_transaction
|
||||
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccountTransaction
|
||||
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.scrollable
|
||||
@ -10,13 +18,13 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.BasicAlertDialog
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CheckboxDefaults
|
||||
import androidx.compose.material3.DatePicker
|
||||
import androidx.compose.material3.DatePickerDialog
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.rememberDatePickerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@ -26,26 +34,27 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import org.mifos.mobile.core.common.utils.DateHelper
|
||||
import org.mifos.mobile.core.common.utils.DateHelper.getDateAsStringFromLong
|
||||
import org.mifos.mobile.core.designsystem.components.MifosIconTextButton
|
||||
import org.mifos.mobile.core.designsystem.components.MifosRadioButton
|
||||
import org.mifos.mobile.core.designsystem.components.MifosTextButton
|
||||
import org.mifos.mobile.core.designsystem.icons.MifosIcons
|
||||
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
|
||||
import org.mifos.mobile.core.ui.component.MifosCheckBox
|
||||
import org.mifos.mobile.core.ui.utils.DevicePreviews
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
import java.time.Instant
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SavingsTransactionFilterDialog(
|
||||
internal fun SavingsTransactionFilterDialog(
|
||||
onDismiss: () -> Unit,
|
||||
savingsTransactionFilterDataModel: SavingsTransactionFilterDataModel,
|
||||
filter: (SavingsTransactionFilterDataModel) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
var radioFilter by rememberSaveable { mutableStateOf(savingsTransactionFilterDataModel.radioFilter) }
|
||||
val checkBoxFilters by rememberSaveable { mutableStateOf(savingsTransactionFilterDataModel.checkBoxFilters) }
|
||||
var startDate by rememberSaveable { mutableStateOf(savingsTransactionFilterDataModel.startDate) }
|
||||
@ -58,7 +67,7 @@ fun SavingsTransactionFilterDialog(
|
||||
|
||||
LaunchedEffect(key1 = checkBoxFilters) {
|
||||
checkBoxFilters.forEach { filter ->
|
||||
when(filter) {
|
||||
when (filter) {
|
||||
SavingsTransactionCheckBoxFilter.DEPOSIT -> isDepositChecked = true
|
||||
SavingsTransactionCheckBoxFilter.DIVIDEND_PAYOUT -> isDividendPayoutChecked = true
|
||||
SavingsTransactionCheckBoxFilter.WITHDRAWAL -> isWithdrawalChecked = true
|
||||
@ -67,12 +76,13 @@ fun SavingsTransactionFilterDialog(
|
||||
}
|
||||
}
|
||||
|
||||
Dialog(
|
||||
onDismissRequest = { onDismiss.invoke() },
|
||||
BasicAlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
modifier = modifier,
|
||||
) {
|
||||
Card(shape = RoundedCornerShape(20.dp)) {
|
||||
Column(
|
||||
modifier = Modifier.padding(vertical = 20.dp, horizontal = 10.dp)
|
||||
modifier = Modifier.padding(vertical = 20.dp, horizontal = 10.dp),
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.select_you_want))
|
||||
|
||||
@ -82,29 +92,41 @@ fun SavingsTransactionFilterDialog(
|
||||
selectedStartDate = startDate,
|
||||
selectedEndDate = endDate,
|
||||
radioFilter = radioFilter,
|
||||
selectRadioFilter = { radioFilter = it },
|
||||
setStartDate = { startDate = it },
|
||||
isDepositChecked = isDepositChecked,
|
||||
isDividendPayoutChecked = isDividendPayoutChecked,
|
||||
isWithdrawalChecked = isWithdrawalChecked,
|
||||
isInterestPostingChecked = isInterestPostingChecked,
|
||||
isDividendPayoutChecked = isDividendPayoutChecked,
|
||||
setEndDate = { endDate = it },
|
||||
selectRadioFilter = { radioFilter = it },
|
||||
toggleCheckBox = { filter, isEnabled ->
|
||||
when(filter) {
|
||||
when (filter) {
|
||||
SavingsTransactionCheckBoxFilter.DEPOSIT -> isDepositChecked = isEnabled
|
||||
SavingsTransactionCheckBoxFilter.DIVIDEND_PAYOUT -> isDividendPayoutChecked = isEnabled
|
||||
SavingsTransactionCheckBoxFilter.WITHDRAWAL -> isWithdrawalChecked = isEnabled
|
||||
SavingsTransactionCheckBoxFilter.INTEREST_POSTING -> isInterestPostingChecked = isEnabled
|
||||
SavingsTransactionCheckBoxFilter.DIVIDEND_PAYOUT ->
|
||||
isDividendPayoutChecked =
|
||||
isEnabled
|
||||
|
||||
SavingsTransactionCheckBoxFilter.WITHDRAWAL ->
|
||||
isWithdrawalChecked =
|
||||
isEnabled
|
||||
|
||||
SavingsTransactionCheckBoxFilter.INTEREST_POSTING ->
|
||||
isInterestPostingChecked =
|
||||
isEnabled
|
||||
}
|
||||
if(isEnabled) checkBoxFilters.add(filter)
|
||||
else checkBoxFilters.remove(filter)
|
||||
}
|
||||
if (isEnabled) {
|
||||
checkBoxFilters.add(filter)
|
||||
} else {
|
||||
checkBoxFilters.remove(filter)
|
||||
}
|
||||
},
|
||||
setStartDate = { startDate = it },
|
||||
setEndDate = { endDate = it },
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(20.dp))
|
||||
|
||||
Row {
|
||||
TextButton(
|
||||
MifosTextButton(
|
||||
text = stringResource(R.string.clear_filters),
|
||||
onClick = {
|
||||
radioFilter = null
|
||||
isDepositChecked = false
|
||||
@ -112,20 +134,18 @@ fun SavingsTransactionFilterDialog(
|
||||
isInterestPostingChecked = false
|
||||
isDividendPayoutChecked = false
|
||||
checkBoxFilters.clear()
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.clear_filters))
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
TextButton(
|
||||
onClick = { onDismiss() }
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.cancel))
|
||||
}
|
||||
MifosTextButton(
|
||||
onClick = onDismiss,
|
||||
text = stringResource(id = R.string.cancel),
|
||||
)
|
||||
|
||||
TextButton(
|
||||
MifosTextButton(
|
||||
text = stringResource(id = R.string.filter),
|
||||
onClick = {
|
||||
onDismiss()
|
||||
filter(
|
||||
@ -133,13 +153,11 @@ fun SavingsTransactionFilterDialog(
|
||||
startDate = startDate,
|
||||
endDate = endDate,
|
||||
radioFilter = radioFilter,
|
||||
checkBoxFilters = checkBoxFilters
|
||||
)
|
||||
checkBoxFilters = checkBoxFilters,
|
||||
),
|
||||
)
|
||||
}
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.filter))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -148,23 +166,26 @@ fun SavingsTransactionFilterDialog(
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun SavingsTransactionFilterDialogContent(
|
||||
private fun SavingsTransactionFilterDialogContent(
|
||||
selectedStartDate: Long,
|
||||
selectedEndDate: Long,
|
||||
radioFilter: SavingsTransactionRadioFilter?,
|
||||
selectRadioFilter: (SavingsTransactionRadioFilter) -> Unit,
|
||||
isDepositChecked: Boolean,
|
||||
isDividendPayoutChecked: Boolean,
|
||||
isWithdrawalChecked: Boolean,
|
||||
isInterestPostingChecked: Boolean,
|
||||
selectRadioFilter: (SavingsTransactionRadioFilter) -> Unit,
|
||||
toggleCheckBox: (SavingsTransactionCheckBoxFilter, Boolean) -> Unit,
|
||||
setStartDate: (Long) -> Unit,
|
||||
setEndDate: (Long) -> Unit
|
||||
setEndDate: (Long) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
var showStartDatePickerDialog by rememberSaveable { mutableStateOf(false) }
|
||||
var showEndDatePickerDialog by rememberSaveable { mutableStateOf(false) }
|
||||
val startDatePickerState = rememberDatePickerState(initialSelectedDateMillis = selectedStartDate)
|
||||
val startDatePickerState =
|
||||
rememberDatePickerState(initialSelectedDateMillis = selectedStartDate)
|
||||
val endDatePickerState = rememberDatePickerState(initialSelectedDateMillis = selectedEndDate)
|
||||
var isDatesEnabled by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
@ -192,13 +213,14 @@ fun SavingsTransactionFilterDialogContent(
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.scrollable(state = scrollState, orientation = Orientation.Vertical)
|
||||
modifier = modifier
|
||||
.scrollable(state = scrollState, orientation = Orientation.Vertical),
|
||||
) {
|
||||
SavingsTransactionRadioFilter.entries.forEach { filter ->
|
||||
MifosRadioButton(
|
||||
selected = radioFilter == filter,
|
||||
onClick = { selectRadioFilter(filter) },
|
||||
textResId = filter.textResId
|
||||
textResId = filter.textResId,
|
||||
)
|
||||
|
||||
if (filter == SavingsTransactionRadioFilter.DATE) {
|
||||
@ -207,13 +229,13 @@ fun SavingsTransactionFilterDialogContent(
|
||||
text = getDateAsStringFromLong(selectedStartDate),
|
||||
imageVector = MifosIcons.Edit,
|
||||
enabled = radioFilter == SavingsTransactionRadioFilter.DATE,
|
||||
onClick = { showStartDatePickerDialog = true }
|
||||
onClick = { showStartDatePickerDialog = true },
|
||||
)
|
||||
MifosIconTextButton(
|
||||
text = getDateAsStringFromLong(selectedEndDate),
|
||||
imageVector = MifosIcons.Edit,
|
||||
enabled = radioFilter == SavingsTransactionRadioFilter.DATE,
|
||||
onClick = { showEndDatePickerDialog = true }
|
||||
onClick = { showEndDatePickerDialog = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -221,7 +243,7 @@ fun SavingsTransactionFilterDialogContent(
|
||||
|
||||
SavingsTransactionCheckBoxFilter.entries.forEach { filter ->
|
||||
MifosCheckBox(
|
||||
checked = when(filter) {
|
||||
checked = when (filter) {
|
||||
SavingsTransactionCheckBoxFilter.DEPOSIT -> isDepositChecked
|
||||
SavingsTransactionCheckBoxFilter.DIVIDEND_PAYOUT -> isDividendPayoutChecked
|
||||
SavingsTransactionCheckBoxFilter.WITHDRAWAL -> isWithdrawalChecked
|
||||
@ -235,7 +257,7 @@ fun SavingsTransactionFilterDialogContent(
|
||||
checkedBorderColor = filter.checkBoxColor,
|
||||
uncheckedBorderColor = filter.checkBoxColor,
|
||||
checkedBoxColor = filter.checkBoxColor,
|
||||
)
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -244,8 +266,8 @@ fun SavingsTransactionFilterDialogContent(
|
||||
DatePickerDialog(
|
||||
onDismissRequest = { showStartDatePickerDialog = false },
|
||||
confirmButton = {
|
||||
startDatePickerState.selectedDateMillis?.let{ setStartDate(it) }
|
||||
}
|
||||
startDatePickerState.selectedDateMillis?.let { setStartDate(it) }
|
||||
},
|
||||
) { DatePicker(state = startDatePickerState) }
|
||||
}
|
||||
|
||||
@ -254,25 +276,24 @@ fun SavingsTransactionFilterDialogContent(
|
||||
onDismissRequest = { showEndDatePickerDialog = false },
|
||||
confirmButton = {
|
||||
endDatePickerState.selectedDateMillis?.let { setEndDate(it) }
|
||||
}
|
||||
},
|
||||
) { DatePicker(state = endDatePickerState) }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Preview
|
||||
@DevicePreviews
|
||||
@Composable
|
||||
fun SavingsTransactionFilterDialogPreview() {
|
||||
private fun SavingsTransactionFilterDialogPreview() {
|
||||
MifosMobileTheme {
|
||||
SavingsTransactionFilterDialog(
|
||||
savingsTransactionFilterDataModel = SavingsTransactionFilterDataModel(
|
||||
radioFilter = null,
|
||||
checkBoxFilters = mutableListOf(),
|
||||
startDate = Instant.now().toEpochMilli(),
|
||||
endDate = Instant.now().toEpochMilli()
|
||||
endDate = Instant.now().toEpochMilli(),
|
||||
),
|
||||
filter = {},
|
||||
onDismiss = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account_transaction
|
||||
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccountTransaction
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@ -8,7 +16,6 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
@ -17,7 +24,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
@ -31,35 +37,34 @@ import org.mifos.mobile.core.model.entity.accounts.savings.Transactions
|
||||
import org.mifos.mobile.core.ui.component.EmptyDataView
|
||||
import org.mifos.mobile.core.ui.component.MifosErrorComponent
|
||||
import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay
|
||||
import org.mifos.mobile.core.ui.utils.DevicePreviews
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
import java.time.Instant
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountTransactionScreen(
|
||||
viewModel: SavingAccountsTransactionViewModel = hiltViewModel(),
|
||||
internal fun SavingsAccountTransactionScreen(
|
||||
navigateBack: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: SavingAccountsTransactionViewModel = hiltViewModel(),
|
||||
) {
|
||||
val uiState by viewModel.savingAccountsTransactionUiState.collectAsStateWithLifecycle()
|
||||
val savingsId by viewModel.savingsId.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(key1 = savingsId) {
|
||||
viewModel.loadSavingsWithAssociations(accountId = savingsId)
|
||||
}
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
SavingsAccountTransactionScreen(
|
||||
uiState = uiState,
|
||||
navigateBack = navigateBack,
|
||||
retryConnection = { viewModel.loadSavingsWithAssociations(savingsId) },
|
||||
filterList = { viewModel.filterList(filter = it) }
|
||||
retryConnection = viewModel::loadSavingsWithAssociations,
|
||||
filterList = viewModel::filterList,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountTransactionScreen(
|
||||
internal fun SavingsAccountTransactionScreen(
|
||||
uiState: SavingsAccountTransactionUiState,
|
||||
navigateBack: () -> Unit,
|
||||
retryConnection: () -> Unit,
|
||||
filterList: (SavingsTransactionFilterDataModel) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var transactionList by rememberSaveable { mutableStateOf(listOf<Transactions>()) }
|
||||
@ -70,12 +75,13 @@ fun SavingsAccountTransactionScreen(
|
||||
startDate = Instant.now().toEpochMilli(),
|
||||
endDate = Instant.now().toEpochMilli(),
|
||||
radioFilter = null,
|
||||
checkBoxFilters = mutableListOf()
|
||||
)
|
||||
checkBoxFilters = mutableListOf(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
MifosScaffold(
|
||||
modifier = modifier,
|
||||
topBar = {
|
||||
MifosTopBar(
|
||||
navigateBack = navigateBack,
|
||||
@ -83,7 +89,7 @@ fun SavingsAccountTransactionScreen(
|
||||
Text(
|
||||
text = stringResource(id = R.string.savings_account_transaction),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1
|
||||
maxLines = 1,
|
||||
)
|
||||
},
|
||||
actions = {
|
||||
@ -91,10 +97,10 @@ fun SavingsAccountTransactionScreen(
|
||||
Icon(
|
||||
imageVector = MifosIcons.FilterList,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSurface
|
||||
tint = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
content = { paddingValues ->
|
||||
@ -109,7 +115,7 @@ fun SavingsAccountTransactionScreen(
|
||||
isNetworkConnected = Network.isConnected(context),
|
||||
isEmptyData = false,
|
||||
isRetryEnabled = true,
|
||||
onRetry = retryConnection
|
||||
onRetry = retryConnection,
|
||||
)
|
||||
}
|
||||
|
||||
@ -117,7 +123,7 @@ fun SavingsAccountTransactionScreen(
|
||||
if (uiState.savingAccountsTransactionList.isNullOrEmpty()) {
|
||||
EmptyDataView(
|
||||
icon = R.drawable.ic_compare_arrows_black_24dp,
|
||||
error = R.string.no_transaction_found
|
||||
error = R.string.no_transaction_found,
|
||||
)
|
||||
} else {
|
||||
transactionList = uiState.savingAccountsTransactionList
|
||||
@ -126,7 +132,7 @@ fun SavingsAccountTransactionScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
if (isDialogOpen) {
|
||||
@ -141,7 +147,7 @@ fun SavingsAccountTransactionScreen(
|
||||
}
|
||||
}
|
||||
|
||||
class SavingsAccountTransactionUiStatesParameterProvider :
|
||||
internal class SavingsAccountTransactionUiStatesParameterProvider :
|
||||
PreviewParameterProvider<SavingsAccountTransactionUiState> {
|
||||
override val values: Sequence<SavingsAccountTransactionUiState>
|
||||
get() = sequenceOf(
|
||||
@ -151,17 +157,18 @@ class SavingsAccountTransactionUiStatesParameterProvider :
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showSystemUi = true)
|
||||
@DevicePreviews
|
||||
@Composable
|
||||
fun SavingsAccountTransactionScreenPreview(
|
||||
@PreviewParameter(SavingsAccountTransactionUiStatesParameterProvider::class) savingsAccountUiState: SavingsAccountTransactionUiState
|
||||
private fun SavingsAccountTransactionScreenPreview(
|
||||
@PreviewParameter(SavingsAccountTransactionUiStatesParameterProvider::class)
|
||||
savingsAccountUiState: SavingsAccountTransactionUiState,
|
||||
) {
|
||||
MifosMobileTheme {
|
||||
SavingsAccountTransactionScreen(
|
||||
uiState = savingsAccountUiState,
|
||||
navigateBack = { },
|
||||
retryConnection = { },
|
||||
filterList = { }
|
||||
filterList = { },
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account_transaction
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccountTransaction
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@ -8,6 +17,7 @@ import androidx.lifecycle.viewModelScope
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.parcelize.Parcelize
|
||||
@ -20,40 +30,42 @@ import org.mifos.mobile.core.designsystem.theme.RedLight
|
||||
import org.mifos.mobile.core.model.entity.accounts.savings.TransactionType
|
||||
import org.mifos.mobile.core.model.entity.accounts.savings.Transactions
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
import org.mifos.mobile.feature.savings.savingsAccountTransaction.SavingsAccountTransactionUiState.Loading
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SavingAccountsTransactionViewModel @Inject constructor(
|
||||
internal class SavingAccountsTransactionViewModel @Inject constructor(
|
||||
private val savingsAccountRepositoryImp: SavingsAccountRepository,
|
||||
savedStateHandle: SavedStateHandle
|
||||
) :
|
||||
ViewModel() {
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _savingAccountsTransactionUiState = MutableStateFlow<SavingsAccountTransactionUiState>(
|
||||
SavingsAccountTransactionUiState.Loading
|
||||
)
|
||||
val savingAccountsTransactionUiState: StateFlow<SavingsAccountTransactionUiState> get() = _savingAccountsTransactionUiState
|
||||
private val mUiState = MutableStateFlow<SavingsAccountTransactionUiState>(Loading)
|
||||
val uiState = mUiState.asStateFlow()
|
||||
|
||||
private var _transactionsList: List<Transactions> = mutableListOf()
|
||||
private val transactionsList: List<Transactions> get() = _transactionsList
|
||||
|
||||
val savingsId: StateFlow<Long> = savedStateHandle.getStateFlow<Long>(
|
||||
private val savingsId: StateFlow<Long> = savedStateHandle.getStateFlow(
|
||||
key = SAVINGS_ID,
|
||||
initialValue = -1L
|
||||
initialValue = -1L,
|
||||
)
|
||||
|
||||
fun loadSavingsWithAssociations(accountId: Long) {
|
||||
init {
|
||||
loadSavingsWithAssociations()
|
||||
}
|
||||
|
||||
fun loadSavingsWithAssociations(accountId: Long = savingsId.value) {
|
||||
viewModelScope.launch {
|
||||
_savingAccountsTransactionUiState.value = SavingsAccountTransactionUiState.Loading
|
||||
mUiState.value = Loading
|
||||
savingsAccountRepositoryImp.getSavingsWithAssociations(
|
||||
accountId,
|
||||
org.mifos.mobile.core.common.Constants.TRANSACTIONS,
|
||||
).catch {
|
||||
_savingAccountsTransactionUiState.value =
|
||||
mUiState.value =
|
||||
SavingsAccountTransactionUiState.Error(it.message)
|
||||
}.collect {
|
||||
_transactionsList = it.transactions
|
||||
_savingAccountsTransactionUiState.value =
|
||||
mUiState.value =
|
||||
SavingsAccountTransactionUiState.Success(it.transactions)
|
||||
}
|
||||
}
|
||||
@ -65,55 +77,66 @@ class SavingAccountsTransactionViewModel @Inject constructor(
|
||||
filterByDateAndType(
|
||||
startDate = filter.startDate,
|
||||
endDate = filter.endDate,
|
||||
checkBoxFilters = filter.checkBoxFilters
|
||||
checkBoxFilters = filter.checkBoxFilters,
|
||||
)
|
||||
}
|
||||
|
||||
filter.radioFilter != null -> {
|
||||
filterByDate(
|
||||
startDate = filter.startDate,
|
||||
endDate = filter.endDate
|
||||
endDate = filter.endDate,
|
||||
)
|
||||
}
|
||||
|
||||
filter.checkBoxFilters.isNotEmpty() -> {
|
||||
filterByType(
|
||||
checkBoxFilters = filter.checkBoxFilters
|
||||
checkBoxFilters = filter.checkBoxFilters,
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
_savingAccountsTransactionUiState.value =
|
||||
mUiState.value =
|
||||
SavingsAccountTransactionUiState.Success(transactionsList)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun filterByDateAndType(
|
||||
startDate: Long,
|
||||
endDate: Long,
|
||||
checkBoxFilters: MutableList<SavingsTransactionCheckBoxFilter>
|
||||
checkBoxFilters: MutableList<SavingsTransactionCheckBoxFilter>,
|
||||
) {
|
||||
val typeFilteredList = filterSavingsAccountTransactionsByType(checkBoxFilters = checkBoxFilters)
|
||||
val dateAndTypeFilteredList = filterTransactionListByDate(transactions = typeFilteredList, startDate = startDate, endDate = endDate)
|
||||
_savingAccountsTransactionUiState.value =
|
||||
val typeFilteredList =
|
||||
filterSavingsAccountTransactionsByType(checkBoxFilters = checkBoxFilters)
|
||||
val dateAndTypeFilteredList = filterTransactionListByDate(
|
||||
transactions = typeFilteredList,
|
||||
startDate = startDate,
|
||||
endDate = endDate,
|
||||
)
|
||||
mUiState.value =
|
||||
SavingsAccountTransactionUiState.Success(dateAndTypeFilteredList)
|
||||
}
|
||||
|
||||
private fun filterByDate(startDate: Long, endDate: Long) {
|
||||
val list = filterTransactionListByDate(transactions = transactionsList, startDate = startDate, endDate = endDate)
|
||||
_savingAccountsTransactionUiState.value = SavingsAccountTransactionUiState.Success(list)
|
||||
val list = filterTransactionListByDate(
|
||||
transactions = transactionsList,
|
||||
startDate = startDate,
|
||||
endDate = endDate,
|
||||
)
|
||||
mUiState.value = SavingsAccountTransactionUiState.Success(list)
|
||||
}
|
||||
|
||||
private fun filterByType(
|
||||
checkBoxFilters: MutableList<SavingsTransactionCheckBoxFilter>
|
||||
checkBoxFilters: MutableList<SavingsTransactionCheckBoxFilter>,
|
||||
) {
|
||||
val list = filterSavingsAccountTransactionsByType(checkBoxFilters = checkBoxFilters)
|
||||
_savingAccountsTransactionUiState.value = SavingsAccountTransactionUiState.Success(list)
|
||||
mUiState.value = SavingsAccountTransactionUiState.Success(list)
|
||||
}
|
||||
|
||||
private fun filterTransactionListByDate(
|
||||
transactions: List<Transactions>,
|
||||
startDate: Long,
|
||||
endDate: Long
|
||||
endDate: Long,
|
||||
): List<Transactions> {
|
||||
return transactions.filter {
|
||||
(DateHelper.getDateAsLongFromList(it.date) in startDate..endDate)
|
||||
@ -121,7 +144,7 @@ class SavingAccountsTransactionViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun filterSavingsAccountTransactionsByType(
|
||||
checkBoxFilters: MutableList<SavingsTransactionCheckBoxFilter>
|
||||
checkBoxFilters: MutableList<SavingsTransactionCheckBoxFilter>,
|
||||
): List<Transactions> {
|
||||
var filteredSavingsTransactions: List<Transactions> = ArrayList()
|
||||
checkBoxFilters.forEach { filter ->
|
||||
@ -129,12 +152,15 @@ class SavingAccountsTransactionViewModel @Inject constructor(
|
||||
SavingsTransactionCheckBoxFilter.DEPOSIT -> {
|
||||
transactionsList.filter { it.transactionType?.deposit == true }
|
||||
}
|
||||
|
||||
SavingsTransactionCheckBoxFilter.DIVIDEND_PAYOUT -> {
|
||||
transactionsList.filter { it.transactionType?.dividendPayout == true }
|
||||
}
|
||||
|
||||
SavingsTransactionCheckBoxFilter.WITHDRAWAL -> {
|
||||
transactionsList.filter { it.transactionType?.withdrawal == true }
|
||||
}
|
||||
|
||||
SavingsTransactionCheckBoxFilter.INTEREST_POSTING -> {
|
||||
transactionsList.filter { it.transactionType?.interestPosting == true }
|
||||
}
|
||||
@ -145,13 +171,14 @@ class SavingAccountsTransactionViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SavingsAccountTransactionUiState {
|
||||
internal sealed class SavingsAccountTransactionUiState {
|
||||
data object Loading : SavingsAccountTransactionUiState()
|
||||
data class Error(val errorMessage: String?) : SavingsAccountTransactionUiState()
|
||||
data class Success(val savingAccountsTransactionList: List<Transactions>?) : SavingsAccountTransactionUiState()
|
||||
data class Success(val savingAccountsTransactionList: List<Transactions>?) :
|
||||
SavingsAccountTransactionUiState()
|
||||
}
|
||||
|
||||
fun getTransactionTriangleResId(transactionType: TransactionType?): Int {
|
||||
internal fun getTransactionTriangleResId(transactionType: TransactionType?): Int {
|
||||
return transactionType?.run {
|
||||
when {
|
||||
deposit == true -> R.drawable.triangular_green_view
|
||||
@ -170,38 +197,38 @@ fun getTransactionTriangleResId(transactionType: TransactionType?): Int {
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class SavingsTransactionFilterDataModel(
|
||||
internal data class SavingsTransactionFilterDataModel(
|
||||
val startDate: Long,
|
||||
val endDate: Long,
|
||||
val radioFilter: SavingsTransactionRadioFilter?,
|
||||
val checkBoxFilters: MutableList<SavingsTransactionCheckBoxFilter>
|
||||
): Parcelable
|
||||
val checkBoxFilters: MutableList<SavingsTransactionCheckBoxFilter>,
|
||||
) : Parcelable
|
||||
|
||||
enum class SavingsTransactionRadioFilter(val textResId: Int) {
|
||||
internal enum class SavingsTransactionRadioFilter(val textResId: Int) {
|
||||
DATE(textResId = R.string.date),
|
||||
FOUR_WEEKS(textResId = R.string.four_weeks),
|
||||
THREE_MONTHS(textResId = R.string.three_months),
|
||||
SIX_MONTHS(textResId = R.string.six_months)
|
||||
SIX_MONTHS(textResId = R.string.six_months),
|
||||
}
|
||||
|
||||
enum class SavingsTransactionCheckBoxFilter(
|
||||
internal enum class SavingsTransactionCheckBoxFilter(
|
||||
val textResId: Int,
|
||||
val checkBoxColor: Color,
|
||||
) {
|
||||
DEPOSIT(
|
||||
textResId = R.string.deposit,
|
||||
checkBoxColor = DepositGreen
|
||||
checkBoxColor = DepositGreen,
|
||||
),
|
||||
DIVIDEND_PAYOUT(
|
||||
textResId = R.string.dividend_payout,
|
||||
checkBoxColor = RedLight
|
||||
checkBoxColor = RedLight,
|
||||
),
|
||||
WITHDRAWAL(
|
||||
textResId = R.string.withdrawal,
|
||||
checkBoxColor = RedLight
|
||||
checkBoxColor = RedLight,
|
||||
),
|
||||
INTEREST_POSTING(
|
||||
textResId = R.string.interest_posting,
|
||||
checkBoxColor = GreenSuccess
|
||||
)
|
||||
}
|
||||
checkBoxColor = GreenSuccess,
|
||||
),
|
||||
}
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account_withdraw
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccountWithdraw
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
@ -9,7 +18,6 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -22,7 +30,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.compose.ui.unit.dp
|
||||
@ -30,6 +37,7 @@ import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import org.mifos.mobile.core.common.Network
|
||||
import org.mifos.mobile.core.common.utils.getTodayFormatted
|
||||
import org.mifos.mobile.core.designsystem.components.MifosButton
|
||||
import org.mifos.mobile.core.designsystem.components.MifosOutlinedTextField
|
||||
import org.mifos.mobile.core.designsystem.components.MifosTopBar
|
||||
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
|
||||
@ -37,56 +45,65 @@ import org.mifos.mobile.core.model.entity.accounts.savings.SavingsWithAssociatio
|
||||
import org.mifos.mobile.core.ui.component.MifosProgressIndicator
|
||||
import org.mifos.mobile.core.ui.component.MifosTitleDescSingleLineEqual
|
||||
import org.mifos.mobile.core.ui.component.NoInternet
|
||||
import org.mifos.mobile.core.ui.utils.DevicePreviews
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountWithdrawScreen(
|
||||
viewModel: SavingsAccountWithdrawViewModel = hiltViewModel(),
|
||||
internal fun SavingsAccountWithdrawScreen(
|
||||
navigateBack: (withdrawSuccess: Boolean) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: SavingsAccountWithdrawViewModel = hiltViewModel(),
|
||||
) {
|
||||
val uiState by viewModel.savingsAccountWithdrawUiState.collectAsStateWithLifecycle()
|
||||
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
||||
val savingsWithAssociations by viewModel.savingsWithAssociations.collectAsStateWithLifecycle()
|
||||
|
||||
SavingsAccountWithdrawScreen(
|
||||
uiState = uiState,
|
||||
savingsWithAssociations = savingsWithAssociations,
|
||||
navigateBack = navigateBack,
|
||||
withdraw = { viewModel.submitWithdrawSavingsAccount(it) }
|
||||
withdraw = viewModel::submitWithdrawSavingsAccount,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountWithdrawScreen(
|
||||
private fun SavingsAccountWithdrawScreen(
|
||||
uiState: SavingsAccountWithdrawUiState,
|
||||
savingsWithAssociations: SavingsWithAssociations?,
|
||||
navigateBack: (withdrawSuccess: Boolean) -> Unit,
|
||||
withdraw: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
Column(modifier = modifier.fillMaxSize()) {
|
||||
MifosTopBar(
|
||||
navigateBack = { navigateBack(false) },
|
||||
title = { Text(text = stringResource(id = R.string.withdraw_savings_account)) }
|
||||
title = { Text(text = stringResource(id = R.string.withdraw_savings_account)) },
|
||||
)
|
||||
|
||||
Box(modifier= Modifier.weight(1f)) {
|
||||
Box(modifier = Modifier.weight(1f)) {
|
||||
SavingsAccountWithdrawContent(
|
||||
savingsWithAssociations = savingsWithAssociations,
|
||||
withdraw = withdraw
|
||||
withdraw = withdraw,
|
||||
)
|
||||
|
||||
when (uiState) {
|
||||
is SavingsAccountWithdrawUiState.Loading -> {
|
||||
MifosProgressIndicator(modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background.copy(alpha = 0.7f)))
|
||||
MifosProgressIndicator(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background.copy(alpha = 0.7f)),
|
||||
)
|
||||
}
|
||||
|
||||
is SavingsAccountWithdrawUiState.Success -> {
|
||||
LaunchedEffect(true) {
|
||||
Toast.makeText(context, R.string.savings_account_withdraw_successful, Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(
|
||||
context,
|
||||
R.string.savings_account_withdraw_successful,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
navigateBack(true)
|
||||
}
|
||||
@ -98,12 +115,11 @@ fun SavingsAccountWithdrawScreen(
|
||||
is SavingsAccountWithdrawUiState.WithdrawUiReady -> {}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ErrorComponent(
|
||||
private fun ErrorComponent(
|
||||
errorToast: String?,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
@ -120,31 +136,32 @@ fun ErrorComponent(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SavingsAccountWithdrawContent(
|
||||
private fun SavingsAccountWithdrawContent(
|
||||
savingsWithAssociations: SavingsWithAssociations?,
|
||||
withdraw: (String) -> Unit
|
||||
withdraw: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var remark by remember { mutableStateOf(TextFieldValue("")) }
|
||||
var remarkFieldError by remember { mutableStateOf(false) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.padding(16.dp)
|
||||
.fillMaxSize()
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
MifosTitleDescSingleLineEqual(
|
||||
title = stringResource(id = R.string.client_name),
|
||||
description = savingsWithAssociations?.clientName ?: ""
|
||||
description = savingsWithAssociations?.clientName ?: "",
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
MifosTitleDescSingleLineEqual(
|
||||
title = stringResource(id = R.string.account_number),
|
||||
description = savingsWithAssociations?.accountNo ?: ""
|
||||
description = savingsWithAssociations?.accountNo ?: "",
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
MifosTitleDescSingleLineEqual(
|
||||
title = stringResource(id = R.string.withdrawal_date),
|
||||
description = getTodayFormatted()
|
||||
description = getTodayFormatted(),
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
MifosOutlinedTextField(
|
||||
@ -154,15 +171,16 @@ fun SavingsAccountWithdrawContent(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
supportingText = stringResource(
|
||||
R.string.error_validation_blank,
|
||||
stringResource(R.string.remark)
|
||||
stringResource(R.string.remark),
|
||||
),
|
||||
onValueChange = {
|
||||
remark = it
|
||||
remarkFieldError = false
|
||||
}
|
||||
},
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Button(
|
||||
MifosButton(
|
||||
textResId = R.string.withdraw_savings_account,
|
||||
onClick = {
|
||||
if (remark.text.isEmpty()) {
|
||||
remarkFieldError = true
|
||||
@ -170,34 +188,33 @@ fun SavingsAccountWithdrawContent(
|
||||
withdraw.invoke(remark.text)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Text(text = stringResource(id = R.string.withdraw_savings_account))
|
||||
}
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class UiStatesParameterProvider : PreviewParameterProvider<SavingsAccountWithdrawUiState> {
|
||||
internal class UiStatesParameterProvider : PreviewParameterProvider<SavingsAccountWithdrawUiState> {
|
||||
override val values: Sequence<SavingsAccountWithdrawUiState>
|
||||
get() = sequenceOf(
|
||||
SavingsAccountWithdrawUiState.WithdrawUiReady,
|
||||
SavingsAccountWithdrawUiState.Error(message = ""),
|
||||
SavingsAccountWithdrawUiState.Loading,
|
||||
SavingsAccountWithdrawUiState.Success
|
||||
SavingsAccountWithdrawUiState.Success,
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showSystemUi = true)
|
||||
@DevicePreviews
|
||||
@Composable
|
||||
fun SavingsAccountWithdrawScreenPreview(
|
||||
@PreviewParameter(UiStatesParameterProvider::class) savingsAccountWithdrawUiState: SavingsAccountWithdrawUiState
|
||||
private fun SavingsAccountWithdrawScreenPreview(
|
||||
@PreviewParameter(UiStatesParameterProvider::class)
|
||||
savingsAccountWithdrawUiState: SavingsAccountWithdrawUiState,
|
||||
) {
|
||||
MifosMobileTheme {
|
||||
SavingsAccountWithdrawScreen(
|
||||
uiState = savingsAccountWithdrawUiState,
|
||||
savingsWithAssociations = SavingsWithAssociations(
|
||||
clientName = "Mifos Mobile",
|
||||
accountNo = "0001"
|
||||
accountNo = "0001",
|
||||
),
|
||||
navigateBack = {},
|
||||
withdraw = {},
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_account_withdraw
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsAccountWithdraw
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
@ -7,6 +16,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
@ -15,33 +25,34 @@ import org.mifos.mobile.core.common.Constants
|
||||
import org.mifos.mobile.core.common.utils.getTodayFormatted
|
||||
import org.mifos.mobile.core.data.repository.SavingsAccountRepository
|
||||
import org.mifos.mobile.core.model.entity.accounts.savings.SavingsAccountWithdrawPayload
|
||||
import org.mifos.mobile.feature.savings.savingsAccountWithdraw.SavingsAccountWithdrawUiState.WithdrawUiReady
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SavingsAccountWithdrawViewModel @Inject constructor(
|
||||
internal class SavingsAccountWithdrawViewModel @Inject constructor(
|
||||
private val savingsAccountRepositoryImp: SavingsAccountRepository,
|
||||
savedStateHandle: SavedStateHandle
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
val savingsAccountWithdrawUiState: StateFlow<SavingsAccountWithdrawUiState> get() = _savingsAccountWithdrawUiState
|
||||
private val _savingsAccountWithdrawUiState = MutableStateFlow<SavingsAccountWithdrawUiState>(
|
||||
SavingsAccountWithdrawUiState.WithdrawUiReady
|
||||
)
|
||||
|
||||
val savingsId: StateFlow<Long> = savedStateHandle.getStateFlow<Long>(
|
||||
private val mUiState = MutableStateFlow<SavingsAccountWithdrawUiState>(WithdrawUiReady)
|
||||
val uiState = mUiState.asStateFlow()
|
||||
|
||||
private val savingsId: StateFlow<Long> = savedStateHandle.getStateFlow(
|
||||
key = Constants.SAVINGS_ID,
|
||||
initialValue = -1L
|
||||
initialValue = -1L,
|
||||
)
|
||||
|
||||
val savingsWithAssociations = savingsId
|
||||
.flatMapLatest {
|
||||
savingsAccountRepositoryImp.getSavingsWithAssociations(
|
||||
savingsId.value, Constants.TRANSACTIONS,
|
||||
savingsId.value,
|
||||
Constants.TRANSACTIONS,
|
||||
)
|
||||
}
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
started = SharingStarted.WhileSubscribed(5_000),
|
||||
initialValue = null
|
||||
initialValue = null,
|
||||
)
|
||||
|
||||
fun submitWithdrawSavingsAccount(remark: String) {
|
||||
@ -50,22 +61,25 @@ class SavingsAccountWithdrawViewModel @Inject constructor(
|
||||
payload.withdrawnOnDate = getTodayFormatted()
|
||||
|
||||
viewModelScope.launch {
|
||||
_savingsAccountWithdrawUiState.value = SavingsAccountWithdrawUiState.Loading
|
||||
savingsAccountRepositoryImp.submitWithdrawSavingsAccount(savingsWithAssociations.value?.accountNo, payload)
|
||||
mUiState.value = SavingsAccountWithdrawUiState.Loading
|
||||
savingsAccountRepositoryImp.submitWithdrawSavingsAccount(
|
||||
savingsWithAssociations.value?.accountNo,
|
||||
payload,
|
||||
)
|
||||
.catch { e ->
|
||||
_savingsAccountWithdrawUiState.value =
|
||||
mUiState.value =
|
||||
SavingsAccountWithdrawUiState.Error(e.message)
|
||||
}.collect {
|
||||
_savingsAccountWithdrawUiState.value =
|
||||
mUiState.value =
|
||||
SavingsAccountWithdrawUiState.Success
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class SavingsAccountWithdrawUiState {
|
||||
data object WithdrawUiReady: SavingsAccountWithdrawUiState()
|
||||
data object Loading: SavingsAccountWithdrawUiState()
|
||||
data object Success: SavingsAccountWithdrawUiState()
|
||||
data class Error(val message: String?): SavingsAccountWithdrawUiState()
|
||||
internal sealed class SavingsAccountWithdrawUiState {
|
||||
data object WithdrawUiReady : SavingsAccountWithdrawUiState()
|
||||
data object Loading : SavingsAccountWithdrawUiState()
|
||||
data object Success : SavingsAccountWithdrawUiState()
|
||||
data class Error(val message: String?) : SavingsAccountWithdrawUiState()
|
||||
}
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_make_transfer
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsMakeTransfer
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@ -9,9 +18,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
@ -26,13 +33,13 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.mifos.mobile.core.designsystem.components.MifosButton
|
||||
import org.mifos.mobile.core.designsystem.components.MifosOutlinedButton
|
||||
import org.mifos.mobile.core.designsystem.components.MifosOutlinedTextField
|
||||
import org.mifos.mobile.core.designsystem.theme.DarkGray
|
||||
import org.mifos.mobile.core.designsystem.theme.MifosMobileTheme
|
||||
@ -43,13 +50,15 @@ import org.mifos.mobile.core.ui.component.MFStepProcess
|
||||
import org.mifos.mobile.core.ui.component.MifosDropDownDoubleTextField
|
||||
import org.mifos.mobile.core.ui.component.StepProcessState
|
||||
import org.mifos.mobile.core.ui.component.getStepState
|
||||
import org.mifos.mobile.core.ui.utils.DevicePreviews
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
|
||||
@Composable
|
||||
fun SavingsMakeTransferContent(
|
||||
internal fun SavingsMakeTransferContent(
|
||||
uiData: SavingsMakeTransferUiData,
|
||||
reviewTransfer: (ReviewTransferPayload) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onCancelledClicked: () -> Unit = {},
|
||||
reviewTransfer: (ReviewTransferPayload) -> Unit
|
||||
) {
|
||||
val scrollState = rememberScrollState()
|
||||
|
||||
@ -82,14 +91,14 @@ fun SavingsMakeTransferContent(
|
||||
Pair(payToStepState, R.string.one),
|
||||
Pair(payFromStepState, R.string.two),
|
||||
Pair(amountStepState, R.string.three),
|
||||
Pair(remarkStepState, R.string.four)
|
||||
Pair(remarkStepState, R.string.four),
|
||||
)
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.verticalScroll(scrollState)
|
||||
.padding(horizontal = 12.dp)
|
||||
.fillMaxSize()
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
for (step in stepsState) {
|
||||
MFStepProcess(
|
||||
@ -97,48 +106,51 @@ fun SavingsMakeTransferContent(
|
||||
activateColor = Primary,
|
||||
processState = step.first,
|
||||
deactivateColor = DarkGray,
|
||||
isLastStep = step == stepsState.last()
|
||||
) { modifier ->
|
||||
isLastStep = step == stepsState.last(),
|
||||
) { processModifier ->
|
||||
when (step.second) {
|
||||
R.string.one -> PayToStepContent(modifier = modifier,
|
||||
R.string.one -> PayToStepContent(
|
||||
modifier = processModifier,
|
||||
processState = payToStepState,
|
||||
toAccountOptions = uiData.accountOptionsTemplate.fromAccountOptions,
|
||||
prefilledAccount = payToAccount,
|
||||
onContinueClick = {
|
||||
payToAccount = it
|
||||
currentStep += 1
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
R.string.two -> PayFromStep(modifier = modifier,
|
||||
R.string.two -> PayFromStep(
|
||||
modifier = processModifier,
|
||||
processState = payFromStepState,
|
||||
fromAccountOptions = uiData.accountOptionsTemplate.fromAccountOptions,
|
||||
prefilledAccount = payFromAccount,
|
||||
onContinueClick = {
|
||||
payFromAccount = it
|
||||
currentStep += 1
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
R.string.three -> EnterAmountStep(modifier = modifier,
|
||||
R.string.three -> EnterAmountStep(
|
||||
processState = amountStepState,
|
||||
outstandingBalance = uiData.outstandingBalance,
|
||||
onContinueClick = {
|
||||
amount = it
|
||||
currentStep += 1
|
||||
}
|
||||
},
|
||||
modifier = processModifier,
|
||||
outstandingBalance = uiData.outstandingBalance,
|
||||
)
|
||||
|
||||
R.string.four -> RemarkStep(
|
||||
modifier = modifier,
|
||||
modifier = processModifier,
|
||||
processState = remarkStepState,
|
||||
onContinueClicked = {
|
||||
remark = it
|
||||
reviewTransfer(
|
||||
ReviewTransferPayload(payToAccount, payFromAccount, amount, remark)
|
||||
ReviewTransferPayload(payToAccount, payFromAccount, amount, remark),
|
||||
)
|
||||
},
|
||||
onCancelledClicked = onCancelledClicked
|
||||
onCancelledClicked = onCancelledClicked,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -147,12 +159,12 @@ fun SavingsMakeTransferContent(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PayToStepContent(
|
||||
modifier: Modifier,
|
||||
private fun PayToStepContent(
|
||||
toAccountOptions: List<AccountOption>,
|
||||
prefilledAccount: AccountOption?,
|
||||
processState: StepProcessState,
|
||||
onContinueClick: (AccountOption) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var payToAccount by rememberSaveable { mutableStateOf(prefilledAccount) }
|
||||
var payToStepError by rememberSaveable { mutableStateOf(false) }
|
||||
@ -168,28 +180,31 @@ fun PayToStepContent(
|
||||
onClick = { index, _ ->
|
||||
payToAccount = toAccountOptions[index]
|
||||
payToStepError = false
|
||||
}
|
||||
},
|
||||
)
|
||||
if (processState == StepProcessState.ACTIVE) {
|
||||
Button(onClick = {
|
||||
if (payToAccount == null) payToStepError = true
|
||||
else onContinueClick(payToAccount ?: AccountOption())
|
||||
}, content = {
|
||||
Text(text = stringResource(id = R.string.continue_str))
|
||||
})
|
||||
MifosButton(
|
||||
textResId = R.string.continue_str,
|
||||
onClick = {
|
||||
if (payToAccount == null) {
|
||||
payToStepError = true
|
||||
} else {
|
||||
onContinueClick(payToAccount ?: AccountOption())
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PayFromStep(
|
||||
modifier: Modifier,
|
||||
private fun PayFromStep(
|
||||
fromAccountOptions: List<AccountOption>,
|
||||
prefilledAccount: AccountOption?,
|
||||
processState: StepProcessState,
|
||||
onContinueClick: (AccountOption) -> Unit
|
||||
onContinueClick: (AccountOption) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
|
||||
var payFromAccount by rememberSaveable { mutableStateOf(prefilledAccount) }
|
||||
var payFromError by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
@ -197,14 +212,14 @@ fun PayFromStep(
|
||||
Text(
|
||||
text = stringResource(id = R.string.pay_from),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontWeight = FontWeight.Bold
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
if (processState == StepProcessState.ACTIVE) {
|
||||
MifosDropDownDoubleTextField(
|
||||
optionsList = fromAccountOptions.map {
|
||||
Pair(
|
||||
it.accountNo ?: "",
|
||||
it.clientName ?: ""
|
||||
it.clientName ?: "",
|
||||
)
|
||||
},
|
||||
selectedOption = payFromAccount?.accountNo ?: "",
|
||||
@ -214,26 +229,29 @@ fun PayFromStep(
|
||||
onClick = { index, _ ->
|
||||
payFromAccount = fromAccountOptions[index]
|
||||
payFromError = false
|
||||
}
|
||||
},
|
||||
)
|
||||
MifosButton(
|
||||
textResId = R.string.continue_str,
|
||||
onClick = {
|
||||
if (payFromAccount == null) {
|
||||
payFromError = true
|
||||
} else {
|
||||
onContinueClick(payFromAccount ?: AccountOption())
|
||||
}
|
||||
},
|
||||
)
|
||||
Button(onClick = {
|
||||
if (payFromAccount == null) payFromError = true
|
||||
else onContinueClick(payFromAccount ?: AccountOption())
|
||||
}, content = {
|
||||
Text(text = stringResource(id = R.string.continue_str))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun EnterAmountStep(
|
||||
modifier: Modifier,
|
||||
outstandingBalance: Double? = null,
|
||||
private fun EnterAmountStep(
|
||||
processState: StepProcessState,
|
||||
onContinueClick: (String) -> Unit
|
||||
onContinueClick: (String) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
outstandingBalance: Double? = null,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var amount by remember { mutableStateOf(TextFieldValue(outstandingBalance?.toString() ?: "")) }
|
||||
var amountError by rememberSaveable { mutableStateOf<Int?>(null) }
|
||||
var showAmountError by rememberSaveable { mutableStateOf(false) }
|
||||
@ -252,7 +270,7 @@ fun EnterAmountStep(
|
||||
Text(
|
||||
text = stringResource(id = R.string.amount),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontWeight = FontWeight.Bold
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
if (processState == StepProcessState.ACTIVE) {
|
||||
MifosOutlinedTextField(
|
||||
@ -265,25 +283,26 @@ fun EnterAmountStep(
|
||||
enabled = outstandingBalance == null,
|
||||
label = R.string.enter_amount,
|
||||
)
|
||||
Button(
|
||||
MifosButton(
|
||||
textResId = R.string.continue_str,
|
||||
onClick = {
|
||||
if(amountError == null) { onContinueClick(amount.text) }
|
||||
else { showAmountError = true }
|
||||
if (amountError == null) {
|
||||
onContinueClick(amount.text)
|
||||
} else {
|
||||
showAmountError = true
|
||||
}
|
||||
},
|
||||
content = {
|
||||
Text(text = stringResource(id = R.string.continue_str))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RemarkStep(
|
||||
modifier: Modifier,
|
||||
private fun RemarkStep(
|
||||
processState: StepProcessState,
|
||||
onContinueClicked: (String) -> Unit,
|
||||
onCancelledClicked: () -> Unit = {}
|
||||
modifier: Modifier = Modifier,
|
||||
onCancelledClicked: () -> Unit = {},
|
||||
) {
|
||||
var remark by remember { mutableStateOf(TextFieldValue("")) }
|
||||
var remarkError by rememberSaveable { mutableStateOf<Int?>(null) }
|
||||
@ -301,7 +320,7 @@ fun RemarkStep(
|
||||
Text(
|
||||
text = stringResource(id = R.string.remark),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontWeight = FontWeight.Bold
|
||||
fontWeight = FontWeight.Bold,
|
||||
)
|
||||
if (processState == StepProcessState.ACTIVE) {
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
@ -311,21 +330,21 @@ fun RemarkStep(
|
||||
isError = showRemarkError,
|
||||
supportingText = { remarkError?.let { stringResource(id = it) } },
|
||||
onValueChange = { remark = it },
|
||||
label = { Text(text = stringResource(id = R.string.remark)) }
|
||||
label = { Text(text = stringResource(id = R.string.remark)) },
|
||||
)
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
Row {
|
||||
Button(
|
||||
MifosButton(
|
||||
textResId = R.string.review,
|
||||
onClick = {
|
||||
remarkError?.let { showRemarkError = true }
|
||||
?: onContinueClicked(remark.text)
|
||||
},
|
||||
content = { Text(text = stringResource(id = R.string.review)) }
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
OutlinedButton(
|
||||
onClick = { onCancelledClicked() },
|
||||
content = { Text(text = stringResource(id = R.string.cancel)) }
|
||||
MifosOutlinedButton(
|
||||
textResId = R.string.cancel,
|
||||
onClick = onCancelledClicked,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@ -333,20 +352,19 @@ fun RemarkStep(
|
||||
text = stringResource(id = R.string.enter_remarks),
|
||||
color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f),
|
||||
fontWeight = FontWeight.Bold,
|
||||
style = MaterialTheme.typography.labelMedium
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showSystemUi = true)
|
||||
@DevicePreviews
|
||||
@Composable
|
||||
fun SavingsMakeTransferContentPreview() {
|
||||
private fun SavingsMakeTransferContentPreview() {
|
||||
MifosMobileTheme {
|
||||
SavingsMakeTransferContent(
|
||||
uiData = SavingsMakeTransferUiData(),
|
||||
reviewTransfer = {}
|
||||
reviewTransfer = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_make_transfer
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsMakeTransfer
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@ -6,7 +15,6 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
@ -19,14 +27,16 @@ import org.mifos.mobile.core.model.entity.payload.ReviewTransferPayload
|
||||
import org.mifos.mobile.core.model.enums.TransferType
|
||||
import org.mifos.mobile.core.ui.component.MifosErrorComponent
|
||||
import org.mifos.mobile.core.ui.component.MifosProgressIndicatorOverlay
|
||||
import org.mifos.mobile.core.ui.utils.DevicePreviews
|
||||
import org.mifos.mobile.feature.savings.R
|
||||
|
||||
@Composable
|
||||
fun SavingsMakeTransferScreen(
|
||||
viewModel: SavingsMakeTransferViewModel = hiltViewModel(),
|
||||
internal fun SavingsMakeTransferScreen(
|
||||
onCancelledClicked: () -> Unit,
|
||||
navigateBack: () -> Unit,
|
||||
reviewTransfer: (ReviewTransferPayload, TransferType) -> Unit
|
||||
reviewTransfer: (ReviewTransferPayload, TransferType) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: SavingsMakeTransferViewModel = hiltViewModel(),
|
||||
) {
|
||||
val uiState = viewModel.savingsMakeTransferUiState.collectAsStateWithLifecycle()
|
||||
val uiData = viewModel.savingsMakeTransferUiData.collectAsStateWithLifecycle()
|
||||
@ -36,36 +46,46 @@ fun SavingsMakeTransferScreen(
|
||||
onCancelledClicked = onCancelledClicked,
|
||||
uiState = uiState.value,
|
||||
uiData = uiData.value,
|
||||
reviewTransfer = { reviewTransfer(it, TransferType.SELF) }
|
||||
modifier = modifier,
|
||||
reviewTransfer = { reviewTransfer(it, TransferType.SELF) },
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SavingsMakeTransferScreen(
|
||||
private fun SavingsMakeTransferScreen(
|
||||
uiState: SavingsMakeTransferUiState,
|
||||
uiData: SavingsMakeTransferUiData,
|
||||
onCancelledClicked: () -> Unit = {},
|
||||
navigateBack: () -> Unit,
|
||||
reviewTransfer: (ReviewTransferPayload) -> Unit
|
||||
reviewTransfer: (ReviewTransferPayload) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
onCancelledClicked: () -> Unit = {},
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
||||
MifosScaffold(
|
||||
topBarTitleResId = if(uiData.transferType == Constants.TRANSFER_PAY_TO) R.string.deposit
|
||||
else R.string.transfer,
|
||||
topBarTitleResId = if (uiData.transferType == Constants.TRANSFER_PAY_TO) {
|
||||
R.string.deposit
|
||||
} else {
|
||||
R.string.transfer
|
||||
},
|
||||
navigateBack = navigateBack,
|
||||
modifier = modifier,
|
||||
content = {
|
||||
Box(modifier = Modifier.padding(it).fillMaxSize()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(it)
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
SavingsMakeTransferContent(
|
||||
uiData = uiData,
|
||||
reviewTransfer = reviewTransfer,
|
||||
onCancelledClicked = onCancelledClicked,
|
||||
reviewTransfer = reviewTransfer
|
||||
)
|
||||
|
||||
when(uiState) {
|
||||
when (uiState) {
|
||||
is SavingsMakeTransferUiState.ShowUI -> Unit
|
||||
|
||||
is SavingsMakeTransferUiState.Loading -> { MifosProgressIndicatorOverlay() }
|
||||
is SavingsMakeTransferUiState.Loading -> MifosProgressIndicatorOverlay()
|
||||
|
||||
is SavingsMakeTransferUiState.Error -> {
|
||||
MifosErrorComponent(
|
||||
@ -76,13 +96,12 @@ fun SavingsMakeTransferScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
class SavingsMakeTransferUiStatesPreviews : PreviewParameterProvider<SavingsMakeTransferUiState> {
|
||||
internal class SavingsMakeTransferUiStatesPreviews :
|
||||
PreviewParameterProvider<SavingsMakeTransferUiState> {
|
||||
override val values: Sequence<SavingsMakeTransferUiState>
|
||||
get() = sequenceOf(
|
||||
SavingsMakeTransferUiState.ShowUI,
|
||||
@ -91,18 +110,19 @@ class SavingsMakeTransferUiStatesPreviews : PreviewParameterProvider<SavingsMake
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showSystemUi = true)
|
||||
@DevicePreviews
|
||||
@Composable
|
||||
fun SavingsMakeTransferContentPreview(
|
||||
@PreviewParameter(SavingsMakeTransferUiStatesPreviews::class) savingsMakeTransferUIState: SavingsMakeTransferUiState
|
||||
private fun SavingsMakeTransferContentPreview(
|
||||
@PreviewParameter(SavingsMakeTransferUiStatesPreviews::class)
|
||||
savingsMakeTransferUIState: SavingsMakeTransferUiState,
|
||||
) {
|
||||
MifosMobileTheme {
|
||||
SavingsMakeTransferScreen(
|
||||
navigateBack = { },
|
||||
onCancelledClicked = { },
|
||||
reviewTransfer = { },
|
||||
navigateBack = { },
|
||||
onCancelledClicked = { },
|
||||
reviewTransfer = { },
|
||||
uiState = savingsMakeTransferUIState,
|
||||
uiData = SavingsMakeTransferUiData()
|
||||
uiData = SavingsMakeTransferUiData(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,13 @@
|
||||
package org.mifos.mobile.feature.savings.savings_make_transfer
|
||||
/*
|
||||
* Copyright 2024 Mifos Initiative
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*
|
||||
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
*/
|
||||
package org.mifos.mobile.feature.savings.savingsMakeTransfer
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
@ -19,23 +28,22 @@ import org.mifos.mobile.core.network.Result
|
||||
import org.mifos.mobile.core.network.asResult
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@HiltViewModel
|
||||
class SavingsMakeTransferViewModel @Inject constructor(
|
||||
internal class SavingsMakeTransferViewModel @Inject constructor(
|
||||
private val savingsAccountRepositoryImp: SavingsAccountRepository,
|
||||
savedStateHandle: SavedStateHandle
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
|
||||
val accountId = savedStateHandle.getStateFlow(key = Constants.ACCOUNT_ID, initialValue = -1L)
|
||||
|
||||
private val transferType: StateFlow<String> = savedStateHandle.getStateFlow(
|
||||
key = Constants.TRANSFER_TYPE,
|
||||
initialValue = TRANSFER_PAY_TO
|
||||
initialValue = TRANSFER_PAY_TO,
|
||||
)
|
||||
|
||||
private val outstandingBalance: StateFlow<Double?> = savedStateHandle.getStateFlow<String?>(
|
||||
key = Constants.OUTSTANDING_BALANCE,
|
||||
initialValue = null
|
||||
initialValue = null,
|
||||
).map { balanceString ->
|
||||
balanceString?.toDoubleOrNull() ?: 0.0
|
||||
}.stateIn(
|
||||
@ -54,16 +62,22 @@ class SavingsMakeTransferViewModel @Inject constructor(
|
||||
.asResult()
|
||||
.map { result ->
|
||||
when (result) {
|
||||
is Result.Success -> SavingsMakeTransferUiState.ShowUI
|
||||
.also {
|
||||
_savingsMakeTransferUiData.value = _savingsMakeTransferUiData.value
|
||||
.copy(
|
||||
accountOptionsTemplate = result.data,
|
||||
transferType = transferType.value,
|
||||
outstandingBalance = if(outstandingBalance.value == 0.0) null else outstandingBalance.value,
|
||||
accountId = accountId.value
|
||||
)
|
||||
}
|
||||
is Result.Success ->
|
||||
SavingsMakeTransferUiState.ShowUI
|
||||
.also {
|
||||
_savingsMakeTransferUiData.value = _savingsMakeTransferUiData.value
|
||||
.copy(
|
||||
accountOptionsTemplate = result.data,
|
||||
transferType = transferType.value,
|
||||
outstandingBalance = if (outstandingBalance.value == 0.0) {
|
||||
null
|
||||
} else {
|
||||
outstandingBalance.value
|
||||
},
|
||||
accountId = accountId.value,
|
||||
)
|
||||
}
|
||||
|
||||
is Result.Loading -> SavingsMakeTransferUiState.Loading
|
||||
is Result.Error -> SavingsMakeTransferUiState.Error(result.exception.message)
|
||||
}
|
||||
@ -72,21 +86,19 @@ class SavingsMakeTransferViewModel @Inject constructor(
|
||||
started = SharingStarted.WhileSubscribed(5_000),
|
||||
initialValue = SavingsMakeTransferUiState.Loading,
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
sealed class SavingsMakeTransferUiState {
|
||||
internal sealed class SavingsMakeTransferUiState {
|
||||
data object Loading : SavingsMakeTransferUiState()
|
||||
data class Error(val errorMessage: String?) : SavingsMakeTransferUiState()
|
||||
data object ShowUI : SavingsMakeTransferUiState()
|
||||
}
|
||||
|
||||
data class SavingsMakeTransferUiData(
|
||||
internal data class SavingsMakeTransferUiData(
|
||||
var accountId: Long? = null,
|
||||
var transferType: String? = null,
|
||||
var outstandingBalance: Double? = null,
|
||||
var accountOptionsTemplate: AccountOptionsTemplate = AccountOptionsTemplate(),
|
||||
var toAccountOptionPrefilled: AccountOption? = null,
|
||||
var fromAccountOptionPrefilled: AccountOption? = null
|
||||
var fromAccountOptionPrefilled: AccountOption? = null,
|
||||
)
|
||||
|
||||
@ -1,3 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file,
|
||||
You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@ -1,3 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file,
|
||||
You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@ -1,3 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file,
|
||||
You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@ -1,3 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file,
|
||||
You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
|
||||
@ -1,3 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file,
|
||||
You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="24dp"
|
||||
|
||||
@ -1,3 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file,
|
||||
You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="32dp"
|
||||
android:height="24dp"
|
||||
|
||||
@ -1,4 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright 2024 Mifos Initiative
|
||||
|
||||
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
|
||||
If a copy of the MPL was not distributed with this file,
|
||||
You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
|
||||
-->
|
||||
<resources>
|
||||
<string name="active">Active</string>
|
||||
<string name="need_approval">Need Approval</string>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user