Skip to content

feat(loan): implement filter in loan accounts screen#2602

Merged
biplab1 merged 2 commits intoopenMF:developmentfrom
piyushs-05:feature/Implemented-filter-loan-accounts-screen
Feb 11, 2026
Merged

feat(loan): implement filter in loan accounts screen#2602
biplab1 merged 2 commits intoopenMF:developmentfrom
piyushs-05:feature/Implemented-filter-loan-accounts-screen

Conversation

@piyushs-05
Copy link
Contributor

@piyushs-05 piyushs-05 commented Feb 5, 2026

Fixes - Jira-#646

Please Add Screenshots If there are any UI changes.

Before
before_filter.mp4
After
filter_after.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 filtering for loan accounts by status (Active, Pending, Waiting for Disbursal, Closed, Overpaid)
    • Filter options available in a bottom sheet with Clear and Apply actions
    • Active filter indicator badge shown on the filter icon when filters are applied
    • New localized labels and section title for filters and account status

@coderabbitai
Copy link

coderabbitai bot commented Feb 5, 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 status-based loan filtering feature: a FilterBottomSheet UI on ClientLoanAccountsScreen, new filter actions/state and LoanStatusFilter enum in the ViewModel, selection-based filtering logic with unfiltered backup, and new localized strings for filter labels and statuses.

Changes

Cohort / File(s) Summary
Loan Accounts Screen UI
feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsScreen.kt
Adds FilterBottomSheet composable, wires ModalBottomSheetState and dialog visibility, binds checkbox selections to actions, replaces fixed spacer with design tokens, and extends ClientsAccountHeader with isFilterActive to show badge.
Loan Accounts ViewModel & Domain
feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsViewModel.kt
Adds unfilteredLoanAccounts and selectedStatus to state, new actions HandleFilterClick and ClearFilters, introduces LoanStatusFilter enum, implements toggle/filtering logic and loan-status predicate extensions, and updates action handling.
String Resources
feature/client/src/commonMain/composeResources/values/strings.xml
Adds localized strings for filters and statuses: feature_client_filters, feature_client_account_status, feature_client_status_active, _pending, _waiting, _closed, _overpaid.

Sequence Diagram

sequenceDiagram
    participant User
    participant Screen as ClientLoanAccountsScreen
    participant ViewModel as ClientLoanAccountsViewModel
    participant State as ClientLoanAccountsState

    User->>Screen: Tap filter icon
    Screen->>Screen: Show FilterBottomSheet
    User->>Screen: Toggle status checkbox
    Screen->>ViewModel: Dispatch HandleFilterClick(status)
    ViewModel->>ViewModel: Toggle selectedStatus<br/>Filter loanAccounts from unfilteredLoanAccounts
    ViewModel->>State: Emit updated selectedStatus & loanAccounts
    State-->>Screen: New state (filtered list)
    Screen->>Screen: Update UI & show active badge

    User->>Screen: Tap Clear button
    Screen->>ViewModel: Dispatch ClearFilters
    ViewModel->>ViewModel: Reset selectedStatus<br/>Restore unfilteredLoanAccounts
    ViewModel->>State: Emit reset state
    State-->>Screen: New state (all loans)
    Screen->>Screen: Update UI, remove badge
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Suggested reviewers

  • biplab1
  • therajanmaurya
  • revanthkumarJ

Poem

🐰
Filters blink where bottom-sheets play,
Checkboxes hop the statuses away.
Active, pending, waiting in a row,
Clear and apply — then off I 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 'feat(loan): implement filter in loan accounts screen' directly and clearly summarizes the main change—adding a filter feature to the loan accounts screen, which aligns with all file modifications.

✏️ 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/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsScreen.kt`:
- Around line 292-298: Rename the typo'd parameter handelFilterClick to
handleFilterClick in the FilterBottomSheet function signature and update all
uses inside that function to refer to handleFilterClick; then update every call
site that passes or references that parameter (e.g., where FilterBottomSheet is
invoked and any related lambdas in ClientListScreen) to use the corrected name
so the symbol matches across definitions and usages.

In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsViewModel.kt`:
- Around line 149-151: The inner variable `val status = loan.status` inside the
filter lambda in ClientLoanAccountsViewModel is shadowing the enclosing function
parameter `status: String`; rename the inner variable (e.g., to `loanStatus`)
and update all uses within the lambda (the filter condition and any comparisons)
so the lambda reads `val loanStatus = loan.status ?: return@filter false`
thereby avoiding shadowing and improving readability while preserving behavior.
🧹 Nitpick comments (2)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsScreen.kt (2)

305-311: Hardcoded strings should use string resources for localization.

The status types and UI labels ("Filters", "Account Status", "Clear", "Apply") are hardcoded. For i18n support, extract these to string resources similar to other strings in the file.

♻️ Suggested approach

Add string resources in the appropriate resources file:

// In strings file
feature_client_filter_title = "Filters"
feature_client_account_status = "Account Status"
feature_client_status_active = "Active"
feature_client_status_pending = "Pending Approval"
// etc.

Then use stringResource():

 Text(
-    text = "Filters",
+    text = stringResource(Res.string.feature_client_filter_title),
     style = MifosTypography.titleLargeEmphasized,
     color = MaterialTheme.colorScheme.primary,
 )

Also applies to: 323-327, 355-358


291-292: Consider making FilterBottomSheet private.

This composable appears to be used only within this screen file. Making it private would be consistent with other composables in this file (e.g., ClientsAccountHeader, ClientLoanAccountsDialog) and prevent unintended external usage.

♻️ Proposed fix
 `@OptIn`(ExperimentalMaterial3Api::class)
 `@Composable`
-fun FilterBottomSheet(
+private fun FilterBottomSheet(

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/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsScreen.kt`:
- Around line 335-353: Replace the hard-coded English contentDescription values
for the two Icon composables with localized string resources: change the Icon
using MifosIcons.Redo (inside the IconButton that calls clearFilters() and
onDismissRequest()) to use stringResource(R.string.clear) and change the Icon
using MifosIcons.Check (inside the IconButton that calls onDismissRequest()) to
use stringResource(R.string.apply); then add corresponding entries ("clear" and
"apply") to the platform strings.xml so accessibility labels are localized.

In
`@feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsViewModel.kt`:
- Around line 138-166: The filter currently compares localized display strings
in handleFilterClick (and similar logic around selectedStatus usage), so replace
the string-based selectedStatus with a Set of stable keys (e.g., a new
LoanStatusFilter enum/sealed class with values like ACTIVE, CLOSED,
PENDING_APPROVAL, WAITING_FOR_DISBURSAL, OVERPAID), update handleFilterClick to
toggle those enum values and filter unfilteredLoanAccounts by matching
loan.status boolean flags against the enum values (set loanAccounts =
filteredList), and update the UI to pass LoanStatusFilter values while rendering
labels via stringResource(...) so localization does not break filtering; also
apply the same enum-based replacements where selectedStatus is read/modified
elsewhere (the other occurrences mentioned).

Copy link
Contributor

@itsPronay itsPronay left a comment

Choose a reason for hiding this comment

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

LGTM, Please get it approved by another team member and then request a merge

@piyushs-05 piyushs-05 force-pushed the feature/Implemented-filter-loan-accounts-screen branch from eb70b97 to 9df4098 Compare February 5, 2026 16:38
@piyushs-05 piyushs-05 force-pushed the feature/Implemented-filter-loan-accounts-screen branch 2 times, most recently from 58af90e to 7e606ba Compare February 5, 2026 17:29
@piyushs-05 piyushs-05 force-pushed the feature/Implemented-filter-loan-accounts-screen branch from 7e606ba to 0259834 Compare February 6, 2026 10:50
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: 0

Caution

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

⚠️ Outside diff range comments (1)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsViewModel.kt (1)

117-124: ⚠️ Potential issue | 🟠 Major

Filter state becomes inconsistent after refresh/search.

When getLoanAccounts succeeds, it resets both loanAccounts and unfilteredLoanAccounts to the full result set, but selectedStatus is not cleared. This means the filter chips will still appear selected in the UI while the list shows unfiltered data.

Either clear selectedStatus here, or re-apply the active filters to the new data.

Proposed fix (clear filters on reload)
                 mutableStateFlow.update {
                     it.copy(
                         loanAccounts = loanAccounts,
                         unfilteredLoanAccounts = loanAccounts,
+                        selectedStatus = emptyList(),
                         dialogState = null,
                         isLoading = false,
                     )
                 }
🧹 Nitpick comments (1)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsViewModel.kt (1)

138-168: Consider using Set<LoanStatusFilter> instead of List<LoanStatusFilter>.

selectedStatus represents a collection of unique selections. Set is semantically more appropriate, guarantees uniqueness without relying on the toggle guard, and makes the intent clearer. The + / - / in operators work identically on Set.

Diff across affected locations
 // In ClientLoanAccountsState:
-    val selectedStatus: List<LoanStatusFilter> = emptyList(),
+    val selectedStatus: Set<LoanStatusFilter> = emptySet(),

 // In clearFilters():
-                selectedStatus = emptyList(),
+                selectedStatus = emptySet(),

@piyushs-05 piyushs-05 force-pushed the feature/Implemented-filter-loan-accounts-screen branch from 0259834 to 8b1be9e Compare February 6, 2026 11:11
@biplab1
Copy link
Contributor

biplab1 commented Feb 6, 2026

@piyushs-05 Please run ./ci-prepush.sh to fix spotless error and push the updates.

@piyushs-05 piyushs-05 force-pushed the feature/Implemented-filter-loan-accounts-screen branch from a6b83db to d0bf28d Compare February 6, 2026 17:03
@biplab1
Copy link
Contributor

biplab1 commented Feb 6, 2026

@piyushs-05 Have you updated the red dot on filter if any of the options are selected? If yes, then update the screen recording.

@piyushs-05
Copy link
Contributor Author

@biplab1 not in this one sir . Just be sure i need to change the colour of filter icon if any of the filters is selected right ?

@piyushs-05 piyushs-05 force-pushed the feature/Implemented-filter-loan-accounts-screen branch 3 times, most recently from 0649670 to 1f87381 Compare February 8, 2026 09:26
@piyushs-05
Copy link
Contributor Author

@biplab1 @therajanmaurya i have updated the code to have red filter dot , please review it sir

@therajanmaurya
Copy link
Member

@biplab1 do final review

@piyushs-05 piyushs-05 force-pushed the feature/Implemented-filter-loan-accounts-screen branch from 1f87381 to bf006d5 Compare February 9, 2026 15:54
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/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsViewModel.kt`:
- Around line 233-239: Change the enum LoanStatusFilter from public to internal
since it's only used within the clientLoanAccounts package; update the
declaration of LoanStatusFilter in ClientLoanAccountsViewModel.kt to use
internal visibility and ensure any usages in ClientLoanAccountsViewModel and
ClientLoanAccountsScreen continue to compile (no external references exist).
🧹 Nitpick comments (4)
feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsViewModel.kt (1)

172-193: Missing blank line between clearFilters() and the extension properties.

There's no blank line separating the clearFilters function (closing brace on line 179) from the isActuallyClosed extension property (line 180), which reduces readability.

Proposed fix
     private fun clearFilters() {
         mutableStateFlow.update {
             it.copy(
                 loanAccounts = it.unfilteredLoanAccounts,
                 selectedStatus = emptySet(),
             )
         }
     }
+
     private val LoanStatusEntity.isActuallyClosed: Boolean
feature/client/src/commonMain/kotlin/com/mifos/feature/client/clientLoanAccounts/ClientLoanAccountsScreen.kt (3)

266-289: Hard-coded padding values for the filter-active badge indicator.

The badge positioning uses magic numbers (top = 12.dp, end = 16.dp, size = 8.dp). Consider extracting these to named constants or using DesignToken values for consistency with the rest of the codebase.


349-354: Clearing filters also dismisses the sheet — is this intended?

The "Clear" button calls both clearFilters() and onDismissRequest(), so tapping it clears all selections and closes the bottom sheet. If the user wants to clear and then reselect different filters, they'd need to reopen the sheet. Consider whether clear should only reset the checkboxes without dismissing.


392-401: Consider adding a click target on the entire row for better UX.

Currently only the Checkbox is clickable. Wrapping the Row with a Modifier.clickable (or using toggleable with Role.Checkbox) would make the label text tappable too, which is both a better UX and an accessibility improvement.

Suggested direction
                 Row(
                     verticalAlignment = Alignment.CenterVertically,
+                    modifier = Modifier
+                        .fillMaxWidth()
+                        .toggleable(
+                            value = isChecked,
+                            role = Role.Checkbox,
+                            onValueChange = { handleFilterClick(status) },
+                        ),
                 ) {
                     Spacer(modifier = Modifier.width(DesignToken.padding.medium))
                     Checkbox(
                         checked = isChecked,
-                        onCheckedChange = { handleFilterClick(status) },
+                        onCheckedChange = null,
                     )
                     Text(text = statusLabel)
                 }

Copy link
Contributor

@biplab1 biplab1 left a comment

Choose a reason for hiding this comment

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

These are the statuses I can see on the web-app:

 "status": {
      "Active": "Active",
      "Approved": "Approved",
      "Closed": "Closed",
      "Closed (obligations met)": "Closed (obligations met)",
      "Closed (rescheduled)": "Closed (rescheduled)",
      "Closed (written off)": "Closed (written off)",
      "Overpaid": "Overpaid",
      "Pending": "Pending",
      "Rejected": "Rejected",
      "Submitted and pending approval": "Submitted and pending approval",
      "Transfer in progress": "Transfer in progress",
      "Transfer on hold": "Transfer on hold",
      "Withdrawn": "Withdrawn"
    },

Ref: https://github.com/openMF/web-app/blob/dev/src/assets/translations/en-US.json
Search: "status": {

@biplab1
Copy link
Contributor

biplab1 commented Feb 10, 2026

@piyushs-05 For now, please restrict the filters to these statuses: Active, Closed, Overpaid, Pending, so that we can merge this PR. Rest of the statuses can be added to the filter later on.

@biplab1 biplab1 enabled auto-merge (squash) February 10, 2026 17:06
@biplab1 biplab1 disabled auto-merge February 10, 2026 17:17
@piyushs-05 piyushs-05 force-pushed the feature/Implemented-filter-loan-accounts-screen branch from 7e3353e to bbaa45b Compare February 11, 2026 11:12
@biplab1 biplab1 changed the title Feature/implemented filter loan accounts screen feat(loan): implement filter loan accounts screen Feb 11, 2026
@biplab1 biplab1 changed the title feat(loan): implement filter loan accounts screen feat(loan): implement filter in loan accounts screen Feb 11, 2026
@sonarqubecloud
Copy link

@biplab1 biplab1 enabled auto-merge (squash) February 11, 2026 11:58
@biplab1 biplab1 merged commit 25b0b56 into openMF:development Feb 11, 2026
5 checks passed
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