Skip to content

Added Functional Filters & Export Button UI to Loan Transactions Screen#2600

Open
gurnoorpannu wants to merge 9 commits intoopenMF:developmentfrom
gurnoorpannu:feature/add-loan-transactions-filters
Open

Added Functional Filters & Export Button UI to Loan Transactions Screen#2600
gurnoorpannu wants to merge 9 commits intoopenMF:developmentfrom
gurnoorpannu:feature/add-loan-transactions-filters

Conversation

@gurnoorpannu
Copy link
Contributor

@gurnoorpannu gurnoorpannu commented Feb 4, 2026

Fixes - MIFOSAC-652

Before:-
image

After:-
image

transactionScreen_filters.mp4

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 transaction filtering: toggles to hide reversed transactions and hide accruals via a filter sheet.
    • Added an Export action in the transactions toolbar to download transaction data.
    • Shows a clear empty state when active filters hide all transactions.
    • Added new UI strings and a filter icon for the transactions toolbar.
    • Added previews to demonstrate filtered and unfiltered views.

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

Adds four string resources and a filter UI for loan transactions: two toggle flags (hideReversed, hideAccruals), FilterBottomSheet, export action placeholder, ViewModel state/mutators, and filtered rendering with empty-state handling.

Changes

Cohort / File(s) Summary
String Resources
feature/loan/src/commonMain/composeResources/values/strings.xml
Added four new strings: feature_loan_filters, feature_loan_hide_reversed, feature_loan_hide_accruals, feature_loan_export.
Drawable
feature/loan/src/commonMain/composeResources/drawable/filter.xml
Added new vector drawable for filter icon.
Loan Transactions Screen
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt
Added filter/export actions in scaffold, showFilterBottomSheet flow, FilterBottomSheet composable with two checkboxes, hideReversed/hideAccruals state and callbacks, filteredTransactions computation, empty-state UI, previews, and export click placeholder.
Loan Transactions ViewModel
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsViewModel.kt
Added SavedStateHandle-derived loanId, new StateFlows _hideReversed/_hideAccruals with public accessors, mutators setHideReversed/setHideAccruals, refresh() method, and changed loadLoanTransaction() visibility to private.

Sequence Diagram(s)

sequenceDiagram
    participant User as User
    participant UI as LoanTransactionsScreen
    participant VM as LoanTransactionsViewModel
    participant Repo as LoanTransactionsRepository

    User->>UI: Open screen / tap filter or export
    UI->>VM: request UI state (transactions, hide flags)
    VM->>Repo: loadLoanTransaction() / refresh()
    Repo-->>VM: return transactions
    VM-->>UI: emit uiState + hide flags
    UI->>UI: compute filteredTransactions (apply hide flags)
    User->>UI: toggle hideReversed / hideAccruals
    UI->>VM: setHideReversed(value) / setHideAccruals(value)
    VM-->>UI: emit updated flags -> UI recomputes filteredTransactions
    User->>UI: tap Export
    UI-->>VM: onExportClick() (placeholder)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Suggested reviewers

  • biplab1

Poem

🐰 I hopped through strings and icons bright,
I toggled reversed and accruals out of sight,
A bottom sheet unfurled with checkboxes in row,
Filters applied, the list learns what to show,
Export sits waiting — ready to go. ✨

🚥 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 accurately describes the main change: adding functional filters and an Export button UI to the Loan Transactions screen, which aligns with the file modifications and PR objectives.

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

✨ Finishing touches
🧪 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: 2

🤖 Fix all issues with AI agents
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt`:
- Around line 19-21: Reorder the Kotlin imports so they follow alphabetical
order to satisfy spotlessKotlinCheck: move
androidclient.feature.loan.generated.resources.feature_loan_export,
feature_loan_hide_accruals, and feature_loan_hide_reversed to appear after
feature_loan_break_down and before feature_loan_id (or run ./gradlew
:feature:loan:spotlessApply to auto-fix); ensure the import block around
LoanTransactionsScreen.kt reflects this ordering.
- Around line 160-168: The filtering in the remember block for
filteredTransactions should use the explicit accrual boolean on Type instead of
string matching; update the hideAccruals branch to check
transaction.type?.accrual == true (keep the Reversed string check as-is), so
modify the lambda that references transactions, hideAccruals and
transaction.type to return false when hideAccruals && transaction.type?.accrual
== true and otherwise preserve the existing logic.
🧹 Nitpick comments (1)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt (1)

177-177: Export functionality is not yet implemented.

The export button currently has a placeholder TODO. If this is intentional for this PR (UI-only as per the title), consider tracking the implementation work.

Would you like me to open an issue to track the export functionality implementation?

@piyushs-05
Copy link
Contributor

Looks good to me

@biplab1
Copy link
Contributor

biplab1 commented Feb 4, 2026

@gurnoorpannu Could you please upload screen recording showing the actions of the filters?

hideAccruals = hideAccruals,
onHideReversedChange = { hideReversed = it },
onHideAccrualsChange = { hideAccruals = it },
onExportClick = { /* TODO: Export functionality */ },
Copy link
Contributor

Choose a reason for hiding this comment

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

Please avoid adding inline comments.

@gurnoorpannu
Copy link
Contributor Author

@gurnoorpannu Could you please upload screen recording showing the actions of the filters?

@biplab1 I have uploaded the recording

@biplab1
Copy link
Contributor

biplab1 commented Feb 5, 2026

@gurnoorpannu Please refer to the Client flow for the placement and implementation of the filters. Also, if possible, place the export button with a icon by its side in the title bar, or if it does not fit, we can have only the icon. Choose an appropriate icon from the material theme icons.

We should think mobile first as much as possible rather than imitating the web-app.

@Kartikey15dem
Copy link
Contributor

@gurnoorpannu
Copy link
Contributor Author

@biplab1 sir , I have implemented requested ui changes and updated the screen recording for the ui

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsViewModel.kt (1)

22-68: ⚠️ Potential issue | 🟠 Major

Missing initial data load.

The ViewModel lacks an init block to trigger loadLoanTransaction() on creation. Without this, the screen will remain in the loading/empty state until refresh() is explicitly called from the UI.

🔧 Proposed fix to add initial load
 class LoanTransactionsViewModel(
     private val repository: LoanTransactionsRepository,
     savedStateHandle: SavedStateHandle,
 ) : ViewModel() {

     val loanId = savedStateHandle.toRoute<LoanTransactionScreenRoute>().loanAccountNumber

     private val _loanTransactionsUiState =
         MutableStateFlow<LoanTransactionsUiState>(LoanTransactionsUiState.ShowProgressBar)
     val loanTransactionsUiState: StateFlow<LoanTransactionsUiState> get() = _loanTransactionsUiState

+    init {
+        refresh()
+    }
+
     private val _hideReversed = MutableStateFlow(false)
🤖 Fix all issues with AI agents
In
`@feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsScreen.kt`:
- Around line 101-122: The export icon's onExportClick in the
LoanTransactionsScreen call is a no-op; update the call site to invoke the
ViewModel export action (e.g., call viewModel.exportTransactions() or the
appropriate export method in LoanTransactionsViewModel) or conditionally
disable/hide the export button until implemented; locate the
LoanTransactionsScreen composable invocation and replace the onExportClick = {
/* UI-only, no logic */ } with onExportClick = { viewModel.exportTransactions(/*
params if needed */) } or wire it to the existing export method and ensure the
ViewModel exposes a suspend/flow-safe exportTransactions function to handle the
export flow.
🧹 Nitpick comments (2)
feature/loan/src/commonMain/kotlin/com/mifos/feature/loan/loanTransaction/LoanTransactionsViewModel.kt (2)

47-51: Potential for concurrent collectors on repeated refresh() calls.

Each refresh() call launches a new coroutine that collects from the repository flow. If the flow is long-lived (e.g., observes database changes) or refresh() is called rapidly, multiple collectors may run concurrently, causing redundant emissions and potential state overwrites.

Consider canceling the previous job before starting a new one:

♻️ Proposed fix using a tracked Job
+import kotlinx.coroutines.Job
+
 class LoanTransactionsViewModel(
     private val repository: LoanTransactionsRepository,
     savedStateHandle: SavedStateHandle,
 ) : ViewModel() {
+
+    private var loadJob: Job? = null

     // ... existing code ...

     fun refresh() {
+        loadJob?.cancel()
-        viewModelScope.launch {
+        loadJob = viewModelScope.launch {
             loadLoanTransaction()
         }
     }

33-45: Filter state is not persisted to SavedStateHandle.

The filter flags (_hideReversed, _hideAccruals) are stored in plain MutableStateFlow and will be lost on process death or configuration changes. Per the PR discussion recommending alignment with the Client list pattern, consider persisting these flags via SavedStateHandle to preserve user selections.

♻️ Proposed approach using SavedStateHandle
-    private val _hideReversed = MutableStateFlow(false)
-    val hideReversed: StateFlow<Boolean> get() = _hideReversed
-
-    private val _hideAccruals = MutableStateFlow(false)
-    val hideAccruals: StateFlow<Boolean> get() = _hideAccruals
-
-    fun setHideReversed(value: Boolean) {
-        _hideReversed.value = value
-    }
-
-    fun setHideAccruals(value: Boolean) {
-        _hideAccruals.value = value
-    }
+    private companion object {
+        const val KEY_HIDE_REVERSED = "hideReversed"
+        const val KEY_HIDE_ACCRUALS = "hideAccruals"
+    }
+
+    val hideReversed: StateFlow<Boolean> = savedStateHandle.getStateFlow(KEY_HIDE_REVERSED, false)
+    val hideAccruals: StateFlow<Boolean> = savedStateHandle.getStateFlow(KEY_HIDE_ACCRUALS, false)
+
+    fun setHideReversed(value: Boolean) {
+        savedStateHandle[KEY_HIDE_REVERSED] = value
+    }
+
+    fun setHideAccruals(value: Boolean) {
+        savedStateHandle[KEY_HIDE_ACCRUALS] = value
+    }

@gurnoorpannu
Copy link
Contributor Author

@biplab1 any further changes needed on this one?

@gurnoorpannu gurnoorpannu requested a review from biplab1 February 12, 2026 17:37
@sonarqubecloud
Copy link

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.

4 participants