From c721f03ffc2a34c40daaa07f727401c04591c270 Mon Sep 17 00:00:00 2001 From: kalpesh Date: Tue, 21 Oct 2025 23:07:14 +0530 Subject: [PATCH 01/28] 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 b005d0dc5c9..ec1eadeff18 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 08425ac6059..1ec9c43afce 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 b6da3df34a3..3c20a6d7a0a 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 72ffcc43ca7..6aa0050b849 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 00000000000..139597f9cb0 --- /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 fd3c1caa913..92201a86aa0 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 7c25d67f599..1fe6795272a 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 1fb57076371..c44146cd090 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 25d86d0b010..e96b89390af 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 65c25ce11d1..d057e7c1951 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 217caa04d9f..93daf678f3d 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 00000000000..ad378247600 --- /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 b3c674c7c31..7e2d44c0b1c 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 33abc8b65b4..f22e59deb82 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 ab427d5420e..6d6bdef4f6c 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 365a9381430..c6d4ad6bed6 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/28] 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 c6d4ad6bed6..573f495b57b 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/28] 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 1ec9c43afce..4d14674e00f 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 1fe6795272a..2fa161c0735 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 c44146cd090..1a90f3c8ed6 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/28] 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 ad378247600..bf5e9f53fa6 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 7e2d44c0b1c..b84e5f18c94 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 f22e59deb82..7558a093306 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 6d6bdef4f6c..d7126de51ee 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 573f495b57b..e7940be1c7f 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/28] 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 139597f9cb0..00000000000 --- 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/28] 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 7558a093306..9ca6e52a050 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 d7126de51ee..740c8e25cf8 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/28] 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 9ca6e52a050..19deb6e42a8 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/28] 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 740c8e25cf8..bce389e7557 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/28] 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 3c20a6d7a0a..6744704f683 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 6aa0050b849..11410bc7e66 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 93daf678f3d..b75db479693 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 19deb6e42a8..6edf759a278 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 bce389e7557..619bb8e86af 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 e7940be1c7f..cff5ea594ed 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/28] 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 619bb8e86af..5669c320e22 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 cff5ea594ed..927585d17f0 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/28] 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 4d14674e00f..1fbb285b378 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 00000000000..139597f9cb0 --- /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/28] 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 91fd393753c..8e43459a9b7 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/28] 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 5669c320e22..5179f14c442 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 927585d17f0..288d01f2ad6 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 8e43459a9b7..656e6042196 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/28] 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 1fbb285b378..4d14674e00f 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 139597f9cb0..00000000000 --- 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/28] 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 5179f14c442..0e05945288a 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 288d01f2ad6..d5b9e59012f 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/28] 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 0e05945288a..035012c9ac5 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/28] 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 035012c9ac5..caf6b6c7e45 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 d5b9e59012f..51bc45975f8 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/28] 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 00000000000..6270c0c6d2e --- /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 b75db479693..00000000000 --- 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 6edf759a278..c98b88f516f 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 caf6b6c7e45..d21b490f0df 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 51bc45975f8..1159c65b652 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/28] 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 d057e7c1951..530d602c162 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 06b44b01f6a..23f097298a3 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 b84e5f18c94..cf3b9b764e3 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 c98b88f516f..a829d599526 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 -> { From 2e6e4e0aecc059a0003cca6def8a7699dacdeafa Mon Sep 17 00:00:00 2001 From: thekalpeshpawar Date: Sun, 9 Nov 2025 18:10:38 +0530 Subject: [PATCH 20/28] feat: rename string resources for recurring deposit feature and update UI components --- .../component/MifosOutlinedTextField.kt | 8 +- .../feature_recurringDeposit_string.xml | 39 ------ .../feature_recurring_deposit_string.xml | 39 ++++++ .../RecurringAccountScreen.kt | 24 ++-- .../RecurringAccountViewModel.kt | 62 ++++------ .../pages/SettingsPage.kt | 111 ++++++++++-------- version.txt | 2 +- 7 files changed, 142 insertions(+), 143 deletions(-) delete mode 100644 feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurringDeposit_string.xml create mode 100644 feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurring_deposit_string.xml diff --git a/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt b/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt index 64fb1a74270..9379c2c11f2 100644 --- a/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt +++ b/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt @@ -425,7 +425,13 @@ fun MifosOutlinedTextField( shape = shape, colors = colors, value = value, - label = { Text(text = label) }, + label = { + Text( + text = label, + maxLines = 1, + softWrap = true + ) + }, onValueChange = onValueChange, textStyle = textStyle, modifier = modifier.fillMaxWidth(), diff --git a/feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurringDeposit_string.xml b/feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurringDeposit_string.xml deleted file mode 100644 index 6270c0c6d2e..00000000000 --- a/feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurringDeposit_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/composeResources/values/feature_recurring_deposit_string.xml b/feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurring_deposit_string.xml new file mode 100644 index 00000000000..ba66a27ad1d --- /dev/null +++ b/feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurring_deposit_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/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt index a829d599526..22043d836af 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.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 androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_create_recurring_deposit_account +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_charges +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_interest +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_settings +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_terms +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_details import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -75,28 +75,28 @@ private fun RecurringAccountScaffold( onAction: (RecurringAccountAction) -> Unit, ) { val steps = listOf( - Step(name = stringResource(Res.string.feature_recurringDeposit_step_details)) { + Step(name = stringResource(Res.string.feature_recurring_deposit_step_details)) { DetailsPage( onNext = { onAction(RecurringAccountAction.NextStep) }, ) }, - Step(name = stringResource(Res.string.feature_recurringDeposit_step_terms)) { + Step(name = stringResource(Res.string.feature_recurring_deposit_step_terms)) { TermsPage( onNext = { onAction(RecurringAccountAction.NextStep) }, ) }, - Step(name = stringResource(Res.string.feature_recurringDeposit_step_settings)) { + Step(name = stringResource(Res.string.feature_recurring_deposit_step_settings)) { SettingPage( state = state, onAction = onAction, ) }, - Step(name = stringResource(Res.string.feature_recurringDeposit_step_interest)) { + Step(name = stringResource(Res.string.feature_recurring_deposit_step_interest)) { InterestPage( onNext = { onAction(RecurringAccountAction.NextStep) }, ) }, - Step(name = stringResource(Res.string.feature_recurringDeposit_step_charges)) { + Step(name = stringResource(Res.string.feature_recurring_deposit_step_charges)) { ChargesPage( onNext = { onAction(RecurringAccountAction.NextStep) }, ) @@ -104,7 +104,7 @@ private fun RecurringAccountScaffold( ) MifosScaffold( - title = stringResource(Res.string.feature_recurringDeposit_create_recurring_deposit_account), + title = stringResource(Res.string.feature_recurring_deposit_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 d21b490f0df..c8dcd0c3e18 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.feature_recurringDeposit_no_internet_connection +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_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.feature_recurringDeposit_no_internet_connection), + getString(Res.string.feature_recurring_deposit_no_internet_connection), ) } } @@ -102,7 +102,7 @@ class RecurringAccountViewModel( viewModelScope.launch { val online = networkMonitor.isOnline.first() if (!online) { - setErrorState(getString(Res.string.feature_recurringDeposit_no_internet_connection)) + setErrorState(getString(Res.string.feature_recurring_deposit_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.feature_recurringDeposit_no_internet_connection)) + setErrorState(getString(Res.string.feature_recurring_deposit_no_internet_connection)) return@launch } recurringAccountRepository.getRecurringAccountTemplateByProduct( @@ -157,44 +157,26 @@ class RecurringAccountViewModel( } private fun formattedAmount(amount: String): String { - val currencySymbol = state.recurringDepositAccountTemplate.currency?.displaySymbol ?: "" - if (amount.isEmpty()) return "" + // Remove any non-numeric characters except dot, and remove minus signs to prevent negative amounts + val cleaned = amount.replace("[^0-9.]".toRegex(), "").replace("-", "") - val cleaned = amount.replace("[^0-9.-]".toRegex(), "") + if (cleaned.isEmpty()) return "$" - if (cleaned.isEmpty()) return "" - if (cleaned == "-") return "-" - if (cleaned == ".") return "." // Allows user to type just '.' + // Format the number + val parts = cleaned.split('.', limit = 2) + val intPart = parts[0].trimStart('0').ifEmpty { "0" } + val decimalPart = if (parts.size > 1) { + val dec = parts[1].take(2) + if (dec.isNotEmpty()) ".$dec" else "" + } else "" - val negative = cleaned.startsWith("-") - val magnitude = if (negative) cleaned.substring(1) else cleaned - - val parts = magnitude.split('.', limit = 2) - var intPart = parts[0].trimStart('0').ifEmpty { "0" } // Clean up leading zeros - - var decimalPart = "" - if (parts.size > 1) { - decimalPart = parts[1].take(2) - if (decimalPart.isNotEmpty() || parts[1].isEmpty()) { - decimalPart = ".$decimalPart" - } - } - - if (intPart == "0" && magnitude.startsWith('.') && decimalPart.isNotEmpty()) { - intPart = "" - } - - val numericPart = when { - amount.startsWith('.') && decimalPart.isNotEmpty() -> decimalPart - amount.startsWith("-.") && decimalPart.isNotEmpty() -> decimalPart - else -> "$intPart$decimalPart" + val numericPart = if (intPart == "0" && cleaned.startsWith('.') && decimalPart.isNotEmpty()) { + decimalPart + } else { + "$intPart$decimalPart" } - val signedNumericPart = if (negative && numericPart.isNotEmpty()) "-$numericPart" else numericPart - - if (amount == "." && signedNumericPart.isEmpty()) return "." - - return "$currencySymbol$signedNumericPart" + return "$$numericPart" } private fun createRecurringDepositAccount() { @@ -204,7 +186,7 @@ class RecurringAccountViewModel( val online = networkMonitor.isOnline.first() if (!online) { - setErrorState(getString(Res.string.feature_recurringDeposit_no_internet_connection)) + setErrorState(getString(Res.string.feature_recurring_deposit_no_internet_connection)) return@launch } @@ -434,7 +416,7 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( preMatureClosure = state.recurringDepositAccountSettings.preMatureClosure.copy( - minimumBalanceForInterestCalculation = formattedAmount(action.minimumBalanceForInterestCalculation), + minimumBalanceForInterestCalculation = action.minimumBalanceForInterestCalculation, ), ), ) @@ -457,7 +439,7 @@ class RecurringAccountViewModel( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( recurringDepositDetails = state.recurringDepositAccountSettings .recurringDepositDetails.copy( - depositAmount = formattedAmount(action.depositAmount), + depositAmount = action.depositAmount, ), ), ) 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 1159c65b652..62a7434fe91 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.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 androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_adjust_advance_payments +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_allow_withdrawals +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_apply_penal_interest +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_back +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_deposit_frequency_same_as_meeting +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_deposit_period +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_for_pre_mature_closure +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_frequency +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_in_multiples_of +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_is_mandatory_deposit +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_lock_in_period +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_maximum_deposit_term +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_minimum_balance_for_interest +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_minimum_deposit_term +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_next +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_penal_interest_percentage +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_period +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_recurring_deposit_amount +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_recurring_deposit_details +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_settings +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_type import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -57,6 +57,7 @@ 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 io.ktor.websocket.Frame import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.ui.tooling.preview.Preview @@ -77,7 +78,7 @@ fun SettingPage( horizontalAlignment = Alignment.Start, verticalArrangement = Arrangement.spacedBy(20.dp), ) { - Text(stringResource(Res.string.feature_recurringDeposit_step_settings), fontWeight = FontWeight.Bold, fontSize = 18.sp) + Text(stringResource(Res.string.feature_recurring_deposit_step_settings), fontWeight = FontWeight.Bold, fontSize = 18.sp) Column( horizontalAlignment = Alignment.Start, @@ -85,29 +86,29 @@ fun SettingPage( ) { Row(verticalAlignment = Alignment.CenterVertically) { Checkbox(settingsState.isMandatory, onCheckedChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleMandatoryDeposit) }) - Text(stringResource(Res.string.feature_recurringDeposit_is_mandatory_deposit)) + Text(stringResource(Res.string.feature_recurring_deposit_is_mandatory_deposit)) } Row(verticalAlignment = Alignment.CenterVertically) { MifosCheckBox( - text = stringResource(Res.string.feature_recurringDeposit_adjust_advance_payments), + text = stringResource(Res.string.feature_recurring_deposit_adjust_advance_payments), checked = settingsState.adjustAdvancePayments, onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAdvancePaymentsTowardsFutureInstallments) }, ) } Row(verticalAlignment = Alignment.CenterVertically) { MifosCheckBox( - text = stringResource(Res.string.feature_recurringDeposit_allow_withdrawals), + text = stringResource(Res.string.feature_recurring_deposit_allow_withdrawals), checked = settingsState.allowWithdrawals, onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleAllowWithdrawals) }, ) } } - Text(stringResource(Res.string.feature_recurringDeposit_lock_in_period), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurring_deposit_lock_in_period), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.lockInPeriod.frequency, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriod(it)) }, - label = stringResource(Res.string.feature_recurringDeposit_frequency), + label = stringResource(Res.string.feature_recurring_deposit_frequency), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -131,26 +132,31 @@ fun SettingPage( RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriodType(id), ) }, - label = stringResource(Res.string.feature_recurringDeposit_type), + label = stringResource(Res.string.feature_recurring_deposit_type), ) - Text(stringResource(Res.string.feature_recurringDeposit_recurring_deposit_details), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurring_deposit_recurring_deposit_details), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.recurringDepositDetails.depositAmount, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetRecurringDepositAmount(it)) }, - label = stringResource(Res.string.feature_recurringDeposit_recurring_deposit_amount), + label = stringResource(Res.string.feature_recurring_deposit_recurring_deposit_amount), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, imeAction = ImeAction.Next, ), + leadingIcon = { + Text( + text = state.recurringDepositAccountTemplate.currency?.displaySymbol?: "" + ) + } ), modifier = Modifier.fillMaxWidth(), ) - Text(stringResource(Res.string.feature_recurringDeposit_deposit_period), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurring_deposit_deposit_period), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.depositPeriod.period, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetDepositPeriod(it)) }, - label = stringResource(Res.string.feature_recurringDeposit_deposit_period), + label = stringResource(Res.string.feature_recurring_deposit_deposit_period), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -172,23 +178,23 @@ fun SettingPage( onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetDepositPeriodType(id)) }, - label = stringResource(Res.string.feature_recurringDeposit_type), + label = stringResource(Res.string.feature_recurring_deposit_type), modifier = Modifier.fillMaxWidth(), ) Row(verticalAlignment = Alignment.CenterVertically) { MifosCheckBox( - text = stringResource(Res.string.feature_recurringDeposit_deposit_frequency_same_as_meeting), + text = stringResource(Res.string.feature_recurring_deposit_deposit_frequency_same_as_meeting), checked = settingsState.depositPeriod.depositFrequencySameAsGroupCenterMeeting, onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleDepositFrequencySameAsGroupCenterMeeting) }, ) } - Text(stringResource(Res.string.feature_recurringDeposit_minimum_deposit_term), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurring_deposit_minimum_deposit_term), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.minDepositTerm.frequency, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreq(it)) }, - label = stringResource(Res.string.feature_recurringDeposit_frequency), + label = stringResource(Res.string.feature_recurring_deposit_frequency), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -210,14 +216,14 @@ fun SettingPage( onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqType(id)) }, - label = stringResource(Res.string.feature_recurringDeposit_type), + label = stringResource(Res.string.feature_recurring_deposit_type), modifier = Modifier.fillMaxWidth(), ) - Text(stringResource(Res.string.feature_recurringDeposit_in_multiples_of), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurring_deposit_in_multiples_of), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.minDepositTerm.frequencyAfterInMultiplesOf, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqAfterInMultiOf(it)) }, - label = stringResource(Res.string.feature_recurringDeposit_frequency), + label = stringResource(Res.string.feature_recurring_deposit_frequency), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -239,14 +245,14 @@ fun SettingPage( onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqTypeAfterInMultiOf(id)) }, - label = stringResource(Res.string.feature_recurringDeposit_type), + label = stringResource(Res.string.feature_recurring_deposit_type), modifier = Modifier.fillMaxWidth(), ) - Text(stringResource(Res.string.feature_recurringDeposit_maximum_deposit_term), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurring_deposit_maximum_deposit_term), fontWeight = FontWeight.Bold) MifosOutlinedTextField( value = settingsState.maxDepositTerm.frequency, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMaxDepositTermFreq(it)) }, - label = stringResource(Res.string.feature_recurringDeposit_frequency), + label = stringResource(Res.string.feature_recurring_deposit_frequency), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -268,16 +274,16 @@ fun SettingPage( onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMaxDepositTermFreqType(id)) }, - label = stringResource(Res.string.feature_recurringDeposit_type), + label = stringResource(Res.string.feature_recurring_deposit_type), modifier = Modifier.fillMaxWidth(), ) - Text(stringResource(Res.string.feature_recurringDeposit_for_pre_mature_closure), fontWeight = FontWeight.Bold) + Text(stringResource(Res.string.feature_recurring_deposit_for_pre_mature_closure), fontWeight = FontWeight.Bold) Row(verticalAlignment = Alignment.CenterVertically) { Checkbox( settingsState.preMatureClosure.applyPenalInterest, onCheckedChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.TogglePreMatureClosureApplyPenalInterest) }, ) - Text(stringResource(Res.string.feature_recurringDeposit_apply_penal_interest)) + Text(stringResource(Res.string.feature_recurring_deposit_apply_penal_interest)) } AnimatedVisibility( visible = settingsState.preMatureClosure.applyPenalInterest, @@ -289,7 +295,7 @@ fun SettingPage( MifosOutlinedTextField( value = settingsState.preMatureClosure.penalInterest, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosurePenalInterest(it)) }, - label = stringResource(Res.string.feature_recurringDeposit_penal_interest_percentage), + label = stringResource(Res.string.feature_recurring_deposit_penal_interest_percentage), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, @@ -311,18 +317,23 @@ fun SettingPage( onOptionSelected = { id, name -> onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureInterestPeriodIndex(id)) }, - label = stringResource(Res.string.feature_recurringDeposit_period), + label = stringResource(Res.string.feature_recurring_deposit_period), modifier = Modifier.fillMaxWidth(), ) MifosOutlinedTextField( value = settingsState.preMatureClosure.minimumBalanceForInterestCalculation, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosureMinimumBalanceForInterestCalculation(it)) }, - label = stringResource(Res.string.feature_recurringDeposit_minimum_balance_for_interest), + label = stringResource(Res.string.feature_recurring_deposit_minimum_balance_for_interest), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Number, imeAction = ImeAction.Next, ), + leadingIcon = { + Text( + text = state.recurringDepositAccountTemplate.currency?.displaySymbol?: "" + ) + } ), modifier = Modifier.fillMaxWidth(), ) @@ -339,8 +350,8 @@ fun SettingPage( settingsState.maxDepositTerm.frequency.isNotBlank() MifosTwoButtonRow( - firstBtnText = stringResource(Res.string.feature_recurringDeposit_back), - secondBtnText = stringResource(Res.string.feature_recurringDeposit_next), + firstBtnText = stringResource(Res.string.feature_recurring_deposit_back), + secondBtnText = stringResource(Res.string.feature_recurring_deposit_next), onFirstBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnBackPress) }, onSecondBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnNextPress) }, isButtonIconVisible = true, diff --git a/version.txt b/version.txt index 656e6042196..a28cdf22d8b 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2025.9.2-beta.0.59 \ No newline at end of file +2025.11.2-beta.0.21 \ No newline at end of file From ccb2f54ee4fcf8f613900c230d55cf1169a00fdc Mon Sep 17 00:00:00 2001 From: thekalpeshpawar Date: Sun, 9 Nov 2025 21:13:44 +0530 Subject: [PATCH 21/28] Refactor string resources for recurring deposit feature: move hardcoded strings to feature_recurring_deposit_string.xml and update references in ViewModel and page composables. --- .../feature_recurring_deposit_string.xml | 13 + .../composeResources/values/string.xml | 28 -- .../RecurringAccountScreen.kt | 26 +- .../RecurringAccountViewModel.kt | 428 ++++++++---------- .../pages/ChargesPage.kt | 8 +- .../pages/DetailsPage.kt | 74 +-- .../pages/InterestPage.kt | 8 +- .../pages/SettingsPage.kt | 53 ++- .../pages/TermsPage.kt | 8 +- 9 files changed, 293 insertions(+), 353 deletions(-) delete mode 100644 feature/recurringDeposit/src/commonMain/composeResources/values/string.xml diff --git a/feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurring_deposit_string.xml b/feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurring_deposit_string.xml index ba66a27ad1d..b82f0de3f86 100644 --- a/feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurring_deposit_string.xml +++ b/feature/recurringDeposit/src/commonMain/composeResources/values/feature_recurring_deposit_string.xml @@ -36,4 +36,17 @@ Back Next No Internet Connection + Select + Cancel + Product Name + Submitted On + Field Officer + External Id + Deposit amount is required + Lock-in period frequency is required + Recurring frequency is required + Terms Page + Next Button + Interest Page + Charges Page \ 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 524d3433da7..00000000000 --- a/feature/recurringDeposit/src/commonMain/composeResources/values/string.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - Details - Terms - Settings - Interest - Charges - Create Recurring Deposit Account - - - Select - Cancel - Product Name - Submitted On - Field Officer - External Id - Back - Next - \ 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 a33fd5513f2..9c24ded3a69 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 @@ -17,22 +17,22 @@ import androidclient.feature.recurringdeposit.generated.resources.feature_recurr import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_terms import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_details 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.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import com.mifos.core.designsystem.component.MifosScaffold import com.mifos.core.designsystem.component.MifosSweetError import com.mifos.core.ui.components.MifosBreadcrumbNavBar +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 +import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.RecurringAccountAction.NavigateToStep import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages.ChargesPage import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages.DetailsPage import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages.InterestPage @@ -84,7 +84,7 @@ private fun RecurringAccountScaffold( }, Step(name = stringResource(Res.string.feature_recurring_deposit_step_terms)) { TermsPage( - onNext = { onAction(RecurringAccountAction.NextStep) }, + onNext = { onAction(RecurringAccountAction.OnNextPress) }, ) }, Step(name = stringResource(Res.string.feature_recurring_deposit_step_settings)) { @@ -95,12 +95,12 @@ private fun RecurringAccountScaffold( }, Step(name = stringResource(Res.string.feature_recurring_deposit_step_interest)) { InterestPage( - onNext = { onAction(RecurringAccountAction.NextStep) }, + onNext = { onAction(RecurringAccountAction.OnNextPress) }, ) }, Step(name = stringResource(Res.string.feature_recurring_deposit_step_charges)) { ChargesPage( - onNext = { onAction(RecurringAccountAction.NextStep) }, + onNext = { onAction(RecurringAccountAction.OnNextPress) }, ) }, ) @@ -116,31 +116,33 @@ private fun RecurringAccountScaffold( .padding(paddingValues), ) { MifosBreadcrumbNavBar(navController) - when (state.state) { - is RecurringAccountState.State.Error -> { + when (state.screenState) { + is RecurringAccountState.ScreenState.Error -> { MifosErrorComponent( - message = state.state.message, + message = state.screenState.message, isRetryEnabled = true, ) { onAction(RecurringAccountAction.Retry) } } - is RecurringAccountState.State.Loading -> { + is RecurringAccountState.ScreenState.Loading -> { MifosProgressIndicator() } - is RecurringAccountState.State.Success -> { + is RecurringAccountState.ScreenState.Success -> { MifosStepper( steps = steps, currentIndex = state.currentStep, onStepChange = { newIndex -> - onAction(RecurringAccountAction.OnStepChange(newIndex)) + onAction(NavigateToStep(newIndex)) }, modifier = Modifier .fillMaxWidth(), ) } + + null -> {} } } } @@ -160,6 +162,6 @@ fun RecurringDepositAccountDialogBox( RecurringAccountState.ScreenState.Loading -> { MifosProgressIndicator() } - null -> {} + else -> {} } } 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 d65f4acacb7..33d406bfd02 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 @@ -17,33 +17,26 @@ import com.mifos.core.data.repository.RecurringAccountRepository import com.mifos.core.model.objects.template.recurring.FieldOfficerOption import androidclient.feature.recurringdeposit.generated.resources.Res import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_no_internet_connection -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 androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_deposit_amount_is_required +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_lock_in_period_frequency_is_required +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_recurring_frequency_is_required 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.ScreenState import com.mifos.room.entities.templates.recurringDeposit.RecurringDepositAccountTemplate import kotlinx.coroutines.flow.first -import com.mifos.room.entities.templates.recurringDeposit.RecurringDepositAccountTemplate import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch -import org.jetbrains.compose.resources.StringResource -import kotlinx.coroutines.launch import org.jetbrains.compose.resources.getString -class RecurringAccountViewModel( - private val recurringAccountRepo: RecurringAccountRepository, - private val savedStateHandle: SavedStateHandle, -) : BaseViewModel< + +const val TOTAL_STEPS = 4 + class RecurringAccountViewModel( savedStateHandle: SavedStateHandle, private val networkMonitor: NetworkMonitor, - private val recurringAccountRepository: RecurringAccountRepository, + private val recurringAccountRepo: RecurringAccountRepository, ) : BaseViewModel< RecurringAccountState, RecurringAccountEvent, @@ -59,11 +52,6 @@ class RecurringAccountViewModel( loadRecurringAccountTemplate() } - init { - checkInitialConnectivity() - } - private val clientId = savedStateHandle.toRoute().clientId - private fun setLoadingState() { mutableStateFlow.update { it.copy( @@ -71,6 +59,7 @@ class RecurringAccountViewModel( ) } } + private fun setErrorState(message: String) { mutableStateFlow.update { it.copy( @@ -79,35 +68,6 @@ class RecurringAccountViewModel( } } - private fun checkInitialConnectivity() { - 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( - getString(Res.string.feature_recurring_deposit_no_internet_connection), - ) - } - } - } - private fun handleRetry() { - mutableStateFlow.update { - it.copy( - screenState = null, - recurringDepositAccountTemplate = RecurringDepositAccountTemplate(), - ) - } - checkInitialConnectivity() - } - private fun moveToNextStep() { if (state.currentStep < state.totalSteps) { mutableStateFlow.update { @@ -120,87 +80,6 @@ class RecurringAccountViewModel( } } - private fun loadTemplate() { - viewModelScope.launch { - val online = networkMonitor.isOnline.first() - if (!online) { - setErrorState(getString(Res.string.feature_recurring_deposit_no_internet_connection)) - return@launch - } - recurringAccountRepository.getRecurringAccountTemplate().collect { templateState -> - when (templateState) { - is DataState.Error -> { - setErrorState(message = templateState.message) - } - is DataState.Loading -> { - setLoadingState() - } - is DataState.Success -> { - mutableStateFlow.update { - it.copy( - screenState = null, - recurringDepositAccountTemplate = templateState.data, - ) - } - } - } - } - } - } - private fun loadTemplateByProduct() { - viewModelScope.launch { - val online = networkMonitor.isOnline.first() - if (!online) { - setErrorState(getString(Res.string.feature_recurring_deposit_no_internet_connection)) - return@launch - } - recurringAccountRepository.getRecurringAccountTemplateByProduct( - 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( - screenState = null, - recurringDepositAccountTemplate = templateState.data, - ) - } - } - } - } - } - } - - private fun formattedAmount(amount: String): String { - // Remove any non-numeric characters except dot, and remove minus signs to prevent negative amounts - val cleaned = amount.replace("[^0-9.]".toRegex(), "").replace("-", "") - - if (cleaned.isEmpty()) return "$" - - // Format the number - val parts = cleaned.split('.', limit = 2) - val intPart = parts[0].trimStart('0').ifEmpty { "0" } - val decimalPart = if (parts.size > 1) { - val dec = parts[1].take(2) - if (dec.isNotEmpty()) ".$dec" else "" - } else "" - - val numericPart = if (intPart == "0" && cleaned.startsWith('.') && decimalPart.isNotEmpty()) { - decimalPart - } else { - "$intPart$decimalPart" - } - - return "$$numericPart" - } - private fun createRecurringDepositAccount() { viewModelScope.launch { val s = state @@ -211,17 +90,16 @@ class RecurringAccountViewModel( setErrorState(getString(Res.string.feature_recurring_deposit_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 recurringFreq = settings.minimumDepositTerm.frequency.toIntOrNull() val payload = RecurringDepositAccountPayload( adjustAdvanceTowardsFuturePayments = settings.adjustAdvancePayments, allowWithdrawal = settings.allowWithdrawals, - clientId = clientId, + clientId = state.clientId, dateFormat = "dd MMMM yyyy", depositPeriod = settings.depositPeriod.period.toIntOrNull(), depositPeriodFrequencyId = settings.depositPeriod.periodType, @@ -241,28 +119,30 @@ class RecurringAccountViewModel( monthDayFormat = "dd MMMM", productId = s.recurringDepositAccountDetail.productId, recurringFrequency = recurringFreq, - recurringFrequencyType = settings.minDepositTerm.frequencyTypeIndex, + recurringFrequencyType = settings.minimumDepositTerm.frequencyTypeIndex, // date in dd MM yyyy format. submittedOnDate = s.recurringDepositAccountDetail.submittedOnDate, ) if (state.isOnline) { - recurringAccountRepository.createRecurringDepositAccount(payload).collect { dataState -> + recurringAccountRepo.createRecurringDepositAccount(payload).collect { dataState -> when (dataState) { is DataState.Error -> { if (depositAmountInt == null) { - setErrorState("Deposit amount is required") + setErrorState(getString(Res.string.feature_recurring_deposit_deposit_amount_is_required)) } else if (lockinFreq == null) { - setErrorState("Lock-in period frequency is required") + setErrorState(getString(Res.string.feature_recurring_deposit_lock_in_period_frequency_is_required)) } else if (recurringFreq == null) { - setErrorState("Recurring frequency is required") + setErrorState(getString(Res.string.feature_recurring_deposit_recurring_frequency_is_required)) } else { setErrorState(dataState.message) } } + is DataState.Loading -> { setLoadingState() } + is DataState.Success -> { mutableStateFlow.update { it.copy(screenState = null) @@ -274,96 +154,93 @@ class RecurringAccountViewModel( } } - override fun handleAction(action: RecurringAccountAction) { - when (action) { - RecurringAccountAction.NextStep -> { - moveToNextStep() - } - - is RecurringAccountAction.OnStepChange -> { - mutableStateFlow.update { it.copy(currentStep = action.index) } - } - - RecurringAccountAction.NavigateBack -> { - sendEvent(RecurringAccountEvent.NavigateBack) - } - - RecurringAccountAction.Finish -> { - sendEvent(RecurringAccountEvent.Finish) - } - - - is RecurringAccountAction.OnProductNameChange -> handleProductNameChange(action) - - is RecurringAccountAction.OnSubmissionDateChange -> handleSubmissionDateChange(action) - - is RecurringAccountAction.OnSubmissionDatePick -> handleSubmissionDatePick(action) - - is RecurringAccountAction.OnFieldOfficerChange -> handleFieldOfficerChange(action) - - is RecurringAccountAction.OnExternalIdChange -> handleExternalIdChange(action) - - RecurringAccountAction.Retry -> resetForRetry() - } - } - - private fun handleProductNameChange(action: RecurringAccountAction.OnProductNameChange) { + private fun handleProductNameChange(action: RecurringAccountAction.RecurringAccountDetailsAction.OnProductNameChange) { mutableStateFlow.update { it.copy( - loanProductSelected = action.index, + recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( + loanProductSelected = action.index, + ), ) } - loadRecurringAccountTemplateWithProduct(state.clientId, state.template?.productOptions?.get(state.loanProductSelected)?.id ?: -1) + loadRecurringAccountTemplateWithProduct( + state.clientId, + state.template.productOptions?.get(state.recurringDepositAccountDetail.loanProductSelected)?.id ?: -1 + ) } private fun resetForRetry() { mutableStateFlow.update { it.copy( + isOnline = false, + clientId = -1, currentStep = 0, - template = null, - state = RecurringAccountState.State.Loading, - fieldOfficerOptions = emptyList(), + screenState = null, + recurringDepositAccountDetail = RecurringAccountDetailsState(), + template = RecurringDepositAccountTemplate(), + recurringDepositAccountSettings = RecurringAccountSettingsState(), + currencyIndex = -1, + currencyError = null, ) } loadRecurringAccountTemplate() } - private fun handleFieldOfficerChange(action: RecurringAccountAction.OnFieldOfficerChange) { + private fun handleFieldOfficerChange(action: RecurringAccountAction.RecurringAccountDetailsAction.OnFieldOfficerChange) { mutableStateFlow.update { it.copy( - fieldOfficerIndex = action.index, - fieldOfficerError = null, + recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( + fieldOfficerIndex = action.index, + fieldOfficerError = null, + ), ) } } - private fun handleSubmissionDatePick(action: RecurringAccountAction.OnSubmissionDatePick) { - mutableStateFlow.update { it.copy(showSubmissionDatePick = action.state) } + private fun handleSubmissionDatePick(action: RecurringAccountAction.RecurringAccountDetailsAction.OnSubmissionDatePick) { + mutableStateFlow.update { + it.copy( + recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( + showSubmissionDatePick = action.state + ), + ) + } } - private fun handleSubmissionDateChange(action: RecurringAccountAction.OnSubmissionDateChange) { - mutableStateFlow.update { it.copy(submissionDate = action.date) } + private fun handleSubmissionDateChange(action: RecurringAccountAction.RecurringAccountDetailsAction.OnSubmissionDateChange) { + mutableStateFlow.update { + it.copy( + recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( + submissionDate = action.date + ), + ) + } } - private fun handleExternalIdChange(action: RecurringAccountAction.OnExternalIdChange) { - mutableStateFlow.update { it.copy(externalId = action.value) } + private fun handleExternalIdChange(action: RecurringAccountAction.RecurringAccountDetailsAction.OnExternalIdChange) { + mutableStateFlow.update { + it.copy( + recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( + externalId = action.value + ), + ) + } } private fun loadRecurringAccountTemplate() = viewModelScope.launch { - recurringAccountRepo.getRecuttingAccountRepository().collect { state -> + recurringAccountRepo.getRecurringAccountTemplate().collect { state -> when (state) { is DataState.Success -> { mutableStateFlow.update { it.copy( - state = RecurringAccountState.State.Success, - template = state.data, + screenState = ScreenState.Success, ) } } + is DataState.Error -> { mutableStateFlow.update { it.copy( - state = RecurringAccountState.State.Error(state.message), + screenState = ScreenState.Error(state.message), ) } } @@ -371,7 +248,7 @@ class RecurringAccountViewModel( DataState.Loading -> { mutableStateFlow.update { it.copy( - state = RecurringAccountState.State.Loading, + screenState = ScreenState.Loading, ) } } @@ -383,23 +260,27 @@ class RecurringAccountViewModel( clientId: Int, productId: Int, ) = viewModelScope.launch { - recurringAccountRepo.getRecuttingAccountRepositoryBtProduct(clientId, productId).collect { state -> + recurringAccountRepo.getRecurringAccountTemplateByProduct(clientId, productId).collect { state -> when (state) { is DataState.Success -> { mutableStateFlow.update { it.copy( - state = RecurringAccountState.State.Success, - template = state.data, - fieldOfficerOptions = state.data.fieldOfficerOptions, - isMiniLoaderActive = false, + screenState = ScreenState.Success, + recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( + fieldOfficerOptions = state.data.fieldOfficerOptions, + isMiniLoaderActive = false, + ), ) } } + is DataState.Error -> { mutableStateFlow.update { it.copy( - state = RecurringAccountState.State.Error(state.message), - isMiniLoaderActive = false, + screenState = ScreenState.Error(state.message), + recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( + isMiniLoaderActive = false, + ), ) } } @@ -407,7 +288,9 @@ class RecurringAccountViewModel( DataState.Loading -> { mutableStateFlow.update { it.copy( - isMiniLoaderActive = true, + recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( + isMiniLoaderActive = true, + ), ) } } @@ -415,17 +298,15 @@ class RecurringAccountViewModel( } } + override fun handleAction(action: RecurringAccountAction) { + when (action) { + RecurringAccountAction.Retry -> { - handleRetry() + resetForRetry() } + is RecurringAccountAction.RecurringAccountSettingsAction -> { when (action) { - RecurringAccountAction.RecurringAccountSettingsAction.OnBackPress -> { - sendEvent(RecurringAccountEvent.NavigateBack) - } - RecurringAccountAction.RecurringAccountSettingsAction.OnNextPress -> { - moveToNextStep() - } is RecurringAccountAction.RecurringAccountSettingsAction.SetDepositPeriod -> { mutableStateFlow.update { state -> state.copy( @@ -461,6 +342,7 @@ class RecurringAccountViewModel( ) } } + is RecurringAccountAction.RecurringAccountSettingsAction.SetLockInPeriodType -> { mutableStateFlow.update { state -> state.copy( @@ -477,7 +359,7 @@ class RecurringAccountViewModel( mutableStateFlow.update { state -> state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( - minDepositTerm = state.recurringDepositAccountSettings.minDepositTerm.copy( + minimumDepositTerm = state.recurringDepositAccountSettings.minimumDepositTerm.copy( frequency = action.frequency, ), ), @@ -489,7 +371,7 @@ class RecurringAccountViewModel( mutableStateFlow.update { state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( - minDepositTerm = state.recurringDepositAccountSettings.minDepositTerm.copy( + minimumDepositTerm = state.recurringDepositAccountSettings.minimumDepositTerm.copy( frequencyTypeIndex = action.frequencyTypeIndex, ), ), @@ -501,18 +383,19 @@ class RecurringAccountViewModel( mutableStateFlow.update { state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( - minDepositTerm = state.recurringDepositAccountSettings.minDepositTerm.copy( + minimumDepositTerm = state.recurringDepositAccountSettings.minimumDepositTerm.copy( frequencyAfterInMultiplesOf = action.frequencyAfterInMultiplesOf, ), ), ) } } + is RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqTypeAfterInMultiOf -> { mutableStateFlow.update { state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( - minDepositTerm = state.recurringDepositAccountSettings.minDepositTerm.copy( + minimumDepositTerm = state.recurringDepositAccountSettings.minimumDepositTerm.copy( frequencyTypeIndexAfterInMultiplesOf = action.frequencyTypeIndexAfterInMultiplesOf, ), ), @@ -567,6 +450,7 @@ class RecurringAccountViewModel( ) } } + is RecurringAccountAction.RecurringAccountSettingsAction.SetPreMatureClosurePenalInterest -> { mutableStateFlow.update { state.copy( @@ -578,6 +462,7 @@ class RecurringAccountViewModel( ) } } + is RecurringAccountAction.RecurringAccountSettingsAction.SetRecurringDepositAmount -> { mutableStateFlow.update { state.copy( @@ -590,6 +475,7 @@ class RecurringAccountViewModel( ) } } + RecurringAccountAction.RecurringAccountSettingsAction.ToggleAdvancePaymentsTowardsFutureInstallments -> { mutableStateFlow.update { state.copy( @@ -599,6 +485,7 @@ class RecurringAccountViewModel( ) } } + RecurringAccountAction.RecurringAccountSettingsAction.ToggleAllowWithdrawals -> { mutableStateFlow.update { state.copy( @@ -608,6 +495,7 @@ class RecurringAccountViewModel( ) } } + RecurringAccountAction.RecurringAccountSettingsAction.ToggleDepositFrequencySameAsGroupCenterMeeting -> { mutableStateFlow.update { state.copy( @@ -620,6 +508,7 @@ class RecurringAccountViewModel( ) } } + RecurringAccountAction.RecurringAccountSettingsAction.ToggleMandatoryDeposit -> { mutableStateFlow.update { state.copy( @@ -629,6 +518,7 @@ class RecurringAccountViewModel( ) } } + RecurringAccountAction.RecurringAccountSettingsAction.TogglePreMatureClosureApplyPenalInterest -> { mutableStateFlow.update { state.copy( @@ -644,52 +534,105 @@ class RecurringAccountViewModel( } } - is RecurringAccountAction.RecurringAccountDetailsAction.SetProductId -> { + is RecurringAccountAction.RecurringAccountDetailsAction -> { + when (action) { + is RecurringAccountAction.RecurringAccountDetailsAction.OnProductNameChange -> { + handleProductNameChange(action) + } + is RecurringAccountAction.RecurringAccountDetailsAction.OnSubmissionDateChange -> { + handleSubmissionDateChange(action) + } + is RecurringAccountAction.RecurringAccountDetailsAction.OnSubmissionDatePick -> { + handleSubmissionDatePick(action) + } + is RecurringAccountAction.RecurringAccountDetailsAction.OnFieldOfficerChange -> { + handleFieldOfficerChange(action) + } + is RecurringAccountAction.RecurringAccountDetailsAction.OnExternalIdChange -> { + handleExternalIdChange(action) + } + is RecurringAccountAction.RecurringAccountDetailsAction.SetProductId -> { + mutableStateFlow.update { + it.copy( + recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( + productId = action.productId, + ), + ) + } + loadRecurringAccountTemplateWithProduct( + state.clientId, + action.productId + ) + } + } + } + + RecurringAccountAction.NavigateBack -> { + sendEvent(RecurringAccountEvent.NavigateBack) + } + + RecurringAccountAction.Finish -> { + sendEvent(RecurringAccountEvent.Finish) + } + + is RecurringAccountAction.NavigateToStep -> { + val newIndex = action.index + if (newIndex in 0..state.totalSteps) { + mutableStateFlow.update { + it.copy(currentStep = newIndex) + } + } + } + + RecurringAccountAction.OnBackPress -> { mutableStateFlow.update { - it.copy( - recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( - productId = action.productId, - ), - ) + it.copy(currentStep = 3) } - loadTemplateByProduct() + } + + RecurringAccountAction.OnNextPress -> { + moveToNextStep() } } } + + } data class RecurringAccountState( val isOnline: Boolean = false, - val clientId: Int, + val clientId: Int = -1, val currentStep: Int = 0, - val totalSteps: Int = 4, + val totalSteps: Int = TOTAL_STEPS, val screenState: ScreenState? = null, val recurringDepositAccountDetail: RecurringAccountDetailsState = RecurringAccountDetailsState(), - val recurringDepositAccountTemplate: RecurringDepositAccountTemplate = RecurringDepositAccountTemplate(), + val template: RecurringDepositAccountTemplate = RecurringDepositAccountTemplate(), + val recurringDepositAccountSettings: RecurringAccountSettingsState = RecurringAccountSettingsState(), val currencyIndex: Int = -1, val currencyError: String? = null, - val loanProductSelected: Int = -1, - val template: RecurringDepositAccountTemplate? = null, - val fieldOfficerOptions: List? = null, - val recurringDepositAccountSettings: RecurringAccountSettingsState = RecurringAccountSettingsState(), ) { sealed interface ScreenState { data class Error(val message: String) : ScreenState data object Loading : ScreenState + data object Success : ScreenState } - val isDetailButtonEnabled = fieldOfficerIndex != -1 && submissionDate.isNotEmpty() - } 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, - val externalId: String = "", + val productId: Int = -1, val submittedOnDate: String = "", val fieldOfficer: FieldOfficerOption? = null, -) + val showSubmissionDatePick: Boolean = false, + val loanProductSelected: Int = -1, + val submissionDate: String = "", + val fieldOfficerIndex: Int = -1, + val fieldOfficerError: String? = null, + val externalId: String = "", + val isMiniLoaderActive: Boolean = false, + val fieldOfficerOptions: List? = null, +) { + val isDetailButtonEnabled = fieldOfficerIndex != -1 && submissionDate.isNotEmpty() +} data class RecurringAccountSettingsState( val canDoNext: Boolean = false, @@ -699,7 +642,7 @@ data class RecurringAccountSettingsState( val lockInPeriod: LockInPeriod = LockInPeriod(), val recurringDepositDetails: RecurringDepositDetails = RecurringDepositDetails(), val depositPeriod: DepositPeriod = DepositPeriod(), - val minDepositTerm: MinDepositTerm = MinDepositTerm(), + val minimumDepositTerm: MinimumDepositTerm = MinimumDepositTerm(), val maxDepositTerm: MaxDepositTerm = MaxDepositTerm(), val preMatureClosure: PreMatureClosure = PreMatureClosure(), ) { @@ -745,22 +688,20 @@ data class RecurringAccountSettingsState( } sealed class RecurringAccountAction { - object NextStep : RecurringAccountAction() - data class OnStepChange(val index: Int) : RecurringAccountAction() + data class NavigateToStep(val index: Int) : RecurringAccountAction() object NavigateBack : RecurringAccountAction() + object OnBackPress : RecurringAccountAction() + object OnNextPress : RecurringAccountAction() object Finish : RecurringAccountAction() data object Retry : RecurringAccountAction() - /* - data class OnProductNameChange(val index: Int) : RecurringAccountAction - data class OnSubmissionDateChange(val date: String) : RecurringAccountAction - data class OnSubmissionDatePick(val state: Boolean) : RecurringAccountAction - data class OnFieldOfficerChange(val index: Int) : RecurringAccountAction - data class OnExternalIdChange(val value: String) : RecurringAccountAction -} - */ sealed class RecurringAccountDetailsAction : RecurringAccountAction() { + data class OnProductNameChange(val index: Int) : RecurringAccountDetailsAction() + data class OnSubmissionDateChange(val date: String) : RecurringAccountDetailsAction() + data class OnSubmissionDatePick(val state: Boolean) : RecurringAccountDetailsAction() + data class OnFieldOfficerChange(val index: Int) : RecurringAccountDetailsAction() + data class OnExternalIdChange(val value: String) : RecurringAccountDetailsAction() data class SetProductId(val productId: Int) : RecurringAccountDetailsAction() } @@ -786,9 +727,6 @@ sealed class RecurringAccountAction { 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/ChargesPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/ChargesPage.kt index a7d1069b32e..716139dfb19 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/ChargesPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/ChargesPage.kt @@ -9,6 +9,10 @@ */ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages +import androidclient.feature.recurringdeposit.generated.resources.Res +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_charges_page +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_next_button +import org.jetbrains.compose.resources.stringResource import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -22,10 +26,10 @@ import androidx.compose.ui.unit.dp @Composable fun ChargesPage(onNext: () -> Unit) { Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text("Charges Page") + Text(stringResource(Res.string.feature_recurring_deposit_charges_page)) Spacer(Modifier.height(8.dp)) Button(onClick = onNext) { - Text("Next Button") + Text(stringResource(Res.string.feature_recurring_deposit_next_button)) } } } diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt index 718bf203323..c372518e655 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt @@ -10,14 +10,14 @@ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages import androidclient.feature.recurringdeposit.generated.resources.Res -import androidclient.feature.recurringdeposit.generated.resources.back -import androidclient.feature.recurringdeposit.generated.resources.cancel -import androidclient.feature.recurringdeposit.generated.resources.external_id -import androidclient.feature.recurringdeposit.generated.resources.field_officer -import androidclient.feature.recurringdeposit.generated.resources.next -import androidclient.feature.recurringdeposit.generated.resources.product_name -import androidclient.feature.recurringdeposit.generated.resources.select -import androidclient.feature.recurringdeposit.generated.resources.submitted_on +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_back +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_cancel +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_external_id +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_field_officer +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_next +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_product_name +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_select +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_submitted_on import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -63,31 +63,31 @@ fun DetailsPage( }, ) - if (state.showSubmissionDatePick) { + if (state.recurringDepositAccountDetail.showSubmissionDatePick) { DatePickerDialog( onDismissRequest = { - onAction(RecurringAccountAction.OnSubmissionDatePick(state = false)) + onAction(RecurringAccountAction.RecurringAccountDetailsAction.OnSubmissionDatePick(state = false)) }, confirmButton = { TextButton( onClick = { - onAction(RecurringAccountAction.OnSubmissionDatePick(state = false)) + onAction(RecurringAccountAction.RecurringAccountDetailsAction.OnSubmissionDatePick(state = false)) submissionDatePickerState.selectedDateMillis?.let { onAction( - RecurringAccountAction.OnSubmissionDateChange( + RecurringAccountAction.RecurringAccountDetailsAction.OnSubmissionDateChange( DateHelper.getDateAsStringFromLong(it), ), ) } }, - ) { Text(stringResource(Res.string.select)) } + ) { Text(stringResource(Res.string.feature_recurring_deposit_select)) } }, dismissButton = { TextButton( onClick = { - onAction(RecurringAccountAction.OnSubmissionDatePick(state = false)) + onAction(RecurringAccountAction.RecurringAccountDetailsAction.OnSubmissionDatePick(state = false)) }, - ) { Text(stringResource(Res.string.cancel)) } + ) { Text(stringResource(Res.string.feature_recurring_deposit_cancel)) } }, ) { DatePicker(state = submissionDatePickerState) @@ -96,53 +96,53 @@ fun DetailsPage( Column(horizontalAlignment = Alignment.CenterHorizontally) { MifosTextFieldDropdown( - value = if (state.loanProductSelected == -1) { + value = if (state.recurringDepositAccountDetail.loanProductSelected == -1) { "" } else { - state.template?.productOptions?.get(state.loanProductSelected)?.name ?: "" + state.template.productOptions?.get(state.recurringDepositAccountDetail.loanProductSelected)?.name ?: "" }, onValueChanged = {}, onOptionSelected = { index, value -> - onAction(RecurringAccountAction.OnProductNameChange(index)) + onAction(RecurringAccountAction.RecurringAccountDetailsAction.OnProductNameChange(index)) }, - options = state.template?.productOptions?.map { + options = state.template.productOptions?.map { it.name ?: "" } ?: emptyList(), - label = stringResource(Res.string.product_name), + label = stringResource(Res.string.feature_recurring_deposit_product_name), ) - if (!state.template?.fieldOfficerOptions.isNullOrEmpty()) { + if (!state.template.fieldOfficerOptions.isNullOrEmpty()) { MifosDatePickerTextField( - value = state.submissionDate, - label = stringResource(Res.string.submitted_on), + value = state.recurringDepositAccountDetail.submissionDate, + label = stringResource(Res.string.feature_recurring_deposit_submitted_on), openDatePicker = { - onAction(RecurringAccountAction.OnSubmissionDatePick(true)) + onAction(RecurringAccountAction.RecurringAccountDetailsAction.OnSubmissionDatePick(true)) }, ) Spacer(Modifier.height(DesignToken.padding.large)) MifosTextFieldDropdown( - value = if (state.fieldOfficerIndex == -1) { + value = if (state.recurringDepositAccountDetail.fieldOfficerIndex == -1) { "" } else { - state.fieldOfficerOptions?.get(state.fieldOfficerIndex)?.displayName ?: "" + state.recurringDepositAccountDetail.fieldOfficerOptions?.get(state.recurringDepositAccountDetail.fieldOfficerIndex)?.displayName ?: "" }, onValueChanged = {}, onOptionSelected = { index, value -> - onAction(RecurringAccountAction.OnFieldOfficerChange(index)) + onAction(RecurringAccountAction.RecurringAccountDetailsAction.OnFieldOfficerChange(index)) }, - options = state.fieldOfficerOptions?.mapNotNull { + options = state.recurringDepositAccountDetail.fieldOfficerOptions?.mapNotNull { it.displayName } ?: emptyList(), - label = stringResource(Res.string.field_officer), + label = stringResource(Res.string.feature_recurring_deposit_field_officer), ) MifosOutlinedTextField( - value = state.externalId, + value = state.recurringDepositAccountDetail.externalId, onValueChange = { - onAction(RecurringAccountAction.OnExternalIdChange(it)) + onAction(RecurringAccountAction.RecurringAccountDetailsAction.OnExternalIdChange(it)) }, - label = stringResource(Res.string.external_id), + label = stringResource(Res.string.feature_recurring_deposit_external_id), config = MifosTextFieldConfig( keyboardOptions = KeyboardOptions( keyboardType = KeyboardType.Text, @@ -152,16 +152,16 @@ fun DetailsPage( Spacer(Modifier.height(DesignToken.padding.large)) } - if (state.isMiniLoaderActive) { + if (state.recurringDepositAccountDetail.isMiniLoaderActive) { MifosProgressIndicatorMini() } MifosTwoButtonRow( - firstBtnText = stringResource(Res.string.back), - secondBtnText = stringResource(Res.string.next), + firstBtnText = stringResource(Res.string.feature_recurring_deposit_back), + secondBtnText = stringResource(Res.string.feature_recurring_deposit_next), onFirstBtnClick = { onAction(RecurringAccountAction.NavigateBack) }, - onSecondBtnClick = { onAction(RecurringAccountAction.NextStep) }, - isSecondButtonEnabled = state.isDetailButtonEnabled, + onSecondBtnClick = { onAction(RecurringAccountAction.OnNextPress) }, + isSecondButtonEnabled = state.recurringDepositAccountDetail.isDetailButtonEnabled, modifier = Modifier.padding(top = DesignToken.padding.small), ) } diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/InterestPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/InterestPage.kt index eda2c9dfc33..0d3d1fadfde 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/InterestPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/InterestPage.kt @@ -9,6 +9,10 @@ */ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages +import androidclient.feature.recurringdeposit.generated.resources.Res +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_interest_page +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_next_button +import org.jetbrains.compose.resources.stringResource import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -22,10 +26,10 @@ import androidx.compose.ui.unit.dp @Composable fun InterestPage(onNext: () -> Unit) { Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text("Interest Page") + Text(stringResource(Res.string.feature_recurring_deposit_interest_page)) Spacer(Modifier.height(8.dp)) Button(onClick = onNext) { - Text("Next Button") + Text(stringResource(Res.string.feature_recurring_deposit_next_button)) } } } 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 62a7434fe91..3ce3b96ea87 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 @@ -57,7 +57,6 @@ 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 io.ktor.websocket.Frame import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.ui.tooling.preview.Preview @@ -65,7 +64,7 @@ import org.jetbrains.compose.ui.tooling.preview.Preview @Composable fun SettingPage( state: RecurringAccountState, - onAction: (RecurringAccountAction.RecurringAccountSettingsAction) -> Unit, + onAction: (RecurringAccountAction) -> Unit, ) { val settingsState = state.recurringDepositAccountSettings @@ -119,11 +118,12 @@ fun SettingPage( ) MifosTextFieldDropdown( value = if (settingsState.lockInPeriod.frequencyTypeIndex != -1) { - state.recurringDepositAccountTemplate.lockinPeriodFrequencyTypeOptions?.get(settingsState.lockInPeriod.frequencyTypeIndex)?.value ?: "" + state.template.lockinPeriodFrequencyTypeOptions?. + get(settingsState.lockInPeriod.frequencyTypeIndex)?.value ?: "" } else { "" }, - options = state.recurringDepositAccountTemplate.lockinPeriodFrequencyTypeOptions?.map { + options = state.template.lockinPeriodFrequencyTypeOptions?.map { it.value ?: "" } ?: emptyList(), onValueChanged = {}, @@ -146,7 +146,7 @@ fun SettingPage( ), leadingIcon = { Text( - text = state.recurringDepositAccountTemplate.currency?.displaySymbol?: "" + text = state.template.currency?.displaySymbol?: "" ) } ), @@ -167,11 +167,12 @@ fun SettingPage( ) MifosTextFieldDropdown( value = if (settingsState.depositPeriod.periodType != -1) { - state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.depositPeriod.periodType)?.value ?: "" + state.template.periodFrequencyTypeOptions?. + get(settingsState.depositPeriod.periodType)?.value ?: "" } else { "" }, - options = state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.map { + options = state.template.periodFrequencyTypeOptions?.map { it.value ?: "" } ?: emptyList(), onValueChanged = {}, @@ -185,12 +186,14 @@ fun SettingPage( MifosCheckBox( text = stringResource(Res.string.feature_recurring_deposit_deposit_frequency_same_as_meeting), checked = settingsState.depositPeriod.depositFrequencySameAsGroupCenterMeeting, - onCheckChanged = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleDepositFrequencySameAsGroupCenterMeeting) }, + onCheckChanged = { + onAction(RecurringAccountAction.RecurringAccountSettingsAction.ToggleDepositFrequencySameAsGroupCenterMeeting) + }, ) } Text(stringResource(Res.string.feature_recurring_deposit_minimum_deposit_term), fontWeight = FontWeight.Bold) MifosOutlinedTextField( - value = settingsState.minDepositTerm.frequency, + value = settingsState.minimumDepositTerm.frequency, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreq(it)) }, @@ -204,12 +207,12 @@ fun SettingPage( modifier = Modifier.fillMaxWidth(), ) MifosTextFieldDropdown( - value = if (settingsState.minDepositTerm.frequencyTypeIndex != -1) { - state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.minDepositTerm.frequencyTypeIndex)?.value ?: "" + value = if (settingsState.minimumDepositTerm.frequencyTypeIndex != -1) { + state.template.periodFrequencyTypeOptions?.get(settingsState.minimumDepositTerm.frequencyTypeIndex)?.value ?: "" } else { "" }, - options = state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.map { + options = state.template.periodFrequencyTypeOptions?.map { it.value ?: "" } ?: emptyList(), onValueChanged = {}, @@ -221,7 +224,7 @@ fun SettingPage( ) Text(stringResource(Res.string.feature_recurring_deposit_in_multiples_of), fontWeight = FontWeight.Bold) MifosOutlinedTextField( - value = settingsState.minDepositTerm.frequencyAfterInMultiplesOf, + value = settingsState.minimumDepositTerm.frequencyAfterInMultiplesOf, onValueChange = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.SetMinDepositTermFreqAfterInMultiOf(it)) }, label = stringResource(Res.string.feature_recurring_deposit_frequency), config = MifosTextFieldConfig( @@ -233,12 +236,12 @@ fun SettingPage( modifier = Modifier.fillMaxWidth(), ) MifosTextFieldDropdown( - value = if (settingsState.minDepositTerm.frequencyTypeIndexAfterInMultiplesOf != -1) { - state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.minDepositTerm.frequencyTypeIndexAfterInMultiplesOf)?.value ?: "" + value = if (settingsState.minimumDepositTerm.frequencyTypeIndexAfterInMultiplesOf != -1) { + state.template.periodFrequencyTypeOptions?.get(settingsState.minimumDepositTerm.frequencyTypeIndexAfterInMultiplesOf)?.value ?: "" } else { "" }, - options = state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.map { + options = state.template.periodFrequencyTypeOptions?.map { it.value ?: "" } ?: emptyList(), onValueChanged = {}, @@ -263,11 +266,11 @@ fun SettingPage( ) MifosTextFieldDropdown( value = if (settingsState.maxDepositTerm.frequencyTypeIndex != -1) { - state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.get(settingsState.maxDepositTerm.frequencyTypeIndex)?.value ?: "" + state.template.periodFrequencyTypeOptions?.get(settingsState.maxDepositTerm.frequencyTypeIndex)?.value ?: "" } else { "" }, - options = state.recurringDepositAccountTemplate.periodFrequencyTypeOptions?.map { + options = state.template.periodFrequencyTypeOptions?.map { it.value ?: "" } ?: emptyList(), onValueChanged = {}, @@ -306,11 +309,11 @@ fun SettingPage( ) MifosTextFieldDropdown( value = if (settingsState.preMatureClosure.interestPeriodIndex != -1) { - state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" + state.template.preClosurePenalInterestOnTypeOptions?.get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" } else { "" }, - options = state.recurringDepositAccountTemplate.preClosurePenalInterestOnTypeOptions?.map { + options = state.template.preClosurePenalInterestOnTypeOptions?.map { it.value ?: "" } ?: emptyList(), onValueChanged = {}, @@ -331,7 +334,7 @@ fun SettingPage( ), leadingIcon = { Text( - text = state.recurringDepositAccountTemplate.currency?.displaySymbol?: "" + text = state.template.currency?.displaySymbol?: "" ) } ), @@ -345,15 +348,15 @@ fun SettingPage( settingsState.recurringDepositDetails.depositAmount.isNotBlank() && settingsState.depositPeriod.period.isNotBlank() && settingsState.lockInPeriod.frequency.isNotBlank() && - settingsState.minDepositTerm.frequency.isNotBlank() && - settingsState.minDepositTerm.frequencyAfterInMultiplesOf.isNotBlank() && + settingsState.minimumDepositTerm.frequency.isNotBlank() && + settingsState.minimumDepositTerm.frequencyAfterInMultiplesOf.isNotBlank() && settingsState.maxDepositTerm.frequency.isNotBlank() MifosTwoButtonRow( firstBtnText = stringResource(Res.string.feature_recurring_deposit_back), secondBtnText = stringResource(Res.string.feature_recurring_deposit_next), - onFirstBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnBackPress) }, - onSecondBtnClick = { onAction(RecurringAccountAction.RecurringAccountSettingsAction.OnNextPress) }, + onFirstBtnClick = { onAction(RecurringAccountAction.OnBackPress) }, + onSecondBtnClick = { onAction(RecurringAccountAction.OnNextPress) }, isButtonIconVisible = true, isSecondButtonEnabled = isNextButtonActive, ) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/TermsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/TermsPage.kt index 09785bdd831..6cf254b5ff2 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/TermsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/TermsPage.kt @@ -9,6 +9,10 @@ */ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages +import androidclient.feature.recurringdeposit.generated.resources.Res +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_next_button +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_terms_page +import org.jetbrains.compose.resources.stringResource import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -22,10 +26,10 @@ import androidx.compose.ui.unit.dp @Composable fun TermsPage(onNext: () -> Unit) { Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text("Terms Page") + Text(stringResource(Res.string.feature_recurring_deposit_terms_page)) Spacer(Modifier.height(8.dp)) Button(onClick = onNext) { - Text("Next Button") + Text(stringResource(Res.string.feature_recurring_deposit_next_button)) } } } From 20190bc198f85e8a8fbe2be9254809a65d2782a8 Mon Sep 17 00:00:00 2001 From: thekalpeshpawar Date: Mon, 10 Nov 2025 00:13:19 +0530 Subject: [PATCH 22/28] Refactor string resources and UI spacing for recurring deposit feature Move hardcoded strings from string.xml to feature_recurring_deposit_string.xml, following the naming convention "feature_recurring_deposit_" followed by lowercase with underscores. Update RecurringAccountViewModel.kt to replace hardcoded error messages ("Deposit amount is required", "Lock-in period frequency is required", "Recurring frequency is required") with references to the new string resources. Update page composables (DetailsPage.kt, TermsPage.kt, InterestPage.kt, ChargesPage.kt) to use string resources instead of hardcoded strings for titles and buttons. Adjust spacing in SettingsPage.kt by wrapping the leadingIcon Text in a Box with Modifier.padding(end = 4.dp) to decrease the gap between the currency symbol icon and the text input in both MifosOutlinedTextField instances (around lines 139 and 329). Verify that all string resources used in the updated files are present in feature_recurring_deposit_string.xml. --- .../component/MifosOutlinedTextField.kt | 3 + .../RecurringAccountScreen.kt | 21 +---- .../RecurringAccountViewModel.kt | 82 ++++++++----------- .../pages/DetailsPage.kt | 7 +- .../pages/SettingsPage.kt | 17 ++-- 5 files changed, 52 insertions(+), 78 deletions(-) diff --git a/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt b/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt index 9379c2c11f2..7d207b79ada 100644 --- a/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt +++ b/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt @@ -13,6 +13,7 @@ import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsFocusedAsState +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions @@ -443,6 +444,7 @@ fun MifosOutlinedTextField( interactionSource = interactionSource, singleLine = config.singleLine, maxLines = config.maxLines, + prefix = config.prefix, minLines = config.minLines, leadingIcon = config.leadingIcon, isError = config.isError, @@ -489,6 +491,7 @@ data class MifosTextFieldConfig( val keyboardOptions: KeyboardOptions = KeyboardOptions(imeAction = ImeAction.Next), val trailingIcon: @Composable (() -> Unit)? = null, val leadingIcon: @Composable (() -> Unit)? = null, + val prefix: @Composable (() -> Unit)? = null, ) @Preview 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 9c24ded3a69..3957cffe1e3 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 @@ -58,7 +58,6 @@ internal fun RecurringAccountScreen( } } - RecurringDepositAccountDialogBox(state = state) RecurringAccountScaffold( navController = navController, @@ -141,27 +140,9 @@ private fun RecurringAccountScaffold( .fillMaxWidth(), ) } - - null -> {} } } } } -@Composable -fun RecurringDepositAccountDialogBox( - state: RecurringAccountState, -) { - when (state.screenState) { - is RecurringAccountState.ScreenState.Error -> { - MifosSweetError( - message = state.screenState.message, - isRetryEnabled = true, - ) - } - RecurringAccountState.ScreenState.Loading -> { - MifosProgressIndicator() - } - else -> {} - } -} + 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 33d406bfd02..adb9eb11500 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 @@ -59,6 +59,11 @@ class RecurringAccountViewModel( ) } } + private fun setSuccessState() { + mutableStateFlow.update { + it.copy(screenState = ScreenState.Success,) + } + } private fun setErrorState(message: String) { mutableStateFlow.update { @@ -144,9 +149,7 @@ class RecurringAccountViewModel( } is DataState.Success -> { - mutableStateFlow.update { - it.copy(screenState = null) - } + setSuccessState() } } } @@ -174,7 +177,7 @@ class RecurringAccountViewModel( isOnline = false, clientId = -1, currentStep = 0, - screenState = null, + screenState = ScreenState.Loading, recurringDepositAccountDetail = RecurringAccountDetailsState(), template = RecurringDepositAccountTemplate(), recurringDepositAccountSettings = RecurringAccountSettingsState(), @@ -230,27 +233,21 @@ class RecurringAccountViewModel( recurringAccountRepo.getRecurringAccountTemplate().collect { state -> when (state) { is DataState.Success -> { + setSuccessState() mutableStateFlow.update { it.copy( - screenState = ScreenState.Success, + template = state.data, + isOnline = true, ) } } is DataState.Error -> { - mutableStateFlow.update { - it.copy( - screenState = ScreenState.Error(state.message), - ) - } + setErrorState(state.message,) } DataState.Loading -> { - mutableStateFlow.update { - it.copy( - screenState = ScreenState.Loading, - ) - } + setLoadingState() } } } @@ -266,8 +263,8 @@ class RecurringAccountViewModel( mutableStateFlow.update { it.copy( screenState = ScreenState.Success, + template = state.data, recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( - fieldOfficerOptions = state.data.fieldOfficerOptions, isMiniLoaderActive = false, ), ) @@ -275,9 +272,9 @@ class RecurringAccountViewModel( } is DataState.Error -> { + setErrorState(state.message) mutableStateFlow.update { it.copy( - screenState = ScreenState.Error(state.message), recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( isMiniLoaderActive = false, ), @@ -286,13 +283,7 @@ class RecurringAccountViewModel( } DataState.Loading -> { - mutableStateFlow.update { - it.copy( - recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( - isMiniLoaderActive = true, - ), - ) - } + setLoadingState() } } } @@ -324,7 +315,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( depositPeriod = state.recurringDepositAccountSettings.depositPeriod.copy( - periodType = action.periodType, + periodType = state.template.periodFrequencyTypeOptions?.get( + action.periodType + )?.id?:-1, ), ), ) @@ -348,7 +341,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( lockInPeriod = state.recurringDepositAccountSettings.lockInPeriod.copy( - frequencyTypeIndex = action.frequencyTypeIndex, + frequencyTypeIndex = state.template.lockinPeriodFrequencyTypeOptions?.get( + action.frequencyTypeIndex + )?.id ?: -1, ), ), ) @@ -372,7 +367,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( minimumDepositTerm = state.recurringDepositAccountSettings.minimumDepositTerm.copy( - frequencyTypeIndex = action.frequencyTypeIndex, + frequencyTypeIndex = state.template.periodFrequencyTypeOptions?.get( + action.frequencyTypeIndex + )?.id ?: -1, ), ), ) @@ -396,7 +393,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( minimumDepositTerm = state.recurringDepositAccountSettings.minimumDepositTerm.copy( - frequencyTypeIndexAfterInMultiplesOf = action.frequencyTypeIndexAfterInMultiplesOf, + frequencyTypeIndexAfterInMultiplesOf = state.template.periodFrequencyTypeOptions?.get( + action.frequencyTypeIndexAfterInMultiplesOf + )?.id ?:-1, ), ), ) @@ -408,7 +407,7 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( maxDepositTerm = state.recurringDepositAccountSettings.maxDepositTerm.copy( - frequency = action.frequency, + frequency = action.frequency ), ), ) @@ -420,7 +419,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( maxDepositTerm = state.recurringDepositAccountSettings.maxDepositTerm.copy( - frequencyTypeIndex = action.frequencyTypeIndex, + frequencyTypeIndex = state.template.periodFrequencyTypeOptions?.get( + action.frequencyTypeIndex + )?.id ?: -1, ), ), ) @@ -432,7 +433,9 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( preMatureClosure = state.recurringDepositAccountSettings.preMatureClosure.copy( - interestPeriodIndex = action.interestPeriodIndex, + interestPeriodIndex = state.template.preClosurePenalInterestOnTypeOptions?.get( + action.interestPeriodIndex + )?.id ?: -1, ), ), ) @@ -551,19 +554,6 @@ class RecurringAccountViewModel( is RecurringAccountAction.RecurringAccountDetailsAction.OnExternalIdChange -> { handleExternalIdChange(action) } - is RecurringAccountAction.RecurringAccountDetailsAction.SetProductId -> { - mutableStateFlow.update { - it.copy( - recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( - productId = action.productId, - ), - ) - } - loadRecurringAccountTemplateWithProduct( - state.clientId, - action.productId - ) - } } } @@ -604,7 +594,7 @@ data class RecurringAccountState( val clientId: Int = -1, val currentStep: Int = 0, val totalSteps: Int = TOTAL_STEPS, - val screenState: ScreenState? = null, + val screenState: ScreenState = ScreenState.Loading, val recurringDepositAccountDetail: RecurringAccountDetailsState = RecurringAccountDetailsState(), val template: RecurringDepositAccountTemplate = RecurringDepositAccountTemplate(), val recurringDepositAccountSettings: RecurringAccountSettingsState = RecurringAccountSettingsState(), @@ -702,7 +692,7 @@ sealed class RecurringAccountAction { data class OnSubmissionDatePick(val state: Boolean) : RecurringAccountDetailsAction() data class OnFieldOfficerChange(val index: Int) : RecurringAccountDetailsAction() data class OnExternalIdChange(val value: String) : RecurringAccountDetailsAction() - data class SetProductId(val productId: Int) : RecurringAccountDetailsAction() +// data class SetProductId(val productId: Int) : RecurringAccountDetailsAction() } sealed class RecurringAccountSettingsAction : RecurringAccountAction() { diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt index c372518e655..4c953e8e292 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt @@ -125,14 +125,15 @@ fun DetailsPage( value = if (state.recurringDepositAccountDetail.fieldOfficerIndex == -1) { "" } else { - state.recurringDepositAccountDetail.fieldOfficerOptions?.get(state.recurringDepositAccountDetail.fieldOfficerIndex)?.displayName ?: "" + state.template.fieldOfficerOptions?. + get(state.recurringDepositAccountDetail.fieldOfficerIndex)?.displayName ?: "" }, onValueChanged = {}, onOptionSelected = { index, value -> onAction(RecurringAccountAction.RecurringAccountDetailsAction.OnFieldOfficerChange(index)) }, - options = state.recurringDepositAccountDetail.fieldOfficerOptions?.mapNotNull { - it.displayName + options = state.template.fieldOfficerOptions?.map { + it.displayName ?: "" } ?: emptyList(), label = stringResource(Res.string.feature_recurring_deposit_field_officer), ) 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 3ce3b96ea87..a67b8e91a55 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 @@ -33,9 +33,11 @@ import androidclient.feature.recurringdeposit.generated.resources.feature_recurr import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_type import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll @@ -144,10 +146,8 @@ fun SettingPage( keyboardType = KeyboardType.Number, imeAction = ImeAction.Next, ), - leadingIcon = { - Text( - text = state.template.currency?.displaySymbol?: "" - ) + prefix = { + Text(state.template.currency?.displaySymbol ?: "") } ), modifier = Modifier.fillMaxWidth(), @@ -309,7 +309,8 @@ fun SettingPage( ) MifosTextFieldDropdown( value = if (settingsState.preMatureClosure.interestPeriodIndex != -1) { - state.template.preClosurePenalInterestOnTypeOptions?.get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" + state.template.preClosurePenalInterestOnTypeOptions?. + get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" } else { "" }, @@ -332,10 +333,8 @@ fun SettingPage( keyboardType = KeyboardType.Number, imeAction = ImeAction.Next, ), - leadingIcon = { - Text( - text = state.template.currency?.displaySymbol?: "" - ) + prefix = { + Text(state.template.currency?.displaySymbol ?: "") } ), modifier = Modifier.fillMaxWidth(), From 1b8932bf4650a4eb39d172adecc8ae4d7c208d8d Mon Sep 17 00:00:00 2001 From: thekalpeshpawar Date: Mon, 10 Nov 2025 12:20:26 +0530 Subject: [PATCH 23/28] Reduce leading icon spacing in MifosOutlinedTextField and tidy recurring-deposit UI Adjusted the leading icon spacing inside `core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt` to bring the icon closer to the text input for a more compact, consistent form appearance. Updated recurring deposit pages to use the revised text field behavior and to align paddings and labels: `feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt`, `feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/SettingsPage.kt`, and `feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountScreen.kt`. Also applied minor UX refinements: ensured date picker text field and dropdowns follow the updated spacing, preserved accessibility content descriptions, and kept existing validation and view-model flows intact (`feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/RecurringAccountViewModel.kt`). Verified visual alignment across steps, updated related localized strings where labels changed, and smoke-tested navigation and form submission paths to prevent regressions. --- .../RecurringAccountScreen.kt | 6 +-- .../RecurringAccountViewModel.kt | 52 ++++++++----------- .../pages/ChargesPage.kt | 2 +- .../pages/DetailsPage.kt | 4 +- .../pages/InterestPage.kt | 2 +- .../pages/SettingsPage.kt | 18 +++---- .../pages/TermsPage.kt | 2 +- 7 files changed, 35 insertions(+), 51 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 3957cffe1e3..6d9f1a85c86 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 @@ -12,10 +12,10 @@ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount import androidclient.feature.recurringdeposit.generated.resources.Res import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_create_recurring_deposit_account import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_charges +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_details import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_interest import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_settings import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_terms -import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_details import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -25,7 +25,6 @@ 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.designsystem.component.MifosSweetError import com.mifos.core.ui.components.MifosBreadcrumbNavBar import com.mifos.core.ui.components.MifosErrorComponent import com.mifos.core.ui.components.MifosProgressIndicator @@ -58,7 +57,6 @@ internal fun RecurringAccountScreen( } } - RecurringAccountScaffold( navController = navController, modifier = modifier, @@ -144,5 +142,3 @@ private fun RecurringAccountScaffold( } } } - - 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 adb9eb11500..7d35e9b0e33 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,19 +9,19 @@ */ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount +import androidclient.feature.recurringdeposit.generated.resources.Res +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_deposit_amount_is_required +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_lock_in_period_frequency_is_required +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_no_internet_connection +import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_recurring_frequency_is_required 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.model.objects.template.recurring.FieldOfficerOption -import androidclient.feature.recurringdeposit.generated.resources.Res -import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_no_internet_connection -import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_deposit_amount_is_required -import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_lock_in_period_frequency_is_required -import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_recurring_frequency_is_required 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.ScreenState import com.mifos.room.entities.templates.recurringDeposit.RecurringDepositAccountTemplate @@ -30,7 +30,6 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.jetbrains.compose.resources.getString - const val TOTAL_STEPS = 4 class RecurringAccountViewModel( @@ -61,7 +60,7 @@ class RecurringAccountViewModel( } private fun setSuccessState() { mutableStateFlow.update { - it.copy(screenState = ScreenState.Success,) + it.copy(screenState = ScreenState.Success) } } @@ -167,7 +166,7 @@ class RecurringAccountViewModel( } loadRecurringAccountTemplateWithProduct( state.clientId, - state.template.productOptions?.get(state.recurringDepositAccountDetail.loanProductSelected)?.id ?: -1 + state.template.productOptions?.get(state.recurringDepositAccountDetail.loanProductSelected)?.id ?: -1, ) } @@ -203,7 +202,7 @@ class RecurringAccountViewModel( mutableStateFlow.update { it.copy( recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( - showSubmissionDatePick = action.state + showSubmissionDatePick = action.state, ), ) } @@ -213,7 +212,7 @@ class RecurringAccountViewModel( mutableStateFlow.update { it.copy( recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( - submissionDate = action.date + submissionDate = action.date, ), ) } @@ -223,7 +222,7 @@ class RecurringAccountViewModel( mutableStateFlow.update { it.copy( recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( - externalId = action.value + externalId = action.value, ), ) } @@ -243,7 +242,7 @@ class RecurringAccountViewModel( } is DataState.Error -> { - setErrorState(state.message,) + setErrorState(state.message) } DataState.Loading -> { @@ -291,7 +290,6 @@ class RecurringAccountViewModel( override fun handleAction(action: RecurringAccountAction) { when (action) { - RecurringAccountAction.Retry -> { resetForRetry() } @@ -316,8 +314,8 @@ class RecurringAccountViewModel( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( depositPeriod = state.recurringDepositAccountSettings.depositPeriod.copy( periodType = state.template.periodFrequencyTypeOptions?.get( - action.periodType - )?.id?:-1, + action.periodType, + )?.id ?: -1, ), ), ) @@ -342,7 +340,7 @@ class RecurringAccountViewModel( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( lockInPeriod = state.recurringDepositAccountSettings.lockInPeriod.copy( frequencyTypeIndex = state.template.lockinPeriodFrequencyTypeOptions?.get( - action.frequencyTypeIndex + action.frequencyTypeIndex, )?.id ?: -1, ), ), @@ -368,7 +366,7 @@ class RecurringAccountViewModel( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( minimumDepositTerm = state.recurringDepositAccountSettings.minimumDepositTerm.copy( frequencyTypeIndex = state.template.periodFrequencyTypeOptions?.get( - action.frequencyTypeIndex + action.frequencyTypeIndex, )?.id ?: -1, ), ), @@ -394,8 +392,8 @@ class RecurringAccountViewModel( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( minimumDepositTerm = state.recurringDepositAccountSettings.minimumDepositTerm.copy( frequencyTypeIndexAfterInMultiplesOf = state.template.periodFrequencyTypeOptions?.get( - action.frequencyTypeIndexAfterInMultiplesOf - )?.id ?:-1, + action.frequencyTypeIndexAfterInMultiplesOf, + )?.id ?: -1, ), ), ) @@ -407,7 +405,7 @@ class RecurringAccountViewModel( state.copy( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( maxDepositTerm = state.recurringDepositAccountSettings.maxDepositTerm.copy( - frequency = action.frequency + frequency = action.frequency, ), ), ) @@ -420,7 +418,7 @@ class RecurringAccountViewModel( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( maxDepositTerm = state.recurringDepositAccountSettings.maxDepositTerm.copy( frequencyTypeIndex = state.template.periodFrequencyTypeOptions?.get( - action.frequencyTypeIndex + action.frequencyTypeIndex, )?.id ?: -1, ), ), @@ -434,7 +432,7 @@ class RecurringAccountViewModel( recurringDepositAccountSettings = state.recurringDepositAccountSettings.copy( preMatureClosure = state.recurringDepositAccountSettings.preMatureClosure.copy( interestPeriodIndex = state.template.preClosurePenalInterestOnTypeOptions?.get( - action.interestPeriodIndex + action.interestPeriodIndex, )?.id ?: -1, ), ), @@ -561,10 +559,6 @@ class RecurringAccountViewModel( sendEvent(RecurringAccountEvent.NavigateBack) } - RecurringAccountAction.Finish -> { - sendEvent(RecurringAccountEvent.Finish) - } - is RecurringAccountAction.NavigateToStep -> { val newIndex = action.index if (newIndex in 0..state.totalSteps) { @@ -585,8 +579,6 @@ class RecurringAccountViewModel( } } } - - } data class RecurringAccountState( @@ -682,10 +674,8 @@ sealed class RecurringAccountAction { object NavigateBack : RecurringAccountAction() object OnBackPress : RecurringAccountAction() object OnNextPress : RecurringAccountAction() - object Finish : RecurringAccountAction() data object Retry : RecurringAccountAction() - sealed class RecurringAccountDetailsAction : RecurringAccountAction() { data class OnProductNameChange(val index: Int) : RecurringAccountDetailsAction() data class OnSubmissionDateChange(val date: String) : RecurringAccountDetailsAction() diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/ChargesPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/ChargesPage.kt index 716139dfb19..cacdd0e1248 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/ChargesPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/ChargesPage.kt @@ -12,7 +12,6 @@ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages import androidclient.feature.recurringdeposit.generated.resources.Res import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_charges_page import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_next_button -import org.jetbrains.compose.resources.stringResource import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -22,6 +21,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import org.jetbrains.compose.resources.stringResource @Composable fun ChargesPage(onNext: () -> Unit) { diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt index 4c953e8e292..0a7673bc10d 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/DetailsPage.kt @@ -125,8 +125,8 @@ fun DetailsPage( value = if (state.recurringDepositAccountDetail.fieldOfficerIndex == -1) { "" } else { - state.template.fieldOfficerOptions?. - get(state.recurringDepositAccountDetail.fieldOfficerIndex)?.displayName ?: "" + state.template.fieldOfficerOptions + ?.get(state.recurringDepositAccountDetail.fieldOfficerIndex)?.displayName ?: "" }, onValueChanged = {}, onOptionSelected = { index, value -> diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/InterestPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/InterestPage.kt index 0d3d1fadfde..cb8ed3d4097 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/InterestPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/InterestPage.kt @@ -12,7 +12,6 @@ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages import androidclient.feature.recurringdeposit.generated.resources.Res import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_interest_page import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_next_button -import org.jetbrains.compose.resources.stringResource import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -22,6 +21,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import org.jetbrains.compose.resources.stringResource @Composable fun InterestPage(onNext: () -> Unit) { 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 a67b8e91a55..dfbbc189de2 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 @@ -33,11 +33,9 @@ import androidclient.feature.recurringdeposit.generated.resources.feature_recurr import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_type import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll @@ -120,8 +118,8 @@ fun SettingPage( ) MifosTextFieldDropdown( value = if (settingsState.lockInPeriod.frequencyTypeIndex != -1) { - state.template.lockinPeriodFrequencyTypeOptions?. - get(settingsState.lockInPeriod.frequencyTypeIndex)?.value ?: "" + state.template.lockinPeriodFrequencyTypeOptions + ?.get(settingsState.lockInPeriod.frequencyTypeIndex)?.value ?: "" } else { "" }, @@ -148,7 +146,7 @@ fun SettingPage( ), prefix = { Text(state.template.currency?.displaySymbol ?: "") - } + }, ), modifier = Modifier.fillMaxWidth(), ) @@ -167,8 +165,8 @@ fun SettingPage( ) MifosTextFieldDropdown( value = if (settingsState.depositPeriod.periodType != -1) { - state.template.periodFrequencyTypeOptions?. - get(settingsState.depositPeriod.periodType)?.value ?: "" + state.template.periodFrequencyTypeOptions + ?.get(settingsState.depositPeriod.periodType)?.value ?: "" } else { "" }, @@ -309,8 +307,8 @@ fun SettingPage( ) MifosTextFieldDropdown( value = if (settingsState.preMatureClosure.interestPeriodIndex != -1) { - state.template.preClosurePenalInterestOnTypeOptions?. - get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" + state.template.preClosurePenalInterestOnTypeOptions + ?.get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" } else { "" }, @@ -335,7 +333,7 @@ fun SettingPage( ), prefix = { Text(state.template.currency?.displaySymbol ?: "") - } + }, ), modifier = Modifier.fillMaxWidth(), ) diff --git a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/TermsPage.kt b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/TermsPage.kt index 6cf254b5ff2..4be700953c1 100644 --- a/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/TermsPage.kt +++ b/feature/recurringDeposit/src/commonMain/kotlin/com/mifos/feature/recurringDeposit/newRecurringDepositAccount/pages/TermsPage.kt @@ -12,7 +12,6 @@ package com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages import androidclient.feature.recurringdeposit.generated.resources.Res import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_next_button import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_terms_page -import org.jetbrains.compose.resources.stringResource import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.height @@ -22,6 +21,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp +import org.jetbrains.compose.resources.stringResource @Composable fun TermsPage(onNext: () -> Unit) { From 6754fecf43f0a9ed09d62976e772417ce18f3f30 Mon Sep 17 00:00:00 2001 From: thekalpeshpawar Date: Mon, 10 Nov 2025 12:33:59 +0530 Subject: [PATCH 24/28] fix: correct variable naming and improve null safety in RecurringAccountViewModel and SettingsPage --- .../RecurringAccountViewModel.kt | 17 ++++++-- .../pages/SettingsPage.kt | 39 +++++++------------ 2 files changed, 28 insertions(+), 28 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 7d35e9b0e33..5ea039a9a5f 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 @@ -94,7 +94,7 @@ class RecurringAccountViewModel( setErrorState(getString(Res.string.feature_recurring_deposit_no_internet_connection)) return@launch } - val lockinFreq = settings.lockInPeriod.frequency.toIntOrNull() + val lockInFreq = settings.lockInPeriod.frequency.toIntOrNull() val depositAmountInt = settings.recurringDepositDetails.depositAmount .filter(Char::isDigit) .toIntOrNull() @@ -117,7 +117,7 @@ class RecurringAccountViewModel( isCalendarInherited = null, isMandatoryDeposit = settings.isMandatory, locale = "en", - lockinPeriodFrequency = lockinFreq, + lockinPeriodFrequency = lockInFreq, lockinPeriodFrequencyType = settings.lockInPeriod.frequencyTypeIndex, mandatoryRecommendedDepositAmount = depositAmountInt, monthDayFormat = "dd MMMM", @@ -134,7 +134,7 @@ class RecurringAccountViewModel( is DataState.Error -> { if (depositAmountInt == null) { setErrorState(getString(Res.string.feature_recurring_deposit_deposit_amount_is_required)) - } else if (lockinFreq == null) { + } else if (lockInFreq == null) { setErrorState(getString(Res.string.feature_recurring_deposit_lock_in_period_frequency_is_required)) } else if (recurringFreq == null) { setErrorState(getString(Res.string.feature_recurring_deposit_recurring_frequency_is_required)) @@ -628,6 +628,7 @@ data class RecurringAccountSettingsState( val maxDepositTerm: MaxDepositTerm = MaxDepositTerm(), val preMatureClosure: PreMatureClosure = PreMatureClosure(), ) { + data class LockInPeriod( val frequency: String = "", val frequencyTypeIndex: Int = -1, @@ -667,6 +668,15 @@ data class RecurringAccountSettingsState( val interestPeriodIndexError: String? = null, val minimumBalanceForInterestCalculation: String = "", ) + + val isSettingsNextEnabled = preMatureClosure.penalInterest.isNotBlank() && + preMatureClosure.minimumBalanceForInterestCalculation.isNotBlank() && + recurringDepositDetails.depositAmount.isNotBlank() && + depositPeriod.period.isNotBlank() && + lockInPeriod.frequency.isNotBlank() && + minimumDepositTerm.frequency.isNotBlank() && + minimumDepositTerm.frequencyAfterInMultiplesOf.isNotBlank() && + maxDepositTerm.frequency.isNotBlank() } sealed class RecurringAccountAction { @@ -682,7 +692,6 @@ sealed class RecurringAccountAction { data class OnSubmissionDatePick(val state: Boolean) : RecurringAccountDetailsAction() data class OnFieldOfficerChange(val index: Int) : RecurringAccountDetailsAction() data class OnExternalIdChange(val value: String) : RecurringAccountDetailsAction() -// data class SetProductId(val productId: Int) : RecurringAccountDetailsAction() } sealed class RecurringAccountSettingsAction : RecurringAccountAction() { 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 dfbbc189de2..ac486f71ce2 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 @@ -119,12 +119,12 @@ fun SettingPage( MifosTextFieldDropdown( value = if (settingsState.lockInPeriod.frequencyTypeIndex != -1) { state.template.lockinPeriodFrequencyTypeOptions - ?.get(settingsState.lockInPeriod.frequencyTypeIndex)?.value ?: "" + ?.getOrNull(settingsState.lockInPeriod.frequencyTypeIndex)?.value.orEmpty() } else { "" }, options = state.template.lockinPeriodFrequencyTypeOptions?.map { - it.value ?: "" + it.value.orEmpty() } ?: emptyList(), onValueChanged = {}, onOptionSelected = { id, name -> @@ -145,7 +145,7 @@ fun SettingPage( imeAction = ImeAction.Next, ), prefix = { - Text(state.template.currency?.displaySymbol ?: "") + Text(state.template.currency?.displaySymbol.orEmpty()) }, ), modifier = Modifier.fillMaxWidth(), @@ -166,12 +166,12 @@ fun SettingPage( MifosTextFieldDropdown( value = if (settingsState.depositPeriod.periodType != -1) { state.template.periodFrequencyTypeOptions - ?.get(settingsState.depositPeriod.periodType)?.value ?: "" + ?.getOrNull(settingsState.depositPeriod.periodType)?.value.orEmpty() } else { "" }, options = state.template.periodFrequencyTypeOptions?.map { - it.value ?: "" + it.value.orEmpty() } ?: emptyList(), onValueChanged = {}, onOptionSelected = { id, name -> @@ -206,12 +206,12 @@ fun SettingPage( ) MifosTextFieldDropdown( value = if (settingsState.minimumDepositTerm.frequencyTypeIndex != -1) { - state.template.periodFrequencyTypeOptions?.get(settingsState.minimumDepositTerm.frequencyTypeIndex)?.value ?: "" + state.template.periodFrequencyTypeOptions?.getOrNull(settingsState.minimumDepositTerm.frequencyTypeIndex)?.value.orEmpty() } else { "" }, options = state.template.periodFrequencyTypeOptions?.map { - it.value ?: "" + it.value.orEmpty() } ?: emptyList(), onValueChanged = {}, onOptionSelected = { id, name -> @@ -235,12 +235,12 @@ fun SettingPage( ) MifosTextFieldDropdown( value = if (settingsState.minimumDepositTerm.frequencyTypeIndexAfterInMultiplesOf != -1) { - state.template.periodFrequencyTypeOptions?.get(settingsState.minimumDepositTerm.frequencyTypeIndexAfterInMultiplesOf)?.value ?: "" + state.template.periodFrequencyTypeOptions?.getOrNull(settingsState.minimumDepositTerm.frequencyTypeIndexAfterInMultiplesOf)?.value.orEmpty() } else { "" }, options = state.template.periodFrequencyTypeOptions?.map { - it.value ?: "" + it.value.orEmpty() } ?: emptyList(), onValueChanged = {}, onOptionSelected = { id, name -> @@ -264,12 +264,12 @@ fun SettingPage( ) MifosTextFieldDropdown( value = if (settingsState.maxDepositTerm.frequencyTypeIndex != -1) { - state.template.periodFrequencyTypeOptions?.get(settingsState.maxDepositTerm.frequencyTypeIndex)?.value ?: "" + state.template.periodFrequencyTypeOptions?.getOrNull(settingsState.maxDepositTerm.frequencyTypeIndex)?.value.orEmpty() } else { "" }, options = state.template.periodFrequencyTypeOptions?.map { - it.value ?: "" + it.value.orEmpty() } ?: emptyList(), onValueChanged = {}, onOptionSelected = { id, name -> @@ -308,12 +308,12 @@ fun SettingPage( MifosTextFieldDropdown( value = if (settingsState.preMatureClosure.interestPeriodIndex != -1) { state.template.preClosurePenalInterestOnTypeOptions - ?.get(settingsState.preMatureClosure.interestPeriodIndex)?.value ?: "" + ?.getOrNull(settingsState.preMatureClosure.interestPeriodIndex)?.value.orEmpty() } else { "" }, options = state.template.preClosurePenalInterestOnTypeOptions?.map { - it.value ?: "" + it.value.orEmpty() } ?: emptyList(), onValueChanged = {}, onOptionSelected = { id, name -> @@ -332,7 +332,7 @@ fun SettingPage( imeAction = ImeAction.Next, ), prefix = { - Text(state.template.currency?.displaySymbol ?: "") + Text(state.template.currency?.displaySymbol.orEmpty()) }, ), modifier = Modifier.fillMaxWidth(), @@ -340,22 +340,13 @@ fun SettingPage( } } - val isNextButtonActive = settingsState.preMatureClosure.penalInterest.isNotBlank() && - settingsState.preMatureClosure.minimumBalanceForInterestCalculation.isNotBlank() && - settingsState.recurringDepositDetails.depositAmount.isNotBlank() && - settingsState.depositPeriod.period.isNotBlank() && - settingsState.lockInPeriod.frequency.isNotBlank() && - settingsState.minimumDepositTerm.frequency.isNotBlank() && - settingsState.minimumDepositTerm.frequencyAfterInMultiplesOf.isNotBlank() && - settingsState.maxDepositTerm.frequency.isNotBlank() - MifosTwoButtonRow( firstBtnText = stringResource(Res.string.feature_recurring_deposit_back), secondBtnText = stringResource(Res.string.feature_recurring_deposit_next), onFirstBtnClick = { onAction(RecurringAccountAction.OnBackPress) }, onSecondBtnClick = { onAction(RecurringAccountAction.OnNextPress) }, isButtonIconVisible = true, - isSecondButtonEnabled = isNextButtonActive, + isSecondButtonEnabled = settingsState.isSettingsNextEnabled, ) } } From 5112a3e002f4aae892d61ed1a2b52ffe58455b40 Mon Sep 17 00:00:00 2001 From: thekalpeshpawar Date: Mon, 10 Nov 2025 12:36:52 +0530 Subject: [PATCH 25/28] fix: remove unused import and correct formatting in MifosOutlinedTextField --- .../core/designsystem/component/MifosOutlinedTextField.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt b/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt index 7d207b79ada..0b387a05867 100644 --- a/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt +++ b/core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosOutlinedTextField.kt @@ -13,7 +13,6 @@ import androidx.compose.animation.AnimatedContent import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.collectIsFocusedAsState -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardActions @@ -430,7 +429,7 @@ fun MifosOutlinedTextField( Text( text = label, maxLines = 1, - softWrap = true + softWrap = true, ) }, onValueChange = onValueChange, From b0b053c14ca964d17b957ddc89c5b23630f7ef64 Mon Sep 17 00:00:00 2001 From: Kalpesh Pawar Date: Mon, 10 Nov 2025 13:24:28 +0000 Subject: [PATCH 26/28] Update version.txt Co-authored-by: Biplab Dutta --- version.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.txt b/version.txt index a28cdf22d8b..91fd393753c 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2025.11.2-beta.0.21 \ No newline at end of file +2025.10.4-beta.0.6 \ No newline at end of file From 792616f40e94e7a6e975bd0afcae754cb89894ee Mon Sep 17 00:00:00 2001 From: thekalpeshpawar Date: Mon, 10 Nov 2025 19:35:50 +0530 Subject: [PATCH 27/28] refactor: unify recurring account template retrieval methods and improve null safety --- .../data/repository/RecurringAccountRepository.kt | 8 +++----- .../repositoryImp/RecurringAccountRepositoryImp.kt | 12 ++++-------- .../datamanager/DataManagerRecurringAccount.kt | 12 ++++-------- .../core/network/services/RecurringAccountService.kt | 9 +++------ .../RecurringAccountViewModel.kt | 2 +- 5 files changed, 15 insertions(+), 28 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 6744704f683..83821cdff13 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,11 +17,9 @@ import kotlinx.coroutines.flow.Flow interface RecurringAccountRepository { - fun getRecurringAccountTemplate(): Flow> - - fun getRecurringAccountTemplateByProduct( - clientId: Int, - productId: Int, + fun getRecurringAccountTemplate( + clientId: Int? = null, + productId: Int? = null, ): Flow> fun createRecurringDepositAccount( 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 11410bc7e66..f2d1abfa56c 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,16 +21,12 @@ import kotlinx.coroutines.flow.Flow class RecurringAccountRepositoryImp( val dataManagerRecurringAccount: DataManagerRecurringAccount, ) : RecurringAccountRepository { - override fun getRecurringAccountTemplate(): Flow> { - return dataManagerRecurringAccount.getRecurringDepositAccountTemplate - .asDataStateFlow() - } - override fun getRecurringAccountTemplateByProduct( - clientId: Int, - productId: Int, + override fun getRecurringAccountTemplate( + clientId: Int?, + productId: Int?, ): Flow> { - return dataManagerRecurringAccount.getRecurringDepositAccountTemplateByProduct( + return dataManagerRecurringAccount.getRecurringDepositAccountTemplate( clientId, productId, ).asDataStateFlow() 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 2fa161c0735..6d147de9007 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, ) { - fun createRecurringDepositAccount( recurringDepositAccountPayload: RecurringDepositAccountPayload?, ): Flow { @@ -27,14 +26,11 @@ class DataManagerRecurringAccount( ) } - val getRecurringDepositAccountTemplate: Flow - get() = mBaseApiManager.recurringSavingsAccountService.getRecurringDepositAccountTemplate() - - fun getRecurringDepositAccountTemplateByProduct( - clientId: Int, - productId: Int, + fun getRecurringDepositAccountTemplate( + clientId: Int?, + productId: Int?, ): Flow { - return mBaseApiManager.recurringSavingsAccountService.getRecurringDepositAccountTemplateByProduct( + return mBaseApiManager.recurringSavingsAccountService.getRecurringDepositAccountTemplate( clientId, productId, ) diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/services/RecurringAccountService.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/RecurringAccountService.kt index 350c8b0a277..0f5d3836b31 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/services/RecurringAccountService.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/RecurringAccountService.kt @@ -27,11 +27,8 @@ interface RecurringAccountService { ): Flow @GET(APIEndPoint.CREATE_RECURRING_DEPOSIT_ACCOUNTS + "/template") - fun getRecurringDepositAccountTemplate(): Flow - - @GET(APIEndPoint.CREATE_RECURRING_DEPOSIT_ACCOUNTS + "/template") - fun getRecurringDepositAccountTemplateByProduct( - @Query("clientId") clientId: Int, - @Query("productId") productId: Int, + fun getRecurringDepositAccountTemplate( + @Query("clientId") clientId: Int?, + @Query("productId") productId: Int?, ): Flow } 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 5ea039a9a5f..192aec7ada1 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 @@ -256,7 +256,7 @@ class RecurringAccountViewModel( clientId: Int, productId: Int, ) = viewModelScope.launch { - recurringAccountRepo.getRecurringAccountTemplateByProduct(clientId, productId).collect { state -> + recurringAccountRepo.getRecurringAccountTemplate(clientId, productId).collect { state -> when (state) { is DataState.Success -> { mutableStateFlow.update { From fb6829d77754e72ee31dd8cc7b6ca51b05560066 Mon Sep 17 00:00:00 2001 From: thekalpeshpawar Date: Mon, 10 Nov 2025 19:48:07 +0530 Subject: [PATCH 28/28] fix: enforce non-null clientId in recurring account template methods --- .../mifos/core/data/repository/RecurringAccountRepository.kt | 2 +- .../core/data/repositoryImp/RecurringAccountRepositoryImp.kt | 2 +- .../core/network/datamanager/DataManagerRecurringAccount.kt | 2 +- .../com/mifos/core/network/services/RecurringAccountService.kt | 2 +- .../newRecurringDepositAccount/RecurringAccountViewModel.kt | 2 +- 5 files changed, 5 insertions(+), 5 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 83821cdff13..cee9d8f11de 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 @@ -18,7 +18,7 @@ import kotlinx.coroutines.flow.Flow interface RecurringAccountRepository { fun getRecurringAccountTemplate( - clientId: Int? = null, + clientId: Int, productId: Int? = null, ): 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 f2d1abfa56c..35722fa5342 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 @@ -23,7 +23,7 @@ class RecurringAccountRepositoryImp( ) : RecurringAccountRepository { override fun getRecurringAccountTemplate( - clientId: Int?, + clientId: Int, productId: Int?, ): Flow> { return dataManagerRecurringAccount.getRecurringDepositAccountTemplate( 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 6d147de9007..04b7e73c6e7 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 @@ -27,7 +27,7 @@ class DataManagerRecurringAccount( } fun getRecurringDepositAccountTemplate( - clientId: Int?, + clientId: Int, productId: Int?, ): Flow { return mBaseApiManager.recurringSavingsAccountService.getRecurringDepositAccountTemplate( diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/services/RecurringAccountService.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/RecurringAccountService.kt index 0f5d3836b31..494f654b4f4 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/services/RecurringAccountService.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/services/RecurringAccountService.kt @@ -28,7 +28,7 @@ interface RecurringAccountService { @GET(APIEndPoint.CREATE_RECURRING_DEPOSIT_ACCOUNTS + "/template") fun getRecurringDepositAccountTemplate( - @Query("clientId") clientId: Int?, + @Query("clientId") clientId: Int, @Query("productId") productId: Int?, ): Flow } 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 192aec7ada1..d477224f7f4 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 @@ -229,7 +229,7 @@ class RecurringAccountViewModel( } private fun loadRecurringAccountTemplate() = viewModelScope.launch { - recurringAccountRepo.getRecurringAccountTemplate().collect { state -> + recurringAccountRepo.getRecurringAccountTemplate(clientId = state.clientId).collect { state -> when (state) { is DataState.Success -> { setSuccessState()