Skip to content

feat(loan): Loan Transaction Table#2603

Open
sahilshivekar wants to merge 1 commit intoopenMF:developmentfrom
sahilshivekar:feature/MIFOSAC-651-Loan-Transaction-Table
Open

feat(loan): Loan Transaction Table#2603
sahilshivekar wants to merge 1 commit intoopenMF:developmentfrom
sahilshivekar:feature/MIFOSAC-651-Loan-Transaction-Table

Conversation

@sahilshivekar
Copy link

@sahilshivekar sahilshivekar commented Feb 6, 2026

Jira-MIFOSAC-651

Before After
image
WhatsApp.Video.2026-02-11.at.19.13.24.mp4

Summary by CodeRabbit

  • New Features

    • Redesigned loan transactions from card-based to a scrollable table with breakdown columns and per-row actions (undo, view receipts, view journal entries, view details).
    • New reusable table-row UI component and new size tokens for small/medium/large table cells.
    • Added model fields to surface outstanding balance and manual reversal status.
  • Localization

    • Added string resources for table headers and transaction actions.

@coderabbitai
Copy link

coderabbitai bot commented Feb 6, 2026

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

Walkthrough

Adds a reusable composable MifosTableRow and size tokens, extends Transaction model, adds localization strings, and refactors the LoanTransactions screen, UI state, and ViewModel to render transactions in a multi-column, scrollable table with per-row actions and a bottom sheet.

Changes

Cohort / File(s) Summary
Design System
core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosTableRow.kt, core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/theme/DesignToken.kt
Added MifosTableRow composable (horizontally scrollable, per-cell fixed widths, optional top/bottom/side dividers, click handling). Added smallTableCellWidth, mediumTableCellWidth, largeTableCellWidth to DesignToken.AppSizes.
Core Model
core/model/src/commonMain/kotlin/com/mifos/core/model/objects/account/loan/Transaction.kt
Added optional properties outstandingLoanBalance: Double? and manuallyReversed: Boolean? to Transaction.
Localization
feature/loan/src/commonMain/composeResources/values/strings.xml
Added table header and transaction action string resources (multiple new labels for table columns and actions).
Loan Transactions Feature
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt, .../LoanTransactionsUiState.kt, .../LoanTransactionsViewModel.kt
Refactored from card-based to table-based UI using MifosTableRow. Introduced LoanTransactionsTableContent, TransactionRow, TransactionActionsBottomSheet, new UI state types (LoanTransactionsTableData, TransactionRowData), enums (TransactionType, TransactionAction), and ViewModel APIs (onRowAction, dismissBottomSheet, onActionSelected) to support row selection and bottom-sheet actions. Updated previews and data mapping/formatting in ViewModel.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Screen as LoanTransactionsScreen
    participant VM as LoanTransactionsViewModel
    participant State as LoanTransactionsUiState
    participant Sheet as TransactionActionsBottomSheet

    User->>Screen: open transactions screen
    Screen->>VM: load transactions
    VM->>VM: map & format transactions → transactionsTableData
    VM->>State: set transactionsTableData
    State-->>Screen: render table

    User->>Screen: tap row
    Screen->>VM: onRowAction(row)
    VM->>State: set selectedRow & isBottomSheetOpen=true
    State-->>Sheet: show actions

    User->>Sheet: choose action
    Sheet->>VM: onActionSelected(action, id)
    VM->>State: dismissBottomSheet()
    VM->>VM: perform action handling
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Suggested reviewers

  • biplab1

Poem

🐰 I hopped along rows, each cell in its place,
Scrolls that I nibbled with elegant grace.
Actions that pop and balances shown,
I twitched my whiskers — this table's my home. ✨

🚥 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
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat(loan): Loan Transaction Table' clearly describes the primary change—adding a transaction table UI to the loan feature, which aligns with the comprehensive refactoring of the loan transactions screen from card-based to table-based layout.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In
`@core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosTableRow.kt`:
- Line 33: The parameter cellsBackgroundColor on MifosTableRow is declared but
never applied to each cell's Box; update the cell rendering loop in
MifosTableRow to apply a background color per cell by reading
cellsBackgroundColor.getOrNull(index) ?: Color.Transparent and adding
Modifier.background(thatColor) (or the appropriate Compose background API) to
the existing Box modifier for each cell so callers' colors are actually
rendered; ensure indexes align with the cells List and preserve existing
modifiers like padding/weight.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt`:
- Around line 418-424: The current TransactionActionsBottomSheet is routing user
selection by passing a human-readable displayName string to onAction which is
fragile; change the signature of TransactionActionsBottomSheet's onAction from
(String) -> Unit to (TransactionAction) -> Unit and pass the actual
TransactionAction value (instead of action.displayName) from the UI, then update
the caller site(s) and the ViewModel method onActionSelected to accept a
TransactionAction parameter rather than String so routing uses the enum
directly; adjust any related usages (including the other occurrence around lines
469-471) to compile with the new type.
- Around line 102-103: The inner composable LoanTransactionsScreen should not
receive the viewModel directly; hoist the remaining ViewModel interactions by
adding lambda parameters (e.g., onDismissBottomSheet: () -> Unit and
onActionSelected: (action: ActionType, id: String) -> Unit) to its signature and
invocation, remove the viewModel parameter usage in the call site, and replace
calls to viewModel?.dismissBottomSheet() and viewModel?.onActionSelected(...)
inside the inner composable with the new onDismissBottomSheet() and
onActionSelected(...) callbacks; update the outer composable to pass
viewModel::dismissBottomSheet and viewModel::onActionSelected (or equivalent
lambdas) so previews/tests can supply mocks.
- Around line 151-163: The bottom sheet is rendered even when
uiState.selectedRow can be null, causing TransactionActionsBottomSheet to
receive empty fallbacks and produce an action-less UI; update the conditional to
only render the sheet when both uiState.isBottomSheetOpen and
uiState.selectedRow != null (use the non-null selectedRow to supply
transactionType, manuallyReversed and id to TransactionActionsBottomSheet and to
viewModel?.onActionSelected) so the component never receives empty defaults and
always has a valid selectedRow to act on.
- Around line 362-393: The background logic in textCells uses Color.Unspecified
and a magic range 6..10; replace Color.Unspecified with Color.Transparent to
avoid using a non-drawable sentinel, and replace the hard-coded range with a
named constant or predicate (e.g., BREAKDOWN_COLUMN_INDICES or
isBreakdownColumn(index)) used inside the map so the intent is explicit and
resilient to column reorder; update the background condition in the textCells
mapping to check that named constant/predicate instead of the literal 6..10.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsViewModel.kt`:
- Line 56: DateHelper.getDateAsString currently dereferences transaction.date
list indices directly which will throw IndexOutOfBoundsException when
transaction.date is empty or incomplete; update DateHelper.getDateAsString to
first validate the input list (check for null and size >= 3 or handle
variable-length dates) and return a safe fallback (e.g., empty string or
formatted partial date) when data is missing, and ensure callers like
LoanTransactionsViewModel when mapping transactionDate =
DateHelper.getDateAsString(transaction.date) will receive the safe value instead
of crashing.
🧹 Nitpick comments (6)
core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosTableRow.kt (1)

50-63: Consider setting verticalAlignment on the Row.

The Row defaults to Alignment.Top. For a table, Alignment.CenterVertically is typically more appropriate to keep cell content aligned when heights vary.

 Row(
     modifier = Modifier
         .horizontalScroll(scrollState),
+    verticalAlignment = Alignment.CenterVertically,
 ) {
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsUiState.kt (1)

20-25: Consider separating bottom sheet / selection state from data state.

selectedRow and isBottomSheetOpen are transient UI interaction state mixed into the ShowLoanTransaction data class. Every bottom sheet toggle creates a new copy of the state, potentially triggering broader recomposition. For a small screen this is acceptable, but if the screen grows in complexity, consider extracting these into a separate state holder.

feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsViewModel.kt (1)

119-122: onActionSelected is a no-op with a TODO.

The method accepts raw String parameters instead of leveraging the TransactionAction enum defined in the UI state file. When implementing the TODO, consider using the enum for type safety.

-fun onActionSelected(action: String, id: String) {
-    // TODO: Handle the action based on action string and id
-    dismissBottomSheet()
-}
+fun onActionSelected(action: TransactionAction, id: String) {
+    when (action) {
+        TransactionAction.UNDO_TRANSACTION -> { /* TODO */ }
+        TransactionAction.VIEW_RECEIPTS -> { /* TODO */ }
+        TransactionAction.VIEW_JOURNAL_ENTRIES -> { /* TODO */ }
+        TransactionAction.VIEW_TRANSACTION -> { /* TODO */ }
+    }
+    dismissBottomSheet()
+}

Do you want me to open a follow-up issue to track the implementation of onActionSelected actions?

feature/loan/src/commonMain/composeResources/values/strings.xml (1)

153-153: Consider adding string resources for bottom sheet action labels.

The TransactionAction enum in LoanTransactionsUiState.kt uses hardcoded English strings ("Undo Transaction", "View Receipts", etc.) for user-facing action labels. For i18n support, these should be defined as string resources here and resolved via stringResource() in the composable layer.

feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt (2)

253-319: Header background color computed identically in three places.

The lerp(MaterialTheme.colorScheme.surface, MaterialTheme.colorScheme.primary, 0.3f) expression is repeated at lines 259, 302, and a similar one at line 369–372 (with different fraction). Extract the header color into a local variable at the top of LoanTransactionsTableContent:

Proposed fix
+    val headerBackground = lerp(
+        MaterialTheme.colorScheme.surface,
+        MaterialTheme.colorScheme.primary,
+        0.3f,
+    )

Then use headerBackground in both header Box modifiers instead of repeating the lerp(...) call.


507-508: Remove commented-out preview states.

These commented-out entries (ShowFetchingError, ShowProgressBar) should either be re-enabled to preview all UI states or removed to keep the code clean. Leaving them commented out adds no value and clutters the file.

@sahilshivekar sahilshivekar force-pushed the feature/MIFOSAC-651-Loan-Transaction-Table branch 2 times, most recently from 0f87c40 to a6fe084 Compare February 6, 2026 12:51
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt`:
- Line 139: The code force-unwraps uiState.transactionsTableData with "!!" in
the conditional (if (uiState.transactionsTableData!!.transactions.isEmpty())),
which will crash if transactionsTableData is null; update the check to safely
handle null by either (a) changing the UiState/ShowLoanTransaction so
transactionsTableData is non-null and remove the "!!" usage, or (b) perform a
safe-access/null-coalescing check (e.g., use
uiState.transactionsTableData?.transactions.isNullOrEmpty() or check for null
before accessing transactions) to avoid the force-unwrap; locate references to
transactionsTableData and the conditional in LoanTransactionsScreen.kt to
implement the chosen fix.
- Around line 462-468: The getTextForAction function currently returns
feature_loan_transaction_action_undo for every TransactionAction; update the
when branches in getTextForAction to return the correct stringResource for each
action instead of reusing the undo string—map TransactionAction.VIEW_TRANSACTION
-> feature_loan_transaction_action_view_transaction,
TransactionAction.UNDO_TRANSACTION ->
feature_loan_transaction_action_undo_transaction,
TransactionAction.VIEW_RECEIPTS ->
feature_loan_transaction_action_view_receipts, and
TransactionAction.VIEW_JOURNAL_ENTRIES ->
feature_loan_transaction_action_view_journal_entries (use the corresponding
existing resource names in your project if they differ).

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsUiState.kt`:
- Around line 18-22: Make transactionsTableData non-null to encode the invariant
instead of relying on !! in the UI: change the ShowLoanTransaction data class
field transactionsTableData from LoanTransactionsTableData? to non-null
LoanTransactionsTableData with a sensible default (e.g., an empty/initial
LoanTransactionsTableData instance) and update any callers constructing
ShowLoanTransaction (e.g., ViewModel places that set this state) to pass that
default when needed; alternatively, if you prefer guarding in the UI, remove the
!! in LoanTransactionsScreen and null-check or early-return when
transactionsTableData is null. Ensure references to ShowLoanTransaction and
LoanTransactionsScreen are updated accordingly.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsViewModel.kt`:
- Around line 43-100: In LoanTransactionsViewModel inside the DataState.Success
branch, add null-safe fallbacks when reading loanWithAssociations.currency:
replace direct accesses to currency.code and currency.decimalPlaces with safe
values (e.g., val currencyCode = loanWithAssociations.currency.code ?:
"<fallback>" and val maxDigits = loanWithAssociations.currency.decimalPlaces ?:
<defaultInt>) before mapping transactions; ensure those fallback values are then
passed to CurrencyFormatter.format(...) so CurrencyFormatter.format is never
called with null currencyCode or null maxDigits. Reference symbols:
LoanTransactionsViewModel, loanWithAssociations, currency.code,
currency.decimalPlaces, CurrencyFormatter.format, _loanTransactionsUiState,
LoanTransactionsUiState.
🧹 Nitpick comments (1)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsUiState.kt (1)

45-55: fromValue returns null — prefer returning UNKNOWN to eliminate null-handling at every call site.

Suggested fix
 companion object {
-    fun fromValue(value: String): TransactionType? = entries.find { it.value == value }
+    fun fromValue(value: String): TransactionType = entries.find { it.value == value } ?: UNKNOWN
 }

This also simplifies the call site in the ViewModel.

@sahilshivekar sahilshivekar force-pushed the feature/MIFOSAC-651-Loan-Transaction-Table branch 3 times, most recently from 47e09e2 to 94471eb Compare February 6, 2026 13:17
@sahilshivekar sahilshivekar force-pushed the feature/MIFOSAC-651-Loan-Transaction-Table branch from 94471eb to f679ca6 Compare February 7, 2026 03:45
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt`:
- Around line 164-167: The current onAction handler calls
uiState.selectedRow?.id?.let { id -> onTransactionActionClick(action,
id.toInt()) } which will throw NumberFormatException when id is the "-"
placeholder; change the conversion to use id.toIntOrNull() and guard the null
case (e.g., return early or skip calling onTransactionActionClick) so only valid
integers are passed to onTransactionActionClick; locate this in the onAction
lambda referencing uiState.selectedRow?.id and replace the toInt() conversion
with a safe toIntOrNull() check.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsViewModel.kt`:
- Around line 51-53: The current mapping sets TransactionRowData.id to the
string "-" when transaction.id is null which later causes NumberFormatException
when parsed with toInt(); change the mapping to preserve the numeric nature by
storing id as Int? in
LoanTransactionsUiState.LoanTransactionsTableData.TransactionRowData (assign id
= transaction.id) and update all places that consume TransactionRowData.id
(notably the LoanTransactionsScreen consumer that calls id.toInt()) to handle
nullable Ints directly (or use safe null checks) instead of parsing strings;
alternatively, if you must keep id as String, update the consumer to use
toIntOrNull() and handle nulls safely—adjust the TransactionRowData declaration
and all call sites accordingly.
🧹 Nitpick comments (3)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsUiState.kt (1)

52-54: fromValue returns TransactionType? but callers always fall back to UNKNOWN.

Since every call site uses ?: TransactionType.UNKNOWN, consider returning TransactionType directly with UNKNOWN as the default. This eliminates unnecessary null handling at every call site.

Proposed fix
     companion object {
-        fun fromValue(value: String): TransactionType? = entries.find { it.value == value }
+        fun fromValue(value: String): TransactionType = entries.find { it.value == value } ?: UNKNOWN
     }
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt (2)

224-339: Header construction relies on positional magic indices (index == 6, empty-string placeholders).

The two-tier header uses positional index checks (line 275: index == 6 for center alignment) and a list of empty strings for non-breakdown columns in row 2 (lines 291-303). This is fragile — any column reorder will silently break alignment and styling. Consider defining a column descriptor (e.g., a data class with header, subHeader, width, isBreakdown) and deriving both header rows and data rows from it.


498-502: dismissBottomSheet is called twice per action selection.

onAction(action) routes to viewModel.onActionSelected(...) which internally calls dismissBottomSheet(). Then line 501 calls onDismissRequest() which also calls dismissBottomSheet(). The state update is idempotent so this won't break, but the explicit onDismissRequest() call on line 501 is redundant.

Proposed fix
                        .clickable {
                            onAction(action)
-                            onDismissRequest()
                        }

@kartikey004
Copy link
Contributor

@sahilshivekar rest LGTM. 👍

@techsavvy185
Copy link
Contributor

@sahilshivekar I think it looks good, but I think you can make one more addition, you can add a spacer on the sides of the table so that it looks like a card that starts and also a spacer on the right side such that you can see the spacer after you scroll completely to the right and round the corners of the table. This way the heading won't look very close to the edge of the screen.

@sahilshivekar
Copy link
Author

@sahilshivekar I think it looks good, but I think you can make one more addition, you can add a spacer on the sides of the table so that it looks like a card that starts and also a spacer on the right side such that you can see the spacer after you scroll completely to the right and round the corners of the table. This way the heading won't look very close to the edge of the screen.

I did exactly that initially but Rajan sir told me to remove it during daily standup

@techsavvy185
Copy link
Contributor

@sahilshivekar No, I'm not talking about that, I'm talking about 2 spacer in a row with the table not padding such that it feels like a card sliding into frame.

@biplab1
Copy link
Contributor

biplab1 commented Feb 11, 2026

@sahilshivekar I think we can get rid of the Actions column, instead clicking anywhere in a row should bring up the Actions bottom sheet.

@sahilshivekar
Copy link
Author

@sahilshivekar No, I'm not talking about that, I'm talking about 2 spacer in a row with the table not padding such that it feels like a card sliding into frame.

I got you now, nice idea

@sahilshivekar sahilshivekar force-pushed the feature/MIFOSAC-651-Loan-Transaction-Table branch from f679ca6 to 38dbf5e Compare February 11, 2026 13:47
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In
`@core/designsystem/src/commonMain/kotlin/com/mifos/core/designsystem/component/MifosTableRow.kt`:
- Around line 63-84: The loop in MifosTableRow using cells.forEachIndexed reads
widths[index] directly which can throw IndexOutOfBounds; change to a safe lookup
(e.g., widths.getOrNull(index) with a sensible fallback width) and assign that
to cellWidth before creating the Box; also make HorizontalDivider conditional on
the same showBorders flag (so HorizontalDivider is only rendered when
showBorders is true), and keep the existing VerticalDivider behavior including
the trailing vertical divider when index == cells.lastIndex.
- Around line 71-77: The HorizontalDivider is currently stacked at the top
because Box stacks children; update the MifosTableRow cell implementation so the
divider is positioned at the bottom — either replace Box(modifier =
Modifier.width(cellWidth)) with a Column and place HorizontalDivider after
content(), or keep the Box and add a Modifier.align(Alignment.BottomCenter) (or
BottomStart) to the HorizontalDivider; reference symbols: HorizontalDivider,
Box, content(), cellWidth, borderColor to locate and apply the change.

In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt`:
- Around line 187-215: columnWidths currently defines 13 widths (including a
trailing "actions" smallWidth) but TransactionRow and headersRow2 only render 12
cells, leaving the last width unused; fix by either removing the trailing
actions entry from columnWidths (delete the final smallWidth) or add a
corresponding actions cell in TransactionRow and headersRow2 (e.g., render an
action/composable at the same final index) so the widths and rendered cells
align; reference columnWidths, TransactionRow, and headersRow2 to locate and
apply the change.
- Around line 476-479: The click handler is calling two dismiss paths
redundantly: onAction(action) -> viewModel.onActionSelected(action, id) which
already calls dismissBottomSheet(), and then onDismissRequest() ->
viewModel.dismissBottomSheet() again; remove the redundant explicit dismiss by
deleting the onDismissRequest() call from the clickable lambda (or only call
onAction and not call onDismissRequest there) so the dismissal is performed once
via viewModel.onActionSelected; update the clickable block where
onAction(action) and onDismissRequest() are invoked to only invoke
onAction(action).
🧹 Nitpick comments (2)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsUiState.kt (1)

52-54: fromValue returns nullable but UNKNOWN already serves as the fallback.

Since there's an UNKNOWN enum value, fromValue could return a non-null TransactionType directly, eliminating the need for callers to handle nullability.

Proposed simplification
 companion object {
-    fun fromValue(value: String): TransactionType? = entries.find { it.value == value }
+    fun fromValue(value: String): TransactionType = entries.find { it.value == value } ?: UNKNOWN
 }

Then in the ViewModel (line 63-65):

-transactionType = transaction.type?.value?.let {
-    TransactionType.fromValue(it)
-} ?: TransactionType.UNKNOWN,
+transactionType = transaction.type?.value?.let {
+    TransactionType.fromValue(it)
+} ?: TransactionType.UNKNOWN,
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt (1)

96-99: Accessing viewModel.viewModelScope from the UI layer breaks encapsulation.

The onRetry lambda reaches into the ViewModel's coroutine scope. Consider making loadLoanTransaction non-suspend (launching internally) or exposing a dedicated retry() method.

Proposed approach — add a retry function in the ViewModel

In LoanTransactionsViewModel:

fun retry() {
    viewModelScope.launch { loadLoanTransaction() }
}

Then in the outer composable:

-        onRetry = {
-            viewModel.viewModelScope.launch {
-                viewModel.loadLoanTransaction()
-            }
-        },
+        onRetry = { viewModel.retry() },

@sahilshivekar sahilshivekar force-pushed the feature/MIFOSAC-651-Loan-Transaction-Table branch from 38dbf5e to c53563f Compare February 11, 2026 17:24
@sonarqubecloud
Copy link

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsUiState.kt`:
- Around line 52-54: The companion object's fromValue currently returns null for
unrecognized strings; update TransactionType.fromValue to return the explicit
fallback TransactionType.UNKNOWN instead of null by falling back when
entries.find { it.value == value } yields null (e.g., use the elvis operator or
equivalent to return UNKNOWN).
🧹 Nitpick comments (2)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt (2)

96-99: Inconsistent invocation of loadLoanTransaction.

On line 90, loadLoanTransaction() is called directly inside LaunchedEffect (a coroutine scope). On line 97-99, it's wrapped in viewModel.viewModelScope.launch. If the function is a suspend function, the onRetry lambda (which is not a coroutine scope) needs the launch wrapper—but then the outer composable leaks ViewModel implementation details. If it's a regular function that launches internally, the viewModelScope.launch wrapper is redundant.

Consider making onRetry consistent with the LaunchedEffect call by hoisting the coroutine launch.


282-295: Second header row uses empty strings as spacers — consider using explicit empty composables.

The headersRow2 list uses 6 empty strings and 5 header strings, relying on string content to determine background color (line 304). This couples visual styling to string emptiness, which is fragile. A more explicit approach (e.g., a sealed type distinguishing empty/header cells, or at minimum a named constant for the empty placeholder) would make the intent clearer.

@sahilshivekar
Copy link
Author

@sahilshivekar I think we can get rid of the Actions column, instead clicking anywhere in a row should bring up the Actions bottom sheet.

@biplab1 I have removed the Actions column and now clicking on the row shows the bottom sheet

@sahilshivekar
Copy link
Author

@sahilshivekar No, I'm not talking about that, I'm talking about 2 spacer in a row with the table not padding such that it feels like a card sliding into frame.

@techsavvy185 I have implemented this, check it

@techsavvy185
Copy link
Contributor

@sahilshivekar It actually looks really good to me.

@sahilshivekar sahilshivekar changed the title feat(loan): [MIFOSAC-651] Loan Transaction Table feat(loan): Loan Transaction Table Feb 13, 2026
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.

5 participants