Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
feat(savingsAccount): preview functionality
  • Loading branch information
sam-arth07 committed Oct 9, 2025
commit e243824f6cfa1e1ec99261df1f2a4044590a3d66
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* 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.ui.components

import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
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 androidx.compose.ui.unit.dp
import com.mifos.core.designsystem.theme.DesignToken
import com.mifos.core.designsystem.theme.MifosTypography
import org.jetbrains.compose.ui.tooling.preview.Preview

@Composable
fun MifosGeneralCardComponentOutline(
modifier: Modifier = Modifier,
borderCorner: Dp = DesignToken.sizes.iconMiny,
content: @Composable () -> Unit,
) {
Box(
modifier = modifier
.border(
width = 1.dp,
shape = RoundedCornerShape(
topStart = 12.dp,
topEnd = 12.dp,
bottomStart = borderCorner,
bottomEnd = borderCorner,
),
color = MaterialTheme.colorScheme.secondaryContainer,
),
) {
content()
}
}

@Composable
fun MifosGeneralRowItem(
keyContent: @Composable () -> Unit,
valueContent: @Composable () -> Unit,
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Row(
modifier = Modifier.fillMaxWidth(),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drop the nested Row it’s redundant.

verticalAlignment = Alignment.CenterVertically,
) {
Box(
modifier = Modifier.weight(1f),
contentAlignment = Alignment.CenterStart,
) { keyContent() }

Box(
modifier = Modifier.weight(1f),
contentAlignment = Alignment.CenterEnd,
) { valueContent() }
}
}
}

@Composable
fun MifosGeneralCard(
modifier: Modifier = Modifier,
contentMap: Map<String, String>,
) {
MifosGeneralCardComponentOutline {
Column(
modifier = modifier.padding(DesignToken.padding.large),
) {
contentMap.forEach { map ->
MifosGeneralRowItem(
keyContent = { Text(text = map.key, style = MifosTypography.labelMediumEmphasized) },
valueContent = { Text(text = map.value, style = MifosTypography.labelMediumEmphasized) },
)
Spacer(modifier = Modifier.height(DesignToken.padding.small))
}
}
}
}

@Preview
@Composable
fun MifosPreviewGeneralCard() {
MaterialTheme {
MifosGeneralCard(
contentMap = mapOf(
"title:" to "answer",
"title1:" to "ans1",
"title2:" to "ans2",
"title3:" to "ans3",
"title4:" to "ans4",
),
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,10 @@ private fun SavingsAccountScaffold(
)
},
Step(stringResource(Res.string.step_preview)) {
PreviewPage {
onAction(SavingsAccountAction.NextStep)
}
PreviewPage(
state = state,
onAction = onAction,
)
},
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,174 @@
package com.mifos.feature.savings.savingsAccountv2.pages

import androidclient.feature.savings.generated.resources.Res
import androidclient.feature.savings.generated.resources.feature_savings_back
import androidclient.feature.savings.generated.resources.feature_savings_currency
import androidclient.feature.savings.generated.resources.feature_savings_days_in_year
import androidclient.feature.savings.generated.resources.feature_savings_external_id
import androidclient.feature.savings.generated.resources.feature_savings_field_officer
import androidclient.feature.savings.generated.resources.feature_savings_interest_calc
import androidclient.feature.savings.generated.resources.feature_savings_interest_comp
import androidclient.feature.savings.generated.resources.feature_savings_interest_p_period
import androidclient.feature.savings.generated.resources.feature_savings_product_name
import androidclient.feature.savings.generated.resources.feature_savings_submission_date
import androidclient.feature.savings.generated.resources.feature_savings_submit
import androidclient.feature.savings.generated.resources.step_preview
import androidclient.feature.savings.generated.resources.step_charges
import androidclient.feature.savings.generated.resources.step_charges_active
import androidclient.feature.savings.generated.resources.step_charges_view
import androidclient.feature.savings.generated.resources.step_details
import androidclient.feature.savings.generated.resources.step_terms
import androidclient.feature.savings.generated.resources.step_terms_apply_withdrawal_fee
import androidclient.feature.savings.generated.resources.step_terms_decimal_places
import androidclient.feature.savings.generated.resources.step_terms_is_allowed_overdraft
import androidclient.feature.savings.generated.resources.step_terms_lock_in_period
import androidclient.feature.savings.generated.resources.step_terms_min_opening_balance
import androidclient.feature.savings.generated.resources.step_terms_minimum_balance
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.material3.Button
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
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.theme.DesignToken
import com.mifos.core.designsystem.theme.MifosTypography
import com.mifos.core.ui.components.MifosGeneralCard
import com.mifos.core.ui.components.MifosRowWithTextAndButton
import com.mifos.core.ui.components.MifosTwoButtonRow
import com.mifos.feature.savings.savingsAccountv2.SavingsAccountAction
import com.mifos.feature.savings.savingsAccountv2.SavingsAccountState
import org.jetbrains.compose.resources.stringResource

@Composable
fun PreviewPage(onNext: () -> Unit) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(stringResource(Res.string.step_preview))
Spacer(Modifier.height(8.dp))
Button(onClick = onNext) {
Text(stringResource(Res.string.feature_savings_submit))
fun PreviewPage(
state: SavingsAccountState,
onAction: (SavingsAccountAction) -> Unit,
modifier: Modifier = Modifier,
) {
val previewDetailsMap = mapOf(
stringResource(Res.string.feature_savings_product_name) + " : " to state.savingProductOptions[state.savingsProductSelected].name,
stringResource(Res.string.feature_savings_field_officer) + " : " to state.fieldOfficerOptions[state.fieldOfficerIndex].displayName,
stringResource(Res.string.feature_savings_submission_date) + " : " to state.submissionDate,
stringResource(Res.string.feature_savings_external_id) + " : " to state.externalId,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don’t hardcode : in strings put formatting in your UI component (or use string resources with placeholders), not in the keys. If MifosGeneralCard requires the colon, let it add it; if not possible today, at least keep labels clean and add the colon once when rendering.

)

val termsDetailsMap = mapOf(
stringResource(Res.string.feature_savings_currency) + " : " to (
state.savingsProductTemplate?.currencyOptions?.get(
state.currencyIndex,
)?.name ?: ""
),
stringResource(Res.string.step_terms_decimal_places) + " : " to state.decimalPlaces,
stringResource(Res.string.feature_savings_interest_comp) + " : " to (
state.savingsProductTemplate?.interestCompoundingPeriodTypeOptions?.get(
state.interestCompPeriodIndex,
)?.value
?: ""
),
stringResource(Res.string.feature_savings_interest_p_period) + " : " to (
state.savingsProductTemplate?.interestPostingPeriodTypeOptions?.get(
state.interestPostingPeriodIndex,
)?.value
?: ""
),
stringResource(Res.string.feature_savings_interest_calc) + " : " to (
state.savingsProductTemplate?.interestCalculationTypeOptions?.get(
state.interestCalcIndex,
)?.value
?: ""
),

stringResource(Res.string.feature_savings_days_in_year) + " : " to (
state.savingsProductTemplate?.interestCalculationDaysInYearTypeOptions?.get(
state.daysInYearIndex,
)?.value
?: ""
),
stringResource(Res.string.step_terms_apply_withdrawal_fee) + " : " to (state.isCheckedApplyWithdrawalFee.let { if (it) "Yes" else "No" }),
stringResource(Res.string.step_terms_is_allowed_overdraft) + " : " to (state.isCheckedOverdraftAllowed.let { if (it) "Yes" else "No" }),
stringResource(Res.string.step_terms_lock_in_period) + " : " to
if (state.freqTypeIndex == -1) {
""
} else {
(
state.frequency + (
state.savingsProductTemplate?.lockinPeriodFrequencyTypeOptions?.get(
state.freqTypeIndex,
)?.value ?: ""
)
)
},
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these types of giving names is long and confusing checking if else inside it come when cleaner qpproach see if sth like this is possible:

lockInType = state.savingsProductTemplate?.lockinPeriodFrequencyTypeOptions
    ?.getOrNull(state.freqTypeIndex)?.value.orEmpty()

stringResource(Res.string.step_terms_minimum_balance) + " : " to state.monthlyMinimumBalance,
stringResource(Res.string.step_terms_min_opening_balance) + " : " to state.minimumOpeningBalance,

)

Column(modifier = Modifier.fillMaxSize()) {
LazyColumn(modifier = modifier.weight(1f)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid using LazyColumn iniside Column.

item {
Text(
stringResource(Res.string.step_details),
style = MifosTypography.labelLargeEmphasized,
)
Spacer(Modifier.height(DesignToken.padding.large))
}

item {
MifosGeneralCard(
contentMap = previewDetailsMap,
)
Spacer(Modifier.height(DesignToken.padding.large))
}

item {
Text(
stringResource(Res.string.step_terms),
style = MifosTypography.labelLargeEmphasized,
)
Spacer(Modifier.height(DesignToken.padding.large))
}

item {
MifosGeneralCard(
contentMap = termsDetailsMap,
)
Spacer(Modifier.height(DesignToken.padding.large))
}

item {
Text(
stringResource(Res.string.step_charges),
style = MifosTypography.labelLargeEmphasized,
)
Spacer(Modifier.height(DesignToken.padding.large))
}

item {
MifosRowWithTextAndButton(
onBtnClick = {
onAction(SavingsAccountAction.ShowCharges)
},
btnText = stringResource(Res.string.step_charges_view),
text = state.addedCharges.size.toString() + " " + stringResource(Res.string.step_charges_active) + " " + stringResource(
Res.string.step_charges,
),
btnEnabled = state.addedCharges.isNotEmpty(),
)
}
}
MifosTwoButtonRow(
firstBtnText = stringResource(Res.string.feature_savings_back),
secondBtnText = stringResource(Res.string.feature_savings_submit),
onFirstBtnClick = {
onAction(SavingsAccountAction.PreviousStep)
},
onSecondBtnClick = {
onAction(SavingsAccountAction.Finish)
},
isSecondButtonEnabled = state.isTermsNextEnabled,
modifier = Modifier.padding(top = DesignToken.padding.small),
)
}
}