diff --git a/core/ui/src/commonMain/composeResources/values/strings.xml b/core/ui/src/commonMain/composeResources/values/strings.xml
index c2ba3e9797f..d523980df01 100644
--- a/core/ui/src/commonMain/composeResources/values/strings.xml
+++ b/core/ui/src/commonMain/composeResources/values/strings.xml
@@ -118,4 +118,9 @@
Share Product
Pending For Approval Shares
Approved Shares
+
+
+
+
+
\ No newline at end of file
diff --git a/core/ui/src/commonMain/kotlin/com/mifos/core/ui/util/TextFieldsValidator.kt b/core/ui/src/commonMain/kotlin/com/mifos/core/ui/util/TextFieldsValidator.kt
index bee8680bb0b..94ffdb7fcd7 100644
--- a/core/ui/src/commonMain/kotlin/com/mifos/core/ui/util/TextFieldsValidator.kt
+++ b/core/ui/src/commonMain/kotlin/com/mifos/core/ui/util/TextFieldsValidator.kt
@@ -45,12 +45,13 @@ object TextFieldsValidator {
}
fun doubleNumberValidator(input: String): StringResource? {
+ val trimmedInput = input.trim()
return when {
- input.isBlank() -> Res.string.error_field_empty
- input.count { it == '.' } > 1 -> Res.string.error_invalid_number
- input.any { !it.isDigit() && it != '.' } -> Res.string.error_digits_only
- input.toDoubleOrNull() == null -> Res.string.error_invalid_number
- input == "0" || input == "0.0" || input.toDoubleOrNull() == 0.0 -> Res.string.error_number_zero
+ trimmedInput.isBlank() -> Res.string.error_field_empty
+ trimmedInput.count { it == '.' } > 1 -> Res.string.error_invalid_number
+ trimmedInput.any { !it.isDigit() && it != '.' } -> Res.string.error_digits_only
+ trimmedInput.toDoubleOrNull() == null -> Res.string.error_invalid_number
+ trimmedInput == "0" || trimmedInput == "0.0" || trimmedInput.toDoubleOrNull() == 0.0 -> Res.string.error_number_zero
else -> null
}
}
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 b82f0de3f86..cfc1fcd1625 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
@@ -49,4 +49,11 @@
Next Button
Interest Page
Charges Page
+ Edit Charge
+ Add New
+ Add
+ View
+ Active
+ Submit
+
\ 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 6d9f1a85c86..cb531be3bd4 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,35 +10,65 @@
package com.mifos.feature.recurringDeposit.newRecurringDepositAccount
import androidclient.feature.recurringdeposit.generated.resources.Res
+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_create_recurring_deposit_account
+import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_next
+import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_next_button
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.recurring_step_charges_add
+import androidclient.feature.recurringdeposit.generated.resources.recurring_step_charges_add_new
+import androidclient.feature.recurringdeposit.generated.resources.recurring_step_charges_edit_charge
+import androidclient.feature.recurringdeposit.generated.resources.recurring_step_charges_view
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.itemsIndexed
+import androidx.compose.material3.SnackbarHostState
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavController
+import com.mifos.core.common.utils.DateHelper
+import com.mifos.core.designsystem.component.MifosBottomSheet
import com.mifos.core.designsystem.component.MifosScaffold
+import com.mifos.core.designsystem.theme.DesignToken
+import com.mifos.core.designsystem.theme.MifosTypography
+import com.mifos.core.ui.components.Actions
+import com.mifos.core.ui.components.AddChargeBottomSheet
+import com.mifos.core.ui.components.MifosActionsChargeListingComponent
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.MifosTwoButtonRow
import com.mifos.core.ui.components.Step
import com.mifos.core.ui.util.EventsEffect
+import com.mifos.core.ui.util.TextFieldsValidator.doubleNumberValidator
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
import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages.SettingPage
import com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages.TermsPage
+import kotlinx.coroutines.delay
import org.jetbrains.compose.resources.stringResource
import org.koin.compose.viewmodel.koinViewModel
+import kotlin.time.Clock
+import kotlin.time.ExperimentalTime
@Composable
internal fun RecurringAccountScreen(
@@ -55,14 +85,23 @@ internal fun RecurringAccountScreen(
RecurringAccountEvent.NavigateBack -> onNavigateBack()
RecurringAccountEvent.Finish -> onFinish()
}
+
}
+
RecurringAccountScaffold(
navController = navController,
modifier = modifier,
state = state,
onAction = { viewModel.trySendAction(it) },
)
+ val snackbarHostState = remember { SnackbarHostState() }
+ NewRecurringAccountDialog(
+ state = state,
+ onAction = {viewModel.trySendAction(it)},
+ snackbarHostState = snackbarHostState
+
+ )
}
@Composable
@@ -97,7 +136,8 @@ private fun RecurringAccountScaffold(
},
Step(name = stringResource(Res.string.feature_recurring_deposit_step_charges)) {
ChargesPage(
- onNext = { onAction(RecurringAccountAction.OnNextPress) },
+ state = state,
+ onAction = onAction,
)
},
)
@@ -142,3 +182,205 @@ private fun RecurringAccountScaffold(
}
}
}
+@Composable
+private fun NewRecurringAccountDialog(
+ state: RecurringAccountState,
+ onAction: (RecurringAccountAction) -> Unit,
+ snackbarHostState: SnackbarHostState,
+) {
+ when (state.dialogState) {
+ is RecurringAccountState.DialogState.AddNewCharge -> AddNewChargeDialog(
+ isEdit = state.dialogState.edit,
+ state = state,
+ onAction = onAction,
+ index = state.dialogState.index,
+ )
+
+ is RecurringAccountState.DialogState.showCharges -> ShowChargesDialog(
+ state = state,
+ onAction = onAction,
+ )
+
+ is RecurringAccountState.DialogState.SuccessResponseStatus -> {
+ LaunchedEffect(state.launchEffectKey) {
+ snackbarHostState.showSnackbar(
+ message = state.dialogState.msg,
+ )
+
+ if (state.dialogState.successStatus) {
+ delay(1000)
+ onAction(RecurringAccountAction.Finish)
+ }
+ }
+ }
+
+ null -> Unit
+ }
+}
+@OptIn(ExperimentalTime::class, ExperimentalTime::class)
+@Composable
+private fun AddNewChargeDialog(
+ isEdit: Boolean,
+ index: Int = -1,
+ state: RecurringAccountState,
+ onAction: (RecurringAccountAction) -> Unit,
+) {
+ var isAmountDirty by rememberSaveable { mutableStateOf(false) }
+
+ LaunchedEffect(state.chargeAmount, isAmountDirty) {
+ if (isAmountDirty) {
+ if (state.chargeAmount.isNotEmpty()) {
+ val amountError = doubleNumberValidator(state.chargeAmount)
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.OnChargesAmountChangeError(amountError))
+ } else {
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.OnChargesAmountChangeError(null))
+ }
+ }
+ }
+ fun isSelectableDate(utcTimeMillis: Long): Boolean {
+ return utcTimeMillis >= Clock.System.now().toEpochMilliseconds().minus(86_400_000L)
+ }
+ AddChargeBottomSheet(
+ title = if (isEdit) {
+ stringResource(Res.string.recurring_step_charges_edit_charge)
+ } else {
+ stringResource(Res.string.recurring_step_charges_add_new) + " " + stringResource(Res.string.feature_recurring_deposit_step_charges)
+ },
+ confirmText = if (isEdit) {
+ stringResource(Res.string.recurring_step_charges_edit_charge)
+ } else {
+ stringResource(Res.string.recurring_step_charges_add)
+ },
+ dismissText = stringResource(Res.string.feature_recurring_deposit_cancel),
+ showDatePicker = state.showChargesDatePick,
+ selectedChargeName = if (state.chooseChargeIndex == -1) {
+ ""
+ } else {
+ state.template.chargeOptions?.getOrNull(state.chooseChargeIndex)?.name ?: ""
+ },
+ selectedDate = state.chargeDate,
+ chargeAmount = state.chargeAmount,
+ chargeType = if (state.chooseChargeIndex == -1) {
+ ""
+ } else {
+ state.template.chargeOptions?.get(state.chooseChargeIndex)?.chargeCalculationType?.value
+ ?: ""
+ },
+ chargeCollectedOn = if (state.chooseChargeIndex == -1) {
+ ""
+ } else {
+ state.template.chargeOptions?.getOrNull(state.chooseChargeIndex)?.chargeTimeType?.value ?: ""
+ },
+ chargeOptions = state.template.chargeOptions?.map { it.name ?: "" } ?: emptyList(),
+ onConfirm = {
+ isAmountDirty = true
+ if (state.chargeAmount.isNotEmpty() && state.chargeAmountError == null) {
+ if (isEdit) {
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.EditCharge(index))
+ } else {
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.AddChargeToList)
+ }
+ }
+ },
+ onDismiss = { onAction(RecurringAccountAction.RecurringAccountChargesAction.DismissDialog) },
+ onChargeSelected = { index, _ ->
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.OnChooseChargeIndex(index))
+ },
+ onDatePick = { show ->
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.OnChargesDatePick(show))
+ },
+ onDateChange = { newDate ->
+ if (isSelectableDate(newDate)) {
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.OnChargesDateChange(
+ DateHelper.getDateAsStringFromLong(newDate)))
+ }
+ },
+ amountError = if (state.chargeAmountError != null) stringResource(state.chargeAmountError) else null,
+ onAmountChange = { amount ->
+ isAmountDirty = true
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.OnChargesAmountChange(amount))
+ },
+ )
+}
+
+
+@Composable
+private fun ShowChargesDialog(
+ state: RecurringAccountState,
+ onAction: (RecurringAccountAction) -> Unit,
+) {
+ var expandedIndex: Int? by rememberSaveable { mutableStateOf(-1) }
+ MifosBottomSheet(
+ onDismiss = {
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.DismissDialog)
+ },
+ content = {
+ LazyColumn(
+ modifier = Modifier.fillMaxWidth().padding(DesignToken.padding.large),
+ verticalArrangement = Arrangement.spacedBy(DesignToken.padding.largeIncreased),
+ ) {
+ item {
+ Text(
+ text = stringResource(Res.string.recurring_step_charges_view) + " " + stringResource(
+ Res.string.feature_recurring_deposit_step_charges
+ ),
+ style = MifosTypography.titleMediumEmphasized,
+ )
+ }
+ itemsIndexed(items = state.addedCharges) { index, it ->
+ MifosActionsChargeListingComponent(
+ chargeTitle = it.name.toString(),
+ type = it.type.toString(),
+ date = it.date,
+ collectedOn = it.collectedOn,
+ amount = it.amount.toString(),
+ onActionClicked = { action ->
+ when (action) {
+ is Actions.Delete -> {
+ onAction(
+ RecurringAccountAction.RecurringAccountChargesAction.DeleteChargeFromSelectedCharges(
+ index
+ )
+ )
+ }
+
+ is Actions.Edit -> {
+ onAction(
+ RecurringAccountAction.RecurringAccountChargesAction.EditChargeDialog(
+ index
+ )
+ )
+ }
+
+ else -> {}
+ }
+ },
+
+ isExpanded = expandedIndex == it.id,
+ onExpandToggle = {
+ expandedIndex = if (expandedIndex == it.id) -1 else it.id
+ },
+ )
+
+
+ }
+ item {
+ MifosTwoButtonRow(
+ firstBtnText = stringResource(Res.string.feature_recurring_deposit_back),
+ secondBtnText = stringResource(Res.string.recurring_step_charges_add_new),
+ onFirstBtnClick = {
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.DismissDialog)
+ },
+ onSecondBtnClick = {
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.ShowAddChargeDialog)
+ },
+ )
+ }
+
+
+ }
+
+ }
+ )
+
+}
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 d477224f7f4..426124110e5 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
@@ -18,6 +18,7 @@ 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.RecurringAccountRepository
import com.mifos.core.data.util.NetworkMonitor
import com.mifos.core.model.objects.payloads.RecurringDepositAccountPayload
@@ -28,7 +29,10 @@ import com.mifos.room.entities.templates.recurringDeposit.RecurringDepositAccoun
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
const val TOTAL_STEPS = 4
@@ -251,6 +255,140 @@ class RecurringAccountViewModel(
}
}
}
+ private fun handleChargesAmountError (error: StringResource?){
+ mutableStateFlow.update {
+ it.copy(
+ chargeAmountError = error
+ )
+ }
+ }
+ private fun handleEditChargeDialog(index : Int){
+ val selectedEditCharge = state.addedCharges[index]
+ val chooseChargeIndex = state.template.chargeOptions?.indexOfFirst {
+ it.id == selectedEditCharge.id
+ }?: -1
+ mutableStateFlow.update {
+ it.copy(
+ chargeAmount = selectedEditCharge.amount.toString(),
+ chargeDate = selectedEditCharge.date,
+ chooseChargeIndex = chooseChargeIndex,
+ dialogState = RecurringAccountState.DialogState.AddNewCharge(true,index)
+
+ )
+ }
+ }
+ private fun handleDeleteCharge(index : Int){
+ val newCharges = state.addedCharges.toMutableList().apply {
+ removeAt(index)
+ }
+ mutableStateFlow.update{
+ it.copy(addedCharges = newCharges)
+ }
+ }
+ private fun handleChargesDatePick(action : RecurringAccountAction.RecurringAccountChargesAction.OnChargesDatePick){
+ mutableStateFlow.update {
+ it.copy(
+ showChargesDatePick = action.state
+ )
+ }
+ }
+ private fun handleChooseChargeIndexChange(action : RecurringAccountAction.RecurringAccountChargesAction.OnChooseChargeIndex){
+ mutableStateFlow.update {
+ it.copy(
+ chooseChargeIndex = action.index
+ )
+ }
+ }
+ private fun handleChargesDateChange(action: RecurringAccountAction.RecurringAccountChargesAction.OnChargesDateChange){
+ mutableStateFlow.update {
+ it.copy(
+ chargeDate = action.date
+ )
+ }
+ }
+ private fun handleChargesAmountChange(action: RecurringAccountAction.RecurringAccountChargesAction.OnChargesAmountChange){
+ mutableStateFlow.update {
+ it.copy(
+ chargeAmount = action.amount
+ )
+ }
+ }
+ private fun handleDismissDialog(){
+ mutableStateFlow.update {
+ it.copy(
+ dialogState =null
+ )
+ }
+ }
+ private fun handleAddToChargeList(){
+ val selectedIndex = state.chooseChargeIndex
+ val selectedCharges = state.template?.chargeOptions?.getOrNull(selectedIndex)
+ val amount = state.chargeAmount.toDoubleOrNull()?:selectedCharges?.amount?:0.0
+ if (selectedCharges != null && state.chargeAmountError == null){
+ val newCharge = CreatedCharges(
+ id = selectedCharges.id,
+ name = selectedCharges.name,
+ amount = amount,
+ date = state.chargeDate,
+ type = selectedCharges.chargeCalculationType?.value ?: "",
+ collectedOn = selectedCharges.chargeTimeType?.value ?: "",
+ )
+ mutableStateFlow.update {
+ it.copy(
+ addedCharges = it.addedCharges + newCharge,
+ chooseChargeIndex = -1,
+ dialogState = null,
+ chargeAmount = "",
+ )
+ }
+ } else {
+ mutableStateFlow.update {
+ it.copy(
+ chooseChargeIndex = -1,
+ dialogState = null,
+ chargeAmount = "",
+ )
+ }
+ }
+ }
+
+ private fun handleEditCharge(index: Int) {
+ val selectedIndex = state.chooseChargeIndex
+ val selectedCharge = state.template.chargeOptions?.getOrNull(selectedIndex)
+ val amount = state.chargeAmount.toDoubleOrNull() ?: selectedCharge?.amount ?: 0.0
+ if (selectedCharge != null && state.chargeAmountError == null) {
+ val newCharge = CreatedCharges(
+ id = selectedCharge.id,
+ name = selectedCharge.name,
+ amount = amount,
+ date = state.chargeDate,
+ type = selectedCharge.chargeCalculationType?.value ?: "",
+ collectedOn = selectedCharge.chargeTimeType?.value ?: "",
+ )
+ val currentAddedCharges = state.addedCharges.toMutableList()
+ currentAddedCharges[index] = newCharge
+ mutableStateFlow.update {
+ it.copy(
+ addedCharges = currentAddedCharges,
+ chooseChargeIndex = -1,
+ dialogState = RecurringAccountState.DialogState.showCharges,
+ chargeAmount = "",
+ )
+ }
+ }
+ }
+ private fun handleShowAddChargeDialog() {
+ mutableStateFlow.update {
+ it.copy(dialogState = RecurringAccountState.DialogState.AddNewCharge(false))
+ }
+ }
+
+ private fun handleShowChargeDialog() {
+ mutableStateFlow.update {
+ it.copy(dialogState = RecurringAccountState.DialogState.showCharges)
+ }
+ }
+
private fun loadRecurringAccountTemplateWithProduct(
clientId: Int,
@@ -577,11 +715,28 @@ class RecurringAccountViewModel(
RecurringAccountAction.OnNextPress -> {
moveToNextStep()
}
+
+ RecurringAccountAction.RecurringAccountChargesAction.AddChargeToList -> handleAddToChargeList()
+ is RecurringAccountAction.RecurringAccountChargesAction.DeleteChargeFromSelectedCharges -> handleDeleteCharge(action.index)
+ RecurringAccountAction.RecurringAccountChargesAction.DismissDialog -> handleDismissDialog()
+ is RecurringAccountAction.RecurringAccountChargesAction.EditCharge -> handleEditCharge(action.index)
+ is RecurringAccountAction.RecurringAccountChargesAction.EditChargeDialog -> handleEditChargeDialog(action.index)
+ is RecurringAccountAction.RecurringAccountChargesAction.OnChargesAmountChange -> handleChargesAmountChange(action)
+ is RecurringAccountAction.RecurringAccountChargesAction.OnChargesAmountChangeError -> handleChargesAmountError(action.error)
+ is RecurringAccountAction.RecurringAccountChargesAction.OnChargesDateChange -> handleChargesDateChange(action)
+ is RecurringAccountAction.RecurringAccountChargesAction.OnChargesDatePick -> handleChargesDatePick(action)
+ is RecurringAccountAction.RecurringAccountChargesAction.OnChooseChargeIndex -> handleChooseChargeIndexChange(action)
+ RecurringAccountAction.RecurringAccountChargesAction.ShowAddChargeDialog -> handleShowAddChargeDialog()
+ RecurringAccountAction.RecurringAccountChargesAction.ShowCharge -> handleShowChargeDialog()
+ RecurringAccountAction.Finish -> {
+ sendEvent(RecurringAccountEvent.Finish)
+ }
+
}
}
}
-data class RecurringAccountState(
+data class RecurringAccountState @OptIn(ExperimentalTime::class) constructor(
val isOnline: Boolean = false,
val clientId: Int = -1,
val currentStep: Int = 0,
@@ -590,15 +745,40 @@ data class RecurringAccountState(
val recurringDepositAccountDetail: RecurringAccountDetailsState = RecurringAccountDetailsState(),
val template: RecurringDepositAccountTemplate = RecurringDepositAccountTemplate(),
val recurringDepositAccountSettings: RecurringAccountSettingsState = RecurringAccountSettingsState(),
+ val chargeAmountError : StringResource? = null,
+ val chargeAmount : String = " ",
+ val recurringDepositAccountCharges : RecurringAccountChargesState = RecurringAccountChargesState(),
+ val dialogState: DialogState? = null,
val currencyIndex: Int = -1,
val currencyError: String? = null,
+ val addedCharges : List = emptyList(),
+ val chooseChargeIndex : Int = -1,
+ val showChargesDatePick : Boolean = false,
+ val launchEffectKey: Int? = null,
+ val chargeDate: String = DateHelper.getDateAsStringFromLong(
+ Clock.System.now().toEpochMilliseconds(),
+ ),
) {
sealed interface ScreenState {
data class Error(val message: String) : ScreenState
data object Loading : ScreenState
data object Success : ScreenState
}
+ sealed interface DialogState {
+ data object showCharges : DialogState
+ data class AddNewCharge (val edit : Boolean, val index : Int = -1) : DialogState
+ data class SuccessResponseStatus(val successStatus: Boolean, val msg: String = "") :
+ DialogState
+ }
}
+data class RecurringAccountChargesState @OptIn(ExperimentalTime::class) constructor(
+ val chooseChargeIndex : Int = -1 ,
+
+
+
+
+
+ )
data class RecurringAccountDetailsState(
val productId: Int = -1,
@@ -679,12 +859,34 @@ data class RecurringAccountSettingsState(
maxDepositTerm.frequency.isNotBlank()
}
+
sealed class RecurringAccountAction {
data class NavigateToStep(val index: Int) : RecurringAccountAction()
object NavigateBack : RecurringAccountAction()
object OnBackPress : RecurringAccountAction()
object OnNextPress : RecurringAccountAction()
data object Retry : RecurringAccountAction()
+ data object Finish : RecurringAccountAction()
+
+
+ sealed class RecurringAccountChargesAction : RecurringAccountAction(){
+ object ShowAddChargeDialog : RecurringAccountAction()
+ object ShowCharge : RecurringAccountAction()
+ data class EditCharge (val index : Int ) : RecurringAccountAction()
+ data class OnChooseChargeIndex(val index : Int) : RecurringAccountAction()
+ data class OnChargesDatePick (val state : Boolean): RecurringAccountAction()
+ data class OnChargesDateChange(val date : String): RecurringAccountAction()
+ data class OnChargesAmountChange(val amount : String): RecurringAccountAction()
+ data class OnChargesAmountChangeError (val error : StringResource?): RecurringAccountAction()
+ object AddChargeToList : RecurringAccountAction()
+ object DismissDialog : RecurringAccountAction()
+ data class DeleteChargeFromSelectedCharges (val index : Int): RecurringAccountAction()
+ data class EditChargeDialog(val index : Int): RecurringAccountAction()
+
+ }
+
+
+
sealed class RecurringAccountDetailsAction : RecurringAccountAction() {
data class OnProductNameChange(val index: Int) : RecurringAccountDetailsAction()
@@ -694,6 +896,7 @@ sealed class RecurringAccountAction {
data class OnExternalIdChange(val value: String) : RecurringAccountDetailsAction()
}
+
sealed class RecurringAccountSettingsAction : RecurringAccountAction() {
object ToggleMandatoryDeposit : RecurringAccountSettingsAction()
object ToggleAdvancePaymentsTowardsFutureInstallments : RecurringAccountSettingsAction()
@@ -723,3 +926,13 @@ sealed class RecurringAccountEvent {
object NavigateBack : RecurringAccountEvent()
object Finish : RecurringAccountEvent()
}
+
+
+data class CreatedCharges(
+ val id: Int? = -1,
+ val name: String?,
+ val date: String,
+ val type: String?,
+ val amount: Double? = 0.0,
+ val collectedOn: String = "",
+)
\ No newline at end of file
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 cacdd0e1248..d2d51629cde 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
@@ -10,26 +10,111 @@
package com.mifos.feature.recurringDeposit.newRecurringDepositAccount.pages
import androidclient.feature.recurringdeposit.generated.resources.Res
+import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_back
import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_charges_page
+import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_next
import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_next_button
+import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_charges
+import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_step_settings
+import androidclient.feature.recurringdeposit.generated.resources.feature_recurring_deposit_submitted_on
+import androidclient.feature.recurringdeposit.generated.resources.recurring_step_charges_active
+import androidclient.feature.recurringdeposit.generated.resources.recurring_step_charges_add_new
+import androidclient.feature.recurringdeposit.generated.resources.recurring_step_charges_submit
+import androidclient.feature.recurringdeposit.generated.resources.recurring_step_charges_view
+import androidx.compose.foundation.clickable
+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.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
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 com.mifos.core.designsystem.icon.MifosIcons
+import com.mifos.core.designsystem.theme.DesignToken
+import com.mifos.core.designsystem.theme.MifosTypography
+import com.mifos.core.ui.components.MifosRowWithTextAndButton
+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
@Composable
-fun ChargesPage(onNext: () -> Unit) {
- Column(horizontalAlignment = Alignment.CenterHorizontally) {
- Text(stringResource(Res.string.feature_recurring_deposit_charges_page))
- Spacer(Modifier.height(8.dp))
- Button(onClick = onNext) {
- Text(stringResource(Res.string.feature_recurring_deposit_next_button))
+fun ChargesPage(
+ state : RecurringAccountState,
+ onAction : (RecurringAccountAction) -> Unit,
+
+) {
+ Column(modifier = Modifier.fillMaxSize()) {
+ Column(
+ modifier = Modifier.weight(1f).verticalScroll(rememberScrollState()),
+ ) {
+ Text(
+ stringResource(Res.string.feature_recurring_deposit_charges_page),
+ style = MifosTypography.labelLargeEmphasized,
+ )
+ Spacer(Modifier.height(DesignToken.padding.large))
+ Row(
+ modifier = Modifier.fillMaxWidth(),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.End,
+ ) {
+ Row(
+ modifier = Modifier.clickable {
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.ShowAddChargeDialog)
+ },
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ imageVector = MifosIcons.Add,
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.primary,
+ modifier = Modifier.size(DesignToken.sizes.iconSmall),
+ )
+
+ Text(
+ text = stringResource(Res.string.recurring_step_charges_add_new),
+ color = MaterialTheme.colorScheme.primary,
+ style = MifosTypography.labelLargeEmphasized,
+ )
+ }
+ }
+
+ Spacer(Modifier.height(DesignToken.padding.large))
+
+ MifosRowWithTextAndButton(
+ onBtnClick = {
+ onAction(RecurringAccountAction.RecurringAccountChargesAction.ShowCharge)
+ },
+ btnText = stringResource(Res.string.recurring_step_charges_view),
+ text = state.addedCharges.size.toString() + " " + stringResource(Res.string.recurring_step_charges_active) + " " + stringResource(
+ Res.string.feature_recurring_deposit_step_charges),
+ btnEnabled = state.addedCharges.isNotEmpty(),
+ )
}
+ MifosTwoButtonRow(
+ firstBtnText = stringResource(Res.string.feature_recurring_deposit_back),
+ secondBtnText = stringResource(Res.string.recurring_step_charges_submit),
+ onFirstBtnClick = {
+ onAction(RecurringAccountAction.OnBackPress)
+ },
+ onSecondBtnClick = {
+ onAction(RecurringAccountAction.Finish)
+ },
+ modifier = Modifier.padding(top = DesignToken.padding.small),
+ )
+
+
}
}