Skip to content

feat(loan): add loan account general screen#2629

Open
amanna13 wants to merge 32 commits into
openMF:devfrom
amanna13:feat/general-tab-loan-section
Open

feat(loan): add loan account general screen#2629
amanna13 wants to merge 32 commits into
openMF:devfrom
amanna13:feat/general-tab-loan-section

Conversation

@amanna13

@amanna13 amanna13 commented Mar 2, 2026

Copy link
Copy Markdown
Member

Fixes - Jira-#665

Screenshot [Updated]

595600271-d0770f03-972e-4c4e-8fc7-dca8a12e106e.webm

Please make sure these boxes are checked before submitting your pull request - thanks!

  • Run the static analysis check ./gradlew check or ci-prepush.sh to make sure you didn't break anything

  • If you have multiple commits please combine them into one commit by squashing them.

Summary by CodeRabbit

  • New Features
    • Added a Loan Account General screen with performance history, loan summary (principal, interest, fees, penalties) and detailed loan info (disbursement, purpose, officer, currency, amounts).
    • Added navigation and ViewModel to load and present loan-general data and integrated it into existing loan/profile navigation.
    • Added repository wiring to retrieve loan data for the new screen.
    • Added localized strings for summary and detail labels.

@coderabbitai

coderabbitai Bot commented Mar 2, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds a new Loan Account General feature: repository interface and implementation with DI binding, ViewModel that loads and formats loan data, UI composables and strings for the general screen, and navigation wiring from profile to the new screen.

Changes

Cohort / File(s) Summary
Data Layer — Repository & DI
core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt, core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanAccountGeneralRepository.kt, core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountGeneralRepositoryImp.kt
Add LoanAccountGeneralRepository interface with getLoanById, implement it in LoanAccountGeneralRepositoryImp delegating to DataManagerLoan, and bind implementation in the DI module.
Feature — UI Components & ViewModel
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt, feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt
Introduce LoanAccountGeneralScreen composables (cards, summary table, details, dialogs, preview) and LoanAccountGeneralViewModel which loads loan data, observes network, formats dates/currency, and computes summary rows/totals.
Feature — Navigation & Profile Integration
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralNavigation.kt, feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileNavigation.kt, feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileScreen.kt, feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.kt
Add typed LoanAccountGeneralRoute, destination and navigate helper; add navigateToGeneral: (Int) -> Unit parameter through profile destination and screen; integrate new destination into the loan navigation graph.
Feature — Resources & DI Registration
feature/loan/src/commonMain/composeResources/values/strings.xml, feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/di/LoanModule.kt
Add ~25 string resources for the general screen and register LoanAccountGeneralViewModel in the feature DI module.

Sequence Diagram

sequenceDiagram
    participant UI as LoanAccountGeneralScreen
    participant VM as LoanAccountGeneralViewModel
    participant Repo as LoanAccountGeneralRepository
    participant DM as DataManagerLoan
    participant Net as NetworkMonitor

    UI->>VM: collect state / dispatch actions
    Net->>VM: network status updates
    VM->>VM: init with loanId from route
    VM->>Repo: getLoanById(loanId)
    Repo->>DM: getLoanById(loanId)
    DM-->>Repo: loan data / null
    Repo-->>VM: emit DataState(loan)
    VM->>VM: fillGeneralState(loan) — format dates/currency, calculate totals
    VM-->>UI: updated LoanAccountGeneralState
    UI->>UI: render cards, summary table, details
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • biplab1
  • revanthkumarJ
  • itsPronay

Poem

🐰 I hopped through routes and tables bright,

Numbers lined up neat in soft moonlight,
Dates and totals all in a row,
Cards and details ready to show,
A joyful rabbit gives a tiny bite!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a loan account general screen, which is the primary feature introduced across the repository changes.
Description check ✅ Passed The PR description references a Jira ticket (MIFOSAC-665) and describes the feature being implemented—a General tab screen for loan accounts—which directly matches the changeset scope.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (6)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileScreen.kt (1)

103-103: Avoid propagating sentinel loan IDs to navigation.

Line 103 falls back to -1 when state.loanAccount is null, which can trigger invalid-detail navigation and downstream fetches with an invalid id.

♻️ Suggested guard-based fix
-            is LoanAccountEvent.NavigateToDetail -> onDetailItemClick(state.loanAccount?.id ?: -1, event.detailItem)
+            is LoanAccountEvent.NavigateToDetail -> {
+                val loanId = state.loanAccount?.id ?: return@EventsEffect
+                onDetailItemClick(loanId, event.detailItem)
+            }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileScreen.kt`
at line 103, Avoid passing the sentinel -1 to navigation: update the
LoanAccountEvent.NavigateToDetail handling to guard against null loanAccount
instead of falling back to -1—check state.loanAccount (or its id) and only call
onDetailItemClick with the real id when present; if loanAccount is null, no-op
or handle error/edge-case explicitly so NavigateToDetail never receives an
invalid id from LoanAccountEvent.NavigateToDetail or state.loanAccount?.id.
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralNavigation.kt (1)

17-20: Consider documenting the sentinel default value.

The default loanId = -1 acts as a sentinel for "no loan selected." While the ViewModel extracts this via SavedStateHandle.toRoute<>(), consider adding a KDoc comment clarifying this convention. Alternatively, you could make the parameter non-defaulted to enforce callers always provide a valid ID.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralNavigation.kt`
around lines 17 - 20, The data class LoanAccountGeneralRoute currently uses a
sentinel default loanId = -1 to mean "no loan selected"; update the declaration
by either removing the default so callers must supply an explicit ID, or add a
KDoc above LoanAccountGeneralRoute explaining that loanId == -1 is the sentinel
for no selection and how callers/ SavedStateHandle.toRoute<>() should handle it;
reference the LoanAccountGeneralRoute data class and its loanId property when
making the change so callers and the ViewModel behavior remain consistent.
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt (1)

79-85: Consider logging exceptions for debugging.

The generic exception catch silently converts all exceptions to error messages. Consider logging the exception for debugging purposes while still showing a user-friendly message.

💡 Add logging for debugging
                         try {
                             fillGeneralState(loan)
                         } catch (e: Exception) {
+                            // Consider using a logging framework
+                            e.printStackTrace()
                             mutableStateFlow.update {
                                 it.copy(dialogState = LoanAccountGeneralState.DialogState.Error(e.message.toString()))
                             }
                         }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt`
around lines 79 - 85, The catch block around fillGeneralState in
LoanAccountGeneralViewModel currently swallows exceptions and only sets
LoanAccountGeneralState.DialogState.Error with e.message; update this block to
log the full exception (stack trace and message) before updating
mutableStateFlow so developers can debug; use the existing logging mechanism
available in the module (e.g., a logger instance or platform logger) and include
contextual info like the function name (fillGeneralState /
LoanAccountGeneralViewModel) when calling the logger.
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt (3)

93-153: Consider showing content during loading instead of blank screen.

When dialogState is not null (Loading or Error), the entire content is hidden (line 100). For a better UX, consider showing the content with a loading overlay or skeleton, especially if partial data might already be available. Currently, users see a blank screen until loading completes.

💡 Alternative approach: show content with overlay
 `@Composable`
 internal fun LoanAccountGeneralScreen(
     state: LoanAccountGeneralState,
     onAction: (LoanAccountGeneralAction) -> Unit,
     navController: NavController,
     modifier: Modifier = Modifier,
 ) {
-    if (state.dialogState == null) {
-        Column(modifier = modifier.fillMaxSize()) {
+    Box(modifier = modifier.fillMaxSize()) {
+        Column(modifier = Modifier.fillMaxSize()) {
             MifosBreadcrumbNavBar(navController = navController)
             // ... rest of content
         }
+        
+        // Show loading/error overlay on top of content
+        LoanAccountGeneralDialog(
+            state = state,
+            onAction = onAction,
+        )
     }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt`
around lines 93 - 153, The screen currently hides all content when
state.dialogState != null in LoanAccountGeneralScreen, causing a blank UI during
Loading/Error; instead always render the main Column (so PerformanceHistoryCard,
LoanSummaryTable, LoanDetailsSection still display) and conditionally render an
overlay or dialog when state.dialogState is non-null (e.g., show a
LoadingOverlay/ProgressIndicator or an ErrorDialog on top), keeping the existing
nav bar and content layout; update the if (state.dialogState == null) guard to
render content unconditionally and add a composable that inspects
state.dialogState to show the overlay/dialog while leaving underlying components
visible.

319-320: Same fragility issue with totals and colors.

The totals and totColors lists also rely on parallel indexing. Apply the same data class pattern for consistency and safety.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt`
around lines 319 - 320, The totals and totColors lists are fragile due to
parallel indexing; replace them with a single list of a small data class (e.g.,
TotalEntry with value: BigDecimal/Number and color: Color) and populate it from
state.totalOriginal, state.totalPaid, state.totalWaived, state.totalWrittenOff,
state.totalOutstanding, state.totalOverDue paired with the corresponding colors
(textColor or KptTheme.colorScheme.primary), then use that List<TotalEntry> in
the UI rendering code instead of the separate totals and totColors lists to
ensure safety and clarity.

263-264: Parallel lists for amounts and colors are fragile.

The amounts and amtColors lists rely on matching indices. If one list is modified without the other, it will cause visual bugs. Consider using a data class or pair to keep them together.

💡 Safer approach with paired data
data class AmountCell(val value: String, val color: Color)

val amountCells = listOf(
    AmountCell(row.original, textColor),
    AmountCell(row.paid, KptTheme.colorScheme.primary),
    AmountCell(row.waived, KptTheme.colorScheme.primary),
    AmountCell(row.writtenOff, KptTheme.colorScheme.primary),
    AmountCell(row.outstanding, textColor),
    AmountCell(row.overDue, textColor),
)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt`
around lines 263 - 264, The two parallel lists amounts and amtColors are
fragile; replace them with a single paired structure (e.g., a data class like
AmountCell with properties value and color) and build a list amountCells
containing entries for row.original, row.paid, row.waived, row.writtenOff,
row.outstanding, row.overDue mapped to their respective colors; then update any
iteration or indexing logic that used amounts[i] and amtColors[i] to use
amountCells[i].value and amountCells[i].color (or destructure) so the
value/color pairing cannot get out of sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt`:
- Around line 127-129: The mapping uses loan.approvedPrincipal for both
proposedAmount and approvedAmount in LoanAccountGeneralViewModel
(proposedAmount, approvedAmount), but LoanWithAssociationsEntity only exposes
principal and approvedPrincipal so you cannot read a proposedPrincipal; fix by
either adding a proposedPrincipal field to LoanWithAssociationsEntity (and
populate it from the source) and then use loan.proposedPrincipal for
proposedAmount, or explicitly decide which existing field should represent the
“proposed” value (principal or approvedPrincipal) and update the assignment
accordingly; update the ViewModel assignments (proposedAmount, approvedAmount)
and the entity definition (LoanWithAssociationsEntity) as a pair so the source
field exists and the mapping is correct.

---

Nitpick comments:
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralNavigation.kt`:
- Around line 17-20: The data class LoanAccountGeneralRoute currently uses a
sentinel default loanId = -1 to mean "no loan selected"; update the declaration
by either removing the default so callers must supply an explicit ID, or add a
KDoc above LoanAccountGeneralRoute explaining that loanId == -1 is the sentinel
for no selection and how callers/ SavedStateHandle.toRoute<>() should handle it;
reference the LoanAccountGeneralRoute data class and its loanId property when
making the change so callers and the ViewModel behavior remain consistent.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt`:
- Around line 93-153: The screen currently hides all content when
state.dialogState != null in LoanAccountGeneralScreen, causing a blank UI during
Loading/Error; instead always render the main Column (so PerformanceHistoryCard,
LoanSummaryTable, LoanDetailsSection still display) and conditionally render an
overlay or dialog when state.dialogState is non-null (e.g., show a
LoadingOverlay/ProgressIndicator or an ErrorDialog on top), keeping the existing
nav bar and content layout; update the if (state.dialogState == null) guard to
render content unconditionally and add a composable that inspects
state.dialogState to show the overlay/dialog while leaving underlying components
visible.
- Around line 319-320: The totals and totColors lists are fragile due to
parallel indexing; replace them with a single list of a small data class (e.g.,
TotalEntry with value: BigDecimal/Number and color: Color) and populate it from
state.totalOriginal, state.totalPaid, state.totalWaived, state.totalWrittenOff,
state.totalOutstanding, state.totalOverDue paired with the corresponding colors
(textColor or KptTheme.colorScheme.primary), then use that List<TotalEntry> in
the UI rendering code instead of the separate totals and totColors lists to
ensure safety and clarity.
- Around line 263-264: The two parallel lists amounts and amtColors are fragile;
replace them with a single paired structure (e.g., a data class like AmountCell
with properties value and color) and build a list amountCells containing entries
for row.original, row.paid, row.waived, row.writtenOff, row.outstanding,
row.overDue mapped to their respective colors; then update any iteration or
indexing logic that used amounts[i] and amtColors[i] to use amountCells[i].value
and amountCells[i].color (or destructure) so the value/color pairing cannot get
out of sync.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt`:
- Around line 79-85: The catch block around fillGeneralState in
LoanAccountGeneralViewModel currently swallows exceptions and only sets
LoanAccountGeneralState.DialogState.Error with e.message; update this block to
log the full exception (stack trace and message) before updating
mutableStateFlow so developers can debug; use the existing logging mechanism
available in the module (e.g., a logger instance or platform logger) and include
contextual info like the function name (fillGeneralState /
LoanAccountGeneralViewModel) when calling the logger.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileScreen.kt`:
- Line 103: Avoid passing the sentinel -1 to navigation: update the
LoanAccountEvent.NavigateToDetail handling to guard against null loanAccount
instead of falling back to -1—check state.loanAccount (or its id) and only call
onDetailItemClick with the real id when present; if loanAccount is null, no-op
or handle error/edge-case explicitly so NavigateToDetail never receives an
invalid id from LoanAccountEvent.NavigateToDetail or state.loanAccount?.id.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 62141dd and 0c0912f.

📒 Files selected for processing (12)
  • core/data/src/commonMain/kotlin/com/mifos/core/data/di/RepositoryModule.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repository/LoanAccountGeneralRepository.kt
  • core/data/src/commonMain/kotlin/com/mifos/core/data/repositoryImp/LoanAccountGeneralRepositoryImp.kt
  • core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/theme/Color.kt
  • feature/loan/src/commonMain/composeResources/values/strings.xml
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/di/LoanModule.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralNavigation.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileNavigation.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileScreen.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.kt

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt (1)

100-146: Extract shared content into a single composable to prevent preview/runtime drift.

The section layout is duplicated in both the runtime screen and preview, and it has already diverged (preview titles are hardcoded). Consider a shared LoanAccountGeneralContent(...) used by both call sites.

♻️ Proposed refactor
+@Composable
+private fun LoanAccountGeneralContent(
+    state: LoanAccountGeneralState,
+    modifier: Modifier = Modifier,
+) {
+    Column(
+        modifier = modifier
+            .fillMaxSize()
+            .verticalScroll(rememberScrollState()),
+    ) {
+        Text(
+            text = stringResource(Res.string.feature_loan_general_section_performance_history),
+            style = KptTheme.typography.labelLarge,
+            modifier = Modifier.padding(horizontal = KptTheme.spacing.md),
+        )
+        Spacer(Modifier.height(DesignToken.spacing.medium))
+        PerformanceHistoryCard(
+            state = state,
+            modifier = Modifier.padding(horizontal = KptTheme.spacing.md),
+        )
+        Spacer(Modifier.height(DesignToken.spacing.largeIncreased))
+        Text(
+            text = stringResource(Res.string.feature_loan_general_section_loan_summary),
+            style = KptTheme.typography.labelLarge,
+            modifier = Modifier.padding(horizontal = KptTheme.spacing.md),
+        )
+        Spacer(Modifier.height(DesignToken.spacing.medium))
+        LoanSummaryTable(state = state)
+        Spacer(Modifier.height(DesignToken.spacing.largeIncreased))
+        Text(
+            text = stringResource(Res.string.feature_loan_general_section_loan_details),
+            style = KptTheme.typography.labelLarge,
+            modifier = Modifier.padding(horizontal = KptTheme.spacing.md),
+        )
+        Spacer(Modifier.height(DesignToken.spacing.medium))
+        LoanDetailsSection(
+            state = state,
+            modifier = Modifier.padding(horizontal = KptTheme.spacing.md),
+        )
+        Spacer(Modifier.height(KptTheme.spacing.xl))
+    }
+}

Also applies to: 442-471

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt`
around lines 100 - 146, The column block in LoanAccountGeneralScreen (containing
Text headers, PerformanceHistoryCard, LoanSummaryTable, LoanDetailsSection and
Spacers) is duplicated between runtime and preview causing drift; extract that
repeated UI into a single composable (e.g., LoanAccountGeneralContent(state:
LoanAccountGeneralState, modifier: Modifier = Modifier)) that renders the Text
headers, PerformanceHistoryCard, LoanSummaryTable and LoanDetailsSection with
the same modifiers and spacing, then replace the inline column in
LoanAccountGeneralScreen and the preview call sites with this new
LoanAccountGeneralContent to ensure both share identical layout and strings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt`:
- Around line 100-146: The column block in LoanAccountGeneralScreen (containing
Text headers, PerformanceHistoryCard, LoanSummaryTable, LoanDetailsSection and
Spacers) is duplicated between runtime and preview causing drift; extract that
repeated UI into a single composable (e.g., LoanAccountGeneralContent(state:
LoanAccountGeneralState, modifier: Modifier = Modifier)) that renders the Text
headers, PerformanceHistoryCard, LoanSummaryTable and LoanDetailsSection with
the same modifiers and spacing, then replace the inline column in
LoanAccountGeneralScreen and the preview call sites with this new
LoanAccountGeneralContent to ensure both share identical layout and strings.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c0912f and e848450.

📒 Files selected for processing (1)
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt

@kartikey004 kartikey004 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@amanna13 could you please confirm if the font sizes used here are as intended? right now the headings feel slightly larger compared to other screens.

@amanna13 amanna13 requested a review from a team March 4, 2026 05:59

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@feature/loan/src/commonMain/composeResources/values/strings.xml`:
- Line 239: Update the string resource named
"feature_loan_general_summary_col_overdue" to use the single-word, standardized
UI wording "Overdue" instead of "Over Due"; locate the <string
name="feature_loan_general_summary_col_overdue"> entry and replace its text
value so it matches other occurrences in the file and UI terminology.

ℹ️ Review info
Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 80fb8d49-e750-4600-a122-a28a0c3d2946

📥 Commits

Reviewing files that changed from the base of the PR and between e848450 and 7df5d34.

📒 Files selected for processing (4)
  • feature/loan/src/commonMain/composeResources/values/strings.xml
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileNavigation.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileScreen.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/navigation/LoanNavigation.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountProfile/LoanAccountProfileScreen.kt

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

@niyajali niyajali left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

check out mifos-pay project for referrence and update the PR accordingly with the required changes

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt (1)

432-449: Use resource strings in preview headings to avoid label drift.

Preview labels are hardcoded ("PERFORMANCE HISTORY", "LOAN SUMMARY", "LOAN DETAILS"). Using the same stringResource(...) keys as runtime UI keeps previews aligned with localization/content updates.

Suggested preview-only cleanup
-            Text(
-                text = "PERFORMANCE HISTORY",
+            Text(
+                text = stringResource(Res.string.feature_loan_general_section_performance_history),
                 style = KptTheme.typography.labelLarge,
                 modifier = Modifier.padding(horizontal = KptTheme.spacing.md),
             )
@@
-            Text(
-                text = "LOAN SUMMARY",
+            Text(
+                text = stringResource(Res.string.feature_loan_general_section_loan_summary),
                 style = KptTheme.typography.labelLarge,
                 modifier = Modifier.padding(horizontal = KptTheme.spacing.md),
             )
@@
-            Text(
-                text = "LOAN DETAILS",
+            Text(
+                text = stringResource(Res.string.feature_loan_general_section_loan_details),
                 style = KptTheme.typography.labelLarge,
                 modifier = Modifier.padding(horizontal = KptTheme.spacing.md),
             )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt`
around lines 432 - 449, Replace hardcoded preview headings with the same
localized string resources used at runtime to prevent label drift; locate the
Text composables that currently use literal strings "PERFORMANCE HISTORY", "LOAN
SUMMARY", and "LOAN DETAILS" and swap them to call stringResource(...) with the
existing resource keys used by the runtime UI (the Text usages surrounding
PerformanceHistoryCard and LoanSummaryTable), ensuring the preview uses the same
keys so localization changes reflect in previews.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt`:
- Around line 103-105: The fields currency.code and currency.decimalPlaces on
the loan object are nullable but are read into currencyCode and maxDigits
without fallbacks; update the LoanAccountGeneralViewModel.kt assignment for
currencyCode and maxDigits (where loan.currency.code and
loan.currency.decimalPlaces are read) to provide safe defaults (e.g., "N/A" for
code and 2 for decimal places) before any CurrencyFormatter.format() calls so
downstream formatting uses consistent non-null values.
- Around line 108-117: The calls to DateHelper.getDateAsString in
LoanAccountGeneralViewModel (for maturityDate and disbursementDate) may receive
lists with fewer than 3 elements (e.g., after
actualDisbursementDate.filterNotNull()) and can throw IndexOutOfBoundsException;
before calling DateHelper.getDateAsString on expectedMaturityDate and
actualDisbursementDate.filterNotNull(), guard the list length (or normalize it)
— if the list size is less than 3 return a safe default (empty string) or
pad/validate the list to 3 elements; update the maturityDate and
disbursementDate assignments to check size() or use a small helper that returns
"" when the date parts list is invalid to avoid crashing the screen.

---

Nitpick comments:
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt`:
- Around line 432-449: Replace hardcoded preview headings with the same
localized string resources used at runtime to prevent label drift; locate the
Text composables that currently use literal strings "PERFORMANCE HISTORY", "LOAN
SUMMARY", and "LOAN DETAILS" and swap them to call stringResource(...) with the
existing resource keys used by the runtime UI (the Text usages surrounding
PerformanceHistoryCard and LoanSummaryTable), ensuring the preview uses the same
keys so localization changes reflect in previews.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1f77c7a7-49ed-4d0b-bcab-334c67fce3b3

📥 Commits

Reviewing files that changed from the base of the PR and between 7df5d34 and c9ea3b6.

📒 Files selected for processing (2)
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt

@biplab1

biplab1 commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

@amanna13 The UI doesn't seem to be aligned with the UI of the Client Flow like typography. Please review.

Performance history is a section and not the title of the screen. It should be Loan Account General. The mockup in ticket says General. However, we should add "Loan Account" as prefix since the Client Flow also has "General" screen as well. You may take a look at the client flow's General screen ensure consistency.

image

@biplab1

biplab1 commented Mar 5, 2026

Copy link
Copy Markdown
Contributor

The table components are not being handled properly, please review. Like the "G" of Outstanding appears in the second line. Also the paddings on left and right end seem to be different.

Extreme Left:

Screenshot 2026-03-05 145652

Extreme Right:

Screenshot 2026-03-05 145702

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

♻️ Duplicate comments (3)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt (3)

111-112: ⚠️ Potential issue | 🟡 Minor

Add null-safety fallbacks for currencyCode and maxDigits.

The fields loan.currency.code and loan.currency.decimalPlaces are nullable, but are used directly in all CurrencyFormatter.format() calls throughout lines 145-208. Provide safe defaults to ensure consistent formatting.

🛡️ Suggested fix
     private suspend fun fillGeneralState(loan: LoanWithAssociationsEntity) {
-        val currencyCode = loan.currency.code
-        val maxDigits = loan.currency.decimalPlaces
+        val currencyCode = loan.currency.code ?: "$"
+        val maxDigits = loan.currency.decimalPlaces ?: 2
         val summary = loan.summary

Based on learnings: When a Kotlin function parameter is nullable and downstream calls require a non-null value, add null-safety handling with a safe default.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt`
around lines 111 - 112, The variables currencyCode and maxDigits in
LoanAccountGeneralViewModel are nullable (loan.currency.code and
loan.currency.decimalPlaces) but are passed directly into
CurrencyFormatter.format calls; add null-safety fallbacks (e.g., currencyCode ?:
"" and maxDigits ?: 0 or another appropriate default) where these values are
assigned or right before each CurrencyFormatter.format invocation so all format
calls use non-null arguments and avoid NPEs.

115-127: ⚠️ Potential issue | 🟠 Major

Guard date lists before calling DateHelper.getDateAsString.

At lines 117 and 124, DateHelper.getDateAsString(...) is called without ensuring the list has at least 3 elements. For actualDisbursementDate, filterNotNull() can reduce the list size below 3, causing IndexOutOfBoundsException.

🛡️ Safer date mapping
         val expectedMaturityDate = loan.timeline.expectedMaturityDate
-        val maturityDate = if (!expectedMaturityDate.isNullOrEmpty()) {
-            DateHelper.getDateAsString(expectedMaturityDate)
-        } else {
-            ""
-        }
+        val maturityDate = expectedMaturityDate
+            ?.takeIf { it.size >= 3 }
+            ?.let { runCatching { DateHelper.getDateAsString(it) }.getOrNull() }
+            .orEmpty()

         val actualDisbursementDate = loan.timeline.actualDisbursementDate
-        val disbursementDate = if (!actualDisbursementDate.isNullOrEmpty()) {
-            DateHelper.getDateAsString(actualDisbursementDate.filterNotNull())
-        } else {
-            ""
-        }
+        val disbursementDate = actualDisbursementDate
+            ?.filterNotNull()
+            ?.takeIf { it.size >= 3 }
+            ?.let { runCatching { DateHelper.getDateAsString(it) }.getOrNull() }
+            .orEmpty()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt`
around lines 115 - 127, The code calls DateHelper.getDateAsString on
expectedMaturityDate and actualDisbursementDate without ensuring the underlying
lists contain the expected 3 elements, which can throw IndexOutOfBoundsException
(especially after actualDisbursementDate.filterNotNull()); update the mapping in
LoanAccountGeneralViewModel so you first validate the list size (and after
filterNotNull for actualDisbursementDate) has at least 3 elements before calling
DateHelper.getDateAsString, otherwise return an empty string or a safe default;
reference the symbols expectedMaturityDate, actualDisbursementDate,
DateHelper.getDateAsString to locate and guard the calls.

145-147: ⚠️ Potential issue | 🟠 Major

proposedAmount and approvedAmount use identical source field.

Both proposedAmount (line 145) and approvedAmount (line 146) are populated from loan.approvedPrincipal, which appears incorrect. Based on past discussion, clarify with the team whether loan.principal should map to proposedAmount or if LoanWithAssociationsEntity needs a new proposedPrincipal field.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt`
around lines 145 - 147, proposedAmount and approvedAmount are both set from
loan.approvedPrincipal; update the mapping so proposedAmount uses the intended
source (likely loan.principal) or, if the domain requires a distinct proposed
value, add a proposedPrincipal to LoanWithAssociationsEntity and map
proposedAmount to that field; adjust the assignments in
LoanAccountGeneralViewModel (the proposedAmount and approvedAmount assignments)
and coordinate with the team if a new proposedPrincipal property must be
introduced on LoanWithAssociationsEntity before changing the mapping.
🧹 Nitpick comments (2)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt (2)

65-84: onAction lambda is unused in LoanAccountGeneralContent.

The onAction lambda created at line 72 is only passed to LoanAccountGeneralDialogs but not to LoanAccountGeneralContent. This is fine for now since content doesn't need actions, but the remember block adds unnecessary overhead given it's only used for dialogs.

Consider simplifying:

♻️ Suggested simplification
 `@Composable`
 internal fun LoanAccountGeneralScreen(
     navController: NavController,
     modifier: Modifier = Modifier,
     viewModel: LoanAccountGeneralViewModel = koinViewModel(),
 ) {
     val state by viewModel.stateFlow.collectAsStateWithLifecycle()
-    val onAction = remember(viewModel) { { action: LoanAccountGeneralAction -> viewModel.trySendAction(action) } }

     LoanAccountGeneralContent(
         state = state,
         navController = navController,
         modifier = modifier,
     )

     LoanAccountGeneralDialogs(
         state = state,
-        onAction = onAction,
+        onAction = viewModel::trySendAction,
     )
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt`
around lines 65 - 84, The remembered onAction lambda in LoanAccountGeneralScreen
is unused by LoanAccountGeneralContent and only passed to
LoanAccountGeneralDialogs, so remove the unnecessary remember and local onAction
variable and instead pass a direct lambda (or method reference) to
LoanAccountGeneralDialogs; update LoanAccountGeneralScreen to call
LoanAccountGeneralDialogs with a concise callback (e.g., { action ->
viewModel.trySendAction(action) } or viewModel::trySendAction) and keep
LoanAccountGeneralContent as-is.

197-214: TextStyle default may lose typography properties.

The default textStyle at lines 202-205 only copies fontStyle from KptTheme.typography.labelMedium, losing other properties like fontSize, fontWeight, letterSpacing, and lineHeight. This could result in inconsistent text rendering.

♻️ Suggested fix
 `@Composable`
 private fun PerformanceHistoryRow(
     label: String,
     value: String,
     modifier: Modifier = Modifier,
-    textStyle: TextStyle = TextStyle(
-        color = AppColors.customWhite,
-        fontStyle = KptTheme.typography.labelMedium.fontStyle,
-    ),
+    textStyle: TextStyle = KptTheme.typography.labelMedium.copy(
+        color = AppColors.customWhite,
+    ),
 ) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt`
around lines 197 - 214, The default textStyle for PerformanceHistoryRow
currently constructs a new TextStyle with only fontStyle set, which discards
other typography properties; change the default to derive from the theme by
using KptTheme.typography.labelMedium and overriding only the color (and
optionally fontStyle) — e.g. set the textStyle default to
KptTheme.typography.labelMedium.copy(color = AppColors.customWhite) or
KptTheme.typography.labelMedium.merge(TextStyle(color = AppColors.customWhite))
so that fontSize, fontWeight, letterSpacing, lineHeight, etc., are preserved for
the Text composables in PerformanceHistoryRow.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt`:
- Around line 111-112: The variables currencyCode and maxDigits in
LoanAccountGeneralViewModel are nullable (loan.currency.code and
loan.currency.decimalPlaces) but are passed directly into
CurrencyFormatter.format calls; add null-safety fallbacks (e.g., currencyCode ?:
"" and maxDigits ?: 0 or another appropriate default) where these values are
assigned or right before each CurrencyFormatter.format invocation so all format
calls use non-null arguments and avoid NPEs.
- Around line 115-127: The code calls DateHelper.getDateAsString on
expectedMaturityDate and actualDisbursementDate without ensuring the underlying
lists contain the expected 3 elements, which can throw IndexOutOfBoundsException
(especially after actualDisbursementDate.filterNotNull()); update the mapping in
LoanAccountGeneralViewModel so you first validate the list size (and after
filterNotNull for actualDisbursementDate) has at least 3 elements before calling
DateHelper.getDateAsString, otherwise return an empty string or a safe default;
reference the symbols expectedMaturityDate, actualDisbursementDate,
DateHelper.getDateAsString to locate and guard the calls.
- Around line 145-147: proposedAmount and approvedAmount are both set from
loan.approvedPrincipal; update the mapping so proposedAmount uses the intended
source (likely loan.principal) or, if the domain requires a distinct proposed
value, add a proposedPrincipal to LoanWithAssociationsEntity and map
proposedAmount to that field; adjust the assignments in
LoanAccountGeneralViewModel (the proposedAmount and approvedAmount assignments)
and coordinate with the team if a new proposedPrincipal property must be
introduced on LoanWithAssociationsEntity before changing the mapping.

---

Nitpick comments:
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt`:
- Around line 65-84: The remembered onAction lambda in LoanAccountGeneralScreen
is unused by LoanAccountGeneralContent and only passed to
LoanAccountGeneralDialogs, so remove the unnecessary remember and local onAction
variable and instead pass a direct lambda (or method reference) to
LoanAccountGeneralDialogs; update LoanAccountGeneralScreen to call
LoanAccountGeneralDialogs with a concise callback (e.g., { action ->
viewModel.trySendAction(action) } or viewModel::trySendAction) and keep
LoanAccountGeneralContent as-is.
- Around line 197-214: The default textStyle for PerformanceHistoryRow currently
constructs a new TextStyle with only fontStyle set, which discards other
typography properties; change the default to derive from the theme by using
KptTheme.typography.labelMedium and overriding only the color (and optionally
fontStyle) — e.g. set the textStyle default to
KptTheme.typography.labelMedium.copy(color = AppColors.customWhite) or
KptTheme.typography.labelMedium.merge(TextStyle(color = AppColors.customWhite))
so that fontSize, fontWeight, letterSpacing, lineHeight, etc., are preserved for
the Text composables in PerformanceHistoryRow.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 47b32120-947c-4c4d-bf2f-8cd21c66eeaa

📥 Commits

Reviewing files that changed from the base of the PR and between c9ea3b6 and 5e10cef.

📒 Files selected for processing (3)
  • feature/loan/src/commonMain/composeResources/values/strings.xml
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralScreen.kt
  • feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanAccountGeneral/LoanAccountGeneralViewModel.kt

@biplab1

biplab1 commented Mar 23, 2026

Copy link
Copy Markdown
Contributor

@amanna13 Please update the screen recording if UI has changed after the last changes.

@amanna13 amanna13 requested a review from biplab1 April 3, 2026 08:40
biplab1
biplab1 previously approved these changes Apr 6, 2026

@biplab1 biplab1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good to me. This can be merged.

@amanna13 amanna13 requested review from kartikey004 and niyajali April 6, 2026 13:23
Comment on lines +28 to +38
override fun getLoanById(loanId: Int): Flow<DataState<LoanWithAssociations?>> {
return dataManagerLoan.getLoanById(loanId)
.asDataStateFlow()
.map { response ->
when (response) {
is DataState.Loading -> DataState.Loading
is DataState.Error -> DataState.Error(response.exception, response.data?.toModel())
is DataState.Success -> DataState.Success(response.data?.toModel())
}
}
.flowOn(ioDispatcher)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

should map before converting it to datastate flow. not after that

something like

    override fun getLoanById(loanId: Int): Flow<DataState<LoanWithAssociations?>> {
        return dataManagerLoan.getLoanById(loanId)::mapToModel
            .asDataStateFlow()
            .flowOn(ioDispatcher)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I checked the repo pattern across modules. The common convention here is to wrap with asDataStateFlow() first, and then map if needed.

@itsPronay itsPronay Apr 7, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

could you please tell which module you took reference from?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Sorry for that, my mistake, I overlooked. I have fixed this now.

Comment on lines +26 to +48
data class LoanWithAssociations(
val id: Int = 0,
val accountNo: String = "",
val status: LoanStatus = LoanStatus(),
val clientId: Int = 0,
val clientName: String = "",
val clientOfficeId: Int = 0,
val loanProductId: Int = 0,
val loanProductName: String = "",
val loanProductDescription: String = "",
val fundId: Int = 0,
val fundName: String = "",
val loanPurposeId: Int = 0,
val loanPurposeName: String = "",
val loanOfficerId: Int = 0,
val loanOfficerName: String = "",
val loanType: LoanType = LoanType(),
val currency: SavingAccountCurrency = SavingAccountCurrency(),
val principal: Double = 0.0,
val approvedPrincipal: Double = 0.0,
val termFrequency: Int = 0,
val termPeriodFrequencyType: TermPeriodFrequencyType = TermPeriodFrequencyType(),
val numberOfRepayments: Int = 0,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

are we supossed to have empty values?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yes this kind of default-value pattern is used in other modules too.

@biplab1 Can you also please verify and confirm.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@amanna13 I checked mifos-mobile, it has the fields nullable and I think they can be null and it should be decided in the presentation layer how the null values should be handled.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

yes this kind of default-value pattern is used in other modules too.

It should not have a default value in the first place. Default values should be either null, an empty list, or something similar.

I’m not sure which module you took this reference from, but please create a Jira ticket so we can address it later.

Also, try to take references from the KMP template instead of other modules, as it is almost always up to date and we generally follow its structure.

Comment on lines +90 to +109
@Parcelize
@Serializable
data class LoanType(
val id: Int? = null,
val code: String? = null,
val value: String? = null,
) : Parcelable

@Parcelize
@Serializable
data class SavingAccountCurrency(
val id: Int = 0,
val code: String? = null,
val name: String? = null,
val decimalPlaces: Int? = null,
val inMultiplesOf: Int? = null,
val displaySymbol: String? = null,
val nameCode: String? = null,
val displayLabel: String? = null,
) : Parcelable

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

do not put all the classes in a single file. Do it separtely.

create a package inside loan called loanwithAssociations and put all the files there.

Comment on lines +134 to +146
@Serializable
data class ActualDisbursementDate(
val loanId: Int? = null,
val year: Int? = null,
val month: Int? = null,
val date: Int? = null,
) : Parcelable

@Parcelize
@Serializable
data class LoansAccountSummary(
val loanId: Int? = null,
val currency: SavingAccountCurrency? = null,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

use separate file

Comment on lines +64 to +70
private fun observeNetwork() {
viewModelScope.launch {
networkMonitor.isOnline.collect { isConnected ->
mutableStateFlow.update { it.copy(networkConnection = isConnected) }
}
}
}

@itsPronay itsPronay Apr 6, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I dont see a valid reason to continuously observe network state. we should only check connectivity when making network requests....

Current behavior:
After the user fetches loan details, the data is displayed correctly. However, if the network connection is lost, all the displayed items disappear and a “no internet” screen is shown.

My question - why should users need an active internet connection to view or scroll through data that has already been fetched?>>

we are already showing a snackbar to indicate loss of connectivity, so this additional thing impacts user experience.
Only check the network state before making an API call and show a dialog /screen

@amanna13 amanna13 Apr 6, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I’ve verified the codebase has mixed patterns: some screens observe continuously, while others do one-shot connectivity checks before making a API calls. For instance ClientProfileGeneralViewModel and some LoanAccounts screen observes continously
Why haven't we yet standardize across modules, is there any specific reason we are using different approaches ?

Though I agree totally to the fact that there's no need to continuosly check for network state and I'm doing the necessary changes.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

  • I think most contributors implemented it by taking reference from Mifos Mobile, where we followed that approach.
  • While reviewing PRs, I may have suggested to some contributors to use the one-shot approach, so they adopted that instead.

Hence, the difference.

Yeah, you are right about it. We should standardise it.

@niyajali niyajali left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

and the networkMontior could be moved in to the repository itself, and you can check the status before performing any action on other methods

private fun loadLoanById() {
loadJob?.cancel()
loadJob = viewModelScope.launch {
val isConnected = networkMonitor.isOnline.first()

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

use kotlin combine operator function to combine the two streams into a single stream, and that way it will refeed the data from both sources when back online

Comment on lines +156 to +168
details = listOf(
mapOf(
Res.string.feature_loan_general_detail_disbursement_date to disbursementDate,
Res.string.feature_loan_general_detail_loan_purpose to loanPurpose,
Res.string.feature_loan_general_detail_loan_officer to loanOfficer,
Res.string.feature_loan_general_detail_currency to currencyDisplay,
),
mapOf(
Res.string.feature_loan_general_detail_proposed_amount to CurrencyFormatter.format(loan.approvedPrincipal, currencyCode, maxDigits),
Res.string.feature_loan_general_detail_approved_amount to CurrencyFormatter.format(loan.approvedPrincipal, currencyCode, maxDigits),
Res.string.feature_loan_general_detail_disbursed_amount to CurrencyFormatter.format(loan.principal, currencyCode, maxDigits),
),
),

@biplab1 biplab1 Apr 9, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

One of the issues of using a Map is it is unstructured, so type safety could be a problem.
Instead of Map, we can use,
a proper model:

data class DetailItem(
    val label: StringResource,
    val value: String
)

or even better:

sealed interface LoanDetailItem

Comment on lines +153 to +154
proposedAmount = CurrencyFormatter.format(loan.approvedPrincipal, currencyCode, maxDigits),
approvedAmount = CurrencyFormatter.format(loan.approvedPrincipal, currencyCode, maxDigits),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

How can both use the same field approvedPrincipal?

amanna13 and others added 26 commits May 24, 2026 13:52
fix : merge conflicts resolved

fix : merge conflicts resolved

fix : merge conflicts resolved
fix : dto and mappers added
@Kartikey15dem Kartikey15dem force-pushed the feat/general-tab-loan-section branch from 05df700 to 92a8106 Compare May 24, 2026 08:50
@sonarqubecloud

Copy link
Copy Markdown

@biplab1

biplab1 commented May 27, 2026

Copy link
Copy Markdown
Contributor

@Kartikey15dem I don't think this is the correct way to handle the requested changes.

This PR shouldn't contain changes from other modules. We should preserve the older data classes for modules which are outside the scope of this PR, and introduce new data classes as requested for this module only. Later, other modules will be refactored in separate PRs. Please review.

@Kartikey15dem

Copy link
Copy Markdown
Contributor

@Kartikey15dem I don't think this is the correct way to handle the requested changes.

This PR shouldn't contain changes from other modules. We should preserve the older data classes for modules which are outside the scope of this PR, and introduce new data classes as requested for this module only. Later, other modules will be refactored in separate PRs. Please review.

@biplab1 These changes came as a result of refactoring LoanWithAssociationsDto. That refactor introduced a series of dependent changes which were required to keep the app running correctly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants