Skip to content

Commit

Permalink
feat(feature:accounts): Migrate accounts module to CMP (#2779)
Browse files Browse the repository at this point in the history
  • Loading branch information
HekmatullahAmin authored Feb 21, 2025
1 parent 7737848 commit 9a8a7f7
Show file tree
Hide file tree
Showing 11 changed files with 858 additions and 11 deletions.
2 changes: 1 addition & 1 deletion feature/accounts/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ plugins {
}

android {
namespace = "com.hekmatullahamin.accounts"
namespace = "org.mifos.mobile.feature.accounts"
}

kotlin {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2025 Mifos Initiative
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file,
You can obtain one at https://mozilla.org/MPL/2.0/.
See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
-->
<resources>

<!-- Filter dialog actions-->
<string name="feature_account_clear_filters">Clear Filters</string>
<string name="feature_account_cancel">Cancel</string>
<string name="feature_account_filter">Filter</string>
<string name="feature_account_select_you_want">Select all filters you want to apply</string>


<!-- Filter dialog checkbox labels-->
<string name="feature_account_active">Active</string>
<string name="feature_account_approval_pending">Approval Pending</string>
<string name="feature_account_closed">Closed</string>
<string name="feature_account_matured">Matured</string>
<string name="feature_account_rejected">Rejected</string>
<string name="feature_account_disburse">Waiting for Disburse</string>
<string name="feature_account_overpaid">Overpaid</string>
<string name="feature_account_approved">Approved</string>
<string name="feature_account_in_arrears">In Arrears</string>
<string name="feature_account_withdrawn">Withdrawn</string>

<!-- tabs-->
<string name="feature_account_savings_account">Savings Account</string>
<string name="feature_account_share_account">Share Account</string>
<string name="feature_account_loan_account">Loan Account</string>
</resources>
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.mobile.feature.accounts.component

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import org.jetbrains.compose.resources.stringResource
import org.mifos.mobile.feature.accounts.model.CheckboxStatus

@Composable
internal fun AccountFilterCheckBox(
checkboxOptions: List<CheckboxStatus>,
updateCheckboxList: (List<CheckboxStatus>) -> Unit,
modifier: Modifier = Modifier,
) {
val lazyColumnState = rememberLazyListState()

var checkBoxList by rememberSaveable { mutableStateOf(checkboxOptions) }

LazyColumn(
state = lazyColumnState,
modifier = modifier,
) {
items(
items = checkBoxList,
key = { it.statusLabel?.hashCode() ?: it.hashCode() },
) { checkBox ->
AccountCheckBoxItem(
checkboxStatus = checkBox,
onCheckedChange = { isChecked ->
checkBoxList = checkBoxList.map {
if (it.statusLabel == checkBox.statusLabel) it.copy(isChecked = isChecked) else it
}
updateCheckboxList(checkBoxList)
},
)
}
}
}

@Composable
private fun AccountCheckBoxItem(
checkboxStatus: CheckboxStatus,
onCheckedChange: (Boolean) -> Unit,
modifier: Modifier = Modifier,
) {
Row(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Start,
) {
Checkbox(
checked = checkboxStatus.isChecked,
onCheckedChange = onCheckedChange,
colors = CheckboxDefaults.colors(
checkedColor = MaterialTheme.colorScheme.primary,
uncheckedColor = MaterialTheme.colorScheme.onSurfaceVariant,
),
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = checkboxStatus.statusLabel?.let { stringResource(it) } ?: "",
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurface,
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.mobile.feature.accounts.component

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import mifos_mobile.feature.accounts.generated.resources.Res
import mifos_mobile.feature.accounts.generated.resources.feature_account_cancel
import mifos_mobile.feature.accounts.generated.resources.feature_account_clear_filters
import mifos_mobile.feature.accounts.generated.resources.feature_account_filter
import mifos_mobile.feature.accounts.generated.resources.feature_account_select_you_want
import org.jetbrains.compose.resources.stringResource
import org.mifos.mobile.core.designsystem.component.MifosTextButton
import org.mifos.mobile.feature.accounts.model.CheckboxStatus

@Composable
internal fun AccountFilterDialog(
title: String,
checkboxOptions: List<CheckboxStatus>,
cancelDialog: () -> Unit,
clearFilter: () -> Unit,
updateCheckboxList: (checkBoxList: List<CheckboxStatus>) -> Unit,
modifier: Modifier = Modifier,
) {
var checkBoxList by remember { mutableStateOf(checkboxOptions) }

AlertDialog(
onDismissRequest = cancelDialog,
modifier = modifier,
text = {
Column {
Text(modifier = Modifier.padding(bottom = 8.dp), text = "Filter $title")

Text(
modifier = Modifier.padding(bottom = 16.dp),
text = stringResource(Res.string.feature_account_select_you_want),
)

AccountFilterCheckBox(
checkboxOptions = checkboxOptions,
updateCheckboxList = { checkBoxList = it },
)

Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
) {
MifosTextButton(
onClick = clearFilter,
text = {
Text(text = stringResource(Res.string.feature_account_clear_filters))
},
)

Row {
MifosTextButton(
onClick = cancelDialog,
text = {
Text(text = stringResource(Res.string.feature_account_cancel))
},
)
MifosTextButton(
onClick = { updateCheckboxList(checkBoxList) },
text = {
Text(text = stringResource(Res.string.feature_account_filter))
},
)
}
}
}
},
confirmButton = {},
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.mobile.feature.accounts.component

@Composable
internal fun AccountsScreenTopBar(
navigateBack: () -> Unit,
onChange: (String) -> Unit,
openFilterDialog: () -> Unit,
closeSearch: () -> Unit,
modifier: Modifier = Modifier,
) {
var query by rememberSaveable(stateSaver = TextFieldValue.Saver) {
mutableStateOf(TextFieldValue(""))
}
var isSearchActive by rememberSaveable { mutableStateOf(false) }

Row(
modifier = modifier
.padding(top = 8.dp)
.fillMaxWidth()
.height(50.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
IconButton(
onClick = {
if (isSearchActive) {
query = TextFieldValue("")
isSearchActive = false
closeSearch.invoke()
} else {
navigateBack.invoke()
}
},
modifier = Modifier.size(40.dp),
) {
Icon(
imageVector = MifosIcons.ArrowBack,
contentDescription = "Back Arrow",
)
}

Box(
Modifier
.fillMaxWidth()
.fillMaxHeight(),
contentAlignment = Alignment.CenterStart,
) {
Text(
text = "Accounts",
style = MaterialTheme.typography.titleLarge,
)

Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.End,
verticalAlignment = Alignment.CenterVertically,
) {
IconButton(
onClick = { isSearchActive = true },
modifier = Modifier.size(40.dp),
) {
Image(
imageVector = MifosIcons.Search,
contentDescription = "Add account",
)
}
IconButton(
onClick = openFilterDialog,
modifier = Modifier.size(40.dp),
) {
Image(
imageVector = MifosIcons.FilterList,
contentDescription = "Add account",
)
}
}

if (isSearchActive) {
MifosSearchTextField(
value = query,
onValueChange = {
query = it
onChange(it.text)
},
modifier = Modifier
.padding(end = 40.dp)
.height(52.dp)
.fillMaxWidth()
.background(color = MaterialTheme.colorScheme.background),
onSearchDismiss = {
query = TextFieldValue("")
closeSearch.invoke()
isSearchActive = false
},
)
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.mobile.feature.accounts.di

import org.koin.core.module.dsl.viewModelOf
import org.koin.dsl.module
import org.mifos.mobile.feature.accounts.viewmodel.AccountsViewModel

val accountsModule = module {
viewModelOf(::AccountsViewModel)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2025 Mifos Initiative
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* See https://github.com/openMF/mobile-mobile/blob/master/LICENSE.md
*/
package org.mifos.mobile.feature.accounts.model

import org.jetbrains.compose.resources.StringResource

internal data class CheckboxStatus(
val statusLabel: StringResource?,
val isChecked: Boolean = false,
)
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ fun NavGraphBuilder.accountsScreenRoute(
navigateBack = navigateBack,
navigateToLoanApplicationScreen = navigateToLoanApplicationScreen,
navigateToSavingsApplicationScreen = navigateToSavingsApplicationScreen,
onItemClick = { accountType, accountId ->
onAccountClicked = { accountType, accountId ->
navigateToAccountDetail(
accountType,
accountId,
Expand Down
Loading

0 comments on commit 9a8a7f7

Please sign in to comment.