Skip to content

Commit e7d68c5

Browse files
refactor(loan): centralize loan status mapping across screens (#2697)
1 parent c997b69 commit e7d68c5

8 files changed

Lines changed: 160 additions & 110 deletions

File tree

core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/theme/Color.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,13 @@ object AppColors {
256256

257257
val loanActiveStatus = Color(0xFF5CB85C)
258258
val loanPendingStatus = Color(0xFFFFA500)
259-
val loanOverpaidStatus = Color(0xFF800080)
259+
val loanApprovedStatus = Color(0xFFFFA500)
260+
val loanClosedOverpaidStatus = Color(0xFF800080)
261+
val loanClosedObligationsMetStatus = Color(0xFF5CB85C)
262+
val loanClosedRescheduled = Color(0xFF000AAD)
263+
val loanClosedWrittenOffStatus = Color(0xFF990000)
264+
val loanRejectedStatus = Color(0xFF990000)
265+
val loanWithdrawnByApplicantStatus = Color(0xFF990000)
266+
260267
val loanUnknownStatus = Color(0xFF9E9E9E)
261268
}

core/ui/src/commonMain/composeResources/drawable/send_money.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,6 @@
1616
android:tint="?attr/colorControlNormal"
1717
android:autoMirrored="true">
1818
<path
19-
android:fillColor="@android:color/white"
19+
android:fillColor="#FFFFFF"
2020
android:pathData="M240,790Q136,763 68,678Q0,593 0,480Q0,367 68,282Q136,197 240,170L240,254Q169,278 124.5,340Q80,402 80,480Q80,558 124.5,620Q169,682 240,706L240,790ZM560,800Q427,800 333.5,706.5Q240,613 240,480Q240,347 333.5,253.5Q427,160 560,160Q626,160 684,185Q742,210 786,254L730,310Q697,277 653.5,258.5Q610,240 560,240Q460,240 390,310Q320,380 320,480Q320,580 390,650Q460,720 560,720Q610,720 653.5,701.5Q697,683 730,650L786,706Q742,750 684,775Q626,800 560,800ZM800,640L744,584L808,520L520,520L520,440L808,440L744,376L800,320L960,480L800,640Z"/>
2121
</vector>

feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsScreen.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ import com.mifos.core.ui.components.MifosEmptyCard
7575
import com.mifos.core.ui.components.MifosProgressIndicator
7676
import com.mifos.core.ui.components.MifosSearchBar
7777
import com.mifos.core.ui.util.EventsEffect
78+
import com.mifos.feature.loan.utils.getLoanStatus
7879
import com.mifos.room.entities.accounts.loans.LoanAccountEntity
7980
import org.jetbrains.compose.resources.painterResource
8081
import org.jetbrains.compose.resources.stringResource
@@ -252,7 +253,7 @@ private fun LoanAccountItem(
252253

253254
type = loan.loanType?.value ?: notAvailable,
254255

255-
status = loan.status?.value ?: notAvailable,
256+
status = loan.status?.let { stringResource(it.getLoanStatus().label) } ?: notAvailable,
256257

257258
menuList = buildLoanActions(loan),
258259

feature/loan/src/commonMain/composeResources/values/strings.xml

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,10 +165,6 @@
165165
<string name="feature_loan_profile_item_notes_subtitle">Additional internal comments</string>
166166
<string name="feature_loan_profile_item_standing_instructions_title">Standing Instructions</string>
167167
<string name="feature_loan_profile_item_standing_instructions_subtitle">Automated payment settings</string>
168-
<string name="feature_loan_profile_status_active">Active</string>
169-
<string name="feature_loan_profile_status_pending">Pending</string>
170-
<string name="feature_loan_profile_status_overpaid">Overpaid</string>
171-
<string name="feature_loan_profile_status_unknown">Unknown</string>
172168
<string name="feature_loan_profile_action_approve">Approve Account</string>
173169
<string name="feature_loan_profile_action_repayment">Make Repayment</string>
174170
<string name="feature_loan_profile_action_transfer">Transfer Funds</string>
@@ -475,4 +471,16 @@
475471
<string name="feature_loan_reschedule_fetch_failed">Failed to load reschedule history.</string>
476472
<string name="feature_loan_reschedule_approve_failed">Failed to approve reschedule.</string>
477473
<string name="feature_loan_reschedule_delete_failed">Failed to delete reschedule.</string>
474+
475+
476+
<string name="feature_loan_pending_approval">Pending Approval</string>
477+
<string name="feature_loan_approved">Approved</string>
478+
<string name="feature_loan_withdrawn_by_applicant">Withdrawn by Applicant</string>
479+
<string name="feature_loan_active">Active</string>
480+
<string name="feature_loan_closed_overpaid">Closed (Overpaid)</string>
481+
<string name="feature_loan_closed_obligations_met">Closed (Obligations Met)</string>
482+
<string name="feature_loan_closed_written_off">Closed (Written Off)</string>
483+
<string name="feature_loan_closed_rescheduled">Closed (Rescheduled)</string>
484+
<string name="feature_loan_rejected">Rejected</string>
485+
<string name="feature_loan_unknown">Unknown</string>">
478486
</resources>

feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileScreen.kt

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ import androidclient.feature.loan.generated.resources.feature_loan_profile_label
1919
import androidclient.feature.loan.generated.resources.feature_loan_profile_label_overpaid_by
2020
import androidclient.feature.loan.generated.resources.feature_loan_profile_section_account_overview
2121
import androidclient.feature.loan.generated.resources.feature_loan_profile_section_actions_details
22-
import androidclient.feature.loan.generated.resources.feature_loan_profile_status_active
2322
import androidx.compose.foundation.background
2423
import androidx.compose.foundation.clickable
2524
import androidx.compose.foundation.layout.Arrangement
@@ -63,6 +62,7 @@ import com.mifos.core.ui.util.EventsEffect
6362
import com.mifos.core.ui.util.TextUtil
6463
import com.mifos.feature.loan.loanAccountProfile.components.LoanAccountProfileActionItem
6564
import com.mifos.feature.loan.loanAccountProfile.components.loanProfileActionItems
65+
import com.mifos.feature.loan.utils.getLoanStatus
6666
import com.mifos.room.entities.accounts.loans.LoanStatusEntity
6767
import com.mifos.room.entities.accounts.loans.LoanWithAssociationsEntity
6868
import com.mifos.room.entities.accounts.loans.LoansAccountSummaryEntity
@@ -109,19 +109,24 @@ internal fun LoanAccountProfileScreen(
109109
}
110110
}
111111
}
112+
112113
is LoanAccountEvent.NavigateToDetail -> {
113114
val loanId = state.loanAccount?.id ?: -1
114115

115116
when (event.detailItem) {
116-
LoanAccountProfileActionItem.RepaymentSchedule -> navigateToRepaymentSchedule(loanId)
117+
LoanAccountProfileActionItem.RepaymentSchedule -> navigateToRepaymentSchedule(
118+
loanId,
119+
)
120+
117121
LoanAccountProfileActionItem.Transactions -> navigateToTransactions(loanId)
118122
LoanAccountProfileActionItem.Charges -> navigateToCharges(loanId)
119123
LoanAccountProfileActionItem.Documents -> navigateToDocuments(loanId)
120124
LoanAccountProfileActionItem.Reschedules -> navigateToReschedules(loanId)
121125
LoanAccountProfileActionItem.Notes -> navigateToNotes(loanId)
122-
else -> { }
126+
else -> {}
123127
}
124128
}
129+
125130
LoanAccountEvent.NavigateToAccountDetails -> {}
126131
}
127132
}
@@ -170,7 +175,6 @@ private fun LoanAccountContent(
170175

171176
LoanAccountTopCard(
172177
loanAccount = loanAccount,
173-
statusUi = state.statusUiModel,
174178
onClick = { onAction(LoanAccountAction.OnAccountClick) },
175179
)
176180

@@ -221,15 +225,16 @@ private fun LoanAccountContent(
221225
@Composable
222226
private fun LoanAccountTopCard(
223227
loanAccount: LoanWithAssociationsEntity,
224-
statusUi: LoanStatusUiModel?,
225228
onClick: () -> Unit,
226229
modifier: Modifier = Modifier,
227230
) {
228231
val currencyCode = loanAccount.currency?.code
229232
val decimalPlaces = loanAccount.currency?.decimalPlaces
230233

231-
val balance = CurrencyFormatter.format(loanAccount.summary.totalOutstanding, currencyCode, decimalPlaces)
232-
val arrears = CurrencyFormatter.format(loanAccount.summary.totalOverdue, currencyCode, decimalPlaces)
234+
val balance =
235+
CurrencyFormatter.format(loanAccount.summary.totalOutstanding, currencyCode, decimalPlaces)
236+
val arrears =
237+
CurrencyFormatter.format(loanAccount.summary.totalOverdue, currencyCode, decimalPlaces)
233238
val overpaid = CurrencyFormatter.format(loanAccount.totalOverpaid, currencyCode, decimalPlaces)
234239

235240
MifosCard(
@@ -254,15 +259,18 @@ private fun LoanAccountTopCard(
254259
) {
255260
Column(modifier = Modifier.weight(1f)) {
256261
Text(
257-
text = "${loanAccount.loanProductName?.uppercase().orEmpty()} ${loanAccount.accountNo}".trim(),
262+
text = "${
263+
loanAccount.loanProductName?.uppercase().orEmpty()
264+
} ${loanAccount.accountNo}".trim(),
258265
style = MifosTypography.titleMediumEmphasized,
259266
color = KptTheme.colorScheme.onPrimary,
260267
)
261268

262269
Spacer(Modifier.height(KptTheme.spacing.xs))
263270

264271
Text(
265-
text = loanAccount.clientName ?: stringResource(Res.string.feature_loan_profile_label_client_name_placeholder),
272+
text = loanAccount.clientName
273+
?: stringResource(Res.string.feature_loan_profile_label_client_name_placeholder),
266274
style = MifosTypography.bodyMedium,
267275
color = KptTheme.colorScheme.onPrimary.copy(alpha = 0.8f),
268276
)
@@ -278,21 +286,22 @@ private fun LoanAccountTopCard(
278286

279287
Spacer(Modifier.height(DesignToken.padding.medium))
280288

281-
statusUi?.let { ui ->
282-
Box(
283-
modifier = Modifier
284-
.clip(KptTheme.shapes.large)
285-
.background(ui.color)
286-
.padding(horizontal = DesignToken.padding.medium, vertical = KptTheme.spacing.xs),
287-
contentAlignment = Alignment.Center,
288-
) {
289-
Text(
290-
text = stringResource(ui.labelRes).uppercase(),
291-
color = KptTheme.colorScheme.onPrimary,
292-
style = MifosTypography.labelSmallEmphasized,
293-
fontWeight = FontWeight.Bold,
294-
)
295-
}
289+
Box(
290+
modifier = Modifier
291+
.clip(KptTheme.shapes.large)
292+
.background(loanAccount.status.getLoanStatus().color)
293+
.padding(
294+
horizontal = DesignToken.padding.medium,
295+
vertical = KptTheme.spacing.xs,
296+
),
297+
contentAlignment = Alignment.Center,
298+
) {
299+
Text(
300+
text = stringResource(loanAccount.status.getLoanStatus().label).uppercase(),
301+
color = KptTheme.colorScheme.onPrimary,
302+
style = MifosTypography.labelSmallEmphasized,
303+
fontWeight = FontWeight.Bold,
304+
)
296305
}
297306

298307
Spacer(Modifier.height(KptTheme.spacing.md))
@@ -365,6 +374,7 @@ private fun LoanAccountDialogs(
365374
onRetry = onRetry,
366375
)
367376
}
377+
368378
null -> Unit
369379
}
370380
}
@@ -394,10 +404,6 @@ private class LoanAccountPreviewProvider : PreviewParameterProvider<LoanAccountS
394404
value = "Active",
395405
),
396406
),
397-
statusUiModel = LoanStatusUiModel(
398-
labelRes = Res.string.feature_loan_profile_status_active,
399-
color = AppColors.loanActiveStatus,
400-
),
401407
nextActionButtonRes = Res.string.feature_loan_profile_action_repayment,
402408
dialogState = null,
403409
),

feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileViewModel.kt

Lines changed: 13 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,16 @@ import androidclient.feature.loan.generated.resources.feature_loan_profile_actio
1717
import androidclient.feature.loan.generated.resources.feature_loan_profile_error_details_not_found
1818
import androidclient.feature.loan.generated.resources.feature_loan_profile_error_network_not_available
1919
import androidclient.feature.loan.generated.resources.feature_loan_profile_failed_to_load_loan
20-
import androidclient.feature.loan.generated.resources.feature_loan_profile_status_active
21-
import androidclient.feature.loan.generated.resources.feature_loan_profile_status_overpaid
22-
import androidclient.feature.loan.generated.resources.feature_loan_profile_status_pending
23-
import androidclient.feature.loan.generated.resources.feature_loan_profile_status_unknown
24-
import androidx.compose.ui.graphics.Color
2520
import androidx.lifecycle.SavedStateHandle
2621
import androidx.lifecycle.viewModelScope
2722
import androidx.navigation.toRoute
2823
import com.mifos.core.common.utils.DataState
2924
import com.mifos.core.data.repository.LoanAccountSummaryRepository
3025
import com.mifos.core.data.util.NetworkMonitor
31-
import com.mifos.core.designsystem.theme.AppColors
3226
import com.mifos.core.ui.util.BaseViewModel
3327
import com.mifos.feature.loan.loanAccountProfile.components.LoanAccountProfileActionItem
34-
import com.mifos.room.entities.accounts.loans.LoanStatusEntity
28+
import com.mifos.feature.loan.utils.LoanStatus
29+
import com.mifos.feature.loan.utils.getLoanStatus
3530
import com.mifos.room.entities.accounts.loans.LoanWithAssociationsEntity
3631
import kotlinx.coroutines.Job
3732
import kotlinx.coroutines.flow.update
@@ -85,13 +80,12 @@ internal class LoanAccountProfileViewModel(
8580
}
8681
return@collect
8782
}
88-
val currentStatus = loan.status.toProfileStatus()
83+
val currentStatus = loan.status.getLoanStatus()
8984

9085
mutableStateFlow.update {
9186
it.copy(
9287
loanAccount = loan,
9388
dialogState = null,
94-
statusUiModel = calculateStatusUi(currentStatus),
9589
nextActionButtonRes = calculateNextActionResource(currentStatus),
9690
)
9791
}
@@ -111,21 +105,12 @@ internal class LoanAccountProfileViewModel(
111105
}
112106
}
113107

114-
private fun calculateNextActionResource(status: LoanProfileStatus): StringResource {
108+
private fun calculateNextActionResource(status: LoanStatus): StringResource {
115109
return when (status) {
116-
LoanProfileStatus.PENDING -> Res.string.feature_loan_profile_action_approve
117-
LoanProfileStatus.OVERPAID -> Res.string.feature_loan_profile_action_transfer
118-
LoanProfileStatus.ACTIVE -> Res.string.feature_loan_profile_action_repayment
119-
LoanProfileStatus.UNKNOWN -> Res.string.feature_loan_profile_action_view
120-
}
121-
}
122-
123-
private fun calculateStatusUi(status: LoanProfileStatus): LoanStatusUiModel {
124-
return when (status) {
125-
LoanProfileStatus.ACTIVE -> LoanStatusUiModel(Res.string.feature_loan_profile_status_active, AppColors.loanActiveStatus)
126-
LoanProfileStatus.PENDING -> LoanStatusUiModel(Res.string.feature_loan_profile_status_pending, AppColors.loanPendingStatus)
127-
LoanProfileStatus.OVERPAID -> LoanStatusUiModel(Res.string.feature_loan_profile_status_overpaid, AppColors.loanOverpaidStatus)
128-
LoanProfileStatus.UNKNOWN -> LoanStatusUiModel(Res.string.feature_loan_profile_status_unknown, AppColors.loanUnknownStatus)
110+
LoanStatus.PENDING_APPROVAL -> Res.string.feature_loan_profile_action_approve
111+
LoanStatus.CLOSED_OVERPAID -> Res.string.feature_loan_profile_action_transfer
112+
LoanStatus.ACTIVE -> Res.string.feature_loan_profile_action_repayment
113+
else -> Res.string.feature_loan_profile_action_view
129114
}
130115
}
131116

@@ -148,37 +133,19 @@ internal class LoanAccountProfileViewModel(
148133
private fun handleNextAction() {
149134
val account = mutableStateFlow.value.loanAccount ?: return
150135

151-
when (account.status.toProfileStatus()) {
152-
LoanProfileStatus.PENDING -> sendEvent(LoanAccountEvent.NavigateToAction(LoanProfileAction.Approve))
153-
LoanProfileStatus.OVERPAID -> sendEvent(LoanAccountEvent.NavigateToAction(LoanProfileAction.Transfer))
154-
LoanProfileStatus.ACTIVE -> sendEvent(LoanAccountEvent.NavigateToAction(LoanProfileAction.Repayment))
155-
LoanProfileStatus.UNKNOWN -> sendEvent(LoanAccountEvent.NavigateToAccountDetails)
156-
}
157-
}
158-
159-
private fun LoanStatusEntity?.toProfileStatus(): LoanProfileStatus {
160-
if (this == null) return LoanProfileStatus.UNKNOWN
161-
return when {
162-
this.pendingApproval == true -> LoanProfileStatus.PENDING
163-
this.overpaid == true -> LoanProfileStatus.OVERPAID
164-
this.active == true -> LoanProfileStatus.ACTIVE
165-
else -> LoanProfileStatus.UNKNOWN
136+
when (account.status.getLoanStatus()) {
137+
LoanStatus.PENDING_APPROVAL -> sendEvent(LoanAccountEvent.NavigateToAction(LoanProfileAction.Approve))
138+
LoanStatus.CLOSED_OVERPAID -> sendEvent(LoanAccountEvent.NavigateToAction(LoanProfileAction.Transfer))
139+
LoanStatus.ACTIVE -> sendEvent(LoanAccountEvent.NavigateToAction(LoanProfileAction.Repayment))
140+
else -> sendEvent(LoanAccountEvent.NavigateToAccountDetails)
166141
}
167142
}
168143
}
169144

170-
enum class LoanProfileStatus {
171-
ACTIVE,
172-
PENDING,
173-
OVERPAID,
174-
UNKNOWN,
175-
}
176-
177145
data class LoanAccountState(
178146
val loanAccount: LoanWithAssociationsEntity? = null,
179147
val dialogState: DialogState? = null,
180148
val networkConnection: Boolean = false,
181-
val statusUiModel: LoanStatusUiModel? = null,
182149
val nextActionButtonRes: StringResource = Res.string.feature_loan_profile_action_view,
183150
) {
184151
sealed interface DialogState {
@@ -187,11 +154,6 @@ data class LoanAccountState(
187154
}
188155
}
189156

190-
data class LoanStatusUiModel(
191-
val labelRes: StringResource,
192-
val color: Color,
193-
)
194-
195157
sealed interface LoanProfileAction {
196158
data object Approve : LoanProfileAction
197159
data object Repayment : LoanProfileAction

feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountSummary/LoanAccountSummaryScreen.kt

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,13 @@ import com.mifos.core.designsystem.component.MifosMenuDropDownItem
8888
import com.mifos.core.designsystem.component.MifosScaffold
8989
import com.mifos.core.designsystem.component.MifosSweetError
9090
import com.mifos.core.designsystem.icon.MifosIcons
91-
import com.mifos.core.designsystem.theme.AppColors
9291
import com.mifos.core.designsystem.theme.DesignToken
9392
import com.mifos.core.designsystem.theme.MifosTheme
9493
import com.mifos.core.designsystem.theme.MifosTypography
9594
import com.mifos.core.ui.components.MifosBreadcrumbNavBar
9695
import com.mifos.core.ui.components.MifosProgressIndicator
9796
import com.mifos.core.ui.util.EventsEffect
97+
import com.mifos.feature.loan.utils.getLoanStatus
9898
import com.mifos.room.entities.accounts.loans.LoanStatusEntity
9999
import com.mifos.room.entities.accounts.loans.LoanWithAssociationsEntity
100100
import com.mifos.room.entities.accounts.loans.LoansAccountSummaryEntity
@@ -270,12 +270,7 @@ private fun LoanAccountSummaryContent(
270270
modifier = Modifier.fillMaxWidth(),
271271
verticalAlignment = Alignment.CenterVertically,
272272
) {
273-
val statusDescription = when {
274-
loanWithAssociations.status.active == true -> "Active"
275-
loanWithAssociations.status.pendingApproval == true -> "Pending Approval"
276-
loanWithAssociations.status.waitingForDisbursal == true -> "Waiting for Disbursal"
277-
else -> "Closed"
278-
}
273+
val statusDescription = stringResource(loanWithAssociations.status.getLoanStatus().label)
279274
Canvas(
280275
modifier = Modifier
281276
.size(DesignToken.sizes.iconMedium)
@@ -284,23 +279,7 @@ private fun LoanAccountSummaryContent(
284279
},
285280
onDraw = {
286281
drawCircle(
287-
color = when {
288-
loanWithAssociations.status.active == true -> {
289-
AppColors.loanIndicatorActive
290-
}
291-
292-
loanWithAssociations.status.pendingApproval == true -> {
293-
AppColors.loanIndicatorPending
294-
}
295-
296-
loanWithAssociations.status.waitingForDisbursal == true -> {
297-
AppColors.loanIndicatorWaitingForDisbursal
298-
}
299-
300-
else -> {
301-
AppColors.loanIndicatorOther
302-
}
303-
},
282+
color = loanWithAssociations.status.getLoanStatus().color,
304283
)
305284
},
306285
)

0 commit comments

Comments
 (0)