@@ -12,33 +12,55 @@ package com.mifos.feature.client.clientLoanAccounts
1212import androidclient.feature.client.generated.resources.Res
1313import androidclient.feature.client.generated.resources.cash_bundel
1414import androidclient.feature.client.generated.resources.client_savings_item
15+ import androidclient.feature.client.generated.resources.feature_client_account_status
1516import androidclient.feature.client.generated.resources.feature_client_dialog_action_ok
17+ import androidclient.feature.client.generated.resources.feature_client_filters
1618import androidclient.feature.client.generated.resources.feature_client_loan_account
19+ import androidclient.feature.client.generated.resources.feature_client_status_active
20+ import androidclient.feature.client.generated.resources.feature_client_status_closed
21+ import androidclient.feature.client.generated.resources.feature_client_status_overpaid
22+ import androidclient.feature.client.generated.resources.feature_client_status_pending
1723import androidclient.feature.client.generated.resources.filter
1824import androidclient.feature.client.generated.resources.search
1925import androidclient.feature.client.generated.resources.wallet
26+ import androidx.compose.foundation.background
27+ import androidx.compose.foundation.layout.Arrangement
28+ import androidx.compose.foundation.layout.Box
2029import androidx.compose.foundation.layout.Column
2130import androidx.compose.foundation.layout.Row
2231import androidx.compose.foundation.layout.Spacer
2332import androidx.compose.foundation.layout.fillMaxSize
2433import androidx.compose.foundation.layout.fillMaxWidth
2534import androidx.compose.foundation.layout.height
2635import androidx.compose.foundation.layout.padding
36+ import androidx.compose.foundation.layout.size
37+ import androidx.compose.foundation.layout.width
38+ import androidx.compose.foundation.layout.wrapContentSize
2739import androidx.compose.foundation.lazy.LazyColumn
2840import androidx.compose.foundation.lazy.items
41+ import androidx.compose.foundation.shape.CircleShape
2942import androidx.compose.material3.AlertDialog
43+ import androidx.compose.material3.Checkbox
3044import androidx.compose.material3.ExperimentalMaterial3Api
45+ import androidx.compose.material3.HorizontalDivider
3146import androidx.compose.material3.Icon
3247import androidx.compose.material3.IconButton
48+ import androidx.compose.material3.MaterialTheme
49+ import androidx.compose.material3.ModalBottomSheet
50+ import androidx.compose.material3.SheetState
3351import androidx.compose.material3.Text
3452import androidx.compose.material3.TextButton
53+ import androidx.compose.material3.rememberModalBottomSheetState
3554import androidx.compose.runtime.Composable
3655import androidx.compose.runtime.getValue
3756import androidx.compose.runtime.remember
57+ import androidx.compose.ui.Alignment
3858import androidx.compose.ui.Modifier
59+ import androidx.compose.ui.draw.clip
3960import androidx.compose.ui.unit.dp
4061import androidx.lifecycle.compose.collectAsStateWithLifecycle
4162import androidx.navigation.NavController
63+ import com.mifos.core.designsystem.icon.MifosIcons
4264import com.mifos.core.designsystem.theme.DesignToken
4365import com.mifos.core.designsystem.theme.MifosTypography
4466import com.mifos.core.ui.components.Actions
@@ -53,6 +75,7 @@ import org.jetbrains.compose.resources.stringResource
5375import org.jetbrains.compose.resources.vectorResource
5476import org.koin.compose.viewmodel.koinViewModel
5577
78+ @OptIn(ExperimentalMaterial3Api ::class )
5679@Composable
5780internal fun ClientLoanAccountsScreenRoute (
5881 navigateBack : () -> Unit ,
@@ -62,6 +85,7 @@ internal fun ClientLoanAccountsScreenRoute(
6285 viewModel : ClientLoanAccountsViewModel = koinViewModel(),
6386) {
6487 val state by viewModel.stateFlow.collectAsStateWithLifecycle()
88+ val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true )
6589
6690 EventsEffect (viewModel.eventFlow) { event ->
6791 when (event) {
@@ -81,6 +105,17 @@ internal fun ClientLoanAccountsScreenRoute(
81105 state = state,
82106 onAction = remember(viewModel) { { viewModel.trySendAction(it) } },
83107 )
108+ if (state.isFilterDialogOpen) {
109+ FilterBottomSheet (
110+ onDismissRequest = { viewModel.trySendAction(ClientLoanAccountsAction .ToggleFilter ) },
111+ sheetState = sheetState,
112+ selectedStatuses = state.selectedStatus,
113+ handleFilterClick = { status ->
114+ viewModel.trySendAction(ClientLoanAccountsAction .HandleFilterClick (status))
115+ },
116+ clearFilters = { viewModel.trySendAction(ClientLoanAccountsAction .ClearFilters ) },
117+ )
118+ }
84119}
85120
86121@Composable
@@ -106,6 +141,7 @@ private fun ClientLoanAccountsScreen(
106141 ClientsAccountHeader (
107142 totalItem = state.loanAccounts.size.toString(),
108143 onAction = onAction,
144+ isFilterActive = state.selectedStatus.isNotEmpty(),
109145 )
110146
111147 if (state.isSearchBarActive) {
@@ -183,7 +219,7 @@ private fun ClientLoanAccountsScreen(
183219 },
184220 )
185221
186- Spacer (modifier = Modifier .height(8 .dp ))
222+ Spacer (modifier = Modifier .height(DesignToken .padding.small ))
187223 }
188224 }
189225 }
@@ -196,6 +232,7 @@ private fun ClientLoanAccountsScreen(
196232@Composable
197233private fun ClientsAccountHeader (
198234 totalItem : String ,
235+ isFilterActive : Boolean ,
199236 onAction : (ClientLoanAccountsAction ) -> Unit ,
200237) {
201238 Row (
@@ -225,13 +262,29 @@ private fun ClientsAccountHeader(
225262 )
226263 }
227264
228- IconButton (
229- onClick = { onAction.invoke(ClientLoanAccountsAction .ToggleFilter ) },
265+ Box (
266+ modifier = Modifier .wrapContentSize(),
267+ contentAlignment = Alignment .Center ,
230268 ) {
231- Icon (
232- painter = painterResource(Res .drawable.filter),
233- contentDescription = null ,
234- )
269+ IconButton (
270+ onClick = { onAction.invoke(ClientLoanAccountsAction .ToggleFilter ) },
271+ ) {
272+ Icon (
273+ painter = painterResource(Res .drawable.filter),
274+ contentDescription = null ,
275+ )
276+ }
277+
278+ if (isFilterActive) {
279+ Box (
280+ modifier = Modifier
281+ .align(Alignment .TopEnd )
282+ .padding(top = 12 .dp, end = 16 .dp)
283+ .size(8 .dp)
284+ .clip(CircleShape )
285+ .background(MaterialTheme .colorScheme.error),
286+ )
287+ }
235288 }
236289 }
237290}
@@ -261,3 +314,90 @@ private fun ClientLoanAccountsDialog(
261314 else -> null
262315 }
263316}
317+
318+ @OptIn(ExperimentalMaterial3Api ::class )
319+ @Composable
320+ private fun FilterBottomSheet (
321+ onDismissRequest : () -> Unit ,
322+ sheetState : SheetState ,
323+ handleFilterClick : (LoanStatusFilter ) -> Unit ,
324+ selectedStatuses : Set <LoanStatusFilter >,
325+ clearFilters : () -> Unit ,
326+ ) {
327+ ModalBottomSheet (
328+ onDismissRequest = onDismissRequest,
329+ sheetState = sheetState,
330+ dragHandle = null ,
331+ containerColor = MaterialTheme .colorScheme.background,
332+ ) {
333+ Column (
334+ modifier = Modifier .padding(DesignToken .padding.large),
335+ ) {
336+ Row (
337+ verticalAlignment = Alignment .CenterVertically ,
338+ horizontalArrangement = Arrangement .SpaceBetween ,
339+ modifier = Modifier
340+ .fillMaxWidth()
341+ .padding(DesignToken .padding.medium),
342+ ) {
343+ Text (
344+ text = stringResource(Res .string.feature_client_filters),
345+ style = MifosTypography .titleLargeEmphasized,
346+ color = MaterialTheme .colorScheme.primary,
347+ )
348+ Row {
349+ IconButton (
350+ onClick = {
351+ clearFilters()
352+ onDismissRequest()
353+ },
354+ ) {
355+ Icon (
356+ imageVector = MifosIcons .Redo ,
357+ contentDescription = " Clear" ,
358+ )
359+ }
360+
361+ IconButton (
362+ onClick = onDismissRequest,
363+ ) {
364+ Icon (
365+ imageVector = MifosIcons .Check ,
366+ contentDescription = " Apply" ,
367+ )
368+ }
369+ }
370+ }
371+ }
372+
373+ HorizontalDivider (Modifier .fillMaxWidth(), thickness = 1.5 .dp)
374+ Column (modifier = Modifier .padding(DesignToken .padding.medium)) {
375+ Text (
376+ text = stringResource(Res .string.feature_client_account_status),
377+ style = MifosTypography .titleMediumEmphasized,
378+ modifier = Modifier .padding(bottom = DesignToken .padding.small),
379+ )
380+
381+ LoanStatusFilter .entries.forEach { status ->
382+ val isChecked = selectedStatuses.contains(status)
383+ val statusLabel = when (status) {
384+ LoanStatusFilter .ACTIVE -> stringResource(Res .string.feature_client_status_active)
385+ LoanStatusFilter .PENDING -> stringResource(Res .string.feature_client_status_pending)
386+ LoanStatusFilter .CLOSED -> stringResource(Res .string.feature_client_status_closed)
387+ LoanStatusFilter .OVERPAID -> stringResource(Res .string.feature_client_status_overpaid)
388+ }
389+
390+ Row (
391+ verticalAlignment = Alignment .CenterVertically ,
392+ ) {
393+ Spacer (modifier = Modifier .width(DesignToken .padding.medium))
394+ Checkbox (
395+ checked = isChecked,
396+ onCheckedChange = { handleFilterClick(status) },
397+ )
398+ Text (text = statusLabel)
399+ }
400+ }
401+ }
402+ }
403+ }
0 commit comments