From c721f03ffc2a34c40daaa07f727401c04591c270 Mon Sep 17 00:00:00 2001 From: kalpesh Date: Tue, 21 Oct 2025 23:07:14 +0530 Subject: [PATCH 01/19] feat: enhance recurring deposit account settings and UI components --- cmp-navigation/build.gradle.kts | 1 + .../kotlin/cmp/navigation/di/KoinModules.kt | 2 + .../repository/RecurringAccountRepository.kt | 4 +- .../RecurringAccountRepositoryImp.kt | 4 +- .../RecurringDepositAccountPayload.kt | 2 + .../RecurringDepositAccountPayload.kt | 2 +- .../DataManagerRecurringAccount.kt | 1 - .../mifos/core/network/di/DataMangerModule.kt | 3 + .../ClientApplyNewApplicationRoute.kt | 2 +- .../ClientApplyNewApplicationsScreen.kt | 14 +- .../composeResources/values/string.xml | 20 + .../di/RecurringDepositModule.kt | 10 + .../RecurringAccountRoute.kt | 10 +- .../RecurringAccountScreen.kt | 29 +- .../RecurringAccountViewModel.kt | 510 +++++++++++++++++- .../pages/SettingsPage.kt | 271 +++++++++- 16 files changed, 838 insertions(+), 47 deletions(-) create mode 100644 core/model/objects/payloads/RecurringDepositAccountPayload.kt create mode 100644 feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/di/RecurringDepositModule.kt diff --git a/cmp-navigation/build.gradle.kts b/cmp-navigation/build.gradle.kts index b005d0dc5c..ec1eadeff1 100644 --- a/cmp-navigation/build.gradle.kts +++ b/cmp-navigation/build.gradle.kts @@ -46,6 +46,7 @@ kotlin { implementation(projects.feature.pathTracking) implementation(projects.feature.report) implementation(projects.feature.savings) + implementation(projects.feature.recurringDeposit) implementation(projects.feature.settings) implementation(projects.feature.search) diff --git a/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt b/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt index 08425ac605..1ec9c43afc 100644 --- a/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt +++ b/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt @@ -33,6 +33,7 @@ import com.mifos.feature.offline.di.OfflineModule import com.mifos.feature.path.tracking.di.PathTrackingModule import com.mifos.feature.report.di.ReportModule import com.mifos.feature.savings.di.SavingsModule +import com.mifos.feature.recurringDeposit.di.RecurringDepositModule import com.mifos.feature.search.di.SearchModule import com.mifos.feature.settings.di.SettingsModule import com.mifos.room.di.DaoModule @@ -83,6 +84,7 @@ object KoinModules { OfflineModule, PathTrackingModule, ReportModule, + RecurringDepositModule, SavingsModule, SearchModule, SettingsModule, diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/RecurringAccountRepository.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/RecurringAccountRepository.kt index b6da3df34a..3c20a6d7a0 100644 --- a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/RecurringAccountRepository.kt +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/RecurringAccountRepository.kt @@ -17,9 +17,9 @@ import kotlinx.coroutines.flow.Flow interface RecurringAccountRepository { - fun getRecuttingAccountRepository(): Flow> + fun getRecuttingAccountTemplate(): Flow> - fun getRecuttingAccountRepositoryBtProduct( + fun getRecuttingAccountTemplateByProduct( clientId: Int, productId: Int, ): Flow> diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/RecurringAccountRepositoryImp.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/RecurringAccountRepositoryImp.kt index 72ffcc43ca..6aa0050b84 100644 --- a/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/RecurringAccountRepositoryImp.kt +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/RecurringAccountRepositoryImp.kt @@ -21,12 +21,12 @@ import kotlinx.coroutines.flow.Flow class RecurringAccountRepositoryImp( val dataManagerRecurringAccount: DataManagerRecurringAccount, ) : RecurringAccountRepository { - override fun getRecuttingAccountRepository(): Flow> { + override fun getRecuttingAccountTemplate(): Flow> { return dataManagerRecurringAccount.getRecurringDepositAccountTemplate .asDataStateFlow() } - override fun getRecuttingAccountRepositoryBtProduct( + override fun getRecuttingAccountTemplateByProduct( clientId: Int, productId: Int, ): Flow> { diff --git a/core/model/objects/payloads/RecurringDepositAccountPayload.kt b/core/model/objects/payloads/RecurringDepositAccountPayload.kt new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/core/model/objects/payloads/RecurringDepositAccountPayload.kt @@ -0,0 +1,2 @@ + + diff --git a/core/model/src/commonMain/kotlin/com/mifos/core/model/objects/payloads/RecurringDepositAccountPayload.kt b/core/model/src/commonMain/kotlin/com/mifos/core/model/objects/payloads/RecurringDepositAccountPayload.kt index fd3c1caa91..92201a86aa 100644 --- a/core/model/src/commonMain/kotlin/com/mifos/core/model/objects/payloads/RecurringDepositAccountPayload.kt +++ b/core/model/src/commonMain/kotlin/com/mifos/core/model/objects/payloads/RecurringDepositAccountPayload.kt @@ -13,9 +13,9 @@ import kotlinx.serialization.Serializable @Serializable data class RecurringDepositAccountPayload( + // val charges: List? = null, val adjustAdvanceTowardsFuturePayments: Boolean? = null, val allowWithdrawal: Boolean? = null, -// val charges: List? = null, val clientId: Int? = null, val dateFormat: String? = null, val depositPeriod: Int? = null, diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerRecurringAccount.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerRecurringAccount.kt index 7c25d67f59..1fe6795272 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerRecurringAccount.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerRecurringAccount.kt @@ -18,7 +18,6 @@ import kotlinx.coroutines.flow.Flow class DataManagerRecurringAccount( val mBaseApiManager: BaseApiManager, - private val prefManager: UserPreferencesRepository, ) { fun createRecurringDepositAccount( diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt index 1fb5707637..c44146cd09 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt @@ -24,12 +24,14 @@ import com.mifos.core.network.datamanager.DataManagerIdentifiers import com.mifos.core.network.datamanager.DataManagerLoan import com.mifos.core.network.datamanager.DataManagerNote import com.mifos.core.network.datamanager.DataManagerOffices +import com.mifos.core.network.datamanager.DataManagerRecurringAccount import com.mifos.core.network.datamanager.DataManagerRunReport import com.mifos.core.network.datamanager.DataManagerSavings import com.mifos.core.network.datamanager.DataManagerSearch import com.mifos.core.network.datamanager.DataManagerShare import com.mifos.core.network.datamanager.DataManagerStaff import com.mifos.core.network.datamanager.DataManagerSurveys +import org.koin.core.module.dsl.singleOf import org.koin.dsl.module val DataManagerModule = module { @@ -52,5 +54,6 @@ val DataManagerModule = module { single { DataManagerStaff(get(), get(), get()) } single { DataManagerSurveys(get(), get(), get()) } single { DataManagerIdentifiers(get()) } + single{ DataManagerRecurringAccount(get())} single { DataManagerShare(get()) } } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationRoute.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationRoute.kt index 25d86d0b01..e96b89390a 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationRoute.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationRoute.kt @@ -25,7 +25,7 @@ fun NavGraphBuilder.clientApplyNewApplicationRoute( onNavigateApplyLoanAccount: (Int) -> Unit, onNavigateApplySavingsAccount: (Int) -> Unit, onNavigateApplyShareAccount: (Int) -> Unit, - onNavigateApplyRecurringAccount: () -> Unit, + onNavigateApplyRecurringAccount: (Int) -> Unit, onNavigateApplyFixedAccount: () -> Unit, navController: NavController, ) { diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt index 65c25ce11d..d057e7c195 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt @@ -62,7 +62,7 @@ internal fun ClientApplyNewApplicationsScreen( onNavigateApplyLoanAccount: (Int) -> Unit, onNavigateApplySavingsAccount: (Int) -> Unit, onNavigateApplyShareAccount: (Int) -> Unit, - onNavigateApplyRecurringAccount: () -> Unit, + onNavigateApplyRecurringAccount: (Int) -> Unit, onNavigateApplyFixedAccount: () -> Unit, navController: NavController, viewModel: ClientApplyNewApplicationsViewModel = koinViewModel(), @@ -75,15 +75,9 @@ internal fun ClientApplyNewApplicationsScreen( is ClientApplyNewApplicationsEvent.OnActionClick -> { when (event.action) { ClientApplyNewApplicationsItem.NewFixedAccount -> onNavigateApplyFixedAccount() - ClientApplyNewApplicationsItem.NewLoanAccount -> onNavigateApplyLoanAccount( - state.clientId, - ) - - ClientApplyNewApplicationsItem.NewRecurringAccount -> onNavigateApplyRecurringAccount() - ClientApplyNewApplicationsItem.NewSavingsAccount -> onNavigateApplySavingsAccount( - state.clientId, - ) - + ClientApplyNewApplicationsItem.NewLoanAccount -> onNavigateApplyLoanAccount(state.clientId,) + ClientApplyNewApplicationsItem.NewRecurringAccount -> onNavigateApplyRecurringAccount(state.clientId) + ClientApplyNewApplicationsItem.NewSavingsAccount -> onNavigateApplySavingsAccount(state.clientId,) ClientApplyNewApplicationsItem.NewShareAccount -> onNavigateApplyShareAccount(state.clientId) } } diff --git a/feature/recurringDeposit/src/commonMain/composeResources/values/string.xml b/feature/recurringDeposit/src/commonMain/composeResources/values/string.xml index 217caa04d9..93daf678f3 100644 --- a/feature/recurringDeposit/src/commonMain/composeResources/values/string.xml +++ b/feature/recurringDeposit/src/commonMain/composeResources/values/string.xml @@ -15,4 +15,24 @@ Interest Charges Create Recurring Deposit Account + Is Mandatory Deposit? + Adjust advance payments toward future installments? + Allow Withdrawals? + Lock-in Period + Frequency + Type + Recurring Deposit Details + Recurring Deposit Amount + Deposit Period + Deposit Frequency Same as Group/Center meeting + Minimum Deposit Term + And thereafter, in Multiples of + Maximum Deposit Term + For Pre-mature closure + Apply Penal Interest (less) + Penal Interest (%) + Period + Minimum Balance For Interest Calculation + Back + Next \ No newline at end of file diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/di/RecurringDepositModule.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/di/RecurringDepositModule.kt new file mode 100644 index 0000000000..ad37824760 --- /dev/null +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/di/RecurringDepositModule.kt @@ -0,0 +1,10 @@ +package com.mifos.feature.recurringDeposit.di + +import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.RecurringAccountViewModel +import org.koin.core.module.dsl.viewModelOf +import org.koin.dsl.module + + +val RecurringDepositModule = module { + viewModelOf(::RecurringAccountViewModel) +} \ No newline at end of file diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountRoute.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountRoute.kt index b3c674c7c3..7e2d44c0b1 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountRoute.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountRoute.kt @@ -15,7 +15,9 @@ import androidx.navigation.compose.composable import kotlinx.serialization.Serializable @Serializable -data object RecurringAccountRoute +data class RecurringAccountRoute( + val clientId: Int = -1 +) fun NavGraphBuilder.recurringAccountDestination() { composable { @@ -26,8 +28,10 @@ fun NavGraphBuilder.recurringAccountDestination() { } } -fun NavController.navigateToRecurringAccountRoute() { +fun NavController.navigateToRecurringAccountRoute( + clientId: Int +) { this.navigate( - RecurringAccountRoute, + RecurringAccountRoute(clientId = clientId), ) } diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt index 33abc8b65b..f22e59deb8 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.mifos.core.designsystem.component.MifosScaffold +import com.mifos.core.ui.components.MifosErrorComponent import com.mifos.core.ui.components.MifosStepper import com.mifos.core.ui.components.Step import com.mifos.core.ui.util.EventsEffect @@ -33,13 +34,14 @@ import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages.Inter import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages.SettingPage import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages.TermsPage import org.jetbrains.compose.resources.stringResource +import org.koin.compose.viewmodel.koinViewModel @Composable internal fun RecurringAccountScreen( onNavigateBack: () -> Unit, onFinish: () -> Unit, modifier: Modifier = Modifier, - viewModel: RecurringAccountViewModel = viewModel(), + viewModel: RecurringAccountViewModel = koinViewModel(), ) { val state by viewModel.stateFlow.collectAsStateWithLifecycle() @@ -76,7 +78,8 @@ private fun RecurringAccountScaffold( }, Step(name = stringResource(Res.string.step_settings)) { SettingPage( - onNext = { onAction(RecurringAccountAction.NextStep) }, + state = state, + onAction = onAction, ) }, Step(name = stringResource(Res.string.step_interest)) { @@ -110,3 +113,25 @@ private fun RecurringAccountScaffold( } } } + +@Composable +fun RecurringDepositAccountDialogBox( + state: RecurringAccountState +){ + when(state.dialogState){ + is RecurringAccountState.DialogState.Error ->{ + MifosErrorComponent( + message = state.dialogState.message, + isRetryEnabled = true, + onRetry = { + // Retry action can be handled here + }, + ) + } + RecurringAccountState.DialogState.Loading ->{ + + } + RecurringAccountState.DialogState.Success -> TODO() + null -> TODO() + } +} \ No newline at end of file diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt index ab427d5420..6d6bdef4f6 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt @@ -9,21 +9,190 @@ */ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount +import androidx.lifecycle.SavedStateHandle +import androidx.lifecycle.viewModelScope +import androidx.navigation.toRoute +import com.mifos.core.common.utils.DataState +import com.mifos.core.data.repository.RecurringAccountRepository +import com.mifos.core.data.util.NetworkMonitor +import com.mifos.core.model.objects.payloads.RecurringDepositAccountPayload +import com.mifos.core.model.objects.template.recurring.FieldOfficerOption import com.mifos.core.ui.util.BaseViewModel +import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.RecurringAccountState.DialogState +import com.mifos.room.entities.templates.recurringDeposit.RecurringDepositAccountTemplate +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch -class RecurringAccountViewModel : BaseViewModel< +class RecurringAccountViewModel( + savedStateHandle: SavedStateHandle, + private val networkMonitor: NetworkMonitor, + private val recurringAccountRepository: RecurringAccountRepository +) : BaseViewModel< RecurringAccountState, RecurringAccountEvent, RecurringAccountAction, - >(RecurringAccountState()) { +>(RecurringAccountState()) { + + init { + observeNetwork() + } + private val clientId = savedStateHandle.toRoute().clientId + + private fun setLoadingState() { + mutableStateFlow.update { + it.copy( + dialogState = DialogState.Loading + ) + } + } + private fun setErrorState(message: String) { + mutableStateFlow.update { + it.copy( + dialogState = DialogState.Error(message) + ) + } + } + + private fun observeNetwork() { + viewModelScope.launch { + val isConnected = networkMonitor.isOnline.first() + + if(isConnected) { +// loadTemplate() + // Used only for testing purpose + // Must be removed from here after the implementation of detail screen is finished. + loadTemplateByProduct() + } else { + setErrorState("No internet connection") + } + } + } + private fun handleRetry() { + mutableStateFlow.update { + it.copy( + dialogState = null, + recurringDepositAccountTemplate = RecurringDepositAccountTemplate(), + ) + } + observeNetwork() + } + + private fun loadTemplate() { + viewModelScope.launch { + recurringAccountRepository.getRecuttingAccountTemplate().collect {templateState-> + when(templateState) { + is DataState.Error -> { + setErrorState(message = templateState.message) + } + is DataState.Loading -> { + setLoadingState() + } + is DataState.Success -> { + mutableStateFlow.update { + it.copy( + dialogState = null, + recurringDepositAccountTemplate = templateState.data + ) + } + } + } + + } + } + } + private fun loadTemplateByProduct() { + viewModelScope.launch { + recurringAccountRepository.getRecuttingAccountTemplateByProduct( + clientId = clientId, + productId = state.recurringDepositAccountDetail.productId, + ).collect {templateState-> + when(templateState) { + is DataState.Error -> { + setErrorState(message = templateState.message) + } + is DataState.Loading -> { + setLoadingState() + } + is DataState.Success -> { + mutableStateFlow.update { + it.copy( + dialogState = null, + recurringDepositAccountTemplate = templateState.data + ) + } + } + } + + } + } + } + + fun createRecurringDepositAccount() { + viewModelScope.launch { + val s = state + val settings = s.recurringDepositAccountSettings + val payload = RecurringDepositAccountPayload( + adjustAdvanceTowardsFuturePayments = settings.adjustAdvancePayments, + allowWithdrawal = settings.allowWithdrawals, + clientId = clientId, + dateFormat = "dd MMMM yyyy", + depositPeriod = settings.depositPeriod.period.toIntOrNull(), + depositPeriodFrequencyId = settings.depositPeriod.periodType, + expectedFirstDepositOnDate = null, + externalId = s.recurringDepositAccountDetail.externalId, + fieldOfficerId = s.recurringDepositAccountDetail.fieldOfficer?.id, + interestCalculationDaysInYearType = null, + interestCalculationType = null, + interestCompoundingPeriodType = null, + interestPostingPeriodType = null, + isCalendarInherited = null, + isMandatoryDeposit = settings.isMandatory, + locale = "en", + lockinPeriodFrequency = settings.lockInPeriod.frequency.toInt(), + lockinPeriodFrequencyType = settings.lockInPeriod.frequencyTypeIndex, + mandatoryRecommendedDepositAmount = settings.recurringDepositDetails.depositAmount.toInt(), + monthDayFormat = "dd MMMM", + productId = s.recurringDepositAccountDetail.productId, + recurringFrequency = settings.minDepositTerm.frequency.toIntOrNull(), + recurringFrequencyType = settings.minDepositTerm.frequencyTypeIndex, + // date in dd MM yyyy format. + submittedOnDate = s.recurringDepositAccountDetail.submittedOnDate, + ) + recurringAccountRepository.createRecurringDepositAccount(payload).collect { dataState -> + when (dataState) { + is DataState.Error -> { + mutableStateFlow.update { + it.copy(dialogState = DialogState.Error(dataState.message)) + } + } + is DataState.Loading -> { + mutableStateFlow.update { + it.copy(dialogState = DialogState.Loading) + } + } + is DataState.Success -> { + mutableStateFlow.update { + it.copy(dialogState = DialogState.Success) + } + } + } + } + } + } override fun handleAction(action: RecurringAccountAction) { when (action) { RecurringAccountAction.NextStep -> { - mutableStateFlow.update { state -> - val maxIndex = 4 - state.copy(currentStep = (state.currentStep + 1).coerceAtMost(maxIndex)) + val current = state.currentStep + if (current < state.totalSteps) { + mutableStateFlow.update { + it.copy( + currentStep = current + 1, + ) + } + } else { + sendEvent(RecurringAccountEvent.Finish) } } @@ -38,33 +207,301 @@ class RecurringAccountViewModel : BaseViewModel< RecurringAccountAction.Finish -> { sendEvent(RecurringAccountEvent.Finish) } + + RecurringAccountAction.Retry -> { + handleRetry() + } + is RecurringAccountAction.RecurringAccountSettingsAction -> { + when (action) { + RecurringAccountAction.RecurringAccountSettingsAction.OnBackPress -> { + sendEvent(RecurringAccountEvent.NavigateBack) + } + RecurringAccountAction.RecurringAccountSettingsAction.OnNextPress -> { + if (state.currentStep < state.totalSteps) { + mutableStateFlow.update { + it.copy( + currentStep = state.currentStep + 1, + ) + } + } else { + sendEvent(RecurringAccountEvent.Finish) + } + } + is RecurringAccountAction.RecurringAccountSettingsAction.SetDepositPeriod -> { + mutableStateFlow.update { state -> + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + depositPeriod = state.recurringDepositAccountSettings.depositPeriod.copy( + period = action.period + ) + ) + ) + } + } + + is RecurringAccountAction.RecurringAccountSettingsAction.SetDepositPeriodType -> { + mutableStateFlow.update { state -> + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + depositPeriod = state.recurringDepositAccountSettings.depositPeriod.copy( + periodType = action.periodType + ) + ) + ) + } + } + + + is RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriod -> { + mutableStateFlow.update { state -> + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + lockInPeriod = state.recurringDepositAccountSettings.lockInPeriod.copy( + frequency = action.frequency + ) + ) + ) + } + } + is RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriodType -> { + mutableStateFlow.update { state -> + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + lockInPeriod = state.recurringDepositAccountSettings.lockInPeriod.copy( + frequencyTypeIndex = action.frequencyTypeIndex + ) + ) + ) + } + } + + is RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreq -> { + mutableStateFlow.update { state -> + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + minDepositTerm = state.recurringDepositAccountSettings.minDepositTerm.copy( + frequency = action.frequency + ) + ) + ) + } + } + + is RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqType -> { + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + minDepositTerm = state.recurringDepositAccountSettings.minDepositTerm.copy( + frequencyTypeIndex = action.frequencyTypeIndex + ) + ) + ) + + } + } + + is RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqAfterInMultiOf -> { + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + minDepositTerm = state.recurringDepositAccountSettings.minDepositTerm.copy( + frequencyAfterInMultiplesOf = action.frequencyAfterInMultiplesOf + ) + ) + ) + } + } + is RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqTypeAfterInMultiOf -> { + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + minDepositTerm = state.recurringDepositAccountSettings.minDepositTerm.copy( + frequencyTypeIndexAfterInMultiplesOf = action.frequencyTypeIndexAfterInMultiplesOf + ) + ) + ) + } + } + + is RecurringAccountAction.RecurringAccountSettingsAction.SetMaxDepositTermFreq -> { + mutableStateFlow.update { state -> + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + maxDepositTerm = state.recurringDepositAccountSettings.maxDepositTerm.copy( + frequency = action.frequency + ) + ) + ) + } + } + + is RecurringAccountAction.RecurringAccountSettingsAction.SetMaxDepositTermFreqType -> { + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + maxDepositTerm = state.recurringDepositAccountSettings.maxDepositTerm.copy( + frequencyTypeIndex = action.frequencyTypeIndex + ) + ) + ) + + } + } + + is RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureInterestPeriodIndex -> { + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + preMatureClosure = state.recurringDepositAccountSettings.preMatureClosure.copy( + interestPeriodIndex = action.interestPeriodIndex + ) + ) + ) + } + } + + is RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureMinimumBalanceForInterestCalculation -> { + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + preMatureClosure = state.recurringDepositAccountSettings.preMatureClosure.copy( + minimumBalanceForInterestCalculation = action.minimumBalanceForInterestCalculation + ) + ) + ) + } + } + is RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosurePenalInterest -> { + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + preMatureClosure = state.recurringDepositAccountSettings.preMatureClosure.copy( + penalInterest = action.penalInterest + ) + ) + ) + } + } + is RecurringAccountAction.RecurringAccountSettingsAction.SetRecurringDepositAmount -> { + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + recurringDepositDetails = state.recurringDepositAccountSettings + .recurringDepositDetails.copy( + depositAmount = action.depositAmount + ) + ) + ) + } + } + RecurringAccountAction.RecurringAccountSettingsAction.ToggleAdvancePaymentsTowardsFutureInstallments ->{ + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + adjustAdvancePayments = !state.recurringDepositAccountSettings.adjustAdvancePayments + ) + ) + } + } + RecurringAccountAction.RecurringAccountSettingsAction.ToggleAllowWithdrawals -> { + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + allowWithdrawals = !state.recurringDepositAccountSettings.allowWithdrawals + ) + ) + } + } + RecurringAccountAction.RecurringAccountSettingsAction.ToggleDepositFrequencySameAsGroupCenterMeeting -> { + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + depositPeriod = state.recurringDepositAccountSettings.depositPeriod.copy( + depositFrequencySameAsGroupCenterMeeting = !state.recurringDepositAccountSettings + .depositPeriod.depositFrequencySameAsGroupCenterMeeting + ) + ) + ) + } + } + RecurringAccountAction.RecurringAccountSettingsAction.ToggleMandatoryDeposit -> { + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + isMandatory = !state.recurringDepositAccountSettings.isMandatory + ) + ) + } + } + RecurringAccountAction.RecurringAccountSettingsAction.TogglePreMatureClosureApplyPenalInterest -> { + mutableStateFlow.update { + state.copy( + recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( + preMatureClosure = state.recurringDepositAccountSettings.preMatureClosure.copy( + applyPenalInterest = !state.recurringDepositAccountSettings + .preMatureClosure.applyPenalInterest + ) + ) + ) + } + } + } + } + + is RecurringAccountAction.RecurringAccountDetailsAction.SetProductId -> { + mutableStateFlow.update { + it.copy( + recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( + productId = action.productId + ) + ) + } + loadTemplateByProduct() + } } } + } data class RecurringAccountState( val currentStep: Int = 0, - val dialogState: Any? = null, - val currencyIndex: Int = -1, - val currencyError: String? = null, + val totalSteps: Int = 4, + val dialogState: DialogState? = null, + val recurringDepositAccountDetail: RecurringAccountDetailsState = RecurringAccountDetailsState(), + val recurringDepositAccountTemplate: RecurringDepositAccountTemplate = RecurringDepositAccountTemplate(), val recurringDepositAccountSettings: RecurringAccountSettingsState = RecurringAccountSettingsState(), +) { + sealed interface DialogState { + data class Error(val message: String): DialogState + data object Loading: DialogState + data object Success: DialogState + } +} +data class RecurringAccountDetailsState( + // productId is set 6 only for testing. + // It should be set -1 when the implementation of detail screen is finished. + val productId: Int = 6, + val externalId: String = "", + val submittedOnDate: String = "", + val fieldOfficer: FieldOfficerOption? = null, ) data class RecurringAccountSettingsState( + val canDoNext: Boolean = false, val isMandatory: Boolean = false, val adjustAdvancePayments: Boolean = false, val allowWithdrawals: Boolean = false, val lockInPeriod: LockInPeriod = LockInPeriod(), val recurringDepositDetails: RecurringDepositDetails = RecurringDepositDetails(), val depositPeriod: DepositPeriod = DepositPeriod(), - val minimumDepositTerm: MinimumDepositTerm = MinimumDepositTerm(), + val minDepositTerm: MinDepositTerm = MinDepositTerm(), + val maxDepositTerm: MaxDepositTerm = MaxDepositTerm(), val preMatureClosure: PreMatureClosure = PreMatureClosure(), ) { data class LockInPeriod( val frequency: String = "", - val frequencyTypeIndex: Int = -1, - val freqTypeError: String? = null, + val frequencyTypeIndex: Int = 0, ) data class RecurringDepositDetails( @@ -73,25 +510,28 @@ data class RecurringAccountSettingsState( data class DepositPeriod( val period: String = "", - val periodType: Int = -1, - val periodTypeError: String? = null, + val periodType: Int = 0, val depositFrequencySameAsGroupCenterMeeting: Boolean = false, ) - data class MinimumDepositTerm( + data class MinDepositTerm( val frequency: String = "", - val frequencyTypeIndex: Int = -1, - val freqTypeError: String? = null, + val frequencyTypeIndex: Int = 0, + val frequencyTypeError: String? = null, val frequencyAfterInMultiplesOf: String = "", - val frequencyTypeIndexAfterInMultiplesOf: Int = -1, - val freqTypeAfterInMultiplesOfError: String? = null, + val frequencyTypeIndexAfterInMultiplesOf: Int = 0, + ) + + data class MaxDepositTerm( + val frequency: String = "", + val frequencyTypeIndex: Int = -1, + val frequencyTypeError: String? = null, ) data class PreMatureClosure( val applyPenalInterest: Boolean = false, val penalInterest: String = "", val interestPeriodIndex: Int = -1, - val interestPeriodIndexError: String? = null, val minimumBalanceForInterestCalculation: String = "", ) } @@ -101,6 +541,38 @@ sealed class RecurringAccountAction { data class OnStepChange(val index: Int) : RecurringAccountAction() object NavigateBack : RecurringAccountAction() object Finish : RecurringAccountAction() + data object Retry: RecurringAccountAction() + sealed class RecurringAccountDetailsAction: RecurringAccountAction() { + data class SetProductId(val productId: Int): RecurringAccountDetailsAction() + } + + sealed class RecurringAccountSettingsAction : RecurringAccountAction() { + object ToggleMandatoryDeposit: RecurringAccountSettingsAction() + object ToggleAdvancePaymentsTowardsFutureInstallments: RecurringAccountSettingsAction() + object ToggleAllowWithdrawals: RecurringAccountSettingsAction() + data class SetLockInPeriod(val frequency: String): RecurringAccountSettingsAction() + data class SetLockInPeriodType(val frequencyTypeIndex: Int): RecurringAccountSettingsAction() + data class SetRecurringDepositAmount(val depositAmount: String): RecurringAccountSettingsAction() + data class SetDepositPeriod(val period: String): RecurringAccountSettingsAction() + data class SetDepositPeriodType(val periodType: Int): RecurringAccountSettingsAction() + data object ToggleDepositFrequencySameAsGroupCenterMeeting : RecurringAccountSettingsAction() + + data class SetMinDepositTermFreq(val frequency: String): RecurringAccountSettingsAction() + data class SetMinDepositTermFreqType(val frequencyTypeIndex: Int): RecurringAccountSettingsAction() + data class SetMinDepositTermFreqAfterInMultiOf(val frequencyAfterInMultiplesOf: String): RecurringAccountSettingsAction() + data class SetMinDepositTermFreqTypeAfterInMultiOf(val frequencyTypeIndexAfterInMultiplesOf: Int): RecurringAccountSettingsAction() + + + data class SetMaxDepositTermFreq(val frequency: String): RecurringAccountSettingsAction() + data class SetMaxDepositTermFreqType(val frequencyTypeIndex: Int): RecurringAccountSettingsAction() + data object TogglePreMatureClosureApplyPenalInterest: RecurringAccountSettingsAction() + data class SetPreMatureClosurePenalInterest(val penalInterest: String): RecurringAccountSettingsAction() + data class SetPreMatureClosureInterestPeriodIndex(val interestPeriodIndex: Int): RecurringAccountSettingsAction() + data class SetPreMatureClosureMinimumBalanceForInterestCalculation(val minimumBalanceForInterestCalculation: String): RecurringAccountSettingsAction() + + data object OnBackPress : RecurringAccountSettingsAction() + data object OnNextPress : RecurringAccountSettingsAction() + } } sealed class RecurringAccountEvent { diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt index 365a938143..c6d4ad6bed 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt @@ -9,23 +9,282 @@ */ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages +import androidclient.feature.recurringdeposit.generated.resources.Res +import androidclient.feature.recurringdeposit.generated.resources.adjust_advance_payments +import androidclient.feature.recurringdeposit.generated.resources.allow_withdrawals +import androidclient.feature.recurringdeposit.generated.resources.apply_penal_interest +import androidclient.feature.recurringdeposit.generated.resources.back +import androidclient.feature.recurringdeposit.generated.resources.deposit_frequency_same_as_meeting +import androidclient.feature.recurringdeposit.generated.resources.deposit_period +import androidclient.feature.recurringdeposit.generated.resources.for_pre_mature_closure +import androidclient.feature.recurringdeposit.generated.resources.frequency +import androidclient.feature.recurringdeposit.generated.resources.in_multiples_of +import androidclient.feature.recurringdeposit.generated.resources.is_mandatory_deposit +import androidclient.feature.recurringdeposit.generated.resources.lock_in_period +import androidclient.feature.recurringdeposit.generated.resources.maximum_deposit_term +import androidclient.feature.recurringdeposit.generated.resources.minimum_balance_for_interest +import androidclient.feature.recurringdeposit.generated.resources.minimum_deposit_term +import androidclient.feature.recurringdeposit.generated.resources.next +import androidclient.feature.recurringdeposit.generated.resources.penal_interest_percentage +import androidclient.feature.recurringdeposit.generated.resources.period +import androidclient.feature.recurringdeposit.generated.resources.recurring_deposit_amount +import androidclient.feature.recurringdeposit.generated.resources.recurring_deposit_details +import androidclient.feature.recurringdeposit.generated.resources.step_settings +import androidclient.feature.recurringdeposit.generated.resources.type import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.material3.Button +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Checkbox +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.mifos.core.designsystem.component.MifosOutlinedTextField +import com.mifos.core.designsystem.component.MifosTextFieldDropdown +import com.mifos.core.ui.components.MifosCheckBox +import com.mifos.core.ui.components.MifosTwoButtonRow +import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.RecurringAccountAction +import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.RecurringAccountState +import org.jetbrains.compose.resources.stringResource +import org.jetbrains.compose.ui.tooling.preview.Preview + +@OptIn(ExperimentalMaterial3Api::class) @Composable -fun SettingPage(onNext: () -> Unit) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text("Settings Page") +fun SettingPage( + state: RecurringAccountState, + onAction: (RecurringAccountAction.RecurringAccountSettingsAction) -> Unit +) { + + val settingsState = state.recurringDepositAccountSettings + + val scrollState = rememberScrollState() + + Column( + modifier = Modifier + .fillMaxWidth() + .verticalScroll(scrollState), + horizontalAlignment = Alignment.Start + ) { + Text(stringResource(Res.string.step_settings), fontWeight = FontWeight.Bold, fontSize = 18.sp) Spacer(Modifier.height(8.dp)) - Button(onClick = onNext) { - Text("Next Button") + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox(settingsState.isMandatory, onCheckedChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleMandatoryDeposit) }) + Text(stringResource(Res.string.is_mandatory_deposit)) + } + Row(verticalAlignment = Alignment.CenterVertically) { + MifosCheckBox( + text = stringResource(Res.string.adjust_advance_payments), + checked=settingsState.adjustAdvancePayments, + onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAdvancePaymentsTowardsFutureInstallments) } + ) + } + Row(verticalAlignment = Alignment.CenterVertically) { + MifosCheckBox( + text = stringResource(Res.string.allow_withdrawals), + checked=settingsState.allowWithdrawals, + onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAllowWithdrawals) } + ) } + Spacer(Modifier.height(16.dp)) + Text(stringResource(Res.string.lock_in_period), fontWeight = FontWeight.Bold) + MifosOutlinedTextField( + value = settingsState.lockInPeriod.frequency, + onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriod(it)) }, + label = stringResource(Res.string.frequency), + modifier = Modifier.fillMaxWidth() + ) + MifosTextFieldDropdown( + value = if(settingsState.lockInPeriod.frequencyTypeIndex!=-1) { + state.recurringDepositAccountTemplate.lockinPeriodFrequencyTypeOptions?.get(settingsState.lockInPeriod.frequencyTypeIndex)?.value?: "" + }else { + "" + }, + options = state.recurringDepositAccountTemplate.lockinPeriodFrequencyTypeOptions?.map { + it.value?:"" + } ?:emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction( + RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriodType(id) + ) + }, + label = stringResource(Res.string.type), + ) + Spacer(Modifier.height(16.dp)) + Text(stringResource(Res.string.recurring_deposit_details), fontWeight = FontWeight.Bold) + MifosOutlinedTextField( + value = settingsState.recurringDepositDetails.depositAmount, + onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetRecurringDepositAmount(it)) }, + label = stringResource(Res.string.recurring_deposit_amount), + modifier = Modifier.fillMaxWidth() + ) + Spacer(Modifier.height(16.dp)) + Text(stringResource(Res.string.deposit_period), fontWeight = FontWeight.Bold) + MifosOutlinedTextField( + value = settingsState.depositPeriod.period, + onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetDepositPeriod(it)) }, + label = stringResource(Res.string.deposit_period), + modifier = Modifier.fillMaxWidth() + ) + MifosTextFieldDropdown( + value = if(settingsState.depositPeriod.periodType!=-1) { + state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.depositPeriod.periodType)?.value ?: "" + } else "", + options = state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.map { + it.value?:"" + } ?:emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetDepositPeriodType(id)) + }, + label = stringResource(Res.string.type), + modifier = Modifier.fillMaxWidth() + ) + Row(verticalAlignment = Alignment.CenterVertically) { + MifosCheckBox( + text = stringResource(Res.string.deposit_frequency_same_as_meeting), + checked=settingsState.depositPeriod.depositFrequencySameAsGroupCenterMeeting, + onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleDepositFrequencySameAsGroupCenterMeeting) } + ) + } + Spacer(Modifier.height(16.dp)) + Text(stringResource(Res.string.minimum_deposit_term), fontWeight = FontWeight.Bold) + MifosOutlinedTextField( + value = settingsState.minDepositTerm.frequency, + onValueChange = { + onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreq(it)) + }, + label = stringResource(Res.string.frequency), + modifier = Modifier.fillMaxWidth() + ) + MifosTextFieldDropdown( + value = if(settingsState.depositPeriod.periodType!=-1) { + state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.minDepositTerm.frequencyTypeIndex)?.value ?: "" + } else "", + options = state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.map { + it.value?:"" + } ?:emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqType(id)) + }, + label = stringResource(Res.string.type), + modifier = Modifier.fillMaxWidth() + ) + Spacer(Modifier.height(16.dp)) + Text(stringResource(Res.string.in_multiples_of), fontWeight = FontWeight.Bold) + MifosOutlinedTextField( + value = settingsState.minDepositTerm.frequencyAfterInMultiplesOf, + onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqAfterInMultiOf(it)) }, + label = stringResource(Res.string.frequency), + modifier = Modifier.fillMaxWidth() + ) + MifosTextFieldDropdown( + value = if(settingsState.minDepositTerm.frequencyTypeIndexAfterInMultiplesOf!=-1) { + state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.minDepositTerm.frequencyTypeIndexAfterInMultiplesOf)?.value ?: "" + } else "", + options = state.recurringDepositAccountTemplate?.periodFrequencyTypeOptions?.map { + it.value?:"" + } ?:emptyList(), + onValueChanged = {}, + onOptionSelected = { id , name -> + onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqTypeAfterInMultiOf(id)) + }, + label = stringResource(Res.string.type), + modifier = Modifier.fillMaxWidth() + ) + Spacer(Modifier.height(16.dp)) + Text(stringResource(Res.string.maximum_deposit_term), fontWeight = FontWeight.Bold) + MifosOutlinedTextField( + value = settingsState.maxDepositTerm.frequency, + onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMaxDepositTermFreq(it)) }, + label = stringResource(Res.string.frequency), + modifier = Modifier.fillMaxWidth() + ) + MifosTextFieldDropdown( + value = if(settingsState.maxDepositTerm.frequencyTypeIndex!=-1) { + state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.maxDepositTerm.frequencyTypeIndex)?.value ?: "" + } else "", + options = state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.map { + it.value?:"" + } ?:emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMaxDepositTermFreqType(id)) + }, + label = stringResource(Res.string.type), + modifier = Modifier.fillMaxWidth() + ) + Spacer(Modifier.height(16.dp)) + Text(stringResource(Res.string.for_pre_mature_closure), fontWeight = FontWeight.Bold) + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox( + settingsState.preMatureClosure.applyPenalInterest, + onCheckedChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.TogglePreMatureClosureApplyPenalInterest) } + ) + Text(stringResource(Res.string.apply_penal_interest)) + } + MifosOutlinedTextField( + value = settingsState.preMatureClosure.penalInterest, + onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosurePenalInterest(it)) }, + label = stringResource(Res.string.penal_interest_percentage), + modifier = Modifier.fillMaxWidth() + ) + MifosTextFieldDropdown( + value = if(settingsState.preMatureClosure.interestPeriodIndex!=-1) { + state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" + } else "", + options = state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.map { + it.value?:"" + } ?:emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureInterestPeriodIndex(id)) + }, + label = stringResource(Res.string.period), + modifier = Modifier.fillMaxWidth() + ) + MifosOutlinedTextField( + value = settingsState.preMatureClosure.minimumBalanceForInterestCalculation, + onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureMinimumBalanceForInterestCalculation(it)) }, + label = stringResource(Res.string.minimum_balance_for_interest), + modifier = Modifier.fillMaxWidth() + ) + Spacer(Modifier.height(24.dp)) + MifosTwoButtonRow( + firstBtnText = stringResource(Res.string.back), + secondBtnText = stringResource(Res.string.next), + onFirstBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnBackPress) }, + onSecondBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnNextPress) }, + isButtonIconVisible = true, + isSecondButtonEnabled = settingsState.preMatureClosure.penalInterest.isNotBlank() && + settingsState.preMatureClosure.minimumBalanceForInterestCalculation.isNotBlank() && + settingsState.recurringDepositDetails.depositAmount.isNotBlank() && + settingsState.depositPeriod.period.isNotBlank() && + settingsState.lockInPeriod.frequency.isNotBlank() && + settingsState.minDepositTerm.frequency.isNotBlank() && + settingsState.minDepositTerm.frequencyAfterInMultiplesOf.isNotBlank() && + settingsState.maxDepositTerm.frequency.isNotBlank(), + ) + } } + +@Preview +@Composable +private fun SettingPagePreview() { + SettingPage( + state = RecurringAccountState(), + onAction = {} + ) +} From f36b374ae575bfabd65d9698ba81ce3f0e7af483 Mon Sep 17 00:00:00 2001 From: kalpesh Date: Wed, 22 Oct 2025 01:01:32 +0530 Subject: [PATCH 02/19] feat: add keyboard options for number input in recurring deposit settings --- .../pages/SettingsPage.kt | 53 ++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt index c6d4ad6bed..573f495b57 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt @@ -36,8 +36,8 @@ import androidx.compose.foundation.layout.Row 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.foundation.rememberScrollState +import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Checkbox import androidx.compose.material3.ExperimentalMaterial3Api @@ -46,9 +46,12 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.mifos.core.designsystem.component.MifosOutlinedTextField +import com.mifos.core.designsystem.component.MifosTextFieldConfig import com.mifos.core.designsystem.component.MifosTextFieldDropdown import com.mifos.core.ui.components.MifosCheckBox import com.mifos.core.ui.components.MifosTwoButtonRow @@ -101,6 +104,12 @@ fun SettingPage( value = settingsState.lockInPeriod.frequency, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriod(it)) }, label = stringResource(Res.string.frequency), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next + ), + ), modifier = Modifier.fillMaxWidth() ) MifosTextFieldDropdown( @@ -126,6 +135,12 @@ fun SettingPage( value = settingsState.recurringDepositDetails.depositAmount, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetRecurringDepositAmount(it)) }, label = stringResource(Res.string.recurring_deposit_amount), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next + ), + ), modifier = Modifier.fillMaxWidth() ) Spacer(Modifier.height(16.dp)) @@ -134,6 +149,12 @@ fun SettingPage( value = settingsState.depositPeriod.period, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetDepositPeriod(it)) }, label = stringResource(Res.string.deposit_period), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next + ), + ), modifier = Modifier.fillMaxWidth() ) MifosTextFieldDropdown( @@ -165,6 +186,12 @@ fun SettingPage( onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreq(it)) }, label = stringResource(Res.string.frequency), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next + ), + ), modifier = Modifier.fillMaxWidth() ) MifosTextFieldDropdown( @@ -187,6 +214,12 @@ fun SettingPage( value = settingsState.minDepositTerm.frequencyAfterInMultiplesOf, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqAfterInMultiOf(it)) }, label = stringResource(Res.string.frequency), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next + ), + ), modifier = Modifier.fillMaxWidth() ) MifosTextFieldDropdown( @@ -209,6 +242,12 @@ fun SettingPage( value = settingsState.maxDepositTerm.frequency, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMaxDepositTermFreq(it)) }, label = stringResource(Res.string.frequency), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next + ), + ), modifier = Modifier.fillMaxWidth() ) MifosTextFieldDropdown( @@ -238,6 +277,12 @@ fun SettingPage( value = settingsState.preMatureClosure.penalInterest, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosurePenalInterest(it)) }, label = stringResource(Res.string.penal_interest_percentage), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next + ), + ), modifier = Modifier.fillMaxWidth() ) MifosTextFieldDropdown( @@ -258,6 +303,12 @@ fun SettingPage( value = settingsState.preMatureClosure.minimumBalanceForInterestCalculation, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureMinimumBalanceForInterestCalculation(it)) }, label = stringResource(Res.string.minimum_balance_for_interest), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next + ), + ), modifier = Modifier.fillMaxWidth() ) Spacer(Modifier.height(24.dp)) From f6451c8a051e55b4c21c11323b272cbc99b5d9e1 Mon Sep 17 00:00:00 2001 From: kalpesh Date: Wed, 22 Oct 2025 01:07:09 +0530 Subject: [PATCH 03/19] feat: add keyboard options for number input in recurring deposit settings --- .../src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt | 2 +- .../core/network/datamanager/DataManagerRecurringAccount.kt | 1 - .../kotlin/com/mifos/core/network/di/DataMangerModule.kt | 3 +-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt b/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt index 1ec9c43afc..4d14674e00 100644 --- a/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt +++ b/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt @@ -31,9 +31,9 @@ import com.mifos.feature.loan.di.LoanModule import com.mifos.feature.note.di.NoteModule import com.mifos.feature.offline.di.OfflineModule import com.mifos.feature.path.tracking.di.PathTrackingModule +import com.mifos.feature.recurringDeposit.di.RecurringDepositModule import com.mifos.feature.report.di.ReportModule import com.mifos.feature.savings.di.SavingsModule -import com.mifos.feature.recurringDeposit.di.RecurringDepositModule import com.mifos.feature.search.di.SearchModule import com.mifos.feature.settings.di.SettingsModule import com.mifos.room.di.DaoModule diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerRecurringAccount.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerRecurringAccount.kt index 1fe6795272..2fa161c073 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerRecurringAccount.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/datamanager/DataManagerRecurringAccount.kt @@ -9,7 +9,6 @@ */ package com.mifos.core.network.datamanager -import com.mifos.core.datastore.UserPreferencesRepository import com.mifos.core.model.objects.payloads.RecurringDepositAccountPayload import com.mifos.core.network.BaseApiManager import com.mifos.room.entities.accounts.recurring.RecurringDeposit diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt index c44146cd09..1a90f3c8ed 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/di/DataMangerModule.kt @@ -31,7 +31,6 @@ import com.mifos.core.network.datamanager.DataManagerSearch import com.mifos.core.network.datamanager.DataManagerShare import com.mifos.core.network.datamanager.DataManagerStaff import com.mifos.core.network.datamanager.DataManagerSurveys -import org.koin.core.module.dsl.singleOf import org.koin.dsl.module val DataManagerModule = module { @@ -54,6 +53,6 @@ val DataManagerModule = module { single { DataManagerStaff(get(), get(), get()) } single { DataManagerSurveys(get(), get(), get()) } single { DataManagerIdentifiers(get()) } - single{ DataManagerRecurringAccount(get())} + single { DataManagerRecurringAccount(get()) } single { DataManagerShare(get()) } } From c95396f273b196bcd417e68667ddb21e54e63a73 Mon Sep 17 00:00:00 2001 From: kalpesh Date: Wed, 22 Oct 2025 01:08:07 +0530 Subject: [PATCH 04/19] fix: add missing commas and improve formatting in recurring deposit account files --- .../di/RecurringDepositModule.kt | 12 +- .../RecurringAccountRoute.kt | 4 +- .../RecurringAccountScreen.kt | 13 +- .../RecurringAccountViewModel.kt | 193 +++++++++--------- .../pages/SettingsPage.kt | 145 ++++++------- 5 files changed, 187 insertions(+), 180 deletions(-) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/di/RecurringDepositModule.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/di/RecurringDepositModule.kt index ad37824760..bf5e9f53fa 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/di/RecurringDepositModule.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/di/RecurringDepositModule.kt @@ -1,10 +1,18 @@ +/* + * Copyright 2025 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/android-client/blob/master/LICENSE.md + */ package com.mifos.feature.recurringDeposit.di import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.RecurringAccountViewModel import org.koin.core.module.dsl.viewModelOf import org.koin.dsl.module - val RecurringDepositModule = module { viewModelOf(::RecurringAccountViewModel) -} \ No newline at end of file +} diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountRoute.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountRoute.kt index 7e2d44c0b1..b84e5f18c9 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountRoute.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountRoute.kt @@ -16,7 +16,7 @@ import kotlinx.serialization.Serializable @Serializable data class RecurringAccountRoute( - val clientId: Int = -1 + val clientId: Int = -1, ) fun NavGraphBuilder.recurringAccountDestination() { @@ -29,7 +29,7 @@ fun NavGraphBuilder.recurringAccountDestination() { } fun NavController.navigateToRecurringAccountRoute( - clientId: Int + clientId: Int, ) { this.navigate( RecurringAccountRoute(clientId = clientId), diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt index f22e59deb8..7558a09330 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt @@ -116,10 +116,10 @@ private fun RecurringAccountScaffold( @Composable fun RecurringDepositAccountDialogBox( - state: RecurringAccountState -){ - when(state.dialogState){ - is RecurringAccountState.DialogState.Error ->{ + state: RecurringAccountState, +) { + when (state.dialogState) { + is RecurringAccountState.DialogState.Error -> { MifosErrorComponent( message = state.dialogState.message, isRetryEnabled = true, @@ -128,10 +128,9 @@ fun RecurringDepositAccountDialogBox( }, ) } - RecurringAccountState.DialogState.Loading ->{ - + RecurringAccountState.DialogState.Loading -> { } RecurringAccountState.DialogState.Success -> TODO() null -> TODO() } -} \ No newline at end of file +} diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt index 6d6bdef4f6..d7126de51e 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt @@ -27,12 +27,12 @@ import kotlinx.coroutines.launch class RecurringAccountViewModel( savedStateHandle: SavedStateHandle, private val networkMonitor: NetworkMonitor, - private val recurringAccountRepository: RecurringAccountRepository + private val recurringAccountRepository: RecurringAccountRepository, ) : BaseViewModel< RecurringAccountState, RecurringAccountEvent, RecurringAccountAction, ->(RecurringAccountState()) { + >(RecurringAccountState()) { init { observeNetwork() @@ -42,14 +42,14 @@ class RecurringAccountViewModel( private fun setLoadingState() { mutableStateFlow.update { it.copy( - dialogState = DialogState.Loading + dialogState = DialogState.Loading, ) } } private fun setErrorState(message: String) { mutableStateFlow.update { it.copy( - dialogState = DialogState.Error(message) + dialogState = DialogState.Error(message), ) } } @@ -58,7 +58,7 @@ class RecurringAccountViewModel( viewModelScope.launch { val isConnected = networkMonitor.isOnline.first() - if(isConnected) { + if (isConnected) { // loadTemplate() // Used only for testing purpose // Must be removed from here after the implementation of detail screen is finished. @@ -80,8 +80,8 @@ class RecurringAccountViewModel( private fun loadTemplate() { viewModelScope.launch { - recurringAccountRepository.getRecuttingAccountTemplate().collect {templateState-> - when(templateState) { + recurringAccountRepository.getRecuttingAccountTemplate().collect { templateState -> + when (templateState) { is DataState.Error -> { setErrorState(message = templateState.message) } @@ -92,12 +92,11 @@ class RecurringAccountViewModel( mutableStateFlow.update { it.copy( dialogState = null, - recurringDepositAccountTemplate = templateState.data + recurringDepositAccountTemplate = templateState.data, ) } } } - } } } @@ -106,8 +105,8 @@ class RecurringAccountViewModel( recurringAccountRepository.getRecuttingAccountTemplateByProduct( clientId = clientId, productId = state.recurringDepositAccountDetail.productId, - ).collect {templateState-> - when(templateState) { + ).collect { templateState -> + when (templateState) { is DataState.Error -> { setErrorState(message = templateState.message) } @@ -118,12 +117,11 @@ class RecurringAccountViewModel( mutableStateFlow.update { it.copy( dialogState = null, - recurringDepositAccountTemplate = templateState.data + recurringDepositAccountTemplate = templateState.data, ) } } } - } } } @@ -232,9 +230,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( depositPeriod = state.recurringDepositAccountSettings.depositPeriod.copy( - period = action.period - ) - ) + period = action.period, + ), + ), ) } } @@ -244,22 +242,21 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( depositPeriod = state.recurringDepositAccountSettings.depositPeriod.copy( - periodType = action.periodType - ) - ) + periodType = action.periodType, + ), + ), ) } } - is RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriod -> { mutableStateFlow.update { state -> state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( lockInPeriod = state.recurringDepositAccountSettings.lockInPeriod.copy( - frequency = action.frequency - ) - ) + frequency = action.frequency, + ), + ), ) } } @@ -268,9 +265,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( lockInPeriod = state.recurringDepositAccountSettings.lockInPeriod.copy( - frequencyTypeIndex = action.frequencyTypeIndex - ) - ) + frequencyTypeIndex = action.frequencyTypeIndex, + ), + ), ) } } @@ -280,9 +277,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( minDepositTerm = state.recurringDepositAccountSettings.minDepositTerm.copy( - frequency = action.frequency - ) - ) + frequency = action.frequency, + ), + ), ) } } @@ -292,11 +289,10 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( minDepositTerm = state.recurringDepositAccountSettings.minDepositTerm.copy( - frequencyTypeIndex = action.frequencyTypeIndex - ) - ) + frequencyTypeIndex = action.frequencyTypeIndex, + ), + ), ) - } } @@ -305,9 +301,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( minDepositTerm = state.recurringDepositAccountSettings.minDepositTerm.copy( - frequencyAfterInMultiplesOf = action.frequencyAfterInMultiplesOf - ) - ) + frequencyAfterInMultiplesOf = action.frequencyAfterInMultiplesOf, + ), + ), ) } } @@ -316,9 +312,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( minDepositTerm = state.recurringDepositAccountSettings.minDepositTerm.copy( - frequencyTypeIndexAfterInMultiplesOf = action.frequencyTypeIndexAfterInMultiplesOf - ) - ) + frequencyTypeIndexAfterInMultiplesOf = action.frequencyTypeIndexAfterInMultiplesOf, + ), + ), ) } } @@ -328,9 +324,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( maxDepositTerm = state.recurringDepositAccountSettings.maxDepositTerm.copy( - frequency = action.frequency - ) - ) + frequency = action.frequency, + ), + ), ) } } @@ -340,11 +336,10 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( maxDepositTerm = state.recurringDepositAccountSettings.maxDepositTerm.copy( - frequencyTypeIndex = action.frequencyTypeIndex - ) - ) + frequencyTypeIndex = action.frequencyTypeIndex, + ), + ), ) - } } @@ -353,9 +348,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( preMatureClosure = state.recurringDepositAccountSettings.preMatureClosure.copy( - interestPeriodIndex = action.interestPeriodIndex - ) - ) + interestPeriodIndex = action.interestPeriodIndex, + ), + ), ) } } @@ -365,9 +360,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( preMatureClosure = state.recurringDepositAccountSettings.preMatureClosure.copy( - minimumBalanceForInterestCalculation = action.minimumBalanceForInterestCalculation - ) - ) + minimumBalanceForInterestCalculation = action.minimumBalanceForInterestCalculation, + ), + ), ) } } @@ -376,9 +371,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( preMatureClosure = state.recurringDepositAccountSettings.preMatureClosure.copy( - penalInterest = action.penalInterest - ) - ) + penalInterest = action.penalInterest, + ), + ), ) } } @@ -388,18 +383,18 @@ class RecurringAccountViewModel( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( recurringDepositDetails = state.recurringDepositAccountSettings .recurringDepositDetails.copy( - depositAmount = action.depositAmount - ) - ) + depositAmount = action.depositAmount, + ), + ), ) } } - RecurringAccountAction.RecurringAccountSettingsAction.ToggleAdvancePaymentsTowardsFutureInstallments ->{ + RecurringAccountAction.RecurringAccountSettingsAction.ToggleAdvancePaymentsTowardsFutureInstallments -> { mutableStateFlow.update { state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( - adjustAdvancePayments = !state.recurringDepositAccountSettings.adjustAdvancePayments - ) + adjustAdvancePayments = !state.recurringDepositAccountSettings.adjustAdvancePayments, + ), ) } } @@ -407,8 +402,8 @@ class RecurringAccountViewModel( mutableStateFlow.update { state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( - allowWithdrawals = !state.recurringDepositAccountSettings.allowWithdrawals - ) + allowWithdrawals = !state.recurringDepositAccountSettings.allowWithdrawals, + ), ) } } @@ -418,9 +413,9 @@ class RecurringAccountViewModel( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( depositPeriod = state.recurringDepositAccountSettings.depositPeriod.copy( depositFrequencySameAsGroupCenterMeeting = !state.recurringDepositAccountSettings - .depositPeriod.depositFrequencySameAsGroupCenterMeeting - ) - ) + .depositPeriod.depositFrequencySameAsGroupCenterMeeting, + ), + ), ) } } @@ -428,8 +423,8 @@ class RecurringAccountViewModel( mutableStateFlow.update { state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( - isMandatory = !state.recurringDepositAccountSettings.isMandatory - ) + isMandatory = !state.recurringDepositAccountSettings.isMandatory, + ), ) } } @@ -439,9 +434,9 @@ class RecurringAccountViewModel( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( preMatureClosure = state.recurringDepositAccountSettings.preMatureClosure.copy( applyPenalInterest = !state.recurringDepositAccountSettings - .preMatureClosure.applyPenalInterest - ) - ) + .preMatureClosure.applyPenalInterest, + ), + ), ) } } @@ -452,15 +447,14 @@ class RecurringAccountViewModel( mutableStateFlow.update { it.copy( recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( - productId = action.productId - ) + productId = action.productId, + ), ) } loadTemplateByProduct() } } } - } data class RecurringAccountState( @@ -472,9 +466,9 @@ data class RecurringAccountState( val recurringDepositAccountSettings: RecurringAccountSettingsState = RecurringAccountSettingsState(), ) { sealed interface DialogState { - data class Error(val message: String): DialogState - data object Loading: DialogState - data object Success: DialogState + data class Error(val message: String) : DialogState + data object Loading : DialogState + data object Success : DialogState } } @@ -541,34 +535,33 @@ sealed class RecurringAccountAction { data class OnStepChange(val index: Int) : RecurringAccountAction() object NavigateBack : RecurringAccountAction() object Finish : RecurringAccountAction() - data object Retry: RecurringAccountAction() - sealed class RecurringAccountDetailsAction: RecurringAccountAction() { - data class SetProductId(val productId: Int): RecurringAccountDetailsAction() + data object Retry : RecurringAccountAction() + sealed class RecurringAccountDetailsAction : RecurringAccountAction() { + data class SetProductId(val productId: Int) : RecurringAccountDetailsAction() } sealed class RecurringAccountSettingsAction : RecurringAccountAction() { - object ToggleMandatoryDeposit: RecurringAccountSettingsAction() - object ToggleAdvancePaymentsTowardsFutureInstallments: RecurringAccountSettingsAction() - object ToggleAllowWithdrawals: RecurringAccountSettingsAction() - data class SetLockInPeriod(val frequency: String): RecurringAccountSettingsAction() - data class SetLockInPeriodType(val frequencyTypeIndex: Int): RecurringAccountSettingsAction() - data class SetRecurringDepositAmount(val depositAmount: String): RecurringAccountSettingsAction() - data class SetDepositPeriod(val period: String): RecurringAccountSettingsAction() - data class SetDepositPeriodType(val periodType: Int): RecurringAccountSettingsAction() + object ToggleMandatoryDeposit : RecurringAccountSettingsAction() + object ToggleAdvancePaymentsTowardsFutureInstallments : RecurringAccountSettingsAction() + object ToggleAllowWithdrawals : RecurringAccountSettingsAction() + data class SetLockInPeriod(val frequency: String) : RecurringAccountSettingsAction() + data class SetLockInPeriodType(val frequencyTypeIndex: Int) : RecurringAccountSettingsAction() + data class SetRecurringDepositAmount(val depositAmount: String) : RecurringAccountSettingsAction() + data class SetDepositPeriod(val period: String) : RecurringAccountSettingsAction() + data class SetDepositPeriodType(val periodType: Int) : RecurringAccountSettingsAction() data object ToggleDepositFrequencySameAsGroupCenterMeeting : RecurringAccountSettingsAction() - data class SetMinDepositTermFreq(val frequency: String): RecurringAccountSettingsAction() - data class SetMinDepositTermFreqType(val frequencyTypeIndex: Int): RecurringAccountSettingsAction() - data class SetMinDepositTermFreqAfterInMultiOf(val frequencyAfterInMultiplesOf: String): RecurringAccountSettingsAction() - data class SetMinDepositTermFreqTypeAfterInMultiOf(val frequencyTypeIndexAfterInMultiplesOf: Int): RecurringAccountSettingsAction() - - - data class SetMaxDepositTermFreq(val frequency: String): RecurringAccountSettingsAction() - data class SetMaxDepositTermFreqType(val frequencyTypeIndex: Int): RecurringAccountSettingsAction() - data object TogglePreMatureClosureApplyPenalInterest: RecurringAccountSettingsAction() - data class SetPreMatureClosurePenalInterest(val penalInterest: String): RecurringAccountSettingsAction() - data class SetPreMatureClosureInterestPeriodIndex(val interestPeriodIndex: Int): RecurringAccountSettingsAction() - data class SetPreMatureClosureMinimumBalanceForInterestCalculation(val minimumBalanceForInterestCalculation: String): RecurringAccountSettingsAction() + data class SetMinDepositTermFreq(val frequency: String) : RecurringAccountSettingsAction() + data class SetMinDepositTermFreqType(val frequencyTypeIndex: Int) : RecurringAccountSettingsAction() + data class SetMinDepositTermFreqAfterInMultiOf(val frequencyAfterInMultiplesOf: String) : RecurringAccountSettingsAction() + data class SetMinDepositTermFreqTypeAfterInMultiOf(val frequencyTypeIndexAfterInMultiplesOf: Int) : RecurringAccountSettingsAction() + + data class SetMaxDepositTermFreq(val frequency: String) : RecurringAccountSettingsAction() + data class SetMaxDepositTermFreqType(val frequencyTypeIndex: Int) : RecurringAccountSettingsAction() + data object TogglePreMatureClosureApplyPenalInterest : RecurringAccountSettingsAction() + data class SetPreMatureClosurePenalInterest(val penalInterest: String) : RecurringAccountSettingsAction() + data class SetPreMatureClosureInterestPeriodIndex(val interestPeriodIndex: Int) : RecurringAccountSettingsAction() + data class SetPreMatureClosureMinimumBalanceForInterestCalculation(val minimumBalanceForInterestCalculation: String) : RecurringAccountSettingsAction() data object OnBackPress : RecurringAccountSettingsAction() data object OnNextPress : RecurringAccountSettingsAction() diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt index 573f495b57..e7940be1c7 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt @@ -60,14 +60,12 @@ import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.RecurringAc import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.ui.tooling.preview.Preview - @OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingPage( state: RecurringAccountState, - onAction: (RecurringAccountAction.RecurringAccountSettingsAction) -> Unit + onAction: (RecurringAccountAction.RecurringAccountSettingsAction) -> Unit, ) { - val settingsState = state.recurringDepositAccountSettings val scrollState = rememberScrollState() @@ -76,7 +74,7 @@ fun SettingPage( modifier = Modifier .fillMaxWidth() .verticalScroll(scrollState), - horizontalAlignment = Alignment.Start + horizontalAlignment = Alignment.Start, ) { Text(stringResource(Res.string.step_settings), fontWeight = FontWeight.Bold, fontSize = 18.sp) Spacer(Modifier.height(8.dp)) @@ -87,15 +85,15 @@ fun SettingPage( Row(verticalAlignment = Alignment.CenterVertically) { MifosCheckBox( text = stringResource(Res.string.adjust_advance_payments), - checked=settingsState.adjustAdvancePayments, - onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAdvancePaymentsTowardsFutureInstallments) } + checked = settingsState.adjustAdvancePayments, + onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAdvancePaymentsTowardsFutureInstallments) }, ) } Row(verticalAlignment = Alignment.CenterVertically) { MifosCheckBox( text = stringResource(Res.string.allow_withdrawals), - checked=settingsState.allowWithdrawals, - onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAllowWithdrawals) } + checked = settingsState.allowWithdrawals, + onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAllowWithdrawals) }, ) } Spacer(Modifier.height(16.dp)) @@ -107,24 +105,24 @@ fun SettingPage( config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next + imeAction = ImeAction.Next, ), ), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) MifosTextFieldDropdown( - value = if(settingsState.lockInPeriod.frequencyTypeIndex!=-1) { - state.recurringDepositAccountTemplate.lockinPeriodFrequencyTypeOptions?.get(settingsState.lockInPeriod.frequencyTypeIndex)?.value?: "" - }else { + value = if (settingsState.lockInPeriod.frequencyTypeIndex != -1) { + state.recurringDepositAccountTemplate.lockinPeriodFrequencyTypeOptions?.get(settingsState.lockInPeriod.frequencyTypeIndex)?.value ?: "" + } else { "" }, options = state.recurringDepositAccountTemplate.lockinPeriodFrequencyTypeOptions?.map { - it.value?:"" - } ?:emptyList(), + it.value ?: "" + } ?: emptyList(), onValueChanged = {}, onOptionSelected = { id, name -> onAction( - RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriodType(id) + RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriodType(id), ) }, label = stringResource(Res.string.type), @@ -138,10 +136,10 @@ fun SettingPage( config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next + imeAction = ImeAction.Next, ), ), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) Spacer(Modifier.height(16.dp)) Text(stringResource(Res.string.deposit_period), fontWeight = FontWeight.Bold) @@ -152,30 +150,32 @@ fun SettingPage( config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next + imeAction = ImeAction.Next, ), ), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) MifosTextFieldDropdown( - value = if(settingsState.depositPeriod.periodType!=-1) { + value = if (settingsState.depositPeriod.periodType != -1) { state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.depositPeriod.periodType)?.value ?: "" - } else "", + } else { + "" + }, options = state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.map { - it.value?:"" - } ?:emptyList(), + it.value ?: "" + } ?: emptyList(), onValueChanged = {}, onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetDepositPeriodType(id)) }, label = stringResource(Res.string.type), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) Row(verticalAlignment = Alignment.CenterVertically) { MifosCheckBox( text = stringResource(Res.string.deposit_frequency_same_as_meeting), - checked=settingsState.depositPeriod.depositFrequencySameAsGroupCenterMeeting, - onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleDepositFrequencySameAsGroupCenterMeeting) } + checked = settingsState.depositPeriod.depositFrequencySameAsGroupCenterMeeting, + onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleDepositFrequencySameAsGroupCenterMeeting) }, ) } Spacer(Modifier.height(16.dp)) @@ -189,24 +189,26 @@ fun SettingPage( config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next + imeAction = ImeAction.Next, ), ), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) MifosTextFieldDropdown( - value = if(settingsState.depositPeriod.periodType!=-1) { + value = if (settingsState.depositPeriod.periodType != -1) { state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.minDepositTerm.frequencyTypeIndex)?.value ?: "" - } else "", + } else { + "" + }, options = state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.map { - it.value?:"" - } ?:emptyList(), + it.value ?: "" + } ?: emptyList(), onValueChanged = {}, onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqType(id)) }, label = stringResource(Res.string.type), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) Spacer(Modifier.height(16.dp)) Text(stringResource(Res.string.in_multiples_of), fontWeight = FontWeight.Bold) @@ -217,24 +219,26 @@ fun SettingPage( config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next + imeAction = ImeAction.Next, ), ), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) MifosTextFieldDropdown( - value = if(settingsState.minDepositTerm.frequencyTypeIndexAfterInMultiplesOf!=-1) { + value = if (settingsState.minDepositTerm.frequencyTypeIndexAfterInMultiplesOf != -1) { state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.minDepositTerm.frequencyTypeIndexAfterInMultiplesOf)?.value ?: "" - } else "", + } else { + "" + }, options = state.recurringDepositAccountTemplate?.periodFrequencyTypeOptions?.map { - it.value?:"" - } ?:emptyList(), + it.value ?: "" + } ?: emptyList(), onValueChanged = {}, - onOptionSelected = { id , name -> + onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqTypeAfterInMultiOf(id)) }, label = stringResource(Res.string.type), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) Spacer(Modifier.height(16.dp)) Text(stringResource(Res.string.maximum_deposit_term), fontWeight = FontWeight.Bold) @@ -245,31 +249,33 @@ fun SettingPage( config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next + imeAction = ImeAction.Next, ), ), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) MifosTextFieldDropdown( - value = if(settingsState.maxDepositTerm.frequencyTypeIndex!=-1) { + value = if (settingsState.maxDepositTerm.frequencyTypeIndex != -1) { state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.maxDepositTerm.frequencyTypeIndex)?.value ?: "" - } else "", + } else { + "" + }, options = state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.map { - it.value?:"" - } ?:emptyList(), + it.value ?: "" + } ?: emptyList(), onValueChanged = {}, onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMaxDepositTermFreqType(id)) }, label = stringResource(Res.string.type), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) Spacer(Modifier.height(16.dp)) Text(stringResource(Res.string.for_pre_mature_closure), fontWeight = FontWeight.Bold) Row(verticalAlignment = Alignment.CenterVertically) { Checkbox( settingsState.preMatureClosure.applyPenalInterest, - onCheckedChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.TogglePreMatureClosureApplyPenalInterest) } + onCheckedChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.TogglePreMatureClosureApplyPenalInterest) }, ) Text(stringResource(Res.string.apply_penal_interest)) } @@ -280,24 +286,26 @@ fun SettingPage( config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next + imeAction = ImeAction.Next, ), ), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) MifosTextFieldDropdown( - value = if(settingsState.preMatureClosure.interestPeriodIndex!=-1) { + value = if (settingsState.preMatureClosure.interestPeriodIndex != -1) { state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" - } else "", + } else { + "" + }, options = state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.map { - it.value?:"" - } ?:emptyList(), + it.value ?: "" + } ?: emptyList(), onValueChanged = {}, onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureInterestPeriodIndex(id)) }, label = stringResource(Res.string.period), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) MifosOutlinedTextField( value = settingsState.preMatureClosure.minimumBalanceForInterestCalculation, @@ -306,28 +314,27 @@ fun SettingPage( config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next + imeAction = ImeAction.Next, ), ), - modifier = Modifier.fillMaxWidth() + modifier = Modifier.fillMaxWidth(), ) Spacer(Modifier.height(24.dp)) MifosTwoButtonRow( firstBtnText = stringResource(Res.string.back), secondBtnText = stringResource(Res.string.next), - onFirstBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnBackPress) }, + onFirstBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnBackPress) }, onSecondBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnNextPress) }, isButtonIconVisible = true, isSecondButtonEnabled = settingsState.preMatureClosure.penalInterest.isNotBlank() && - settingsState.preMatureClosure.minimumBalanceForInterestCalculation.isNotBlank() && - settingsState.recurringDepositDetails.depositAmount.isNotBlank() && - settingsState.depositPeriod.period.isNotBlank() && - settingsState.lockInPeriod.frequency.isNotBlank() && - settingsState.minDepositTerm.frequency.isNotBlank() && - settingsState.minDepositTerm.frequencyAfterInMultiplesOf.isNotBlank() && - settingsState.maxDepositTerm.frequency.isNotBlank(), + settingsState.preMatureClosure.minimumBalanceForInterestCalculation.isNotBlank() && + settingsState.recurringDepositDetails.depositAmount.isNotBlank() && + settingsState.depositPeriod.period.isNotBlank() && + settingsState.lockInPeriod.frequency.isNotBlank() && + settingsState.minDepositTerm.frequency.isNotBlank() && + settingsState.minDepositTerm.frequencyAfterInMultiplesOf.isNotBlank() && + settingsState.maxDepositTerm.frequency.isNotBlank(), ) - } } @@ -336,6 +343,6 @@ fun SettingPage( private fun SettingPagePreview() { SettingPage( state = RecurringAccountState(), - onAction = {} + onAction = {}, ) } From 8d71c754fde2d6eabddefbf1d365db0ad3c57677 Mon Sep 17 00:00:00 2001 From: kalpesh Date: Wed, 22 Oct 2025 01:10:35 +0530 Subject: [PATCH 05/19] spotless fixes --- core/model/objects/payloads/RecurringDepositAccountPayload.kt | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 core/model/objects/payloads/RecurringDepositAccountPayload.kt diff --git a/core/model/objects/payloads/RecurringDepositAccountPayload.kt b/core/model/objects/payloads/RecurringDepositAccountPayload.kt deleted file mode 100644 index 139597f9cb..0000000000 --- a/core/model/objects/payloads/RecurringDepositAccountPayload.kt +++ /dev/null @@ -1,2 +0,0 @@ - - From 142f37c7477b18c90d188fbd8093bdd2bee12050 Mon Sep 17 00:00:00 2001 From: kalpesh Date: Wed, 22 Oct 2025 12:52:20 +0530 Subject: [PATCH 06/19] feat: implement loading state indicator and refactor dialog state management in recurring account --- .../RecurringAccountScreen.kt | 5 +++-- .../RecurringAccountViewModel.kt | 12 ++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt index 7558a09330..9ca6e52a05 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt @@ -25,6 +25,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.mifos.core.designsystem.component.MifosScaffold import com.mifos.core.ui.components.MifosErrorComponent +import com.mifos.core.ui.components.MifosProgressIndicator import com.mifos.core.ui.components.MifosStepper import com.mifos.core.ui.components.Step import com.mifos.core.ui.util.EventsEffect @@ -129,8 +130,8 @@ fun RecurringDepositAccountDialogBox( ) } RecurringAccountState.DialogState.Loading -> { + MifosProgressIndicator() } - RecurringAccountState.DialogState.Success -> TODO() - null -> TODO() + null -> {} } } diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt index d7126de51e..740c8e25cf 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt @@ -160,18 +160,15 @@ class RecurringAccountViewModel( recurringAccountRepository.createRecurringDepositAccount(payload).collect { dataState -> when (dataState) { is DataState.Error -> { - mutableStateFlow.update { - it.copy(dialogState = DialogState.Error(dataState.message)) - } + setErrorState(dataState.message) + } is DataState.Loading -> { - mutableStateFlow.update { - it.copy(dialogState = DialogState.Loading) - } + setLoadingState() } is DataState.Success -> { mutableStateFlow.update { - it.copy(dialogState = DialogState.Success) + it.copy(dialogState = null) } } } @@ -468,7 +465,6 @@ data class RecurringAccountState( sealed interface DialogState { data class Error(val message: String) : DialogState data object Loading : DialogState - data object Success : DialogState } } From acf62090e625ef03a759d2db2f782270373b4e11 Mon Sep 17 00:00:00 2001 From: kalpesh Date: Wed, 22 Oct 2025 12:53:25 +0530 Subject: [PATCH 07/19] feat: add RecurringDepositAccountDialogBox to RecurringAccountScreen --- .../newRecurringDepositAccount/RecurringAccountScreen.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt index 9ca6e52a05..19deb6e42a 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt @@ -53,6 +53,8 @@ internal fun RecurringAccountScreen( } } + RecurringDepositAccountDialogBox(state = state) + RecurringAccountScaffold( modifier = modifier, state = state, From e1f16fb8a1d7c77595fa68f3d982840fd0b96d84 Mon Sep 17 00:00:00 2001 From: kalpesh Date: Wed, 22 Oct 2025 12:54:39 +0530 Subject: [PATCH 08/19] fix: remove unnecessary blank line in RecurringAccountViewModel --- .../newRecurringDepositAccount/RecurringAccountViewModel.kt | 1 - 1 file changed, 1 deletion(-) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt index 740c8e25cf..bce389e755 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt @@ -161,7 +161,6 @@ class RecurringAccountViewModel( when (dataState) { is DataState.Error -> { setErrorState(dataState.message) - } is DataState.Loading -> { setLoadingState() From 1a40db787e3e4257ae6091f9e8ae101bb8ed9983 Mon Sep 17 00:00:00 2001 From: kalpesh Date: Thu, 23 Oct 2025 21:33:05 +0530 Subject: [PATCH 09/19] refactor: rename dialogState to screenState and update related logic in RecurringAccount components --- .../repository/RecurringAccountRepository.kt | 4 +- .../RecurringAccountRepositoryImp.kt | 4 +- .../composeResources/values/string.xml | 1 + .../RecurringAccountScreen.kt | 11 +- .../RecurringAccountViewModel.kt | 128 +++++++++++------- .../pages/SettingsPage.kt | 114 ++++++++-------- 6 files changed, 147 insertions(+), 115 deletions(-) diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/RecurringAccountRepository.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/RecurringAccountRepository.kt index 3c20a6d7a0..6744704f68 100644 --- a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/RecurringAccountRepository.kt +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/RecurringAccountRepository.kt @@ -17,9 +17,9 @@ import kotlinx.coroutines.flow.Flow interface RecurringAccountRepository { - fun getRecuttingAccountTemplate(): Flow> + fun getRecurringAccountTemplate(): Flow> - fun getRecuttingAccountTemplateByProduct( + fun getRecurringAccountTemplateByProduct( clientId: Int, productId: Int, ): Flow> diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/RecurringAccountRepositoryImp.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/RecurringAccountRepositoryImp.kt index 6aa0050b84..11410bc7e6 100644 --- a/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/RecurringAccountRepositoryImp.kt +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/RecurringAccountRepositoryImp.kt @@ -21,12 +21,12 @@ import kotlinx.coroutines.flow.Flow class RecurringAccountRepositoryImp( val dataManagerRecurringAccount: DataManagerRecurringAccount, ) : RecurringAccountRepository { - override fun getRecuttingAccountTemplate(): Flow> { + override fun getRecurringAccountTemplate(): Flow> { return dataManagerRecurringAccount.getRecurringDepositAccountTemplate .asDataStateFlow() } - override fun getRecuttingAccountTemplateByProduct( + override fun getRecurringAccountTemplateByProduct( clientId: Int, productId: Int, ): Flow> { diff --git a/feature/recurringDeposit/src/commonMain/composeResources/values/string.xml b/feature/recurringDeposit/src/commonMain/composeResources/values/string.xml index 93daf678f3..b75db47969 100644 --- a/feature/recurringDeposit/src/commonMain/composeResources/values/string.xml +++ b/feature/recurringDeposit/src/commonMain/composeResources/values/string.xml @@ -35,4 +35,5 @@ Minimum Balance For Interest Calculation Back Next + No Internet Connection \ No newline at end of file diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt index 19deb6e42a..6edf759a27 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt @@ -22,7 +22,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle -import androidx.lifecycle.viewmodel.compose.viewModel import com.mifos.core.designsystem.component.MifosScaffold import com.mifos.core.ui.components.MifosErrorComponent import com.mifos.core.ui.components.MifosProgressIndicator @@ -102,7 +101,7 @@ private fun RecurringAccountScaffold( onBackPressed = { onAction(RecurringAccountAction.NavigateBack) }, modifier = modifier, ) { paddingValues -> - if (state.dialogState == null) { + if (state.screenState == null) { MifosStepper( steps = steps, currentIndex = state.currentStep, @@ -121,17 +120,17 @@ private fun RecurringAccountScaffold( fun RecurringDepositAccountDialogBox( state: RecurringAccountState, ) { - when (state.dialogState) { - is RecurringAccountState.DialogState.Error -> { + when (state.screenState) { + is RecurringAccountState.ScreenState.Error -> { MifosErrorComponent( - message = state.dialogState.message, + message = state.screenState.message, isRetryEnabled = true, onRetry = { // Retry action can be handled here }, ) } - RecurringAccountState.DialogState.Loading -> { + RecurringAccountState.ScreenState.Loading -> { MifosProgressIndicator() } null -> {} diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt index bce389e755..619bb8e86a 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt @@ -9,6 +9,7 @@ */ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount +import androidclient.feature.recurringdeposit.generated.resources.no_internet_connection import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import androidx.navigation.toRoute @@ -18,11 +19,14 @@ import com.mifos.core.data.util.NetworkMonitor import com.mifos.core.model.objects.payloads.RecurringDepositAccountPayload import com.mifos.core.model.objects.template.recurring.FieldOfficerOption import com.mifos.core.ui.util.BaseViewModel -import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.RecurringAccountState.DialogState +import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.RecurringAccountState.ScreenState import com.mifos.room.entities.templates.recurringDeposit.RecurringDepositAccountTemplate +import androidclient.feature.recurringdeposit.generated.resources.Res import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import org.jetbrains.compose.resources.getString + class RecurringAccountViewModel( savedStateHandle: SavedStateHandle, @@ -42,14 +46,14 @@ class RecurringAccountViewModel( private fun setLoadingState() { mutableStateFlow.update { it.copy( - dialogState = DialogState.Loading, + screenState = ScreenState.Loading, ) } } private fun setErrorState(message: String) { mutableStateFlow.update { it.copy( - dialogState = DialogState.Error(message), + screenState = ScreenState.Error(message), ) } } @@ -57,30 +61,47 @@ class RecurringAccountViewModel( private fun observeNetwork() { viewModelScope.launch { val isConnected = networkMonitor.isOnline.first() - + mutableStateFlow.update { + it.copy(isOnline = isConnected,) + } if (isConnected) { // loadTemplate() + // TODO // Used only for testing purpose // Must be removed from here after the implementation of detail screen is finished. loadTemplateByProduct() } else { - setErrorState("No internet connection") + setErrorState( + getString(Res.string.no_internet_connection) + ) } } } private fun handleRetry() { mutableStateFlow.update { it.copy( - dialogState = null, + screenState = null, recurringDepositAccountTemplate = RecurringDepositAccountTemplate(), ) } observeNetwork() } + private fun moveToNextStep() { + if (state.currentStep < state.totalSteps) { + mutableStateFlow.update { + it.copy( + currentStep = state.currentStep + 1, + ) + } + } else { + sendEvent(RecurringAccountEvent.Finish) + } + } + private fun loadTemplate() { viewModelScope.launch { - recurringAccountRepository.getRecuttingAccountTemplate().collect { templateState -> + recurringAccountRepository.getRecurringAccountTemplate().collect { templateState -> when (templateState) { is DataState.Error -> { setErrorState(message = templateState.message) @@ -91,7 +112,7 @@ class RecurringAccountViewModel( is DataState.Success -> { mutableStateFlow.update { it.copy( - dialogState = null, + screenState = null, recurringDepositAccountTemplate = templateState.data, ) } @@ -102,7 +123,7 @@ class RecurringAccountViewModel( } private fun loadTemplateByProduct() { viewModelScope.launch { - recurringAccountRepository.getRecuttingAccountTemplateByProduct( + recurringAccountRepository.getRecurringAccountTemplateByProduct( clientId = clientId, productId = state.recurringDepositAccountDetail.productId, ).collect { templateState -> @@ -116,7 +137,7 @@ class RecurringAccountViewModel( is DataState.Success -> { mutableStateFlow.update { it.copy( - dialogState = null, + screenState = null, recurringDepositAccountTemplate = templateState.data, ) } @@ -126,7 +147,19 @@ class RecurringAccountViewModel( } } - fun createRecurringDepositAccount() { + private fun formattedAmount(amount: String): String { + val revStr = amount.reversed() + var formattedAmount = "" + revStr.forEachIndexed { index, ch -> + formattedAmount += ch + if ((index + 1) % 3 == 0 ) { + formattedAmount += "," + } + } + return "$ ${formattedAmount.reversed()}" + } + + private fun createRecurringDepositAccount() { viewModelScope.launch { val s = state val settings = s.recurringDepositAccountSettings @@ -157,17 +190,21 @@ class RecurringAccountViewModel( // date in dd MM yyyy format. submittedOnDate = s.recurringDepositAccountDetail.submittedOnDate, ) - recurringAccountRepository.createRecurringDepositAccount(payload).collect { dataState -> - when (dataState) { - is DataState.Error -> { - setErrorState(dataState.message) - } - is DataState.Loading -> { - setLoadingState() - } - is DataState.Success -> { - mutableStateFlow.update { - it.copy(dialogState = null) + observeNetwork() + + if(state.isOnline) { + recurringAccountRepository.createRecurringDepositAccount(payload).collect { dataState -> + when (dataState) { + is DataState.Error -> { + setErrorState(dataState.message) + } + is DataState.Loading -> { + setLoadingState() + } + is DataState.Success -> { + mutableStateFlow.update { + it.copy(screenState = null) + } } } } @@ -178,16 +215,7 @@ class RecurringAccountViewModel( override fun handleAction(action: RecurringAccountAction) { when (action) { RecurringAccountAction.NextStep -> { - val current = state.currentStep - if (current < state.totalSteps) { - mutableStateFlow.update { - it.copy( - currentStep = current + 1, - ) - } - } else { - sendEvent(RecurringAccountEvent.Finish) - } + moveToNextStep() } is RecurringAccountAction.OnStepChange -> { @@ -211,15 +239,7 @@ class RecurringAccountViewModel( sendEvent(RecurringAccountEvent.NavigateBack) } RecurringAccountAction.RecurringAccountSettingsAction.OnNextPress -> { - if (state.currentStep < state.totalSteps) { - mutableStateFlow.update { - it.copy( - currentStep = state.currentStep + 1, - ) - } - } else { - sendEvent(RecurringAccountEvent.Finish) - } + moveToNextStep() } is RecurringAccountAction.RecurringAccountSettingsAction.SetDepositPeriod -> { mutableStateFlow.update { state -> @@ -356,7 +376,7 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( preMatureClosure = state.recurringDepositAccountSettings.preMatureClosure.copy( - minimumBalanceForInterestCalculation = action.minimumBalanceForInterestCalculation, + minimumBalanceForInterestCalculation = formattedAmount(action.minimumBalanceForInterestCalculation), ), ), ) @@ -375,11 +395,20 @@ class RecurringAccountViewModel( } is RecurringAccountAction.RecurringAccountSettingsAction.SetRecurringDepositAmount -> { mutableStateFlow.update { + var formattedAmount = "" + val strLen = action.depositAmount.length + val rev = action.depositAmount.reversed() + action.depositAmount.forEachIndexed { index, ch -> + formattedAmount+=ch + + } + + state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( recurringDepositDetails = state.recurringDepositAccountSettings .recurringDepositDetails.copy( - depositAmount = action.depositAmount, + depositAmount = formattedAmount(action.depositAmount) ), ), ) @@ -451,23 +480,26 @@ class RecurringAccountViewModel( } } } + } data class RecurringAccountState( + val isOnline: Boolean = false, val currentStep: Int = 0, val totalSteps: Int = 4, - val dialogState: DialogState? = null, + val screenState: ScreenState? = null, val recurringDepositAccountDetail: RecurringAccountDetailsState = RecurringAccountDetailsState(), val recurringDepositAccountTemplate: RecurringDepositAccountTemplate = RecurringDepositAccountTemplate(), val recurringDepositAccountSettings: RecurringAccountSettingsState = RecurringAccountSettingsState(), ) { - sealed interface DialogState { - data class Error(val message: String) : DialogState - data object Loading : DialogState + sealed interface ScreenState { + data class Error(val message: String) : ScreenState + data object Loading : ScreenState } } data class RecurringAccountDetailsState( + // TODO // productId is set 6 only for testing. // It should be set -1 when the implementation of detail screen is finished. val productId: Int = 6, @@ -494,7 +526,7 @@ data class RecurringAccountSettingsState( ) data class RecurringDepositDetails( - val depositAmount: String = "", + val depositAmount: String = "$", ) data class DepositPeriod( diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt index e7940be1c7..cff5ea594e 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt @@ -31,11 +31,11 @@ import androidclient.feature.recurringdeposit.generated.resources.recurring_depo import androidclient.feature.recurringdeposit.generated.resources.recurring_deposit_details import androidclient.feature.recurringdeposit.generated.resources.step_settings import androidclient.feature.recurringdeposit.generated.resources.type +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll @@ -75,9 +75,9 @@ fun SettingPage( .fillMaxWidth() .verticalScroll(scrollState), horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(20.dp), ) { Text(stringResource(Res.string.step_settings), fontWeight = FontWeight.Bold, fontSize = 18.sp) - Spacer(Modifier.height(8.dp)) Row(verticalAlignment = Alignment.CenterVertically) { Checkbox(settingsState.isMandatory, onCheckedChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleMandatoryDeposit) }) Text(stringResource(Res.string.is_mandatory_deposit)) @@ -96,7 +96,6 @@ fun SettingPage( onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAllowWithdrawals) }, ) } - Spacer(Modifier.height(16.dp)) Text(stringResource(Res.string.lock_in_period), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.lockInPeriod.frequency, @@ -127,7 +126,6 @@ fun SettingPage( }, label = stringResource(Res.string.type), ) - Spacer(Modifier.height(16.dp)) Text(stringResource(Res.string.recurring_deposit_details), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.recurringDepositDetails.depositAmount, @@ -141,7 +139,6 @@ fun SettingPage( ), modifier = Modifier.fillMaxWidth(), ) - Spacer(Modifier.height(16.dp)) Text(stringResource(Res.string.deposit_period), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.depositPeriod.period, @@ -178,7 +175,6 @@ fun SettingPage( onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleDepositFrequencySameAsGroupCenterMeeting) }, ) } - Spacer(Modifier.height(16.dp)) Text(stringResource(Res.string.minimum_deposit_term), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.minDepositTerm.frequency, @@ -210,7 +206,6 @@ fun SettingPage( label = stringResource(Res.string.type), modifier = Modifier.fillMaxWidth(), ) - Spacer(Modifier.height(16.dp)) Text(stringResource(Res.string.in_multiples_of), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.minDepositTerm.frequencyAfterInMultiplesOf, @@ -240,7 +235,6 @@ fun SettingPage( label = stringResource(Res.string.type), modifier = Modifier.fillMaxWidth(), ) - Spacer(Modifier.height(16.dp)) Text(stringResource(Res.string.maximum_deposit_term), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.maxDepositTerm.frequency, @@ -270,7 +264,6 @@ fun SettingPage( label = stringResource(Res.string.type), modifier = Modifier.fillMaxWidth(), ) - Spacer(Modifier.height(16.dp)) Text(stringResource(Res.string.for_pre_mature_closure), fontWeight = FontWeight.Bold) Row(verticalAlignment = Alignment.CenterVertically) { Checkbox( @@ -279,61 +272,68 @@ fun SettingPage( ) Text(stringResource(Res.string.apply_penal_interest)) } - MifosOutlinedTextField( - value = settingsState.preMatureClosure.penalInterest, - onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosurePenalInterest(it)) }, - label = stringResource(Res.string.penal_interest_percentage), - config = MifosTextFieldConfig( - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next, + AnimatedVisibility( + visible = settingsState.preMatureClosure.applyPenalInterest, + ){ + MifosOutlinedTextField( + value = settingsState.preMatureClosure.penalInterest, + onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosurePenalInterest(it)) }, + label = stringResource(Res.string.penal_interest_percentage), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next, + ), ), - ), - modifier = Modifier.fillMaxWidth(), - ) - MifosTextFieldDropdown( - value = if (settingsState.preMatureClosure.interestPeriodIndex != -1) { - state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" - } else { - "" - }, - options = state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.map { - it.value ?: "" - } ?: emptyList(), - onValueChanged = {}, - onOptionSelected = { id, name -> - onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureInterestPeriodIndex(id)) - }, - label = stringResource(Res.string.period), - modifier = Modifier.fillMaxWidth(), - ) - MifosOutlinedTextField( - value = settingsState.preMatureClosure.minimumBalanceForInterestCalculation, - onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureMinimumBalanceForInterestCalculation(it)) }, - label = stringResource(Res.string.minimum_balance_for_interest), - config = MifosTextFieldConfig( - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next, + modifier = Modifier.fillMaxWidth(), + ) + MifosTextFieldDropdown( + value = if (settingsState.preMatureClosure.interestPeriodIndex != -1) { + state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" + } else { + "" + }, + options = state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.map { + it.value ?: "" + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureInterestPeriodIndex(id)) + }, + label = stringResource(Res.string.period), + modifier = Modifier.fillMaxWidth(), + ) + MifosOutlinedTextField( + value = settingsState.preMatureClosure.minimumBalanceForInterestCalculation, + onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureMinimumBalanceForInterestCalculation(it)) }, + label = stringResource(Res.string.minimum_balance_for_interest), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next, + ), ), - ), - modifier = Modifier.fillMaxWidth(), - ) - Spacer(Modifier.height(24.dp)) + modifier = Modifier.fillMaxWidth(), + ) + } + + + val isNextButtonActive = settingsState.preMatureClosure.penalInterest.isNotBlank() && + settingsState.preMatureClosure.minimumBalanceForInterestCalculation.isNotBlank() && + settingsState.recurringDepositDetails.depositAmount.isNotBlank() && + settingsState.depositPeriod.period.isNotBlank() && + settingsState.lockInPeriod.frequency.isNotBlank() && + settingsState.minDepositTerm.frequency.isNotBlank() && + settingsState.minDepositTerm.frequencyAfterInMultiplesOf.isNotBlank() && + settingsState.maxDepositTerm.frequency.isNotBlank() + MifosTwoButtonRow( firstBtnText = stringResource(Res.string.back), secondBtnText = stringResource(Res.string.next), onFirstBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnBackPress) }, onSecondBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnNextPress) }, isButtonIconVisible = true, - isSecondButtonEnabled = settingsState.preMatureClosure.penalInterest.isNotBlank() && - settingsState.preMatureClosure.minimumBalanceForInterestCalculation.isNotBlank() && - settingsState.recurringDepositDetails.depositAmount.isNotBlank() && - settingsState.depositPeriod.period.isNotBlank() && - settingsState.lockInPeriod.frequency.isNotBlank() && - settingsState.minDepositTerm.frequency.isNotBlank() && - settingsState.minDepositTerm.frequencyAfterInMultiplesOf.isNotBlank() && - settingsState.maxDepositTerm.frequency.isNotBlank(), + isSecondButtonEnabled = isNextButtonActive, ) } } From acd510ad962c994cfb85f46da98d4076f947831f Mon Sep 17 00:00:00 2001 From: kalpesh Date: Thu, 23 Oct 2025 21:35:00 +0530 Subject: [PATCH 10/19] fix: clean up formatting and remove unnecessary blank lines in RecurringAccountViewModel and SettingsPage --- .../RecurringAccountViewModel.kt | 18 +++++++----------- .../pages/SettingsPage.kt | 5 ++--- 2 files changed, 9 insertions(+), 14 deletions(-) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt index 619bb8e86a..5669c320e2 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt @@ -9,6 +9,7 @@ */ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount +import androidclient.feature.recurringdeposit.generated.resources.Res import androidclient.feature.recurringdeposit.generated.resources.no_internet_connection import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope @@ -21,13 +22,11 @@ import com.mifos.core.model.objects.template.recurring.FieldOfficerOption import com.mifos.core.ui.util.BaseViewModel import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.RecurringAccountState.ScreenState import com.mifos.room.entities.templates.recurringDeposit.RecurringDepositAccountTemplate -import androidclient.feature.recurringdeposit.generated.resources.Res import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.jetbrains.compose.resources.getString - class RecurringAccountViewModel( savedStateHandle: SavedStateHandle, private val networkMonitor: NetworkMonitor, @@ -62,7 +61,7 @@ class RecurringAccountViewModel( viewModelScope.launch { val isConnected = networkMonitor.isOnline.first() mutableStateFlow.update { - it.copy(isOnline = isConnected,) + it.copy(isOnline = isConnected) } if (isConnected) { // loadTemplate() @@ -72,7 +71,7 @@ class RecurringAccountViewModel( loadTemplateByProduct() } else { setErrorState( - getString(Res.string.no_internet_connection) + getString(Res.string.no_internet_connection), ) } } @@ -152,7 +151,7 @@ class RecurringAccountViewModel( var formattedAmount = "" revStr.forEachIndexed { index, ch -> formattedAmount += ch - if ((index + 1) % 3 == 0 ) { + if ((index + 1) % 3 == 0) { formattedAmount += "," } } @@ -192,7 +191,7 @@ class RecurringAccountViewModel( ) observeNetwork() - if(state.isOnline) { + if (state.isOnline) { recurringAccountRepository.createRecurringDepositAccount(payload).collect { dataState -> when (dataState) { is DataState.Error -> { @@ -399,16 +398,14 @@ class RecurringAccountViewModel( val strLen = action.depositAmount.length val rev = action.depositAmount.reversed() action.depositAmount.forEachIndexed { index, ch -> - formattedAmount+=ch - + formattedAmount += ch } - state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( recurringDepositDetails = state.recurringDepositAccountSettings .recurringDepositDetails.copy( - depositAmount = formattedAmount(action.depositAmount) + depositAmount = formattedAmount(action.depositAmount), ), ), ) @@ -480,7 +477,6 @@ class RecurringAccountViewModel( } } } - } data class RecurringAccountState( diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt index cff5ea594e..927585d17f 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt @@ -274,7 +274,7 @@ fun SettingPage( } AnimatedVisibility( visible = settingsState.preMatureClosure.applyPenalInterest, - ){ + ) { MifosOutlinedTextField( value = settingsState.preMatureClosure.penalInterest, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosurePenalInterest(it)) }, @@ -317,8 +317,7 @@ fun SettingPage( ) } - - val isNextButtonActive = settingsState.preMatureClosure.penalInterest.isNotBlank() && + val isNextButtonActive = settingsState.preMatureClosure.penalInterest.isNotBlank() && settingsState.preMatureClosure.minimumBalanceForInterestCalculation.isNotBlank() && settingsState.recurringDepositDetails.depositAmount.isNotBlank() && settingsState.depositPeriod.period.isNotBlank() && From 57eb5cc9eec1b0707bd9c74f91b4ed8a9e14590f Mon Sep 17 00:00:00 2001 From: kalpesh Date: Tue, 21 Oct 2025 23:07:14 +0530 Subject: [PATCH 11/19] feat: enhance recurring deposit account settings and UI components --- .../src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt | 1 + core/model/objects/payloads/RecurringDepositAccountPayload.kt | 2 ++ 2 files changed, 3 insertions(+) create mode 100644 core/model/objects/payloads/RecurringDepositAccountPayload.kt diff --git a/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt b/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt index 4d14674e00..1fbb285b37 100644 --- a/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt +++ b/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt @@ -34,6 +34,7 @@ import com.mifos.feature.path.tracking.di.PathTrackingModule import com.mifos.feature.recurringDeposit.di.RecurringDepositModule import com.mifos.feature.report.di.ReportModule import com.mifos.feature.savings.di.SavingsModule +import com.mifos.feature.recurringDeposit.di.RecurringDepositModule import com.mifos.feature.search.di.SearchModule import com.mifos.feature.settings.di.SettingsModule import com.mifos.room.di.DaoModule diff --git a/core/model/objects/payloads/RecurringDepositAccountPayload.kt b/core/model/objects/payloads/RecurringDepositAccountPayload.kt new file mode 100644 index 0000000000..139597f9cb --- /dev/null +++ b/core/model/objects/payloads/RecurringDepositAccountPayload.kt @@ -0,0 +1,2 @@ + + From 09c4ba146d6898d4cfe9614851525113b0fc887c Mon Sep 17 00:00:00 2001 From: kalpesh Date: Thu, 23 Oct 2025 21:33:05 +0530 Subject: [PATCH 12/19] refactor: rename dialogState to screenState and update related logic in RecurringAccount components --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index 91fd393753..8e43459a9b 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2025.10.4-beta.0.6 \ No newline at end of file +2025.10.4-beta.0.8 \ No newline at end of file From 0ccaf117a9aa7d32ccf8c227177117d2363e853e Mon Sep 17 00:00:00 2001 From: kalpesh Date: Tue, 28 Oct 2025 19:43:59 +0530 Subject: [PATCH 13/19] feat: improve amount formatting logic and enhance UI layout in RecurringAccount settings --- .../RecurringAccountViewModel.kt | 70 +++++++--- .../pages/SettingsPage.kt | 122 ++++++++++-------- version.txt | 2 +- 3 files changed, 123 insertions(+), 71 deletions(-) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt index 5669c320e2..5179f14c44 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt @@ -147,17 +147,64 @@ class RecurringAccountViewModel( } private fun formattedAmount(amount: String): String { - val revStr = amount.reversed() - var formattedAmount = "" - revStr.forEachIndexed { index, ch -> - formattedAmount += ch - if ((index + 1) % 3 == 0) { - formattedAmount += "," + + val currencySymbol = state.recurringDepositAccountTemplate.currency?.displaySymbol ?: "" + // 1. Handle empty input + if (amount.isEmpty()) return "" + + // 2. Remove all non-numeric/non-dot characters, including currency symbols like '$' + // We keep only digits, dots, and a potential leading minus sign. + val cleaned = amount.replace("[^0-9.-]".toRegex(), "") + + // 3. Handle special cases + if (cleaned.isEmpty()) return "" + if (cleaned == "-") return "-" + if (cleaned == ".") return "." // Allows user to type just '.' + + // 4. Separate sign and magnitude + val negative = cleaned.startsWith("-") + val magnitude = if (negative) cleaned.substring(1) else cleaned + + // 5. Separate integer and decimal parts + val parts = magnitude.split('.', limit = 2) + var intPart = parts[0].trimStart('0').ifEmpty { "0" } // Clean up leading zeros + + var decimalPart = "" + if (parts.size > 1) { + // Take the first 2 digits after the dot + decimalPart = parts[1].take(2) + // Only append the dot if there are decimals or the user just typed the dot (i.e., parts[1] is empty) + if (decimalPart.isNotEmpty() || parts[1].isEmpty()) { + decimalPart = ".$decimalPart" } } - return "$ ${formattedAmount.reversed()}" + + // Special handling for ".50" input (no integer part) + if (intPart == "0" && magnitude.startsWith('.') && decimalPart.isNotEmpty()) { + intPart = "" + } + + // 6. Assemble the numeric part + val numericPart = when { + // If the original input started with a dot (e.g., ".50"), and we have decimals, return ".50" + amount.startsWith('.') && decimalPart.isNotEmpty() -> decimalPart + // If the original input was something like "-.50", and we have decimals + amount.startsWith("-.") && decimalPart.isNotEmpty() -> decimalPart + // Otherwise, use the cleaned integer and decimal parts + else -> "$intPart$decimalPart" + } + + // 7. Add back the sign + val signedNumericPart = if (negative && numericPart.isNotEmpty()) "-$numericPart" else numericPart + + // If the input started with just '.', return '.' + if (amount == "." && signedNumericPart.isEmpty()) return "." + + // 8. Attach the currency symbol + return "$currencySymbol$signedNumericPart" } + private fun createRecurringDepositAccount() { viewModelScope.launch { val s = state @@ -394,13 +441,6 @@ class RecurringAccountViewModel( } is RecurringAccountAction.RecurringAccountSettingsAction.SetRecurringDepositAmount -> { mutableStateFlow.update { - var formattedAmount = "" - val strLen = action.depositAmount.length - val rev = action.depositAmount.reversed() - action.depositAmount.forEachIndexed { index, ch -> - formattedAmount += ch - } - state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( recurringDepositDetails = state.recurringDepositAccountSettings @@ -522,7 +562,7 @@ data class RecurringAccountSettingsState( ) data class RecurringDepositDetails( - val depositAmount: String = "$", + val depositAmount: String = "", ) data class DepositPeriod( diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt index 927585d17f..288d01f2ad 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt @@ -78,24 +78,31 @@ fun SettingPage( verticalArrangement = Arrangement.spacedBy(20.dp), ) { Text(stringResource(Res.string.step_settings), fontWeight = FontWeight.Bold, fontSize = 18.sp) - Row(verticalAlignment = Alignment.CenterVertically) { - Checkbox(settingsState.isMandatory, onCheckedChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleMandatoryDeposit) }) - Text(stringResource(Res.string.is_mandatory_deposit)) - } - Row(verticalAlignment = Alignment.CenterVertically) { - MifosCheckBox( - text = stringResource(Res.string.adjust_advance_payments), - checked = settingsState.adjustAdvancePayments, - onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAdvancePaymentsTowardsFutureInstallments) }, - ) - } - Row(verticalAlignment = Alignment.CenterVertically) { - MifosCheckBox( - text = stringResource(Res.string.allow_withdrawals), - checked = settingsState.allowWithdrawals, - onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAllowWithdrawals) }, - ) + + Column ( + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(10.dp) + ){ + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox(settingsState.isMandatory, onCheckedChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleMandatoryDeposit) }) + Text(stringResource(Res.string.is_mandatory_deposit)) + } + Row(verticalAlignment = Alignment.CenterVertically) { + MifosCheckBox( + text = stringResource(Res.string.adjust_advance_payments), + checked = settingsState.adjustAdvancePayments, + onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAdvancePaymentsTowardsFutureInstallments) }, + ) + } + Row(verticalAlignment = Alignment.CenterVertically) { + MifosCheckBox( + text = stringResource(Res.string.allow_withdrawals), + checked = settingsState.allowWithdrawals, + onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAllowWithdrawals) }, + ) + } } + Text(stringResource(Res.string.lock_in_period), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.lockInPeriod.frequency, @@ -275,46 +282,51 @@ fun SettingPage( AnimatedVisibility( visible = settingsState.preMatureClosure.applyPenalInterest, ) { - MifosOutlinedTextField( - value = settingsState.preMatureClosure.penalInterest, - onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosurePenalInterest(it)) }, - label = stringResource(Res.string.penal_interest_percentage), - config = MifosTextFieldConfig( - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next, + Column( + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(20.dp) + ) { + MifosOutlinedTextField( + value = settingsState.preMatureClosure.penalInterest, + onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosurePenalInterest(it)) }, + label = stringResource(Res.string.penal_interest_percentage), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next, + ), ), - ), - modifier = Modifier.fillMaxWidth(), - ) - MifosTextFieldDropdown( - value = if (settingsState.preMatureClosure.interestPeriodIndex != -1) { - state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" - } else { - "" - }, - options = state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.map { - it.value ?: "" - } ?: emptyList(), - onValueChanged = {}, - onOptionSelected = { id, name -> - onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureInterestPeriodIndex(id)) - }, - label = stringResource(Res.string.period), - modifier = Modifier.fillMaxWidth(), - ) - MifosOutlinedTextField( - value = settingsState.preMatureClosure.minimumBalanceForInterestCalculation, - onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureMinimumBalanceForInterestCalculation(it)) }, - label = stringResource(Res.string.minimum_balance_for_interest), - config = MifosTextFieldConfig( - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next, + modifier = Modifier.fillMaxWidth(), + ) + MifosTextFieldDropdown( + value = if (settingsState.preMatureClosure.interestPeriodIndex != -1) { + state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" + } else { + "" + }, + options = state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.map { + it.value ?: "" + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureInterestPeriodIndex(id)) + }, + label = stringResource(Res.string.period), + modifier = Modifier.fillMaxWidth(), + ) + MifosOutlinedTextField( + value = settingsState.preMatureClosure.minimumBalanceForInterestCalculation, + onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureMinimumBalanceForInterestCalculation(it)) }, + label = stringResource(Res.string.minimum_balance_for_interest), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next, + ), ), - ), - modifier = Modifier.fillMaxWidth(), - ) + modifier = Modifier.fillMaxWidth(), + ) + } } val isNextButtonActive = settingsState.preMatureClosure.penalInterest.isNotBlank() && diff --git a/version.txt b/version.txt index 8e43459a9b..656e604219 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2025.10.4-beta.0.8 \ No newline at end of file +2025.9.2-beta.0.59 \ No newline at end of file From cb277b88ef6aa2841ee5b380f5446298d2719a9b Mon Sep 17 00:00:00 2001 From: kalpesh Date: Tue, 28 Oct 2025 20:05:46 +0530 Subject: [PATCH 14/19] fix: remove duplicate import of RecurringDepositModule in KoinModules --- .../src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt | 1 - core/model/objects/payloads/RecurringDepositAccountPayload.kt | 2 -- 2 files changed, 3 deletions(-) delete mode 100644 core/model/objects/payloads/RecurringDepositAccountPayload.kt diff --git a/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt b/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt index 1fbb285b37..4d14674e00 100644 --- a/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt +++ b/cmp-navigation/src/commonMain/kotlin/cmp/navigation/di/KoinModules.kt @@ -34,7 +34,6 @@ import com.mifos.feature.path.tracking.di.PathTrackingModule import com.mifos.feature.recurringDeposit.di.RecurringDepositModule import com.mifos.feature.report.di.ReportModule import com.mifos.feature.savings.di.SavingsModule -import com.mifos.feature.recurringDeposit.di.RecurringDepositModule import com.mifos.feature.search.di.SearchModule import com.mifos.feature.settings.di.SettingsModule import com.mifos.room.di.DaoModule diff --git a/core/model/objects/payloads/RecurringDepositAccountPayload.kt b/core/model/objects/payloads/RecurringDepositAccountPayload.kt deleted file mode 100644 index 139597f9cb..0000000000 --- a/core/model/objects/payloads/RecurringDepositAccountPayload.kt +++ /dev/null @@ -1,2 +0,0 @@ - - From be3bb4eb91e8ddf1e47fdd44e87ff3937e1eb700 Mon Sep 17 00:00:00 2001 From: kalpesh Date: Tue, 28 Oct 2025 20:30:44 +0530 Subject: [PATCH 15/19] feat: enhance amount formatting and validate network connectivity in RecurringAccount --- .../RecurringAccountViewModel.kt | 37 +++++++++---------- .../pages/SettingsPage.kt | 2 +- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt index 5179f14c44..0e05945288 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt @@ -149,58 +149,43 @@ class RecurringAccountViewModel( private fun formattedAmount(amount: String): String { val currencySymbol = state.recurringDepositAccountTemplate.currency?.displaySymbol ?: "" - // 1. Handle empty input if (amount.isEmpty()) return "" - // 2. Remove all non-numeric/non-dot characters, including currency symbols like '$' - // We keep only digits, dots, and a potential leading minus sign. + val cleaned = amount.replace("[^0-9.-]".toRegex(), "") - // 3. Handle special cases if (cleaned.isEmpty()) return "" if (cleaned == "-") return "-" if (cleaned == ".") return "." // Allows user to type just '.' - // 4. Separate sign and magnitude val negative = cleaned.startsWith("-") val magnitude = if (negative) cleaned.substring(1) else cleaned - // 5. Separate integer and decimal parts val parts = magnitude.split('.', limit = 2) var intPart = parts[0].trimStart('0').ifEmpty { "0" } // Clean up leading zeros var decimalPart = "" if (parts.size > 1) { - // Take the first 2 digits after the dot decimalPart = parts[1].take(2) - // Only append the dot if there are decimals or the user just typed the dot (i.e., parts[1] is empty) if (decimalPart.isNotEmpty() || parts[1].isEmpty()) { decimalPart = ".$decimalPart" } } - // Special handling for ".50" input (no integer part) if (intPart == "0" && magnitude.startsWith('.') && decimalPart.isNotEmpty()) { intPart = "" } - // 6. Assemble the numeric part val numericPart = when { - // If the original input started with a dot (e.g., ".50"), and we have decimals, return ".50" amount.startsWith('.') && decimalPart.isNotEmpty() -> decimalPart - // If the original input was something like "-.50", and we have decimals amount.startsWith("-.") && decimalPart.isNotEmpty() -> decimalPart - // Otherwise, use the cleaned integer and decimal parts else -> "$intPart$decimalPart" } - // 7. Add back the sign val signedNumericPart = if (negative && numericPart.isNotEmpty()) "-$numericPart" else numericPart - // If the input started with just '.', return '.' if (amount == "." && signedNumericPart.isEmpty()) return "." - // 8. Attach the currency symbol return "$currencySymbol$signedNumericPart" } @@ -209,6 +194,19 @@ class RecurringAccountViewModel( viewModelScope.launch { val s = state val settings = s.recurringDepositAccountSettings + + val online = networkMonitor.isOnline.first() + if (!online) { + setErrorState(getString(Res.string.no_internet_connection)) + return@launch + } + + val lockinFreq = settings.lockInPeriod.frequency.toIntOrNull() + val depositAmountInt = settings.recurringDepositDetails.depositAmount + .filter(Char::isDigit) + .toIntOrNull() + val recurringFreq = settings.minDepositTerm.frequency.toIntOrNull() + val payload = RecurringDepositAccountPayload( adjustAdvanceTowardsFuturePayments = settings.adjustAdvancePayments, allowWithdrawal = settings.allowWithdrawals, @@ -226,17 +224,16 @@ class RecurringAccountViewModel( isCalendarInherited = null, isMandatoryDeposit = settings.isMandatory, locale = "en", - lockinPeriodFrequency = settings.lockInPeriod.frequency.toInt(), + lockinPeriodFrequency = lockinFreq, lockinPeriodFrequencyType = settings.lockInPeriod.frequencyTypeIndex, - mandatoryRecommendedDepositAmount = settings.recurringDepositDetails.depositAmount.toInt(), + mandatoryRecommendedDepositAmount = depositAmountInt, monthDayFormat = "dd MMMM", productId = s.recurringDepositAccountDetail.productId, - recurringFrequency = settings.minDepositTerm.frequency.toIntOrNull(), + recurringFrequency = recurringFreq, recurringFrequencyType = settings.minDepositTerm.frequencyTypeIndex, // date in dd MM yyyy format. submittedOnDate = s.recurringDepositAccountDetail.submittedOnDate, ) - observeNetwork() if (state.isOnline) { recurringAccountRepository.createRecurringDepositAccount(payload).collect { dataState -> diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt index 288d01f2ad..d5b9e59012 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt @@ -198,7 +198,7 @@ fun SettingPage( modifier = Modifier.fillMaxWidth(), ) MifosTextFieldDropdown( - value = if (settingsState.depositPeriod.periodType != -1) { + value = if (settingsState.minDepositTerm.frequencyTypeIndex != -1) { state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.minDepositTerm.frequencyTypeIndex)?.value ?: "" } else { "" From 71d3b57aec2ae0fcc7c973f4a5e248cd46461069 Mon Sep 17 00:00:00 2001 From: kalpesh Date: Tue, 28 Oct 2025 21:17:54 +0530 Subject: [PATCH 16/19] feat: rename network observation method and enhance connectivity checks in RecurringAccountViewModel --- .../RecurringAccountViewModel.kt | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt index 0e05945288..035012c9ac 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt @@ -38,7 +38,7 @@ class RecurringAccountViewModel( >(RecurringAccountState()) { init { - observeNetwork() + checkInitialConnectivity() } private val clientId = savedStateHandle.toRoute().clientId @@ -57,7 +57,7 @@ class RecurringAccountViewModel( } } - private fun observeNetwork() { + private fun checkInitialConnectivity() { viewModelScope.launch { val isConnected = networkMonitor.isOnline.first() mutableStateFlow.update { @@ -83,7 +83,7 @@ class RecurringAccountViewModel( recurringDepositAccountTemplate = RecurringDepositAccountTemplate(), ) } - observeNetwork() + checkInitialConnectivity() } private fun moveToNextStep() { @@ -100,6 +100,11 @@ class RecurringAccountViewModel( private fun loadTemplate() { viewModelScope.launch { + val online = networkMonitor.isOnline.first() + if (!online) { + setErrorState(getString(Res.string.no_internet_connection)) + return@launch + } recurringAccountRepository.getRecurringAccountTemplate().collect { templateState -> when (templateState) { is DataState.Error -> { @@ -122,6 +127,11 @@ class RecurringAccountViewModel( } private fun loadTemplateByProduct() { viewModelScope.launch { + val online = networkMonitor.isOnline.first() + if (!online) { + setErrorState(getString(Res.string.no_internet_connection)) + return@launch + } recurringAccountRepository.getRecurringAccountTemplateByProduct( clientId = clientId, productId = state.recurringDepositAccountDetail.productId, @@ -235,11 +245,21 @@ class RecurringAccountViewModel( submittedOnDate = s.recurringDepositAccountDetail.submittedOnDate, ) + if (state.isOnline) { recurringAccountRepository.createRecurringDepositAccount(payload).collect { dataState -> when (dataState) { is DataState.Error -> { - setErrorState(dataState.message) + if (depositAmountInt == null) { + setErrorState("Deposit amount is required") + }else if (lockinFreq == null) { + setErrorState("Lock-in period frequency is required") + } else if (recurringFreq == null) { + setErrorState("Recurring frequency is required") + } else { + setErrorState(dataState.message) + } + } is DataState.Loading -> { setLoadingState() From d22f064ab48f5c59c5f3b2b84df8776e05938d0a Mon Sep 17 00:00:00 2001 From: kalpesh Date: Tue, 28 Oct 2025 21:39:00 +0530 Subject: [PATCH 17/19] refactor: clean up code formatting and improve readability in DataManagerModule and RecurringAccountViewModel --- .../RecurringAccountViewModel.kt | 13 ++++--------- .../pages/SettingsPage.kt | 8 ++++---- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt index 035012c9ac..caf6b6c7e4 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt @@ -129,9 +129,9 @@ class RecurringAccountViewModel( viewModelScope.launch { val online = networkMonitor.isOnline.first() if (!online) { - setErrorState(getString(Res.string.no_internet_connection)) - return@launch - } + setErrorState(getString(Res.string.no_internet_connection)) + return@launch + } recurringAccountRepository.getRecurringAccountTemplateByProduct( clientId = clientId, productId = state.recurringDepositAccountDetail.productId, @@ -157,11 +157,9 @@ class RecurringAccountViewModel( } private fun formattedAmount(amount: String): String { - val currencySymbol = state.recurringDepositAccountTemplate.currency?.displaySymbol ?: "" if (amount.isEmpty()) return "" - val cleaned = amount.replace("[^0-9.-]".toRegex(), "") if (cleaned.isEmpty()) return "" @@ -199,7 +197,6 @@ class RecurringAccountViewModel( return "$currencySymbol$signedNumericPart" } - private fun createRecurringDepositAccount() { viewModelScope.launch { val s = state @@ -245,21 +242,19 @@ class RecurringAccountViewModel( submittedOnDate = s.recurringDepositAccountDetail.submittedOnDate, ) - if (state.isOnline) { recurringAccountRepository.createRecurringDepositAccount(payload).collect { dataState -> when (dataState) { is DataState.Error -> { if (depositAmountInt == null) { setErrorState("Deposit amount is required") - }else if (lockinFreq == null) { + } else if (lockinFreq == null) { setErrorState("Lock-in period frequency is required") } else if (recurringFreq == null) { setErrorState("Recurring frequency is required") } else { setErrorState(dataState.message) } - } is DataState.Loading -> { setLoadingState() diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt index d5b9e59012..51bc45975f 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt @@ -79,10 +79,10 @@ fun SettingPage( ) { Text(stringResource(Res.string.step_settings), fontWeight = FontWeight.Bold, fontSize = 18.sp) - Column ( + Column( horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.spacedBy(10.dp) - ){ + verticalArrangement = Arrangement.spacedBy(10.dp), + ) { Row(verticalAlignment = Alignment.CenterVertically) { Checkbox(settingsState.isMandatory, onCheckedChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleMandatoryDeposit) }) Text(stringResource(Res.string.is_mandatory_deposit)) @@ -284,7 +284,7 @@ fun SettingPage( ) { Column( horizontalAlignment = Alignment.Start, - verticalArrangement = Arrangement.spacedBy(20.dp) + verticalArrangement = Arrangement.spacedBy(20.dp), ) { MifosOutlinedTextField( value = settingsState.preMatureClosure.penalInterest, From cb2c4a8fe2415fd309fd1577772af295c499c86f Mon Sep 17 00:00:00 2001 From: kalpesh Date: Thu, 30 Oct 2025 01:02:03 +0530 Subject: [PATCH 18/19] feat: rename string resources for recurring deposit feature --- .../feature_recurringDeposit_string.xml | 39 +++++++ .../composeResources/values/string.xml | 39 ------- .../RecurringAccountScreen.kt | 24 ++--- .../RecurringAccountViewModel.kt | 10 +- .../pages/SettingsPage.kt | 102 +++++++++--------- 5 files changed, 107 insertions(+), 107 deletions(-) create mode 100644 feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurringDeposit_string.xml delete mode 100644 feature/recurringDeposit/src/commonMain/composeResources/values/string.xml diff --git a/feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurringDeposit_string.xml b/feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurringDeposit_string.xml new file mode 100644 index 0000000000..6270c0c6d2 --- /dev/null +++ b/feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurringDeposit_string.xml @@ -0,0 +1,39 @@ + + + + Details + Terms + Settings + Interest + Charges + Create Recurring Deposit Account + Is Mandatory Deposit? + Adjust advance payments toward future installments? + Allow Withdrawals? + Lock-in Period + Frequency + Type + Recurring Deposit Details + Recurring Deposit Amount + Deposit Period + Deposit Frequency Same as Group/Center meeting + Minimum Deposit Term + And thereafter, in Multiples of + Maximum Deposit Term + For Pre-mature closure + Apply Penal Interest (less) + Penal Interest (%) + Period + Minimum Balance For Interest Calculation + Back + Next + No Internet Connection + \ No newline at end of file diff --git a/feature/recurringDeposit/src/commonMain/composeResources/values/string.xml b/feature/recurringDeposit/src/commonMain/composeResources/values/string.xml deleted file mode 100644 index b75db47969..0000000000 --- a/feature/recurringDeposit/src/commonMain/composeResources/values/string.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - Details - Terms - Settings - Interest - Charges - Create Recurring Deposit Account - Is Mandatory Deposit? - Adjust advance payments toward future installments? - Allow Withdrawals? - Lock-in Period - Frequency - Type - Recurring Deposit Details - Recurring Deposit Amount - Deposit Period - Deposit Frequency Same as Group/Center meeting - Minimum Deposit Term - And thereafter, in Multiples of - Maximum Deposit Term - For Pre-mature closure - Apply Penal Interest (less) - Penal Interest (%) - Period - Minimum Balance For Interest Calculation - Back - Next - No Internet Connection - \ No newline at end of file diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt index 6edf759a27..c98b88f516 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt @@ -10,12 +10,12 @@ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount import androidclient.feature.recurringdeposit.generated.resources.Res -import androidclient.feature.recurringdeposit.generated.resources.create_recurring_deposit_account -import androidclient.feature.recurringdeposit.generated.resources.step_charges -import androidclient.feature.recurringdeposit.generated.resources.step_details -import androidclient.feature.recurringdeposit.generated.resources.step_interest -import androidclient.feature.recurringdeposit.generated.resources.step_settings -import androidclient.feature.recurringdeposit.generated.resources.step_terms +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_create_recurring_deposit_account +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_step_charges +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_step_details +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_step_interest +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_step_settings +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_step_terms import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable @@ -68,28 +68,28 @@ private fun RecurringAccountScaffold( onAction: (RecurringAccountAction) -> Unit, ) { val steps = listOf( - Step(name = stringResource(Res.string.step_details)) { + Step(name = stringResource(Res.string.feature_recurringDeposit_step_details)) { DetailsPage( onNext = { onAction(RecurringAccountAction.NextStep) }, ) }, - Step(name = stringResource(Res.string.step_terms)) { + Step(name = stringResource(Res.string.feature_recurringDeposit_step_terms)) { TermsPage( onNext = { onAction(RecurringAccountAction.NextStep) }, ) }, - Step(name = stringResource(Res.string.step_settings)) { + Step(name = stringResource(Res.string.feature_recurringDeposit_step_settings)) { SettingPage( state = state, onAction = onAction, ) }, - Step(name = stringResource(Res.string.step_interest)) { + Step(name = stringResource(Res.string.feature_recurringDeposit_step_interest)) { InterestPage( onNext = { onAction(RecurringAccountAction.NextStep) }, ) }, - Step(name = stringResource(Res.string.step_charges)) { + Step(name = stringResource(Res.string.feature_recurringDeposit_step_charges)) { ChargesPage( onNext = { onAction(RecurringAccountAction.NextStep) }, ) @@ -97,7 +97,7 @@ private fun RecurringAccountScaffold( ) MifosScaffold( - title = stringResource(Res.string.create_recurring_deposit_account), + title = stringResource(Res.string.feature_recurringDeposit_create_recurring_deposit_account), onBackPressed = { onAction(RecurringAccountAction.NavigateBack) }, modifier = modifier, ) { paddingValues -> diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt index caf6b6c7e4..d21b490f0d 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt @@ -10,7 +10,7 @@ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount import androidclient.feature.recurringdeposit.generated.resources.Res -import androidclient.feature.recurringdeposit.generated.resources.no_internet_connection +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_no_internet_connection import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import androidx.navigation.toRoute @@ -71,7 +71,7 @@ class RecurringAccountViewModel( loadTemplateByProduct() } else { setErrorState( - getString(Res.string.no_internet_connection), + getString(Res.string.feature_recurringDeposit_no_internet_connection), ) } } @@ -102,7 +102,7 @@ class RecurringAccountViewModel( viewModelScope.launch { val online = networkMonitor.isOnline.first() if (!online) { - setErrorState(getString(Res.string.no_internet_connection)) + setErrorState(getString(Res.string.feature_recurringDeposit_no_internet_connection)) return@launch } recurringAccountRepository.getRecurringAccountTemplate().collect { templateState -> @@ -129,7 +129,7 @@ class RecurringAccountViewModel( viewModelScope.launch { val online = networkMonitor.isOnline.first() if (!online) { - setErrorState(getString(Res.string.no_internet_connection)) + setErrorState(getString(Res.string.feature_recurringDeposit_no_internet_connection)) return@launch } recurringAccountRepository.getRecurringAccountTemplateByProduct( @@ -204,7 +204,7 @@ class RecurringAccountViewModel( val online = networkMonitor.isOnline.first() if (!online) { - setErrorState(getString(Res.string.no_internet_connection)) + setErrorState(getString(Res.string.feature_recurringDeposit_no_internet_connection)) return@launch } diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt index 51bc45975f..1159c65b65 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt @@ -10,27 +10,27 @@ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages import androidclient.feature.recurringdeposit.generated.resources.Res -import androidclient.feature.recurringdeposit.generated.resources.adjust_advance_payments -import androidclient.feature.recurringdeposit.generated.resources.allow_withdrawals -import androidclient.feature.recurringdeposit.generated.resources.apply_penal_interest -import androidclient.feature.recurringdeposit.generated.resources.back -import androidclient.feature.recurringdeposit.generated.resources.deposit_frequency_same_as_meeting -import androidclient.feature.recurringdeposit.generated.resources.deposit_period -import androidclient.feature.recurringdeposit.generated.resources.for_pre_mature_closure -import androidclient.feature.recurringdeposit.generated.resources.frequency -import androidclient.feature.recurringdeposit.generated.resources.in_multiples_of -import androidclient.feature.recurringdeposit.generated.resources.is_mandatory_deposit -import androidclient.feature.recurringdeposit.generated.resources.lock_in_period -import androidclient.feature.recurringdeposit.generated.resources.maximum_deposit_term -import androidclient.feature.recurringdeposit.generated.resources.minimum_balance_for_interest -import androidclient.feature.recurringdeposit.generated.resources.minimum_deposit_term -import androidclient.feature.recurringdeposit.generated.resources.next -import androidclient.feature.recurringdeposit.generated.resources.penal_interest_percentage -import androidclient.feature.recurringdeposit.generated.resources.period -import androidclient.feature.recurringdeposit.generated.resources.recurring_deposit_amount -import androidclient.feature.recurringdeposit.generated.resources.recurring_deposit_details -import androidclient.feature.recurringdeposit.generated.resources.step_settings -import androidclient.feature.recurringdeposit.generated.resources.type +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_adjust_advance_payments +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_allow_withdrawals +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_apply_penal_interest +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_back +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_deposit_frequency_same_as_meeting +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_deposit_period +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_for_pre_mature_closure +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_frequency +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_in_multiples_of +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_is_mandatory_deposit +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_lock_in_period +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_maximum_deposit_term +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_minimum_balance_for_interest +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_minimum_deposit_term +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_next +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_penal_interest_percentage +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_period +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_recurring_deposit_amount +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_recurring_deposit_details +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_step_settings +import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_type import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -77,7 +77,7 @@ fun SettingPage( horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.spacedBy(20.dp), ) { - Text(stringResource(Res.string.step_settings), fontWeight = FontWeight.Bold, fontSize = 18.sp) + Text(stringResource(Res.string.feature_recurringDeposit_step_settings), fontWeight = FontWeight.Bold, fontSize = 18.sp) Column( horizontalAlignment = Alignment.Start, @@ -85,29 +85,29 @@ fun SettingPage( ) { Row(verticalAlignment = Alignment.CenterVertically) { Checkbox(settingsState.isMandatory, onCheckedChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleMandatoryDeposit) }) - Text(stringResource(Res.string.is_mandatory_deposit)) + Text(stringResource(Res.string.feature_recurringDeposit_is_mandatory_deposit)) } Row(verticalAlignment = Alignment.CenterVertically) { MifosCheckBox( - text = stringResource(Res.string.adjust_advance_payments), + text = stringResource(Res.string.feature_recurringDeposit_adjust_advance_payments), checked = settingsState.adjustAdvancePayments, onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAdvancePaymentsTowardsFutureInstallments) }, ) } Row(verticalAlignment = Alignment.CenterVertically) { MifosCheckBox( - text = stringResource(Res.string.allow_withdrawals), + text = stringResource(Res.string.feature_recurringDeposit_allow_withdrawals), checked = settingsState.allowWithdrawals, onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAllowWithdrawals) }, ) } } - Text(stringResource(Res.string.lock_in_period), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurringDeposit_lock_in_period), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.lockInPeriod.frequency, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriod(it)) }, - label = stringResource(Res.string.frequency), + label = stringResource(Res.string.feature_recurringDeposit_frequency), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -131,13 +131,13 @@ fun SettingPage( RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriodType(id), ) }, - label = stringResource(Res.string.type), + label = stringResource(Res.string.feature_recurringDeposit_type), ) - Text(stringResource(Res.string.recurring_deposit_details), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurringDeposit_recurring_deposit_details), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.recurringDepositDetails.depositAmount, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetRecurringDepositAmount(it)) }, - label = stringResource(Res.string.recurring_deposit_amount), + label = stringResource(Res.string.feature_recurringDeposit_recurring_deposit_amount), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -146,11 +146,11 @@ fun SettingPage( ), modifier = Modifier.fillMaxWidth(), ) - Text(stringResource(Res.string.deposit_period), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurringDeposit_deposit_period), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.depositPeriod.period, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetDepositPeriod(it)) }, - label = stringResource(Res.string.deposit_period), + label = stringResource(Res.string.feature_recurringDeposit_deposit_period), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -172,23 +172,23 @@ fun SettingPage( onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetDepositPeriodType(id)) }, - label = stringResource(Res.string.type), + label = stringResource(Res.string.feature_recurringDeposit_type), modifier = Modifier.fillMaxWidth(), ) Row(verticalAlignment = Alignment.CenterVertically) { MifosCheckBox( - text = stringResource(Res.string.deposit_frequency_same_as_meeting), + text = stringResource(Res.string.feature_recurringDeposit_deposit_frequency_same_as_meeting), checked = settingsState.depositPeriod.depositFrequencySameAsGroupCenterMeeting, onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleDepositFrequencySameAsGroupCenterMeeting) }, ) } - Text(stringResource(Res.string.minimum_deposit_term), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurringDeposit_minimum_deposit_term), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.minDepositTerm.frequency, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreq(it)) }, - label = stringResource(Res.string.frequency), + label = stringResource(Res.string.feature_recurringDeposit_frequency), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -210,14 +210,14 @@ fun SettingPage( onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqType(id)) }, - label = stringResource(Res.string.type), + label = stringResource(Res.string.feature_recurringDeposit_type), modifier = Modifier.fillMaxWidth(), ) - Text(stringResource(Res.string.in_multiples_of), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurringDeposit_in_multiples_of), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.minDepositTerm.frequencyAfterInMultiplesOf, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqAfterInMultiOf(it)) }, - label = stringResource(Res.string.frequency), + label = stringResource(Res.string.feature_recurringDeposit_frequency), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -232,21 +232,21 @@ fun SettingPage( } else { "" }, - options = state.recurringDepositAccountTemplate?.periodFrequencyTypeOptions?.map { + options = state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.map { it.value ?: "" } ?: emptyList(), onValueChanged = {}, onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqTypeAfterInMultiOf(id)) }, - label = stringResource(Res.string.type), + label = stringResource(Res.string.feature_recurringDeposit_type), modifier = Modifier.fillMaxWidth(), ) - Text(stringResource(Res.string.maximum_deposit_term), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurringDeposit_maximum_deposit_term), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.maxDepositTerm.frequency, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMaxDepositTermFreq(it)) }, - label = stringResource(Res.string.frequency), + label = stringResource(Res.string.feature_recurringDeposit_frequency), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -268,16 +268,16 @@ fun SettingPage( onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMaxDepositTermFreqType(id)) }, - label = stringResource(Res.string.type), + label = stringResource(Res.string.feature_recurringDeposit_type), modifier = Modifier.fillMaxWidth(), ) - Text(stringResource(Res.string.for_pre_mature_closure), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurringDeposit_for_pre_mature_closure), fontWeight = FontWeight.Bold) Row(verticalAlignment = Alignment.CenterVertically) { Checkbox( settingsState.preMatureClosure.applyPenalInterest, onCheckedChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.TogglePreMatureClosureApplyPenalInterest) }, ) - Text(stringResource(Res.string.apply_penal_interest)) + Text(stringResource(Res.string.feature_recurringDeposit_apply_penal_interest)) } AnimatedVisibility( visible = settingsState.preMatureClosure.applyPenalInterest, @@ -289,7 +289,7 @@ fun SettingPage( MifosOutlinedTextField( value = settingsState.preMatureClosure.penalInterest, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosurePenalInterest(it)) }, - label = stringResource(Res.string.penal_interest_percentage), + label = stringResource(Res.string.feature_recurringDeposit_penal_interest_percentage), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -311,13 +311,13 @@ fun SettingPage( onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureInterestPeriodIndex(id)) }, - label = stringResource(Res.string.period), + label = stringResource(Res.string.feature_recurringDeposit_period), modifier = Modifier.fillMaxWidth(), ) MifosOutlinedTextField( value = settingsState.preMatureClosure.minimumBalanceForInterestCalculation, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureMinimumBalanceForInterestCalculation(it)) }, - label = stringResource(Res.string.minimum_balance_for_interest), + label = stringResource(Res.string.feature_recurringDeposit_minimum_balance_for_interest), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -339,8 +339,8 @@ fun SettingPage( settingsState.maxDepositTerm.frequency.isNotBlank() MifosTwoButtonRow( - firstBtnText = stringResource(Res.string.back), - secondBtnText = stringResource(Res.string.next), + firstBtnText = stringResource(Res.string.feature_recurringDeposit_back), + secondBtnText = stringResource(Res.string.feature_recurringDeposit_next), onFirstBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnBackPress) }, onSecondBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnNextPress) }, isButtonIconVisible = true, From c2ec4007217e6476827eea2dcc92b1de7c23887b Mon Sep 17 00:00:00 2001 From: thekalpeshpawar Date: Wed, 5 Nov 2025 16:33:39 +0530 Subject: [PATCH 19/19] feat: update navigation for recurring account flow and improve layout in RecurringAccountScreen --- .../ClientApplyNewApplicationsScreen.kt | 4 +- .../client/navigation/ClientNavigation.kt | 4 +- .../RecurringAccountRoute.kt | 5 ++- .../RecurringAccountScreen.kt | 43 ++++++++++++------- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt index d057e7c195..530d602c16 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientApplyNewApplications/ClientApplyNewApplicationsScreen.kt @@ -75,9 +75,9 @@ internal fun ClientApplyNewApplicationsScreen( is ClientApplyNewApplicationsEvent.OnActionClick -> { when (event.action) { ClientApplyNewApplicationsItem.NewFixedAccount -> onNavigateApplyFixedAccount() - ClientApplyNewApplicationsItem.NewLoanAccount -> onNavigateApplyLoanAccount(state.clientId,) + ClientApplyNewApplicationsItem.NewLoanAccount -> onNavigateApplyLoanAccount(state.clientId) ClientApplyNewApplicationsItem.NewRecurringAccount -> onNavigateApplyRecurringAccount(state.clientId) - ClientApplyNewApplicationsItem.NewSavingsAccount -> onNavigateApplySavingsAccount(state.clientId,) + ClientApplyNewApplicationsItem.NewSavingsAccount -> onNavigateApplySavingsAccount(state.clientId) ClientApplyNewApplicationsItem.NewShareAccount -> onNavigateApplyShareAccount(state.clientId) } } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt index 06b44b01f6..23f097298a 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/navigation/ClientNavigation.kt @@ -345,7 +345,9 @@ fun NavGraphBuilder.clientNavGraph( createShareAccountDestination( navController = navController, ) - recurringAccountDestination() + recurringAccountDestination( + navController = navController, + ) fixedAccountDestination() } } diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountRoute.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountRoute.kt index b84e5f18c9..cf3b9b764e 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountRoute.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountRoute.kt @@ -19,9 +19,12 @@ data class RecurringAccountRoute( val clientId: Int = -1, ) -fun NavGraphBuilder.recurringAccountDestination() { +fun NavGraphBuilder.recurringAccountDestination( + navController: NavController, +) { composable { RecurringAccountScreen( + navController = navController, onNavigateBack = {}, onFinish = {}, ) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt index c98b88f516..a829d59952 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt @@ -16,14 +16,18 @@ import androidclient.feature.recurringdeposit.generated.resources.feature_recurr import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_step_interest import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_step_settings import androidclient.feature.recurringdeposit.generated.resources.feature_recurringDeposit_step_terms +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.navigation.NavController import com.mifos.core.designsystem.component.MifosScaffold -import com.mifos.core.ui.components.MifosErrorComponent +import com.mifos.core.designsystem.component.MifosSweetError +import com.mifos.core.ui.components.MifosBreadcrumbNavBar import com.mifos.core.ui.components.MifosProgressIndicator import com.mifos.core.ui.components.MifosStepper import com.mifos.core.ui.components.Step @@ -38,6 +42,7 @@ import org.koin.compose.viewmodel.koinViewModel @Composable internal fun RecurringAccountScreen( + navController: NavController, onNavigateBack: () -> Unit, onFinish: () -> Unit, modifier: Modifier = Modifier, @@ -55,6 +60,7 @@ internal fun RecurringAccountScreen( RecurringDepositAccountDialogBox(state = state) RecurringAccountScaffold( + navController = navController, modifier = modifier, state = state, onAction = { viewModel.trySendAction(it) }, @@ -63,6 +69,7 @@ internal fun RecurringAccountScreen( @Composable private fun RecurringAccountScaffold( + navController: NavController, state: RecurringAccountState, modifier: Modifier = Modifier, onAction: (RecurringAccountAction) -> Unit, @@ -101,17 +108,24 @@ private fun RecurringAccountScaffold( onBackPressed = { onAction(RecurringAccountAction.NavigateBack) }, modifier = modifier, ) { paddingValues -> - if (state.screenState == null) { - MifosStepper( - steps = steps, - currentIndex = state.currentStep, - onStepChange = { newIndex -> - onAction(RecurringAccountAction.OnStepChange(newIndex)) - }, - modifier = Modifier - .fillMaxWidth() - .padding(paddingValues), - ) + Column( + modifier = Modifier.padding(paddingValues) + .fillMaxSize(), + ) { + MifosBreadcrumbNavBar(navController) + + if (state.screenState == null) { + MifosStepper( + steps = steps, + currentIndex = state.currentStep, + onStepChange = { newIndex -> + onAction(RecurringAccountAction.OnStepChange(newIndex)) + }, + modifier = Modifier + .fillMaxWidth() + .padding(paddingValues), + ) + } } } } @@ -122,12 +136,9 @@ fun RecurringDepositAccountDialogBox( ) { when (state.screenState) { is RecurringAccountState.ScreenState.Error -> { - MifosErrorComponent( + MifosSweetError( message = state.screenState.message, isRetryEnabled = true, - onRetry = { - // Retry action can be handled here - }, ) } RecurringAccountState.ScreenState.Loading -> {