diff --git a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt index 15f45764885..1f2b4504ad9 100644 --- a/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt +++ b/core/data/src/commonMain/kotlin/com/mifos/core/data/repository/FixedDepositRepository.kt @@ -16,6 +16,6 @@ import kotlinx.coroutines.flow.Flow interface FixedDepositRepository { fun getFixedDepositTemplate( clientId: Int, - productId: Int?, + productId: Int? = null, ): Flow> } diff --git a/core/model/src/commonMain/kotlin/com/mifos/core/model/objects/template/recurring/MaturityInstructionOption.kt b/core/model/src/commonMain/kotlin/com/mifos/core/model/objects/template/recurring/MaturityInstructionOption.kt new file mode 100644 index 00000000000..1d5567c37a7 --- /dev/null +++ b/core/model/src/commonMain/kotlin/com/mifos/core/model/objects/template/recurring/MaturityInstructionOption.kt @@ -0,0 +1,19 @@ +/* + * 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.core.model.objects.template.recurring + +import kotlinx.serialization.Serializable + +@Serializable +data class MaturityInstructionOption( + val code: String? = null, + val id: Int? = null, + val value: String? = null, +) diff --git a/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt index b86dde9856a..9a8d2900dfc 100644 --- a/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt +++ b/core/network/src/commonMain/kotlin/com/mifos/core/network/model/FixedDepositTemplate.kt @@ -11,10 +11,12 @@ package com.mifos.core.network.model import com.mifos.core.model.objects.account.saving.FieldOfficerOptions import com.mifos.core.model.objects.template.recurring.Currency +import com.mifos.core.model.objects.template.recurring.MaturityInstructionOption import com.mifos.core.model.objects.template.recurring.interest.InterestCalculationDaysInYearTypeOption import com.mifos.core.model.objects.template.recurring.interest.InterestCalculationTypeOption import com.mifos.core.model.objects.template.recurring.interest.InterestCompoundingPeriodTypeOption import com.mifos.core.model.objects.template.recurring.interest.InterestPostingPeriodTypeOption +import com.mifos.core.model.objects.template.recurring.period.LockinPeriodFrequencyTypeOption import com.mifos.core.model.objects.template.recurring.period.PeriodFrequencyTypeOption import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -52,4 +54,6 @@ data class FixedDepositTemplate( @SerialName("interestCalculationTypeOptions") val interestCalculationTypeOptions: List? = null, + val lockinPeriodFrequencyTypeOptions: List? = null, + val maturityInstructionOptions: List? = null, ) diff --git a/feature/client/src/commonMain/composeResources/values/strings.xml b/feature/client/src/commonMain/composeResources/values/strings.xml index 13df11794e1..ed6a316e0e6 100644 --- a/feature/client/src/commonMain/composeResources/values/strings.xml +++ b/feature/client/src/commonMain/composeResources/values/strings.xml @@ -605,4 +605,19 @@ Terms Deposit Period + Lock-in-Period + Frequency + Type + Minimum Deposit Term + And thereafter, in Multiples of + Maximum Deposit Term + Interest Transfer + Transfer Interest to Linked Savings Account? + Maturity Instructions + Investing Account + For Pre-mature closure + Apply Penal Interest (less) + Penal Interest (%%) + Period + \ No newline at end of file diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/ChargesPage.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/ChargesPage.kt index a8a9d0e7134..53ba133af0d 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/ChargesPage.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/ChargesPage.kt @@ -98,6 +98,7 @@ fun ChargesPage( text = state.addedCharges.size.toString() + " " + stringResource(Res.string.feature_share_account_charge_active_charge), btnEnabled = state.addedCharges.isNotEmpty(), ) + Spacer(Modifier.height(DesignToken.padding.large)) } MifosTwoButtonRow( firstBtnText = stringResource(Res.string.feature_share_account_back), diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/DetailsPage.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/DetailsPage.kt index 6b4a66c3167..043ae3dfda9 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/DetailsPage.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/DetailsPage.kt @@ -146,7 +146,6 @@ fun DetailsPage( onSecondBtnClick = { onAction(CreateShareAccountAction.OnDetailNext) }, - modifier = Modifier.padding(top = DesignToken.padding.small), ) } } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/PreviewPage.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/PreviewPage.kt index ba809f72012..95a33df87cb 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/PreviewPage.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/PreviewPage.kt @@ -30,7 +30,9 @@ import androidclient.feature.client.generated.resources.feature_share_account_te import androidclient.feature.client.generated.resources.feature_share_account_terms_total_shares import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -87,6 +89,7 @@ fun PreviewPage( text = state.addedCharges.size.toString() + " " + stringResource(Res.string.feature_share_account_charge_active_charge), btnEnabled = state.addedCharges.isNotEmpty(), ) + Spacer(Modifier.height(DesignToken.padding.large)) } MifosTwoButtonRow( firstBtnText = stringResource(Res.string.feature_share_account_back), @@ -97,7 +100,6 @@ fun PreviewPage( onSecondBtnClick = { onAction(CreateShareAccountAction.SubmitShareAccount) }, - modifier = Modifier.padding(top = DesignToken.padding.small), ) } } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/TermsPage.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/TermsPage.kt index 0bb0be4286b..9d3fe353735 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/TermsPage.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/createShareAccount/pages/TermsPage.kt @@ -224,6 +224,8 @@ fun TermsPage( errorMessage = state.minActivePeriodFreqTypeError?.let { stringResource(it) }, ) + Spacer(Modifier.height(DesignToken.padding.small)) + Text( text = stringResource(Res.string.feature_share_account_terms_lock_in_period), style = MifosTypography.labelLargeEmphasized, @@ -263,6 +265,7 @@ fun TermsPage( label = stringResource(Res.string.feature_share_account_terms_type), errorMessage = state.lockInPeriodFreqTypeError?.let { stringResource(it) }, ) + Spacer(Modifier.height(DesignToken.padding.large)) } MifosTwoButtonRow( firstBtnText = stringResource(Res.string.feature_share_account_back), @@ -273,7 +276,6 @@ fun TermsPage( onSecondBtnClick = { onAction(CreateShareAccountAction.OnTermsNext) }, - modifier = Modifier.padding(top = DesignToken.padding.small), ) } } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/CreateFixedDepositAccountScreen.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/CreateFixedDepositAccountScreen.kt index fea67931450..400d6c46cbd 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/CreateFixedDepositAccountScreen.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/CreateFixedDepositAccountScreen.kt @@ -15,7 +15,6 @@ import androidclient.feature.client.generated.resources.step_details import androidclient.feature.client.generated.resources.step_interest import androidclient.feature.client.generated.resources.step_settings import androidclient.feature.client.generated.resources.step_terms -import androidclient.feature.client.generated.resources.title_new_fixed_deposit_account import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.runtime.Composable @@ -27,6 +26,7 @@ import com.mifos.core.designsystem.component.MifosScaffold 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.MifosProgressIndicatorOverlay import com.mifos.core.ui.components.MifosStepper import com.mifos.core.ui.components.Step import com.mifos.core.ui.util.EventsEffect @@ -55,7 +55,7 @@ internal fun CreateFixedDepositAccountScreen( } FixedDepositAccountScaffold( navController = navController, - newFixedDepositAccountState = state, + state = state, onAction = { viewModel.trySendAction(it) }, modifier = modifier, ) @@ -64,7 +64,7 @@ internal fun CreateFixedDepositAccountScreen( @Composable private fun FixedDepositAccountScaffold( navController: NavController, - newFixedDepositAccountState: NewFixedDepositAccountState, + state: NewFixedDepositAccountState, onAction: (NewFixedDepositAccountAction) -> Unit, modifier: Modifier = Modifier, ) { @@ -72,20 +72,21 @@ private fun FixedDepositAccountScaffold( listOf( Step(stringResource(Res.string.step_details)) { DetailsPage( - state = newFixedDepositAccountState, + state = state, onAction = onAction, ) }, Step(name = stringResource(Res.string.step_terms)) { TermsPage( - state = newFixedDepositAccountState, + state = state, onAction = onAction, ) }, Step(name = stringResource(Res.string.step_settings)) { SettingPage( - onNext = { onAction(NewFixedDepositAccountAction.OnNextPress) }, + state = state, + onAction = onAction, ) }, Step(name = stringResource(Res.string.step_interest)) { @@ -101,17 +102,14 @@ private fun FixedDepositAccountScaffold( ) MifosScaffold( - title = stringResource(Res.string.title_new_fixed_deposit_account), - onBackPressed = { onAction(NewFixedDepositAccountAction.NavigateBack) }, modifier = modifier, - ) { paddingValues -> Column { MifosBreadcrumbNavBar(navController) - when (newFixedDepositAccountState.screenState) { + when (state.screenState) { is NewFixedDepositAccountState.ScreenState.Error -> { MifosErrorComponent( - message = newFixedDepositAccountState.screenState.message, + message = state.screenState.message, isRetryEnabled = true, ) { onAction(NewFixedDepositAccountAction.Retry) @@ -125,7 +123,7 @@ private fun FixedDepositAccountScaffold( is NewFixedDepositAccountState.ScreenState.Success -> { MifosStepper( steps = steps, - currentIndex = newFixedDepositAccountState.currentStep, + currentIndex = state.currentStep, onStepChange = { newIndex -> onAction(NewFixedDepositAccountAction.OnStepChange(newIndex)) }, @@ -136,5 +134,8 @@ private fun FixedDepositAccountScaffold( } } } + if (state.isOverlayLoading) { + MifosProgressIndicatorOverlay() + } } } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/CreateFixedDepositAccountViewmodel.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/CreateFixedDepositAccountViewmodel.kt index 34a29e74854..d5cd1aaf010 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/CreateFixedDepositAccountViewmodel.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/CreateFixedDepositAccountViewmodel.kt @@ -9,24 +9,31 @@ */ package com.mifos.feature.client.newFixedDepositAccount +import androidclient.feature.client.generated.resources.Res +import androidclient.feature.client.generated.resources.feature_client_error_network_not_available import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import androidx.navigation.toRoute import com.mifos.core.common.utils.DataState import com.mifos.core.common.utils.DateHelper import com.mifos.core.data.repository.FixedDepositRepository +import com.mifos.core.data.util.NetworkMonitor import com.mifos.core.model.objects.template.recurring.FieldOfficerOption import com.mifos.core.network.model.FixedDepositTemplate import com.mifos.core.ui.util.BaseViewModel +import com.mifos.core.ui.util.TextFieldsValidator import com.mifos.feature.client.fixedDepositAccount.FixedDepositAccountRoute +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.jetbrains.compose.resources.StringResource +import org.jetbrains.compose.resources.getString import kotlin.time.Clock import kotlin.time.ExperimentalTime class CreateFixedDepositAccountViewmodel( savedStateHandle: SavedStateHandle, + private val networkMonitor: NetworkMonitor, private val fixedDepositRepository: FixedDepositRepository, ) : BaseViewModel< @@ -40,7 +47,7 @@ class CreateFixedDepositAccountViewmodel( ) { init { - loadFixedDepositAccountTemplate() + loadFixedDepositTemplate() } override fun handleAction(action: NewFixedDepositAccountAction) { @@ -50,70 +57,266 @@ class CreateFixedDepositAccountViewmodel( is NewFixedDepositAccountAction.NavigateBack -> sendEvent(NewFixedDepositAccountEvent.NavigateBack) is NewFixedDepositAccountAction.Finish -> sendEvent(NewFixedDepositAccountEvent.Finish) is NewFixedDepositAccountAction.OnSubmissionDatePick -> handleSubmissionDatePick(action) - is NewFixedDepositAccountAction.OnSubmissionDateChange -> handleSubmissionDateChange(action) + is NewFixedDepositAccountAction.OnSubmissionDateChange -> handleSubmissionDateChange( + action, + ) + is NewFixedDepositAccountAction.OnProductNameChange -> handleOnProductNameChange(action) is NewFixedDepositAccountAction.OnFieldOfficerChange -> handleFieldOfficerChange(action) is NewFixedDepositAccountAction.OnExternalIdChange -> handleExternalIdChange(action) - NewFixedDepositAccountAction.OnDetailsSubmit -> handleOnDetailsSubmit() NewFixedDepositAccountAction.Retry -> handleRetry() - is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositAmount -> handleSetFixedDepositAmount(action) - is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositPeriod -> handleSetFixedDepositPeriod(action) - is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositPeriodType -> handleSetFixedDepositPeriodType(action) - is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestCompoundingPeriod -> handleSetInterestCompoundingPeriod(action) - is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestPostingPeriod -> handleSetInterestPostingPeriod(action) - is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestCalculationType -> handleInterestCalculationType(action) - is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestCalculationDaysInYearType -> handleSetInterestCalculationDaysInYearType(action) - } - } + is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositAmount -> handleSetFixedDepositAmount( + action, + ) - private fun handleRetry() { - loadFixedDepositAccountTemplate() - } + is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositPeriod -> handleSetFixedDepositPeriod( + action, + ) + + is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositPeriodType -> handleSetFixedDepositPeriodType( + action, + ) + + is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestCompoundingPeriod -> handleSetInterestCompoundingPeriod( + action, + ) + + is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestPostingPeriod -> handleSetInterestPostingPeriod( + action, + ) - private fun loadFixedDepositAccountTemplate() = viewModelScope.launch { - fixedDepositRepository.getFixedDepositTemplate( - clientId = state.clientId, - productId = state.template.productOptions?.get(state.fixedDepositAccountDetail.productSelected)?.id, - ).collect { state -> - when (state) { - is DataState.Success -> { - setSuccessState() + is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestCalculationType -> handleInterestCalculationType( + action, + ) + + is NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestCalculationDaysInYearType -> handleSetInterestCalculationDaysInYearType( + action, + ) + + NewFixedDepositAccountAction.OnDetailNext -> { + val productError = + TextFieldsValidator.dropDownEmptyValidator(state.fixedDepositAccountDetail.productSelected == -1) + if (productError != null) { mutableStateFlow.update { it.copy( - template = state.data, + fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( + productError = productError, + ), ) } + } else { + moveToNextStep() } + } - is DataState.Error -> { - setErrorState(state.message) - } + NewFixedDepositAccountAction.OnTermNext -> { + val depositAmountError = + TextFieldsValidator.doubleNumberValidator(state.fixedDepositAccountTerms.depositAmount) + val depositPeriodError = + TextFieldsValidator.numberValidator(state.fixedDepositAccountTerms.depositPeriod) + val depositPeriodTypeError = + TextFieldsValidator.dropDownEmptyValidator(state.fixedDepositAccountTerms.depositPeriodTypeIndex == -1) - DataState.Loading -> { + if (depositAmountError != null || depositPeriodError != null || depositPeriodTypeError != null) { mutableStateFlow.update { it.copy( - isOverlayLoading = true, + fixedDepositAccountTerms = it.fixedDepositAccountTerms.copy( + depositAmountError = depositAmountError, + depositPeriodError = depositPeriodError, + depositPeriodTypeError = depositPeriodTypeError, + ), ) } + } else { + moveToNextStep() + } + } + + NewFixedDepositAccountAction.PreviousStep -> { + moveToPreviousStep() + } + + is NewFixedDepositAccountAction.OnApplyPenalInterestChange -> { + mutableStateFlow.update { + it.copy( + applyPenalInterest = action.checked, + ) + } + } + is NewFixedDepositAccountAction.OnLockInPeriodFrequencyChange -> { + mutableStateFlow.update { + it.copy( + lockInPeriodFrequency = action.value, + ) + } + } + is NewFixedDepositAccountAction.OnLockInPeriodTypeIndexChange -> { + mutableStateFlow.update { + it.copy( + lockInPeriodTypeIndex = action.index, + ) + } + } + is NewFixedDepositAccountAction.OnMaturityInstructionIndexChange -> { + mutableStateFlow.update { + it.copy( + maturityInstructionsIndex = action.index, + ) + } + } + is NewFixedDepositAccountAction.OnMaximumDepositFrequencyChange -> { + mutableStateFlow.update { + it.copy( + maximumDispositFrequency = action.value, + ) + } + } + is NewFixedDepositAccountAction.OnMaximumDepositTypeIndexChange -> { + mutableStateFlow.update { + it.copy( + maximumDispositTypeIndex = action.index, + ) + } + } + is NewFixedDepositAccountAction.OnMinimumDepositTermFrequencyChange -> { + mutableStateFlow.update { + it.copy( + minimumDispositTermFrequency = action.value, + ) + } + } + is NewFixedDepositAccountAction.OnMinimumDepositTermTypeIndexChange -> { + mutableStateFlow.update { + it.copy( + minimumDispositTermTypeIndex = action.index, + ) + } + } + is NewFixedDepositAccountAction.OnMultiplesFrequencyChange -> { + mutableStateFlow.update { + it.copy( + multiplesFrequency = action.value, + ) + } + } + is NewFixedDepositAccountAction.OnMultiplesTypeIndexChange -> { + mutableStateFlow.update { + it.copy( + multiplesTypeIndex = action.index, + ) + } + } + is NewFixedDepositAccountAction.OnPenalInterestChange -> { + mutableStateFlow.update { + it.copy( + penalInterest = action.value, + ) + } + } + is NewFixedDepositAccountAction.OnPeriodIndexChange -> { + mutableStateFlow.update { + it.copy( + periodIndex = action.index, + ) + } + } + is NewFixedDepositAccountAction.OnTransferLinkedSavingsAccountInterestChange -> { + mutableStateFlow.update { + it.copy( + transferLinkedSavingAccountInterest = action.checked, + ) } } } } - private fun setSuccessState() { - mutableStateFlow.update { - it.copy( - screenState = NewFixedDepositAccountState.ScreenState.Success, - isOverlayLoading = false, - ) + private fun handleRetry() { + loadFixedDepositTemplate() + } + + private fun loadFixedDepositTemplate() = viewModelScope.launch { + if (networkMonitor.isOnline.first()) { + fixedDepositRepository.getFixedDepositTemplate( + clientId = state.clientId, + ).collect { state -> + when (state) { + is DataState.Success -> { + mutableStateFlow.update { + it.copy( + screenState = NewFixedDepositAccountState.ScreenState.Success, + isOverlayLoading = false, + template = state.data, + ) + } + } + + is DataState.Error -> { + mutableStateFlow.update { + it.copy( + screenState = NewFixedDepositAccountState.ScreenState.Error(state.message), + ) + } + } + + DataState.Loading -> { + mutableStateFlow.update { + it.copy( + screenState = NewFixedDepositAccountState.ScreenState.Loading, + ) + } + } + } + } + } else { + mutableStateFlow.update { + it.copy( + screenState = NewFixedDepositAccountState.ScreenState.Error(getString(Res.string.feature_client_error_network_not_available)), + ) + } } } - private fun setErrorState(message: String) { - mutableStateFlow.update { - it.copy( - screenState = NewFixedDepositAccountState.ScreenState.Error(message), - isOverlayLoading = false, - ) + + private fun loadRecurringAccountTemplateWithProduct() = viewModelScope.launch { + if (networkMonitor.isOnline.first()) { + fixedDepositRepository.getFixedDepositTemplate( + clientId = state.clientId, + productId = state.template.productOptions?.get(state.fixedDepositAccountDetail.productSelected)?.id, + ).collect { state -> + when (state) { + is DataState.Success -> { + mutableStateFlow.update { + it.copy( + screenState = NewFixedDepositAccountState.ScreenState.Success, + isOverlayLoading = false, + template = state.data, + ) + } + } + + is DataState.Error -> { + mutableStateFlow.update { + it.copy( + screenState = NewFixedDepositAccountState.ScreenState.Error(state.message), + isOverlayLoading = false, + ) + } + } + + DataState.Loading -> { + mutableStateFlow.update { + it.copy( + isOverlayLoading = true, + ) + } + } + } + } + } else { + mutableStateFlow.update { + it.copy( + screenState = NewFixedDepositAccountState.ScreenState.Error(getString(Res.string.feature_client_error_network_not_available)), + ) + } } } @@ -146,10 +349,13 @@ class CreateFixedDepositAccountViewmodel( it.copy( fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( productSelected = action.index, + productError = null, ), ) } - loadFixedDepositAccountTemplate() + if (state.template.fieldOfficerOptions == null) { + loadRecurringAccountTemplateWithProduct() + } } private fun handleFieldOfficerChange(action: NewFixedDepositAccountAction.OnFieldOfficerChange) { @@ -162,30 +368,31 @@ class CreateFixedDepositAccountViewmodel( } } - private fun handleExternalIdChange(action: NewFixedDepositAccountAction.OnExternalIdChange) { + private fun moveToPreviousStep() { + val current = state.currentStep mutableStateFlow.update { it.copy( - fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( - externalId = action.value, - ), + currentStep = current - 1, ) } } - private fun handleOnDetailsSubmit() { + private fun handleExternalIdChange(action: NewFixedDepositAccountAction.OnExternalIdChange) { mutableStateFlow.update { it.copy( fixedDepositAccountDetail = it.fixedDepositAccountDetail.copy( - externalIdError = null, + externalId = action.value, ), ) } } + private fun handleSetFixedDepositAmount(action: NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositAmount) { mutableStateFlow.update { it.copy( fixedDepositAccountTerms = it.fixedDepositAccountTerms.copy( depositAmount = action.depositAmount, + depositAmountError = null, ), ) } @@ -196,6 +403,7 @@ class CreateFixedDepositAccountViewmodel( it.copy( fixedDepositAccountTerms = it.fixedDepositAccountTerms.copy( depositPeriod = action.period, + depositPeriodError = null, ), ) } @@ -206,6 +414,7 @@ class CreateFixedDepositAccountViewmodel( it.copy( fixedDepositAccountTerms = it.fixedDepositAccountTerms.copy( depositPeriodTypeIndex = action.depositPeriodTypeIndex, + depositPeriodTypeError = null, ), ) } @@ -275,6 +484,23 @@ data class NewFixedDepositAccountState( val fixedDepositAccountTerms: FixedDepositAccountTermsState = FixedDepositAccountTermsState(), val template: FixedDepositTemplate = FixedDepositTemplate(), val isOverlayLoading: Boolean = false, + + val lockInPeriodFrequency: String = "", + val lockInPeriodTypeIndex: Int = -1, + val minimumDispositTermFrequency: String = "", + val minimumDispositTermTypeIndex: Int = -1, + val maximumDispositFrequency: String = "", + val maximumDispositTypeIndex: Int = -1, + val multiplesFrequency: String = "", + val multiplesTypeIndex: Int = -1, + + val maturityInstructionsIndex: Int = -1, + val periodIndex: Int = -1, + val applyPenalInterest: Boolean = false, + val penalInterest: String = "", + + val transferLinkedSavingAccountInterest: Boolean = false, + ) { sealed interface ScreenState { data class Error(val message: String) : ScreenState @@ -291,10 +517,11 @@ data class FixedDepositAccountTermsState( val interestPostingPeriodTypeIndex: Int = -1, val interestCalculationTypeIndex: Int = -1, val interestCalculationDaysInYearTypeIndex: Int = -1, -) { - val isTermsNextEnabled = depositAmount.isNotEmpty() && depositPeriod.isNotEmpty() && - depositPeriodTypeIndex != -1 && interestCompoundingPeriodTypeIndex != -1 && interestCalculationTypeIndex != -1 && interestCalculationDaysInYearTypeIndex != -1 -} + val depositAmountError: StringResource? = null, + val depositPeriodError: StringResource? = null, + val depositPeriodTypeError: StringResource? = null, +) + data class FixedDepositAccountDetailsState @OptIn(ExperimentalTime::class) constructor( @@ -306,19 +533,19 @@ constructor( Clock.System.now().toEpochMilliseconds(), ), val fieldOfficerIndex: Int = -1, - val fieldOfficerError: String? = null, + val productError: StringResource? = null, val externalId: String = "", val externalIdError: StringResource? = null, val isMiniLoaderActive: Boolean = false, val fieldOfficerOptions: List? = null, -) { - val isDetailsNextEnabled = submissionDate.isNotEmpty() && fieldOfficerIndex != -1 -} +) sealed class NewFixedDepositAccountAction { data object OnNextPress : NewFixedDepositAccountAction() + data object OnDetailNext : NewFixedDepositAccountAction() + data object OnTermNext : NewFixedDepositAccountAction() data class OnStepChange(val newIndex: Int) : NewFixedDepositAccountAction() - + data object PreviousStep : NewFixedDepositAccountAction() data object NavigateBack : NewFixedDepositAccountAction() data class OnSubmissionDatePick(val state: Boolean) : NewFixedDepositAccountAction() data class OnSubmissionDateChange(val date: String) : NewFixedDepositAccountAction() @@ -326,18 +553,41 @@ sealed class NewFixedDepositAccountAction { data object Finish : NewFixedDepositAccountAction() data class OnFieldOfficerChange(val index: Int) : NewFixedDepositAccountAction() data class OnExternalIdChange(val value: String) : NewFixedDepositAccountAction() - data object OnDetailsSubmit : NewFixedDepositAccountAction() - data object Retry : NewFixedDepositAccountAction() + data class OnLockInPeriodFrequencyChange(val value: String) : NewFixedDepositAccountAction() + data class OnLockInPeriodTypeIndexChange(val index: Int) : NewFixedDepositAccountAction() + data class OnMinimumDepositTermFrequencyChange(val value: String) : + NewFixedDepositAccountAction() + + data class OnMinimumDepositTermTypeIndexChange(val index: Int) : NewFixedDepositAccountAction() + data class OnMaximumDepositFrequencyChange(val value: String) : NewFixedDepositAccountAction() + data class OnMaximumDepositTypeIndexChange(val index: Int) : NewFixedDepositAccountAction() + data class OnMultiplesFrequencyChange(val value: String) : NewFixedDepositAccountAction() + data class OnMultiplesTypeIndexChange(val index: Int) : NewFixedDepositAccountAction() + data class OnMaturityInstructionIndexChange(val index: Int) : NewFixedDepositAccountAction() + data class OnPeriodIndexChange(val index: Int) : NewFixedDepositAccountAction() + data class OnApplyPenalInterestChange(val checked: Boolean) : NewFixedDepositAccountAction() + data class OnPenalInterestChange(val value: String) : NewFixedDepositAccountAction() + data class OnTransferLinkedSavingsAccountInterestChange(val checked: Boolean) : + NewFixedDepositAccountAction() sealed class NewFixedDepositAccountTermsAction : NewFixedDepositAccountAction() { data class SetFixedDepositAmount(val depositAmount: String) : NewFixedDepositAccountAction() data class SetFixedDepositPeriod(val period: String) : NewFixedDepositAccountAction() - data class SetFixedDepositPeriodType(val depositPeriodTypeIndex: Int) : NewFixedDepositAccountAction() - data class SetInterestCompoundingPeriod(val interestCompoundingPeriodTypeIndex: Int) : NewFixedDepositAccountAction() - data class SetInterestPostingPeriod(val interestPostingPeriodTypeIndex: Int) : NewFixedDepositAccountAction() - data class SetInterestCalculationType(val interestCalculationPeriodTypeIndex: Int) : NewFixedDepositAccountAction() - data class SetInterestCalculationDaysInYearType(val periodTypeIndex: Int) : NewFixedDepositAccountAction() + data class SetFixedDepositPeriodType(val depositPeriodTypeIndex: Int) : + NewFixedDepositAccountAction() + + data class SetInterestCompoundingPeriod(val interestCompoundingPeriodTypeIndex: Int) : + NewFixedDepositAccountAction() + + data class SetInterestPostingPeriod(val interestPostingPeriodTypeIndex: Int) : + NewFixedDepositAccountAction() + + data class SetInterestCalculationType(val interestCalculationPeriodTypeIndex: Int) : + NewFixedDepositAccountAction() + + data class SetInterestCalculationDaysInYearType(val periodTypeIndex: Int) : + NewFixedDepositAccountAction() } } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/DetailsPage.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/DetailsPage.kt index e2def4a8ac9..eb881c9f102 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/DetailsPage.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/DetailsPage.kt @@ -17,6 +17,7 @@ import androidclient.feature.client.generated.resources.feature_client_external_ import androidclient.feature.client.generated.resources.feature_client_next import androidclient.feature.client.generated.resources.field_officer import androidclient.feature.client.generated.resources.one_year_fixed_deposit +import androidclient.feature.client.generated.resources.step_details import androidclient.feature.client.generated.resources.submission_on import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer @@ -40,6 +41,7 @@ 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.designsystem.theme.DesignToken +import com.mifos.core.designsystem.theme.MifosTypography import com.mifos.core.ui.components.MifosTwoButtonRow import com.mifos.feature.client.newFixedDepositAccount.NewFixedDepositAccountAction import com.mifos.feature.client.newFixedDepositAccount.NewFixedDepositAccountState @@ -93,72 +95,84 @@ fun DetailsPage( DatePicker(state = submissionDatePickerState) } } - Column( - modifier = modifier.fillMaxSize() - .verticalScroll(rememberScrollState()), - ) { - MifosTextFieldDropdown( - value = if (state.fixedDepositAccountDetail.productSelected == -1) { - "" - } else { - state.template.productOptions?.get(state.fixedDepositAccountDetail.productSelected)?.name ?: "" - }, - label = stringResource(Res.string.one_year_fixed_deposit), - onValueChanged = {}, - onOptionSelected = { index, value -> - onAction(NewFixedDepositAccountAction.OnProductNameChange(index)) - }, - options = state.template.productOptions?.map { - it.name ?: "" - } ?: emptyList(), - - ) - - if (!state.template.fieldOfficerOptions.isNullOrEmpty()) { - MifosDatePickerTextField( - value = state.fixedDepositAccountDetail.submissionDate, - label = stringResource(Res.string.submission_on), - openDatePicker = { - onAction(NewFixedDepositAccountAction.OnSubmissionDatePick(true)) - }, + Column(modifier = Modifier.fillMaxSize().padding(bottom = DesignToken.padding.large)) { + Column( + modifier = modifier.weight(1f).verticalScroll(rememberScrollState()), + ) { + Text( + text = stringResource(Res.string.step_details), + style = MifosTypography.labelLargeEmphasized, ) Spacer(Modifier.height(DesignToken.padding.large)) MifosTextFieldDropdown( - value = if (state.fixedDepositAccountDetail.fieldOfficerIndex == -1) { + value = if (state.fixedDepositAccountDetail.productSelected == -1) { "" } else { - state.template.fieldOfficerOptions?.get(state.fixedDepositAccountDetail.fieldOfficerIndex)?.displayName + state.template.productOptions?.get(state.fixedDepositAccountDetail.productSelected)?.name ?: "" }, - label = stringResource(Res.string.field_officer), + label = stringResource(Res.string.one_year_fixed_deposit) + "*", onValueChanged = {}, onOptionSelected = { index, value -> - onAction(NewFixedDepositAccountAction.OnFieldOfficerChange(index)) + onAction(NewFixedDepositAccountAction.OnProductNameChange(index)) }, - options = state.template.fieldOfficerOptions?.map { - it.displayName ?: "" + options = state.template.productOptions?.map { + it.name ?: "" } ?: emptyList(), - - ) - MifosOutlinedTextField( - value = state.fixedDepositAccountDetail.externalId, - onValueChange = { - onAction(NewFixedDepositAccountAction.OnExternalIdChange(it)) - }, - label = stringResource(Res.string.feature_client_external_id), - config = MifosTextFieldConfig( - isError = state.fixedDepositAccountDetail.externalIdError != null, - errorText = if (state.fixedDepositAccountDetail.externalIdError != null) stringResource(state.fixedDepositAccountDetail.externalIdError) else null, - ), + errorMessage = state.fixedDepositAccountDetail.productError?.let { stringResource(it) }, ) - Spacer(Modifier.height(DesignToken.padding.large)) + + if (!state.template.fieldOfficerOptions.isNullOrEmpty()) { + MifosDatePickerTextField( + value = state.fixedDepositAccountDetail.submissionDate, + label = stringResource(Res.string.submission_on) + "*", + openDatePicker = { + onAction(NewFixedDepositAccountAction.OnSubmissionDatePick(true)) + }, + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosTextFieldDropdown( + value = if (state.fixedDepositAccountDetail.fieldOfficerIndex == -1) { + "" + } else { + state.template.fieldOfficerOptions?.get(state.fixedDepositAccountDetail.fieldOfficerIndex)?.displayName + ?: "" + }, + label = stringResource(Res.string.field_officer), + onValueChanged = {}, + onOptionSelected = { index, value -> + onAction(NewFixedDepositAccountAction.OnFieldOfficerChange(index)) + }, + options = state.template.fieldOfficerOptions?.map { + it.displayName ?: "" + } ?: emptyList(), + + ) + MifosOutlinedTextField( + value = state.fixedDepositAccountDetail.externalId, + onValueChange = { + onAction(NewFixedDepositAccountAction.OnExternalIdChange(it)) + }, + label = stringResource(Res.string.feature_client_external_id), + config = MifosTextFieldConfig( + isError = state.fixedDepositAccountDetail.externalIdError != null, + errorText = if (state.fixedDepositAccountDetail.externalIdError != null) { + stringResource( + state.fixedDepositAccountDetail.externalIdError, + ) + } else { + null + }, + ), + ) + Spacer(Modifier.height(DesignToken.padding.large)) + } } MifosTwoButtonRow( firstBtnText = stringResource(Res.string.btn_back), secondBtnText = stringResource(Res.string.feature_client_next), onFirstBtnClick = { onAction(NewFixedDepositAccountAction.NavigateBack) }, - onSecondBtnClick = { onAction(NewFixedDepositAccountAction.OnNextPress) }, - isSecondButtonEnabled = state.fixedDepositAccountDetail.isDetailsNextEnabled, + onSecondBtnClick = { onAction(NewFixedDepositAccountAction.OnDetailNext) }, modifier = Modifier.padding(top = DesignToken.padding.small), ) } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/SettingsPage.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/SettingsPage.kt index 3c600cacaa5..8e75c7a22b2 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/SettingsPage.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/SettingsPage.kt @@ -10,25 +10,317 @@ package com.mifos.feature.client.newFixedDepositAccount.pages import androidclient.feature.client.generated.resources.Res -import androidclient.feature.client.generated.resources.step_settings +import androidclient.feature.client.generated.resources.btn_back +import androidclient.feature.client.generated.resources.feature_client_next +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_apply_penal_interest +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_frequency +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_interest_transfer +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_linked_saving_account +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_lock_in_period +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_maturity_instructions +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_maximum_deposit_term +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_minimum_deposit_term +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_penal_interest +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_period +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_pre_mature_closure +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_thereafter_in_multiples +import androidclient.feature.client.generated.resources.feature_fixed_deposit_setting_type +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +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.text.KeyboardOptions +import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp +import androidx.compose.ui.text.input.ImeAction +import androidx.compose.ui.text.input.KeyboardType +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.designsystem.theme.DesignToken +import com.mifos.core.designsystem.theme.MifosTypography +import com.mifos.core.ui.components.MifosCheckBox +import com.mifos.core.ui.components.MifosTwoButtonRow +import com.mifos.feature.client.newFixedDepositAccount.NewFixedDepositAccountAction +import com.mifos.feature.client.newFixedDepositAccount.NewFixedDepositAccountState import org.jetbrains.compose.resources.stringResource @Composable -fun SettingPage(onNext: () -> Unit) { - Column(horizontalAlignment = Alignment.CenterHorizontally) { - Text(stringResource(Res.string.step_settings)) - Spacer(Modifier.height(8.dp)) - Button(onClick = onNext) { - Text("Next Button") +fun SettingPage( + state: NewFixedDepositAccountState, + onAction: (NewFixedDepositAccountAction) -> Unit, +) { + Column(Modifier.fillMaxSize().padding(bottom = DesignToken.padding.large)) { + Column( + modifier = Modifier.weight(1f).verticalScroll(rememberScrollState()), + ) { + Text( + stringResource(Res.string.feature_fixed_deposit_setting_lock_in_period), + style = MifosTypography.labelLargeEmphasized, + ) + Spacer(Modifier.height(DesignToken.padding.large)) + + MifosOutlinedTextField( + value = state.lockInPeriodFrequency, + onValueChange = { + onAction(NewFixedDepositAccountAction.OnLockInPeriodFrequencyChange(it)) + }, + label = stringResource(Res.string.feature_fixed_deposit_setting_frequency), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next, + ), + ), + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosTextFieldDropdown( + value = if (state.lockInPeriodTypeIndex != -1) { + state.template.lockinPeriodFrequencyTypeOptions?.get(state.lockInPeriodTypeIndex)?.value.orEmpty() + } else { + "" + }, + options = state.template.lockinPeriodFrequencyTypeOptions?.map { + it.value.orEmpty() + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { index, name -> + onAction( + NewFixedDepositAccountAction.OnLockInPeriodTypeIndexChange(index), + ) + }, + label = stringResource(Res.string.feature_fixed_deposit_setting_type), + ) + Spacer(Modifier.height(DesignToken.padding.small)) + + Text( + stringResource(Res.string.feature_fixed_deposit_setting_minimum_deposit_term), + style = MifosTypography.labelLargeEmphasized, + ) + Spacer(Modifier.height(DesignToken.padding.large)) + + MifosOutlinedTextField( + value = state.minimumDispositTermFrequency, + onValueChange = { + onAction( + NewFixedDepositAccountAction.OnMinimumDepositTermFrequencyChange(it), + ) + }, + label = stringResource(Res.string.feature_fixed_deposit_setting_thereafter_in_multiples), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next, + ), + ), + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosTextFieldDropdown( + value = if (state.minimumDispositTermTypeIndex != -1) { + state.template.periodFrequencyTypeOptions?.get(state.minimumDispositTermTypeIndex)?.value.orEmpty() + } else { + "" + }, + options = state.template.periodFrequencyTypeOptions?.map { + it.value.orEmpty() + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { index, name -> + onAction(NewFixedDepositAccountAction.OnMinimumDepositTermTypeIndexChange(index)) + }, + label = stringResource(Res.string.feature_fixed_deposit_setting_type), + ) + + Text( + stringResource(Res.string.feature_fixed_deposit_setting_thereafter_in_multiples), + style = MifosTypography.labelLargeEmphasized, + ) + Spacer(Modifier.height(DesignToken.padding.large)) + + MifosOutlinedTextField( + value = state.multiplesFrequency, + onValueChange = { + onAction(NewFixedDepositAccountAction.OnMultiplesFrequencyChange(it)) + }, + label = stringResource(Res.string.feature_fixed_deposit_setting_frequency), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next, + ), + ), + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosTextFieldDropdown( + value = if (state.multiplesTypeIndex != -1) { + state.template.periodFrequencyTypeOptions?.get(state.multiplesTypeIndex)?.value.orEmpty() + } else { + "" + }, + options = state.template.periodFrequencyTypeOptions?.map { + it.value.orEmpty() + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction( + NewFixedDepositAccountAction.OnMultiplesTypeIndexChange(id), + ) + }, + label = stringResource(Res.string.feature_fixed_deposit_setting_type), + ) + + Text( + stringResource(Res.string.feature_fixed_deposit_setting_maximum_deposit_term), + style = MifosTypography.labelLargeEmphasized, + ) + Spacer(Modifier.height(DesignToken.padding.large)) + + MifosOutlinedTextField( + value = state.maximumDispositFrequency, + onValueChange = { + onAction(NewFixedDepositAccountAction.OnMaximumDepositFrequencyChange(it)) + }, + label = stringResource(Res.string.feature_fixed_deposit_setting_frequency), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next, + ), + ), + modifier = Modifier.fillMaxWidth(), + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosTextFieldDropdown( + value = if (state.maximumDispositTypeIndex != -1) { + state.template.periodFrequencyTypeOptions?.get(state.maximumDispositTypeIndex)?.value.orEmpty() + } else { + "" + }, + options = state.template.periodFrequencyTypeOptions?.map { + it.value.orEmpty() + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { index, name -> + onAction( + NewFixedDepositAccountAction.OnMaximumDepositTypeIndexChange(index), + ) + }, + label = stringResource(Res.string.feature_fixed_deposit_setting_type), + ) + Spacer(Modifier.height(DesignToken.padding.small)) + + Text( + stringResource(Res.string.feature_fixed_deposit_setting_interest_transfer), + style = MifosTypography.labelLargeEmphasized, + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosCheckBox( + text = stringResource(Res.string.feature_fixed_deposit_setting_linked_saving_account), + checked = state.transferLinkedSavingAccountInterest, + onCheckChanged = { + onAction( + NewFixedDepositAccountAction.OnTransferLinkedSavingsAccountInterestChange(it), + ) + }, + ) + Spacer(Modifier.height(DesignToken.padding.small)) + + Text( + stringResource(Res.string.feature_fixed_deposit_setting_maturity_instructions), + style = MifosTypography.labelLargeEmphasized, + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosTextFieldDropdown( + value = if (state.maturityInstructionsIndex != -1) { + state.template.maturityInstructionOptions?.get(state.maturityInstructionsIndex)?.value.orEmpty() + } else { + "" + }, + options = state.template.maturityInstructionOptions?.map { + it.value.orEmpty() + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { index, name -> + onAction( + NewFixedDepositAccountAction.OnMaturityInstructionIndexChange(index), + ) + }, + label = stringResource(Res.string.feature_fixed_deposit_setting_maturity_instructions), + ) + + Spacer(Modifier.height(DesignToken.padding.small)) + + Text( + stringResource(Res.string.feature_fixed_deposit_setting_pre_mature_closure), + style = MifosTypography.labelLargeEmphasized, + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosCheckBox( + text = stringResource(Res.string.feature_fixed_deposit_setting_apply_penal_interest), + checked = state.applyPenalInterest, + onCheckChanged = { + onAction( + NewFixedDepositAccountAction.OnApplyPenalInterestChange(it), + ) + }, + ) + + AnimatedVisibility( + visible = state.applyPenalInterest, + ) { + Column { + MifosOutlinedTextField( + value = state.penalInterest, + onValueChange = { + onAction( + NewFixedDepositAccountAction.OnPenalInterestChange(it), + ) + }, + label = stringResource(Res.string.feature_fixed_deposit_setting_penal_interest), + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next, + ), + ), + ) + + Spacer(Modifier.height(DesignToken.padding.large)) + + MifosTextFieldDropdown( + value = if (state.periodIndex != -1) { + state.template.periodFrequencyTypeOptions?.get(state.periodIndex)?.value.orEmpty() + } else { + "" + }, + options = state.template.periodFrequencyTypeOptions?.map { + it.value.orEmpty() + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { index, name -> + onAction( + NewFixedDepositAccountAction.OnPeriodIndexChange(index), + ) + }, + label = stringResource(Res.string.feature_fixed_deposit_setting_period), + ) + + Spacer(Modifier.height(DesignToken.padding.large)) + } + } } + + MifosTwoButtonRow( + firstBtnText = stringResource(Res.string.btn_back), + secondBtnText = stringResource(Res.string.feature_client_next), + onFirstBtnClick = { onAction(NewFixedDepositAccountAction.PreviousStep) }, + onSecondBtnClick = { onAction(NewFixedDepositAccountAction.OnNextPress) }, + ) } } diff --git a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/TermsPage.kt b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/TermsPage.kt index 44246e82094..920fa457c46 100644 --- a/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/TermsPage.kt +++ b/feature/client/src/commonMain/kotlin/com/mifos/feature/client/newFixedDepositAccount/pages/TermsPage.kt @@ -23,6 +23,7 @@ import androidclient.feature.client.generated.resources.feature_fixed_interest_c import androidclient.feature.client.generated.resources.feature_fixed_interest_posting_period import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -31,11 +32,9 @@ import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.foundation.verticalScroll 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.input.ImeAction import androidx.compose.ui.text.input.KeyboardType -import androidx.compose.ui.unit.dp import com.mifos.core.designsystem.component.MifosOutlinedTextField import com.mifos.core.designsystem.component.MifosTextFieldConfig import com.mifos.core.designsystem.component.MifosTextFieldDropdown @@ -50,140 +49,173 @@ import org.jetbrains.compose.resources.stringResource fun TermsPage( state: NewFixedDepositAccountState, onAction: (NewFixedDepositAccountAction) -> Unit, + modifier: Modifier = Modifier, ) { - Column( - modifier = Modifier - .fillMaxWidth() - .verticalScroll(rememberScrollState()), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text( - modifier = Modifier.align(Alignment.Start), - text = stringResource(Res.string.feature_fixed_deposit_terms_page), - style = MifosTypography.labelLargeEmphasized, - ) - Spacer(Modifier.height(8.dp)) - MifosOutlinedTextField( - value = state.fixedDepositAccountTerms.depositAmount, - onValueChange = { onAction(NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositAmount(it)) }, - label = stringResource(Res.string.feature_fixed_deposit_deposit_amount), - config = MifosTextFieldConfig( - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next, + Column(modifier = Modifier.fillMaxSize().padding(bottom = DesignToken.padding.large)) { + Column( + modifier = modifier.weight(1f).verticalScroll(rememberScrollState()), + ) { + Text( + text = stringResource(Res.string.feature_fixed_deposit_terms_page), + style = MifosTypography.labelLargeEmphasized, + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosOutlinedTextField( + value = state.fixedDepositAccountTerms.depositAmount, + onValueChange = { + onAction( + NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositAmount( + it, + ), + ) + }, + label = stringResource(Res.string.feature_fixed_deposit_deposit_amount) + "*", + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next, + ), + prefix = { + Text(state.template.currency?.displaySymbol.orEmpty()) + }, + isError = state.fixedDepositAccountTerms.depositAmountError != null, + errorText = state.fixedDepositAccountTerms.depositAmountError?.let { + stringResource( + it, + ) + }, ), - prefix = { - Text(state.template.currency?.displaySymbol.orEmpty()) + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosOutlinedTextField( + value = state.fixedDepositAccountTerms.depositPeriod, + onValueChange = { + onAction( + NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositPeriod( + it, + ), + ) }, - ), - modifier = Modifier.fillMaxWidth(), - ) - MifosOutlinedTextField( - value = state.fixedDepositAccountTerms.depositPeriod, - onValueChange = { onAction(NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositPeriod(it)) }, - label = stringResource(Res.string.feature_fixed_deposit_deposit_period), - config = MifosTextFieldConfig( - keyboardOptions = KeyboardOptions( - keyboardType = KeyboardType.Number, - imeAction = ImeAction.Next, + label = stringResource(Res.string.feature_fixed_deposit_deposit_period) + "*", + config = MifosTextFieldConfig( + keyboardOptions = KeyboardOptions( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Next, + ), + isError = state.fixedDepositAccountTerms.depositPeriodError != null, + errorText = state.fixedDepositAccountTerms.depositPeriodError?.let { stringResource(it) }, ), - ), - modifier = Modifier.fillMaxWidth(), - ) - MifosTextFieldDropdown( - value = if (state.fixedDepositAccountTerms.depositPeriodTypeIndex != -1) { - state.template.periodFrequencyTypeOptions?.getOrNull(state.fixedDepositAccountTerms.depositPeriodTypeIndex)?.value.orEmpty() - } else { - "" - }, - options = state.template.periodFrequencyTypeOptions?.map { - it.value.orEmpty() - } ?: emptyList(), - onValueChanged = {}, - onOptionSelected = { id, name -> - onAction(NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositPeriodType(id)) - }, - label = stringResource(Res.string.feature_fixed_deposit_deposit_period_type), - modifier = Modifier.fillMaxWidth(), - ) - Text( - modifier = Modifier - .align(Alignment.Start), - text = stringResource(Res.string.feature_fixed_interest_compounding), - style = MifosTypography.labelLargeEmphasized, - ) - Spacer(modifier = Modifier.height(DesignToken.padding.extraSmall)) - MifosTextFieldDropdown( - value = if (state.fixedDepositAccountTerms.interestCompoundingPeriodTypeIndex != -1) { - state.template.interestCompoundingPeriodTypeOptions?.getOrNull(state.fixedDepositAccountTerms.interestCompoundingPeriodTypeIndex)?.value.orEmpty() - } else { - "" - }, - options = state.template.interestCompoundingPeriodTypeOptions?.map { - it.value.orEmpty() - } ?: emptyList(), - onValueChanged = {}, - onOptionSelected = { id, name -> - onAction(NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestCompoundingPeriod(id)) - }, - label = stringResource(Res.string.feature_fixed_interest_compounding_period), - modifier = Modifier.fillMaxWidth(), - ) - MifosTextFieldDropdown( - value = if (state.fixedDepositAccountTerms.interestPostingPeriodTypeIndex != -1) { - state.template.interestPostingPeriodTypeOptions?.getOrNull(state.fixedDepositAccountTerms.interestPostingPeriodTypeIndex)?.value.orEmpty() - } else { - "" - }, - options = state.template.interestPostingPeriodTypeOptions?.map { - it.value.orEmpty() - } ?: emptyList(), - onValueChanged = {}, - onOptionSelected = { id, name -> - onAction(NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestPostingPeriod(id)) - }, - label = stringResource(Res.string.feature_fixed_interest_posting_period), - modifier = Modifier.fillMaxWidth(), - ) - MifosTextFieldDropdown( - value = if (state.fixedDepositAccountTerms.interestCalculationTypeIndex != -1) { - state.template.interestCalculationTypeOptions?.getOrNull(state.fixedDepositAccountTerms.interestCalculationTypeIndex)?.value.orEmpty() - } else { - "" - }, - options = state.template.interestCalculationTypeOptions?.map { - it.value.orEmpty() - } ?: emptyList(), - onValueChanged = {}, - onOptionSelected = { id, name -> - onAction(NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestCalculationType(id)) - }, - label = stringResource(Res.string.feature_fixed_interest_calculated_using), - modifier = Modifier.fillMaxWidth(), - ) - MifosTextFieldDropdown( - value = if (state.fixedDepositAccountTerms.interestCalculationDaysInYearTypeIndex != -1) { - state.template.interestCalculationDaysInYearTypeOptions?.getOrNull(state.fixedDepositAccountTerms.interestCalculationDaysInYearTypeIndex)?.value.orEmpty() - } else { - "" - }, - options = state.template.interestCalculationDaysInYearTypeOptions?.map { - it.value.orEmpty() - } ?: emptyList(), - onValueChanged = {}, - onOptionSelected = { id, name -> - onAction(NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestCalculationDaysInYearType(id)) - }, - label = stringResource(Res.string.feature_fixed_days_in_year), - modifier = Modifier.fillMaxWidth(), - ) + ) + Spacer(Modifier.height(DesignToken.padding.large)) + MifosTextFieldDropdown( + value = if (state.fixedDepositAccountTerms.depositPeriodTypeIndex != -1) { + state.template.periodFrequencyTypeOptions?.getOrNull(state.fixedDepositAccountTerms.depositPeriodTypeIndex)?.value.orEmpty() + } else { + "" + }, + options = state.template.periodFrequencyTypeOptions?.map { + it.value.orEmpty() + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction( + NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetFixedDepositPeriodType( + id, + ), + ) + }, + label = stringResource(Res.string.feature_fixed_deposit_deposit_period_type) + "*", + errorMessage = state.fixedDepositAccountTerms.depositPeriodTypeError?.let { stringResource(it) }, + ) + Spacer(Modifier.height(DesignToken.padding.small)) + Text( + text = stringResource(Res.string.feature_fixed_interest_compounding), + style = MifosTypography.labelLargeEmphasized, + ) + Spacer(modifier = Modifier.height(DesignToken.padding.large)) + MifosTextFieldDropdown( + value = if (state.fixedDepositAccountTerms.interestCompoundingPeriodTypeIndex != -1) { + state.template.interestCompoundingPeriodTypeOptions?.getOrNull(state.fixedDepositAccountTerms.interestCompoundingPeriodTypeIndex)?.value.orEmpty() + } else { + "" + }, + options = state.template.interestCompoundingPeriodTypeOptions?.map { + it.value.orEmpty() + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction( + NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestCompoundingPeriod( + id, + ), + ) + }, + label = stringResource(Res.string.feature_fixed_interest_compounding_period), + ) + MifosTextFieldDropdown( + value = if (state.fixedDepositAccountTerms.interestPostingPeriodTypeIndex != -1) { + state.template.interestPostingPeriodTypeOptions?.getOrNull(state.fixedDepositAccountTerms.interestPostingPeriodTypeIndex)?.value.orEmpty() + } else { + "" + }, + options = state.template.interestPostingPeriodTypeOptions?.map { + it.value.orEmpty() + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction( + NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestPostingPeriod( + id, + ), + ) + }, + label = stringResource(Res.string.feature_fixed_interest_posting_period), + ) + MifosTextFieldDropdown( + value = if (state.fixedDepositAccountTerms.interestCalculationTypeIndex != -1) { + state.template.interestCalculationTypeOptions?.getOrNull(state.fixedDepositAccountTerms.interestCalculationTypeIndex)?.value.orEmpty() + } else { + "" + }, + options = state.template.interestCalculationTypeOptions?.map { + it.value.orEmpty() + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction( + NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestCalculationType( + id, + ), + ) + }, + label = stringResource(Res.string.feature_fixed_interest_calculated_using), + modifier = Modifier.fillMaxWidth(), + ) + MifosTextFieldDropdown( + value = if (state.fixedDepositAccountTerms.interestCalculationDaysInYearTypeIndex != -1) { + state.template.interestCalculationDaysInYearTypeOptions?.getOrNull(state.fixedDepositAccountTerms.interestCalculationDaysInYearTypeIndex)?.value.orEmpty() + } else { + "" + }, + options = state.template.interestCalculationDaysInYearTypeOptions?.map { + it.value.orEmpty() + } ?: emptyList(), + onValueChanged = {}, + onOptionSelected = { id, name -> + onAction( + NewFixedDepositAccountAction.NewFixedDepositAccountTermsAction.SetInterestCalculationDaysInYearType( + id, + ), + ) + }, + label = stringResource(Res.string.feature_fixed_days_in_year), + ) + } MifosTwoButtonRow( firstBtnText = stringResource(Res.string.btn_back), secondBtnText = stringResource(Res.string.feature_client_next), - onFirstBtnClick = { onAction(NewFixedDepositAccountAction.NavigateBack) }, - onSecondBtnClick = { onAction(NewFixedDepositAccountAction.OnNextPress) }, - isButtonIconVisible = true, - isSecondButtonEnabled = state.fixedDepositAccountTerms.isTermsNextEnabled, + onFirstBtnClick = { onAction(NewFixedDepositAccountAction.PreviousStep) }, + onSecondBtnClick = { onAction(NewFixedDepositAccountAction.OnTermNext) }, ) } } diff --git a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/ChargesPage.kt b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/ChargesPage.kt index 7cdfada885f..194aa69a933 100644 --- a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/ChargesPage.kt +++ b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/ChargesPage.kt @@ -108,6 +108,7 @@ fun ChargesPage( btnEnabled = state.addedCharges.isNotEmpty(), ) } + Spacer(Modifier.height(DesignToken.padding.large)) } MifosTwoButtonRow( diff --git a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/PreviewPage.kt b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/PreviewPage.kt index 882b37c112e..f711eeb5c76 100644 --- a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/PreviewPage.kt +++ b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/PreviewPage.kt @@ -51,8 +51,10 @@ import androidclient.feature.loan.generated.resources.terms import androidclient.feature.loan.generated.resources.yes import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll @@ -189,9 +191,9 @@ fun PreviewPage( btnText = stringResource(Res.string.loan_new_loan_view), modifier = Modifier.fillMaxWidth(), ) + Spacer(Modifier.height(DesignToken.padding.large)) } MifosTwoButtonRow( - modifier = Modifier.padding(top = DesignToken.padding.small), firstBtnText = stringResource(Res.string.back), secondBtnText = stringResource(Res.string.feature_loan_charge_submit), onFirstBtnClick = { onAction(NewLoanAccountAction.PreviousStep) }, diff --git a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/SchedulePage.kt b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/SchedulePage.kt index 6936c1060f4..13be3d4da32 100644 --- a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/SchedulePage.kt +++ b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/SchedulePage.kt @@ -71,6 +71,7 @@ fun SchedulePage( currencyCode = state.loanWithAssociationsEntity.currency.code, maxDigits = state.loanWithAssociationsEntity.currency.decimalPlaces, ) + Spacer(Modifier.height(DesignToken.padding.large)) } MifosTwoButtonRow( @@ -82,7 +83,6 @@ fun SchedulePage( onSecondBtnClick = { onAction(NewLoanAccountAction.NextStep) }, - modifier = Modifier.padding(top = DesignToken.padding.small), ) } } diff --git a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/TermsPage.kt b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/TermsPage.kt index e1976694c9d..9555861c1d8 100644 --- a/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/TermsPage.kt +++ b/feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/newLoanAccount/pages/TermsPage.kt @@ -684,6 +684,7 @@ fun TermsPage( text = stringResource(Res.string.collateral_count, state.addedCollaterals.size), btnEnabled = state.addedCollaterals.isNotEmpty(), ) + Spacer(Modifier.height(DesignToken.padding.large)) } MifosTwoButtonRow( firstBtnText = stringResource(Res.string.back), @@ -695,7 +696,6 @@ fun TermsPage( onAction(NewLoanAccountAction.NextStep) }, isSecondButtonEnabled = state.isDetailsNextEnabled, - modifier = Modifier.padding(top = DesignToken.padding.small), ) } } 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 3124e893771..b69c3df63c9 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 @@ -153,15 +153,19 @@ class RecurringAccountViewModel( recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( loanProductSelected = action.index, productError = null, - fieldOfficerError = null, ), ) } - loadRecurringAccountTemplateWithProduct( - state.clientId, - state.template.productOptions?.get(state.recurringDepositAccountDetail.loanProductSelected)?.id - ?: -1, - ) + + if ( + state.template.fieldOfficerOptions == null + ) { + loadRecurringAccountTemplateWithProduct( + state.clientId, + state.template.productOptions?.get(state.recurringDepositAccountDetail.loanProductSelected)?.id + ?: -1, + ) + } } private fun handleInterestCalculationDaysInYearType(action: RecurringAccountAction.RecurringAccountTermAction.OnInterestCalculationDaysInYearType) { @@ -227,7 +231,6 @@ class RecurringAccountViewModel( it.copy( recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( fieldOfficerIndex = action.index, - fieldOfficerError = null, ), ) } @@ -629,16 +632,11 @@ class RecurringAccountViewModel( state.recurringDepositAccountDetail.loanProductSelected == -1, ) - val fieldOfficerError = TextFieldsValidator.dropDownEmptyValidator( - state.recurringDepositAccountDetail.fieldOfficerIndex == -1, - ) - - if (fieldOfficerError != null || productError != null) { + if (productError != null) { mutableStateFlow.update { it.copy( recurringDepositAccountDetail = it.recurringDepositAccountDetail.copy( productError = productError, - fieldOfficerError = fieldOfficerError, ), ) } @@ -695,6 +693,7 @@ class RecurringAccountViewModel( ) } } + RecurringAccountAction.OnShowRateChartDialog -> { mutableStateFlow.update { it.copy( @@ -711,6 +710,7 @@ class RecurringAccountViewModel( it.copy(addedCharges = newCharges) } } + is RecurringAccountAction.EditCharge -> { val createdData = ChargeItem( chargeId = state.template.chargeOptions?.get(action.index)?.id, @@ -728,6 +728,7 @@ class RecurringAccountViewModel( ) } } + is RecurringAccountAction.EditChargeDialog -> { val selectedEditCharge = state.addedCharges[action.index] val chooseChargeIndex = state.template.chargeOptions @@ -744,6 +745,7 @@ class RecurringAccountViewModel( ) } } + is RecurringAccountAction.OnChargeAmountChange -> { mutableStateFlow.update { it.copy( @@ -751,6 +753,7 @@ class RecurringAccountViewModel( ) } } + is RecurringAccountAction.OnChooseChargeIndexChange -> { mutableStateFlow.update { it.copy( @@ -758,6 +761,7 @@ class RecurringAccountViewModel( ) } } + RecurringAccountAction.ShowAddChargeDialog -> { mutableStateFlow.update { it.copy( @@ -765,6 +769,7 @@ class RecurringAccountViewModel( ) } } + RecurringAccountAction.ShowListOfChargesDialog -> { mutableStateFlow.update { it.copy( @@ -815,6 +820,7 @@ data class RecurringAccountState( data object Loading : ScreenState data object Success : ScreenState } + sealed interface DialogState { data object RateChartDialog : DialogState data class AddNewCharge(val edit: Boolean, val index: Int = -1) : DialogState @@ -839,7 +845,6 @@ constructor( val fieldOfficerOptions: List? = null, val productError: StringResource? = null, - val fieldOfficerError: StringResource? = null, ) data class RecurringAccountInterestChartState( 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 92e56e6d588..3d132c3926a 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 @@ -111,6 +111,7 @@ fun ChargesPage( text = state.addedCharges.size.toString() + " " + stringResource(Res.string.feature_recurring_deposit_active_charge), btnEnabled = state.addedCharges.isNotEmpty(), ) + Spacer(Modifier.height(DesignToken.padding.large)) } MifosTwoButtonRow( firstBtnText = stringResource(Res.string.feature_recurring_deposit_back), 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 8b863b88067..066a327969c 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 @@ -173,8 +173,7 @@ fun DetailsPage( options = state.template.fieldOfficerOptions?.map { it.displayName ?: "" } ?: emptyList(), - label = stringResource(Res.string.feature_recurring_deposit_field_officer) + "*", - errorMessage = state.recurringDepositAccountDetail.fieldOfficerError?.let { stringResource(it) }, + label = stringResource(Res.string.feature_recurring_deposit_field_officer), ) MifosOutlinedTextField( 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 a441d33af40..93c60ce023d 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 @@ -98,6 +98,7 @@ fun InterestPage( }, btnEnabled = state.isRateChartEmpty, ) + Spacer(Modifier.height(DesignToken.padding.large)) } MifosTwoButtonRow( @@ -105,7 +106,6 @@ fun InterestPage( secondBtnText = stringResource(Res.string.feature_recurring_deposit_next), onFirstBtnClick = { onAction(RecurringAccountAction.OnBackPress) }, onSecondBtnClick = { onAction(RecurringAccountAction.OnNextPress) }, - modifier = Modifier.padding(top = DesignToken.padding.small), ) } } diff --git a/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/ChargesPage.kt b/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/ChargesPage.kt index 67be26f8730..762085c88fc 100644 --- a/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/ChargesPage.kt +++ b/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/ChargesPage.kt @@ -94,6 +94,7 @@ fun ChargesPage( text = state.addedCharges.size.toString() + " " + stringResource(Res.string.step_charges_active) + " " + stringResource(Res.string.step_charges), btnEnabled = state.addedCharges.isNotEmpty(), ) + Spacer(Modifier.height(DesignToken.padding.large)) } MifosTwoButtonRow( firstBtnText = stringResource(Res.string.feature_savings_back), diff --git a/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/DetailsPage.kt b/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/DetailsPage.kt index d038159ebe6..d9ab16743ce 100644 --- a/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/DetailsPage.kt +++ b/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/DetailsPage.kt @@ -169,7 +169,6 @@ fun DetailsPage( onFirstBtnClick = { onAction(SavingsAccountAction.NavigateBack) }, onSecondBtnClick = { onAction(SavingsAccountAction.OnDetailsSubmit) }, isSecondButtonEnabled = state.submissionDate.isNotEmpty(), - modifier = Modifier.padding(top = DesignToken.padding.small), ) } } diff --git a/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/PreviewPage.kt b/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/PreviewPage.kt index 3fad37fb7b3..389bbc47355 100644 --- a/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/PreviewPage.kt +++ b/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/PreviewPage.kt @@ -181,6 +181,7 @@ fun PreviewPage( ), btnEnabled = state.addedCharges.isNotEmpty(), ) + Spacer(Modifier.height(DesignToken.padding.large)) } } MifosTwoButtonRow( @@ -192,7 +193,6 @@ fun PreviewPage( onSecondBtnClick = { onAction(SavingsAccountAction.SubmitSavingsApplication) }, - modifier = Modifier.padding(top = DesignToken.padding.small), ) } } diff --git a/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/TermsPage.kt b/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/TermsPage.kt index 7f4acadc982..89f5352504a 100644 --- a/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/TermsPage.kt +++ b/feature/savings/src/commonMain/kotlin/com/mifos/feature/savings/savingsAccountv2/pages/TermsPage.kt @@ -304,7 +304,6 @@ fun TermsPage( onSecondBtnClick = { onAction(SavingsAccountAction.OnTermSubmit) }, - modifier = Modifier.padding(top = DesignToken.padding.small), ) } }