1010package com.mifos.feature.savings.savingsAccountv2
1111
1212import androidclient.feature.savings.generated.resources.Res
13+ import androidclient.feature.savings.generated.resources.feature_savings_back
14+ import androidclient.feature.savings.generated.resources.feature_savings_cancel
1315import androidclient.feature.savings.generated.resources.feature_savings_create_savings_account
1416import androidclient.feature.savings.generated.resources.feature_savings_error_not_connected_internet
1517import androidclient.feature.savings.generated.resources.step_charges
18+ import androidclient.feature.savings.generated.resources.step_charges_add
19+ import androidclient.feature.savings.generated.resources.step_charges_add_new
20+ import androidclient.feature.savings.generated.resources.step_charges_edit_charge
21+ import androidclient.feature.savings.generated.resources.step_charges_view
1622import androidclient.feature.savings.generated.resources.step_details
1723import androidclient.feature.savings.generated.resources.step_preview
1824import androidclient.feature.savings.generated.resources.step_terms
25+ import androidx.compose.foundation.layout.Arrangement
1926import androidx.compose.foundation.layout.Column
2027import androidx.compose.foundation.layout.fillMaxSize
2128import androidx.compose.foundation.layout.fillMaxWidth
2229import androidx.compose.foundation.layout.padding
30+ import androidx.compose.foundation.lazy.LazyColumn
31+ import androidx.compose.foundation.lazy.itemsIndexed
32+ import androidx.compose.material3.ExperimentalMaterial3Api
33+ import androidx.compose.material3.Text
2334import androidx.compose.runtime.Composable
35+ import androidx.compose.runtime.LaunchedEffect
2436import androidx.compose.runtime.getValue
37+ import androidx.compose.ui.Alignment
2538import androidx.compose.ui.Modifier
2639import androidx.lifecycle.compose.collectAsStateWithLifecycle
2740import androidx.navigation.NavController
41+ import com.mifos.core.common.utils.DateHelper
42+ import com.mifos.core.designsystem.component.MifosBottomSheet
2843import com.mifos.core.designsystem.component.MifosScaffold
2944import com.mifos.core.designsystem.component.MifosSweetError
45+ import com.mifos.core.designsystem.theme.DesignToken
46+ import com.mifos.core.designsystem.theme.MifosTypography
47+ import com.mifos.core.ui.components.Actions
48+ import com.mifos.core.ui.components.AddChargeBottomSheet
49+ import com.mifos.core.ui.components.MifosActionsChargeListingComponent
3050import com.mifos.core.ui.components.MifosBreadcrumbNavBar
3151import com.mifos.core.ui.components.MifosProgressIndicator
3252import com.mifos.core.ui.components.MifosProgressIndicatorOverlay
3353import com.mifos.core.ui.components.MifosStepper
54+ import com.mifos.core.ui.components.MifosTwoButtonRow
3455import com.mifos.core.ui.components.Step
3556import com.mifos.core.ui.util.EventsEffect
57+ import com.mifos.core.ui.util.TextFieldsValidator.doubleNumberValidator
3658import com.mifos.feature.savings.savingsAccountv2.pages.ChargesPage
3759import com.mifos.feature.savings.savingsAccountv2.pages.DetailsPage
3860import com.mifos.feature.savings.savingsAccountv2.pages.PreviewPage
3961import com.mifos.feature.savings.savingsAccountv2.pages.TermsPage
4062import org.jetbrains.compose.resources.stringResource
4163import org.koin.compose.viewmodel.koinViewModel
64+ import kotlin.time.Clock
65+ import kotlin.time.ExperimentalTime
4266
4367@Composable
4468internal fun SavingsAccountScreen (
@@ -63,7 +87,7 @@ internal fun SavingsAccountScreen(
6387 )
6488
6589 SavingsAccountScaffold (
66- modifier = modifier,
90+ modifier = modifier.fillMaxWidth() ,
6791 state = state,
6892 onAction = { viewModel.trySendAction(it) },
6993 navController = navController,
@@ -91,9 +115,10 @@ private fun SavingsAccountScaffold(
91115 )
92116 },
93117 Step (stringResource(Res .string.step_charges)) {
94- ChargesPage {
95- onAction(SavingsAccountAction .NextStep )
96- }
118+ ChargesPage (
119+ state = state,
120+ onAction = onAction,
121+ )
97122 },
98123 Step (stringResource(Res .string.step_preview)) {
99124 PreviewPage {
@@ -123,10 +148,11 @@ private fun SavingsAccountScaffold(
123148 onAction(SavingsAccountAction .OnStepChange (newIndex))
124149 },
125150 modifier = Modifier
126- .fillMaxWidth(),
151+ .fillMaxWidth().align( Alignment . CenterHorizontally ) ,
127152 )
128153 }
129154 }
155+
130156 is SavingsAccountState .ScreenState .NetworkError -> {
131157 MifosSweetError (
132158 message = stringResource(Res .string.feature_savings_error_not_connected_internet),
@@ -152,6 +178,156 @@ private fun NewSavingsAccountDialog(
152178 onclick = { onAction(SavingsAccountAction .Retry ) },
153179 )
154180 }
181+
182+ is SavingsAccountState .DialogState .AddNewCharge -> AddNewChargeDialog (
183+ isEdit = state.dialogState.edit,
184+ state = state,
185+ onAction = onAction,
186+ index = state.dialogState.index,
187+ )
188+
189+ is SavingsAccountState .DialogState .ShowCharges -> ShowChargesDialog (
190+ state = state,
191+ onAction = onAction,
192+ )
193+
155194 null -> Unit
156195 }
157196}
197+
198+ @OptIn(ExperimentalTime ::class , ExperimentalMaterial3Api ::class )
199+ @Composable
200+ private fun AddNewChargeDialog (
201+ isEdit : Boolean ,
202+ index : Int = -1,
203+ state : SavingsAccountState ,
204+ onAction : (SavingsAccountAction ) -> Unit ,
205+ ) {
206+ LaunchedEffect (state.chargeAmount) {
207+ if (state.chargeAmount.isNotBlank()) {
208+ val amountError = doubleNumberValidator(state.chargeAmount)
209+ onAction(SavingsAccountAction .OnChargesAmountChangeError (amountError))
210+ } else {
211+ onAction(SavingsAccountAction .OnChargesAmountChangeError (null ))
212+ }
213+ }
214+ fun isSelectableDate (utcTimeMillis : Long ): Boolean {
215+ return utcTimeMillis >= Clock .System .now().toEpochMilliseconds().minus(86_400_000L )
216+ }
217+ AddChargeBottomSheet (
218+ title = if (isEdit) {
219+ stringResource(Res .string.step_charges_edit_charge)
220+ } else {
221+ stringResource(Res .string.step_charges_add_new) + " " + stringResource(Res .string.step_charges)
222+ },
223+ confirmText = if (isEdit) {
224+ stringResource(Res .string.step_charges_edit_charge)
225+ } else {
226+ stringResource(Res .string.step_charges_add)
227+ },
228+ dismissText = stringResource(Res .string.feature_savings_cancel),
229+ showDatePicker = state.showChargesDatePick,
230+ selectedChargeName = if (state.chooseChargeIndex == - 1 ) {
231+ " "
232+ } else {
233+ state.savingsProductTemplate?.chargeOptions?.getOrNull(state.chooseChargeIndex)?.name ? : " "
234+ },
235+ selectedDate = state.chargeDate,
236+ chargeAmount = state.chargeAmount,
237+ chargeType = if (state.chooseChargeIndex == - 1 ) {
238+ " "
239+ } else {
240+ state.savingsProductTemplate?.chargeOptions?.get(state.chooseChargeIndex)?.chargeCalculationType?.value
241+ ? : " "
242+ },
243+ chargeCollectedOn = if (state.chooseChargeIndex == - 1 ) {
244+ " "
245+ } else {
246+ state.savingsProductTemplate?.chargeOptions?.getOrNull(state.chooseChargeIndex)?.chargeTimeType?.value ? : " "
247+ },
248+ chargeOptions = state.savingsProductTemplate?.chargeOptions?.map { it.name ? : " " } ? : emptyList(),
249+ onConfirm = {
250+ if (isEdit) {
251+ onAction(SavingsAccountAction .EditCharge (index))
252+ } else {
253+ onAction(SavingsAccountAction .AddChargeToList )
254+ }
255+ },
256+ onDismiss = { onAction(SavingsAccountAction .DismissDialog ) },
257+ onChargeSelected = { index, _ ->
258+ onAction(SavingsAccountAction .OnChooseChargeIndexChange (index))
259+ },
260+ onDatePick = { show ->
261+ onAction(SavingsAccountAction .OnChargesDatePick (show))
262+ },
263+ onDateChange = { newDate ->
264+ if (isSelectableDate(newDate)) {
265+ onAction(SavingsAccountAction .OnChargesDateChange (DateHelper .getDateAsStringFromLong(newDate)))
266+ }
267+ },
268+ amountError = if (state.chargeAmountError != null ) stringResource(state.chargeAmountError) else null ,
269+ onAmountChange = { amount ->
270+ onAction(SavingsAccountAction .OnChargesAmountChange (amount))
271+ },
272+ )
273+ }
274+
275+ @Composable
276+ private fun ShowChargesDialog (
277+ state : SavingsAccountState ,
278+ onAction : (SavingsAccountAction ) -> Unit ,
279+ ) {
280+ MifosBottomSheet (
281+ onDismiss = {
282+ onAction(SavingsAccountAction .DismissDialog )
283+ },
284+ content = {
285+ LazyColumn (
286+ modifier = Modifier .fillMaxWidth().padding(DesignToken .padding.large),
287+ verticalArrangement = Arrangement .spacedBy(DesignToken .padding.largeIncreased),
288+ ) {
289+ item {
290+ Text (
291+ text = stringResource(Res .string.step_charges_view) + " " + stringResource(Res .string.step_charges),
292+ style = MifosTypography .titleMediumEmphasized,
293+ )
294+ }
295+ itemsIndexed(items = state.addedCharges) { index, it ->
296+ MifosActionsChargeListingComponent (
297+ chargeTitle = it.name.toString(),
298+ type = it.type.toString(),
299+ date = it.date,
300+ collectedOn = it.collectedOn,
301+ amount = it.amount.toString(),
302+ onActionClicked = { action ->
303+ when (action) {
304+ is Actions .Delete -> {
305+ onAction(SavingsAccountAction .DeleteChargeFromSelectedCharges (index))
306+ }
307+
308+ is Actions .Edit -> {
309+ onAction(SavingsAccountAction .EditChargeDialog (index))
310+ }
311+
312+ else -> {}
313+ }
314+ },
315+ isExpandable = true ,
316+ )
317+ }
318+ item {
319+ MifosTwoButtonRow (
320+ firstBtnText = stringResource(Res .string.feature_savings_back),
321+ secondBtnText = stringResource(Res .string.step_charges_add_new),
322+ onFirstBtnClick = {
323+ onAction(SavingsAccountAction .DismissDialog )
324+ },
325+ onSecondBtnClick = {
326+ onAction(SavingsAccountAction .ShowAddChargeDialog )
327+ },
328+ )
329+ }
330+ }
331+ },
332+ )
333+ }
0 commit comments