Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,13 @@ object AppColors {

val loanActiveStatus = Color(0xFF5CB85C)
val loanPendingStatus = Color(0xFFFFA500)
val loanOverpaidStatus = Color(0xFF800080)
val loanApprovedStatus = Color(0xFFFFA500)
val loanClosedOverpaidStatus = Color(0xFF800080)
Comment thread
niyajali marked this conversation as resolved.
val loanClosedObligationsMetStatus = Color(0xFF5CB85C)
val loanClosedRescheduled = Color(0xFF000AAD)
val loanClosedWrittenOffStatus = Color(0xFF990000)
val loanRejectedStatus = Color(0xFF990000)
val loanWithdrawnByApplicantStatus = Color(0xFF990000)

val loanUnknownStatus = Color(0xFF9E9E9E)
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,6 @@
android:tint="?attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:fillColor="#FFFFFF"
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"/>
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ import com.mifos.core.ui.components.MifosEmptyCard
import com.mifos.core.ui.components.MifosProgressIndicator
import com.mifos.core.ui.components.MifosSearchBar
import com.mifos.core.ui.util.EventsEffect
import com.mifos.feature.loan.utils.getLoanStatus
import com.mifos.room.entities.accounts.loans.LoanAccountEntity
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
Expand Down Expand Up @@ -252,7 +253,7 @@ private fun LoanAccountItem(

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

status = loan.status?.value ?: notAvailable,
status = loan.status?.let { stringResource(it.getLoanStatus().label) } ?: notAvailable,

menuList = buildLoanActions(loan),

Expand Down
16 changes: 12 additions & 4 deletions feature/loan/src/commonMain/composeResources/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -165,10 +165,6 @@
<string name="feature_loan_profile_item_notes_subtitle">Additional internal comments</string>
<string name="feature_loan_profile_item_standing_instructions_title">Standing Instructions</string>
<string name="feature_loan_profile_item_standing_instructions_subtitle">Automated payment settings</string>
<string name="feature_loan_profile_status_active">Active</string>
<string name="feature_loan_profile_status_pending">Pending</string>
<string name="feature_loan_profile_status_overpaid">Overpaid</string>
<string name="feature_loan_profile_status_unknown">Unknown</string>
<string name="feature_loan_profile_action_approve">Approve Account</string>
<string name="feature_loan_profile_action_repayment">Make Repayment</string>
<string name="feature_loan_profile_action_transfer">Transfer Funds</string>
Expand Down Expand Up @@ -475,4 +471,16 @@
<string name="feature_loan_reschedule_fetch_failed">Failed to load reschedule history.</string>
<string name="feature_loan_reschedule_approve_failed">Failed to approve reschedule.</string>
<string name="feature_loan_reschedule_delete_failed">Failed to delete reschedule.</string>


<string name="feature_loan_pending_approval">Pending Approval</string>
<string name="feature_loan_approved">Approved</string>
<string name="feature_loan_withdrawn_by_applicant">Withdrawn by Applicant</string>
<string name="feature_loan_active">Active</string>
<string name="feature_loan_closed_overpaid">Closed (Overpaid)</string>
<string name="feature_loan_closed_obligations_met">Closed (Obligations Met)</string>
<string name="feature_loan_closed_written_off">Closed (Written Off)</string>
<string name="feature_loan_closed_rescheduled">Closed (Rescheduled)</string>
<string name="feature_loan_rejected">Rejected</string>
<string name="feature_loan_unknown">Unknown</string>">
</resources>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import androidclient.feature.loan.generated.resources.feature_loan_profile_label
import androidclient.feature.loan.generated.resources.feature_loan_profile_label_overpaid_by
import androidclient.feature.loan.generated.resources.feature_loan_profile_section_account_overview
import androidclient.feature.loan.generated.resources.feature_loan_profile_section_actions_details
import androidclient.feature.loan.generated.resources.feature_loan_profile_status_active
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
Expand Down Expand Up @@ -63,6 +62,7 @@ import com.mifos.core.ui.util.EventsEffect
import com.mifos.core.ui.util.TextUtil
import com.mifos.feature.loan.loanAccountProfile.components.LoanAccountProfileActionItem
import com.mifos.feature.loan.loanAccountProfile.components.loanProfileActionItems
import com.mifos.feature.loan.utils.getLoanStatus
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 Down Expand Up @@ -109,19 +109,24 @@ internal fun LoanAccountProfileScreen(
}
}
}

is LoanAccountEvent.NavigateToDetail -> {
val loanId = state.loanAccount?.id ?: -1

when (event.detailItem) {
LoanAccountProfileActionItem.RepaymentSchedule -> navigateToRepaymentSchedule(loanId)
LoanAccountProfileActionItem.RepaymentSchedule -> navigateToRepaymentSchedule(
loanId,
)

LoanAccountProfileActionItem.Transactions -> navigateToTransactions(loanId)
LoanAccountProfileActionItem.Charges -> navigateToCharges(loanId)
LoanAccountProfileActionItem.Documents -> navigateToDocuments(loanId)
LoanAccountProfileActionItem.Reschedules -> navigateToReschedules(loanId)
LoanAccountProfileActionItem.Notes -> navigateToNotes(loanId)
else -> { }
else -> {}
}
}

LoanAccountEvent.NavigateToAccountDetails -> {}
}
}
Expand Down Expand Up @@ -170,7 +175,6 @@ private fun LoanAccountContent(

LoanAccountTopCard(
loanAccount = loanAccount,
statusUi = state.statusUiModel,
onClick = { onAction(LoanAccountAction.OnAccountClick) },
)

Expand Down Expand Up @@ -221,15 +225,16 @@ private fun LoanAccountContent(
@Composable
private fun LoanAccountTopCard(
loanAccount: LoanWithAssociationsEntity,
statusUi: LoanStatusUiModel?,
onClick: () -> Unit,
modifier: Modifier = Modifier,
) {
val currencyCode = loanAccount.currency?.code
val decimalPlaces = loanAccount.currency?.decimalPlaces

val balance = CurrencyFormatter.format(loanAccount.summary.totalOutstanding, currencyCode, decimalPlaces)
val arrears = CurrencyFormatter.format(loanAccount.summary.totalOverdue, currencyCode, decimalPlaces)
val balance =
CurrencyFormatter.format(loanAccount.summary.totalOutstanding, currencyCode, decimalPlaces)
val arrears =
CurrencyFormatter.format(loanAccount.summary.totalOverdue, currencyCode, decimalPlaces)
val overpaid = CurrencyFormatter.format(loanAccount.totalOverpaid, currencyCode, decimalPlaces)

MifosCard(
Expand All @@ -254,15 +259,18 @@ private fun LoanAccountTopCard(
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = "${loanAccount.loanProductName?.uppercase().orEmpty()} ${loanAccount.accountNo}".trim(),
text = "${
loanAccount.loanProductName?.uppercase().orEmpty()
} ${loanAccount.accountNo}".trim(),
style = MifosTypography.titleMediumEmphasized,
color = KptTheme.colorScheme.onPrimary,
)

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

Text(
text = loanAccount.clientName ?: stringResource(Res.string.feature_loan_profile_label_client_name_placeholder),
text = loanAccount.clientName
?: stringResource(Res.string.feature_loan_profile_label_client_name_placeholder),
style = MifosTypography.bodyMedium,
color = KptTheme.colorScheme.onPrimary.copy(alpha = 0.8f),
)
Expand All @@ -278,21 +286,22 @@ private fun LoanAccountTopCard(

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

statusUi?.let { ui ->
Box(
modifier = Modifier
.clip(KptTheme.shapes.large)
.background(ui.color)
.padding(horizontal = DesignToken.padding.medium, vertical = KptTheme.spacing.xs),
contentAlignment = Alignment.Center,
) {
Text(
text = stringResource(ui.labelRes).uppercase(),
color = KptTheme.colorScheme.onPrimary,
style = MifosTypography.labelSmallEmphasized,
fontWeight = FontWeight.Bold,
)
}
Box(
modifier = Modifier
.clip(KptTheme.shapes.large)
.background(loanAccount.status.getLoanStatus().color)
.padding(
horizontal = DesignToken.padding.medium,
vertical = KptTheme.spacing.xs,
),
contentAlignment = Alignment.Center,
) {
Text(
text = stringResource(loanAccount.status.getLoanStatus().label).uppercase(),
color = KptTheme.colorScheme.onPrimary,
style = MifosTypography.labelSmallEmphasized,
fontWeight = FontWeight.Bold,
)
}

Spacer(Modifier.height(KptTheme.spacing.md))
Expand Down Expand Up @@ -365,6 +374,7 @@ private fun LoanAccountDialogs(
onRetry = onRetry,
)
}

null -> Unit
}
}
Expand Down Expand Up @@ -394,10 +404,6 @@ private class LoanAccountPreviewProvider : PreviewParameterProvider<LoanAccountS
value = "Active",
),
),
statusUiModel = LoanStatusUiModel(
labelRes = Res.string.feature_loan_profile_status_active,
color = AppColors.loanActiveStatus,
),
nextActionButtonRes = Res.string.feature_loan_profile_action_repayment,
dialogState = null,
),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,21 +17,16 @@ import androidclient.feature.loan.generated.resources.feature_loan_profile_actio
import androidclient.feature.loan.generated.resources.feature_loan_profile_error_details_not_found
import androidclient.feature.loan.generated.resources.feature_loan_profile_error_network_not_available
import androidclient.feature.loan.generated.resources.feature_loan_profile_failed_to_load_loan
import androidclient.feature.loan.generated.resources.feature_loan_profile_status_active
import androidclient.feature.loan.generated.resources.feature_loan_profile_status_overpaid
import androidclient.feature.loan.generated.resources.feature_loan_profile_status_pending
import androidclient.feature.loan.generated.resources.feature_loan_profile_status_unknown
import androidx.compose.ui.graphics.Color
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import androidx.navigation.toRoute
import com.mifos.core.common.utils.DataState
import com.mifos.core.data.repository.LoanAccountSummaryRepository
import com.mifos.core.data.util.NetworkMonitor
import com.mifos.core.designsystem.theme.AppColors
import com.mifos.core.ui.util.BaseViewModel
import com.mifos.feature.loan.loanAccountProfile.components.LoanAccountProfileActionItem
import com.mifos.room.entities.accounts.loans.LoanStatusEntity
import com.mifos.feature.loan.utils.LoanStatus
import com.mifos.feature.loan.utils.getLoanStatus
import com.mifos.room.entities.accounts.loans.LoanWithAssociationsEntity
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.update
Expand Down Expand Up @@ -85,13 +80,12 @@ internal class LoanAccountProfileViewModel(
}
return@collect
}
val currentStatus = loan.status.toProfileStatus()
val currentStatus = loan.status.getLoanStatus()

mutableStateFlow.update {
it.copy(
loanAccount = loan,
dialogState = null,
statusUiModel = calculateStatusUi(currentStatus),
nextActionButtonRes = calculateNextActionResource(currentStatus),
)
}
Expand All @@ -111,21 +105,12 @@ internal class LoanAccountProfileViewModel(
}
}

private fun calculateNextActionResource(status: LoanProfileStatus): StringResource {
private fun calculateNextActionResource(status: LoanStatus): StringResource {
return when (status) {
LoanProfileStatus.PENDING -> Res.string.feature_loan_profile_action_approve
LoanProfileStatus.OVERPAID -> Res.string.feature_loan_profile_action_transfer
LoanProfileStatus.ACTIVE -> Res.string.feature_loan_profile_action_repayment
LoanProfileStatus.UNKNOWN -> Res.string.feature_loan_profile_action_view
}
}

private fun calculateStatusUi(status: LoanProfileStatus): LoanStatusUiModel {
return when (status) {
LoanProfileStatus.ACTIVE -> LoanStatusUiModel(Res.string.feature_loan_profile_status_active, AppColors.loanActiveStatus)
LoanProfileStatus.PENDING -> LoanStatusUiModel(Res.string.feature_loan_profile_status_pending, AppColors.loanPendingStatus)
LoanProfileStatus.OVERPAID -> LoanStatusUiModel(Res.string.feature_loan_profile_status_overpaid, AppColors.loanOverpaidStatus)
LoanProfileStatus.UNKNOWN -> LoanStatusUiModel(Res.string.feature_loan_profile_status_unknown, AppColors.loanUnknownStatus)
LoanStatus.PENDING_APPROVAL -> Res.string.feature_loan_profile_action_approve
LoanStatus.CLOSED_OVERPAID -> Res.string.feature_loan_profile_action_transfer
LoanStatus.ACTIVE -> Res.string.feature_loan_profile_action_repayment
else -> Res.string.feature_loan_profile_action_view
}
}

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

when (account.status.toProfileStatus()) {
LoanProfileStatus.PENDING -> sendEvent(LoanAccountEvent.NavigateToAction(LoanProfileAction.Approve))
LoanProfileStatus.OVERPAID -> sendEvent(LoanAccountEvent.NavigateToAction(LoanProfileAction.Transfer))
LoanProfileStatus.ACTIVE -> sendEvent(LoanAccountEvent.NavigateToAction(LoanProfileAction.Repayment))
LoanProfileStatus.UNKNOWN -> sendEvent(LoanAccountEvent.NavigateToAccountDetails)
}
}

private fun LoanStatusEntity?.toProfileStatus(): LoanProfileStatus {
if (this == null) return LoanProfileStatus.UNKNOWN
return when {
this.pendingApproval == true -> LoanProfileStatus.PENDING
this.overpaid == true -> LoanProfileStatus.OVERPAID
this.active == true -> LoanProfileStatus.ACTIVE
else -> LoanProfileStatus.UNKNOWN
when (account.status.getLoanStatus()) {
LoanStatus.PENDING_APPROVAL -> sendEvent(LoanAccountEvent.NavigateToAction(LoanProfileAction.Approve))
LoanStatus.CLOSED_OVERPAID -> sendEvent(LoanAccountEvent.NavigateToAction(LoanProfileAction.Transfer))
LoanStatus.ACTIVE -> sendEvent(LoanAccountEvent.NavigateToAction(LoanProfileAction.Repayment))
else -> sendEvent(LoanAccountEvent.NavigateToAccountDetails)
}
}
}

enum class LoanProfileStatus {
ACTIVE,
PENDING,
OVERPAID,
UNKNOWN,
}

data class LoanAccountState(
val loanAccount: LoanWithAssociationsEntity? = null,
val dialogState: DialogState? = null,
val networkConnection: Boolean = false,
val statusUiModel: LoanStatusUiModel? = null,
val nextActionButtonRes: StringResource = Res.string.feature_loan_profile_action_view,
) {
sealed interface DialogState {
Expand All @@ -187,11 +154,6 @@ data class LoanAccountState(
}
}

data class LoanStatusUiModel(
val labelRes: StringResource,
val color: Color,
)

sealed interface LoanProfileAction {
data object Approve : LoanProfileAction
data object Repayment : LoanProfileAction
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,13 @@ import com.mifos.core.designsystem.component.MifosMenuDropDownItem
import com.mifos.core.designsystem.component.MifosScaffold
import com.mifos.core.designsystem.component.MifosSweetError
import com.mifos.core.designsystem.icon.MifosIcons
import com.mifos.core.designsystem.theme.AppColors
import com.mifos.core.designsystem.theme.DesignToken
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.feature.loan.utils.getLoanStatus
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 Down Expand Up @@ -270,12 +270,7 @@ 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"
}
val statusDescription = stringResource(loanWithAssociations.status.getLoanStatus().label)
Canvas(
modifier = Modifier
.size(DesignToken.sizes.iconMedium)
Expand All @@ -284,23 +279,7 @@ private fun LoanAccountSummaryContent(
},
onDraw = {
drawCircle(
color = when {
loanWithAssociations.status.active == true -> {
AppColors.loanIndicatorActive
}

loanWithAssociations.status.pendingApproval == true -> {
AppColors.loanIndicatorPending
}

loanWithAssociations.status.waitingForDisbursal == true -> {
AppColors.loanIndicatorWaitingForDisbursal
}

else -> {
AppColors.loanIndicatorOther
}
},
color = loanWithAssociations.status.getLoanStatus().color,
)
},
)
Expand Down
Loading
Loading