Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f73627d
fixing MIFOSAC-635
gurnoorpannu Jan 29, 2026
c9b7b09
implemented requested changes
gurnoorpannu Jan 31, 2026
b62727f
implemented requested changes
gurnoorpannu Jan 31, 2026
8d3db77
implemented requested changes
gurnoorpannu Feb 3, 2026
bda7b6b
Merge branch 'development' into refactor/improve-loan-account-summary…
gurnoorpannu Feb 3, 2026
e79f92d
Made changes requested
gurnoorpannu Feb 5, 2026
fabb40e
spotless fix
gurnoorpannu Feb 5, 2026
816a708
coderabit fix
gurnoorpannu Feb 5, 2026
ac65ff0
coderabit fix
gurnoorpannu Feb 5, 2026
30f7e41
spotless fix
gurnoorpannu Feb 5, 2026
47fb216
making changes requested.
gurnoorpannu Feb 5, 2026
f136a4e
Changes requested
gurnoorpannu Feb 5, 2026
89abb83
spotless error fix
gurnoorpannu Feb 5, 2026
8430d46
Merge branch 'development' into refactor/improve-loan-account-summary…
gurnoorpannu Feb 6, 2026
90ccd7a
Fixing changes requested
gurnoorpannu Feb 6, 2026
2d9bffc
Merge remote-tracking branch 'origin/refactor/improve-loan-account-su…
gurnoorpannu Feb 6, 2026
4eaade4
implemented coderabbit suggestion
gurnoorpannu Feb 7, 2026
950b268
Fix structure
itsPronay Feb 7, 2026
f842068
implemented all todos
gurnoorpannu Feb 7, 2026
0d1b745
implemented coderabbit suggestions
gurnoorpannu Feb 7, 2026
afe3ec8
implemented coderabbit suggestions
gurnoorpannu Feb 7, 2026
926685c
implemented coderabbit suggestions
gurnoorpannu Feb 7, 2026
4531979
Merge branch 'development' into refactor/improve-loan-account-summary…
gurnoorpannu Feb 10, 2026
3d09f66
implemented enum class LoanSummaryDropDownAction
gurnoorpannu Feb 10, 2026
295110c
Merge remote-tracking branch 'origin/refactor/improve-loan-account-su…
gurnoorpannu Feb 10, 2026
4cdfd88
fix spotless issue
gurnoorpannu Feb 10, 2026
b1ecfae
Extracted handleDropdownAction function that sends events directly
gurnoorpannu Feb 11, 2026
7cb1732
Merge branch 'development' into refactor/improve-loan-account-summary…
gurnoorpannu Feb 11, 2026
941af70
Changed mutableStateFlow.value.loanWithAssociations to state.loanWith…
gurnoorpannu Feb 12, 2026
a3e5ce2
Merge remote-tracking branch 'origin/refactor/improve-loan-account-su…
gurnoorpannu Feb 12, 2026
eddb90f
Merge branch 'development' into refactor/improve-loan-account-summary…
gurnoorpannu Feb 13, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
Expand All @@ -100,6 +102,7 @@ import com.mifos.core.designsystem.theme.MifosTheme
import com.mifos.core.designsystem.theme.MifosTypography
import com.mifos.core.ui.components.MifosBreadcrumbNavBar
import com.mifos.core.ui.components.MifosProgressIndicator
import com.mifos.core.ui.util.EventsEffect
import com.mifos.room.entities.accounts.loans.LoanStatusEntity
import com.mifos.room.entities.accounts.loans.LoanWithAssociationsEntity
import com.mifos.room.entities.accounts.loans.LoansAccountSummaryEntity
Expand All @@ -112,7 +115,7 @@ import org.koin.compose.viewmodel.koinViewModel

@Composable
internal fun LoanAccountSummaryScreen(
navigateBack: () -> Unit,
onNavigateBack: () -> Unit,
onMoreInfoClicked: (String, loanId: Int) -> Unit,
onTransactionsClicked: (loadId: Int) -> Unit,
onRepaymentScheduleClicked: (loanId: Int) -> Unit,
Expand All @@ -124,65 +127,75 @@ internal fun LoanAccountSummaryScreen(
navController: NavController,
viewModel: LoanAccountSummaryViewModel = koinViewModel(),
) {
val uiState by viewModel.loanAccountSummaryUiState.collectAsStateWithLifecycle()
val loanAccountNumber = viewModel.loanAccountNumber
val state by viewModel.stateFlow.collectAsStateWithLifecycle()
val snackbarHostState = remember { SnackbarHostState() }
val loanIdCopiedMessage = stringResource(Res.string.feature_loan_loan_id_copied)

EventsEffect(viewModel.eventFlow) { event ->
when (event) {
LoanAccountSummaryEvent.NavigateBack -> onNavigateBack()
is LoanAccountSummaryEvent.NavigateToMoreInfo -> {
onMoreInfoClicked(Constants.DATA_TABLE_NAME_LOANS, event.loanId)
}
is LoanAccountSummaryEvent.NavigateToTransactions -> {
onTransactionsClicked(event.loanId)
}
is LoanAccountSummaryEvent.NavigateToRepaymentSchedule -> {
onRepaymentScheduleClicked(event.loanId)
}
is LoanAccountSummaryEvent.NavigateToDocuments -> {
onDocumentsClicked(event.loanId)
}
is LoanAccountSummaryEvent.NavigateToCharges -> {
onChargesClicked(event.loanId)
}
is LoanAccountSummaryEvent.NavigateToApproveLoan -> {
approveLoan(event.loanId, event.loanWithAssociations)
}
is LoanAccountSummaryEvent.NavigateToDisburseLoan -> {
disburseLoan(event.loanId)
}
is LoanAccountSummaryEvent.NavigateToMakeRepayment -> {
onRepaymentClick(event.loanWithAssociations)
}
}
}

LaunchedEffect(key1 = Unit) {
viewModel.loadLoanById()
LaunchedEffect(state.showLoanIdCopiedMessage) {
if (state.showLoanIdCopiedMessage) {
snackbarHostState.showSnackbar(message = loanIdCopiedMessage)
viewModel.trySendAction(LoanAccountSummaryAction.OnMessageShown)
}
}

LoanAccountSummaryScreen(
uiState = uiState,
navigateBack = navigateBack,
onRetry = { viewModel.loadLoanById() },
onMoreInfoClicked = {
onMoreInfoClicked.invoke(
Constants.DATA_TABLE_NAME_LOANS,
loanAccountNumber,
)
},
onTransactionsClicked = { onTransactionsClicked.invoke(loanAccountNumber) },
onRepaymentScheduleClicked = { onRepaymentScheduleClicked.invoke(loanAccountNumber) },
onDocumentsClicked = { onDocumentsClicked(loanAccountNumber) },
onChargesClicked = { onChargesClicked(loanAccountNumber) },
approveLoan = { approveLoan(loanAccountNumber, it) },
disburseLoan = { disburseLoan(loanAccountNumber) },
makeRepayment = onRepaymentClick,
state = state,
onAction = viewModel::trySendAction,
navController = navController,
snackbarHostState = snackbarHostState,
)
}

@Composable
internal fun LoanAccountSummaryScreen(
uiState: LoanAccountSummaryUiState,
navigateBack: () -> Unit,
onRetry: () -> Unit,
onMoreInfoClicked: () -> Unit,
onTransactionsClicked: () -> Unit,
onRepaymentScheduleClicked: () -> Unit,
onDocumentsClicked: () -> Unit,
onChargesClicked: () -> Unit,
approveLoan: (loanWithAssociations: LoanWithAssociationsEntity) -> Unit,
disburseLoan: () -> Unit,
makeRepayment: (loanWithAssociations: LoanWithAssociationsEntity) -> Unit,
state: LoanAccountSummaryState,
onAction: (LoanAccountSummaryAction) -> Unit,
navController: NavController,
snackbarHostState: SnackbarHostState,
) {
val snackbarHostState = remember {
SnackbarHostState()
}
var openDropdown by rememberSaveable {
mutableStateOf(false)
}

MifosScaffold(
title = stringResource(Res.string.feature_loan_loan_account_summary),
onBackPressed = navigateBack,
onBackPressed = { onAction(LoanAccountSummaryAction.NavigateBack) },
snackbarHostState = snackbarHostState,
actions = {
IconButton(onClick = { openDropdown = !openDropdown }) {
Icon(
imageVector = MifosIcons.MoreVert,
contentDescription = null,
contentDescription = "More options",
)
}
if (openDropdown) {
Expand All @@ -194,35 +207,35 @@ internal fun LoanAccountSummaryScreen(
option = Constants.DATA_TABLE_LOAN_NAME,
onClick = {
openDropdown = false
onMoreInfoClicked.invoke()
onAction(LoanAccountSummaryAction.OnMoreInfoClick)
},
)
MifosMenuDropDownItem(
option = stringResource(Res.string.feature_loan_transactions),
onClick = {
openDropdown = false
onTransactionsClicked.invoke()
onAction(LoanAccountSummaryAction.OnTransactionsClick)
},
)
MifosMenuDropDownItem(
option = stringResource(Res.string.feature_loan_repayment_schedule),
onClick = {
openDropdown = false
onRepaymentScheduleClicked.invoke()
onAction(LoanAccountSummaryAction.OnRepaymentScheduleClick)
},
)
MifosMenuDropDownItem(
option = stringResource(Res.string.feature_loan_documents),
onClick = {
openDropdown = false
onDocumentsClicked.invoke()
onAction(LoanAccountSummaryAction.OnDocumentsClick)
},
)
MifosMenuDropDownItem(
option = stringResource(Res.string.feature_loan_loan_charges),
onClick = {
openDropdown = false
onChargesClicked.invoke()
onAction(LoanAccountSummaryAction.OnChargesClick)
},
)
}
Expand All @@ -232,35 +245,35 @@ internal fun LoanAccountSummaryScreen(
Column(
modifier = Modifier
.fillMaxSize()
.padding(it)
.background(MaterialTheme.colorScheme.background),
) {
MifosBreadcrumbNavBar(navController)
Box(
modifier = Modifier
.fillMaxSize()
.padding(it),
.fillMaxWidth()
.weight(1f),
) {
when (uiState) {
is LoanAccountSummaryUiState.ShowFetchingError -> {
when (state.dialogState) {
is LoanAccountSummaryState.DialogState.Error -> {
MifosSweetError(
message = uiState.message,
onclick = onRetry,
message = state.dialogState.message,
onclick = { onAction(LoanAccountSummaryAction.OnRetry) },
)
}

is LoanAccountSummaryUiState.ShowLoanById -> {
val loanWithAssociations = uiState.loanWithAssociations
LoanAccountSummaryContent(
loanWithAssociations = loanWithAssociations,
makeRepayment = { makeRepayment.invoke(loanWithAssociations) },
approveLoan = { approveLoan.invoke(loanWithAssociations) },
disburseLoan = disburseLoan,
snackbarHostState = snackbarHostState,
)
LoanAccountSummaryState.DialogState.Loading -> {
MifosProgressIndicator()
}

LoanAccountSummaryUiState.ShowProgressbar -> {
MifosProgressIndicator()
null -> {
state.loanWithAssociations?.let { loanWithAssociations ->
LoanAccountSummaryContent(
loanWithAssociations = loanWithAssociations,
onAction = onAction,
snackbarHostState = snackbarHostState,
)
}
}
}
}
Expand All @@ -271,9 +284,7 @@ internal fun LoanAccountSummaryScreen(
@Composable
private fun LoanAccountSummaryContent(
loanWithAssociations: LoanWithAssociationsEntity,
makeRepayment: () -> Unit,
approveLoan: () -> Unit,
disburseLoan: () -> Unit,
onAction: (LoanAccountSummaryAction) -> Unit,
snackbarHostState: SnackbarHostState,
) {
val inflateLoanSummary = getInflateLoanSummaryValue(status = loanWithAssociations.status)
Expand All @@ -282,7 +293,6 @@ private fun LoanAccountSummaryContent(
val scope = rememberCoroutineScope()
val clipboardManager = LocalClipboardManager.current
val message = stringResource(Res.string.feature_loan_loan_rejected_message)
val loanIdCopiedMessage = stringResource(Res.string.feature_loan_loan_id_copied)

fun formatCurrency(amount: Double?): String {
if (amount == null) return ""
Expand Down Expand Up @@ -337,10 +347,18 @@ private fun LoanAccountSummaryContent(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
val statusDescription = when {
loanWithAssociations.status.active == true -> "Active"
loanWithAssociations.status.pendingApproval == true -> "Pending Approval"
loanWithAssociations.status.waitingForDisbursal == true -> "Waiting for Disbursal"
else -> "Closed"
}
Canvas(
modifier = Modifier
.size(DesignToken.sizes.iconMedium),
contentDescription = "",
.size(DesignToken.sizes.iconMedium)
.semantics {
contentDescription = "Loan status: $statusDescription"
},
onDraw = {
drawCircle(
color = when {
Expand Down Expand Up @@ -384,11 +402,7 @@ private fun LoanAccountSummaryContent(
IconButton(
onClick = {
clipboardManager.setText(AnnotatedString(loanWithAssociations.accountNo))
scope.launch {
snackbarHostState.showSnackbar(
message = loanIdCopiedMessage,
)
}
onAction(LoanAccountSummaryAction.OnLoanIdCopied)
},
modifier = Modifier.size(DesignToken.sizes.iconSmall),
) {
Expand Down Expand Up @@ -514,15 +528,15 @@ private fun LoanAccountSummaryContent(
shape = DesignToken.shapes.small,
onClick = when {
loanWithAssociations.status.active == true -> {
{ makeRepayment.invoke() }
{ onAction(LoanAccountSummaryAction.OnMakeRepayment(loanWithAssociations)) }
}

loanWithAssociations.status.pendingApproval == true -> {
{ approveLoan.invoke() }
{ onAction(LoanAccountSummaryAction.OnApproveLoan(loanWithAssociations)) }
}

loanWithAssociations.status.waitingForDisbursal == true -> {
{ disburseLoan.invoke() }
{ onAction(LoanAccountSummaryAction.OnDisburseLoan) }
}

loanWithAssociations.status.closedObligationsMet == true -> {
Expand Down Expand Up @@ -762,7 +776,6 @@ private fun getButtonActiveStatus(status: LoanStatusEntity): Boolean {
}
}

@Composable
private fun getInflateLoanSummaryValue(status: LoanStatusEntity): Boolean {
return when {
status.active == true || status.closedObligationsMet == true -> {
Expand All @@ -780,13 +793,15 @@ private fun getInflateLoanSummaryValue(status: LoanStatusEntity): Boolean {
}

private class LoanAccountSummaryPreviewProvider :
PreviewParameterProvider<LoanAccountSummaryUiState> {
PreviewParameterProvider<LoanAccountSummaryState> {
private val demoSummary = LoansAccountSummaryEntity(
loanId = 12345,
principalDisbursed = 10000.0,
principalPaid = 4000.0,
principalWrittenOff = 0.0,
principalOutstanding = 6000.0,
principalOverdue = 500.0,
interestCharged = 500.0,
interestCharged = 700.0,
interestPaid = 300.0,
interestWaived = 0.0,
interestWrittenOff = 0.0,
Expand Down Expand Up @@ -814,12 +829,16 @@ private class LoanAccountSummaryPreviewProvider :
overdueSinceDate = listOf(2024, 6, 1),
)

override val values: Sequence<LoanAccountSummaryUiState>
override val values: Sequence<LoanAccountSummaryState>
get() = sequenceOf(
LoanAccountSummaryUiState.ShowProgressbar,
LoanAccountSummaryUiState.ShowFetchingError("Could not fetch summary"),
LoanAccountSummaryUiState.ShowLoanById(
LoanWithAssociationsEntity(
LoanAccountSummaryState(
dialogState = LoanAccountSummaryState.DialogState.Loading,
),
LoanAccountSummaryState(
dialogState = LoanAccountSummaryState.DialogState.Error("Could not fetch summary"),
),
LoanAccountSummaryState(
loanWithAssociations = LoanWithAssociationsEntity(
accountNo = "90927493938",
status = LoanStatusEntity(
closedObligationsMet = true,
Expand All @@ -829,29 +848,22 @@ private class LoanAccountSummaryPreviewProvider :
loanProductName = "Group Loan",
summary = demoSummary,
),
dialogState = null,
),
)
}

@Composable
@Preview
private fun PreviewLoanAccountSummary(
@PreviewParameter(LoanAccountSummaryPreviewProvider::class) loanAccountSummaryUiState: LoanAccountSummaryUiState,
@PreviewParameter(LoanAccountSummaryPreviewProvider::class) state: LoanAccountSummaryState,
) {
MifosTheme {
LoanAccountSummaryScreen(
uiState = loanAccountSummaryUiState,
navigateBack = { },
onRetry = { },
onMoreInfoClicked = { },
onTransactionsClicked = { },
onRepaymentScheduleClicked = { },
onDocumentsClicked = { },
onChargesClicked = { },
approveLoan = { },
disburseLoan = { },
makeRepayment = { },
state = state,
onAction = { },
navController = rememberNavController(),
snackbarHostState = remember { SnackbarHostState() },
)
}
}
Loading